27 #include "hugin_config.h"
31 #include <wx/window.h>
41 #include <vigra/cornerdetection.hxx>
42 #include <vigra/localminmax.hxx>
61 ImageCache::EntryPtr eptr = ImageCache::getInstance().getSmallImage(i1.getFilename());
63 vigra::BImage leftImg(eptr->get8BitImage()->size());
65 vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> > ga;
69 double scale = i1.getSize().width() / (double) leftImg.width();
74 vigra::BImage leftCorners(leftImg.size());
75 vigra::FImage leftCornerResponse(leftImg.size());
97 vigra::Threshold<double, double>(
101 destImage(leftCorners), std::multiplies<float>());
124 for (
unsigned int x=0; x < (
unsigned int)leftImg.width(); x++ ) {
125 for (
unsigned int y=0; y < (
unsigned int)leftImg.height(); y++) {
126 if (leftCorners(x,y) > 0) {
130 if ( img2x > border && img2x < i2.
getWidth() - border
131 && img2y > border && img2y < i2.
getHeight() - border )
152 if(srcImg.getExifMake() == anchor.getExifMake() &&
153 srcImg.getExifModel() == anchor.getExifModel())
157 if(fabs(redBalanceAnchor)<1e-2)
161 if(fabs(blueBalanceAnchor)<1e-2)
165 redBal=fabs(srcImg.getExifRedBalance()/redBalanceAnchor);
166 blueBal=fabs(srcImg.getExifBlueBalance()/blueBalanceAnchor);
177 srcImg.setWhiteBalanceRed(redBal);
178 srcImg.setWhiteBalanceBlue(blueBal);
185 dlg.CenterOnParent();
186 int ret = dlg.ShowModal();
193 srcImg.setCropFactor(1);
212 if (iccName.compare(0, 4,
"sRGB") == 0)
243 for (
const auto& filename:
files)
249 srcImg.setFilename(filename);
252 vigra::ImageImportInfo
info(filename.c_str());
253 if(
info.width()==0 ||
info.height()==0)
255 wxMessageBox(wxString::Format(_(
"Could not decode image:\n%s\nAbort"), fname.c_str()), _(
"Unsupported image file format"));
259 const std::string pixelType=
info.getPixelType();
261 if (pixelType ==
"BILEVEL")
263 wxMessageBox(wxString::Format(_(
"File \"%s\" is a black/white image.\nHugin does not support this image type. Skipping this image.\nConvert image to grayscale image and try loading again."), fname.c_str()),
264 _(
"Warning"), wxOK|wxICON_EXCLAMATION);
269 const int bands =
info.numBands();
270 const int extraBands =
info.numExtraBands();
271 if (bands != 1 && bands != 3 && !(bands == 2 && extraBands == 1) && !(bands == 4 && extraBands == 1))
273 wxMessageBox(wxString::Format(_(
"Hugin supports only grayscale and RGB images (without and with alpha channel).\nBut file \"%s\" has %d channels and %d extra channels (probably alpha channels).\nHugin does not support this image type. Skipping this image.\nConvert this image to grayscale or RGB image and try loading again."), fname.c_str(), bands, extraBands),
274 _(
"Warning"), wxOK | wxICON_EXCLAMATION);
283 wxString s(_(
"Hugin supports only grayscale or RGB images (without and with alpha channel)."));
287 s.Append(wxString::Format(_(
"File \"%s\" is a grayscale image, but other images in project are color images."), fname.c_str()));
291 s.Append(wxString::Format(_(
"File \"%s\" is a color image, but other images in project are grayscale images."), fname.c_str()));
294 s.Append(_(
"Hugin does not support this mixing. Skipping this image.\nConvert this image to grayscale or RGB image respectively and try loading again."));
295 wxMessageBox(s, _(
"Warning"), wxOK | wxICON_EXCLAMATION);
298 if((pixelType==
"UINT8") || (pixelType==
"UINT16") || (pixelType==
"INT16"))
309 if (newICCProfileDesc.empty())
311 warning = wxString::Format(_(
"File \"%s\" has no embedded icc profile, but other images in project have profile \"%s\" embedded."), fname.c_str(), wxString(pano.
getICCProfileDesc().c_str(), wxConvLocal).c_str());
317 warning = wxString::Format(_(
"File \"%s\" has icc profile \"%s\" embedded, but other images in project have no embedded color profile."), fname.c_str(), wxString(newICCProfileDesc.c_str(), wxConvLocal).c_str());
321 warning = wxString::Format(_(
"File \"%s\" has icc profile \"%s\" embedded, but other images in project have color profile \"%s\" embedded."), fname.c_str(), wxString(newICCProfileDesc.c_str(), wxConvLocal).c_str(), wxString(pano.
getICCProfileDesc().c_str(), wxConvLocal).c_str());
324 warning.Append(wxT(
"\n"));
325 warning.Append(_(
"Hugin expects all images in the same color profile.\nPlease convert all images to same color profile and try again."));
326 wxMessageBox(warning, _(
"Warning"), wxOK | wxICON_EXCLAMATION);
333 if (!
info.getICCProfile().empty())
343 catch(std::exception & e)
345 std::cerr <<
"ERROR: caught exception: " << e.what() << std::endl;
346 std::cerr <<
"Could not get pixel type for file " << filename << std::endl;
347 wxMessageBox(wxString::Format(_(
"Could not decode image:\n%s\nAbort"), fname.c_str()), _(
"Unsupported image file format"));
355 if (srcImg.getCropFactor()<0.1)
358 ok=(srcImg.getExifFocalLength()>0 && srcImg.getCropFactor()>0.1);
364 const bool ignoreFovRectilinear = wxConfigBase::Get()->Read(wxT(
"/General/IgnoreFovRectilinearOnAdd"), 1l) == 1l;
371 srcImg.setProjection(static_cast<HuginBase::BaseSrcPanoImage::Projection>(
m_preferredLensType));
381 if ( other.getSize() == srcImg.getSize() &&
382 other.getExifModel() == srcImg.getExifModel() &&
383 other.getExifMake() == srcImg.getExifMake() &&
384 other.getExifFocalLength() == srcImg.getExifFocalLength()
388 if (srcImg.getCropFactor() <= 0.1)
391 srcImg.setCropFactor(other.getCropFactor());
402 int matchingLensNr=-1;
408 srcImg.setCropFactor(1);
416 DEBUG_INFO(
"Image: " << fn.mb_str() <<
" has disappeared, skipping...");
424 bool set_exposure =
false;
427 if (other.getExifFocalLength()>0) {
428 if (other.getSize() == srcImg.getSize()
429 && other.getExifModel() == srcImg.getExifModel()
430 && other.getExifMake() == srcImg.getExifMake()
431 && other.getExifFocalLength() == srcImg.getExifFocalLength()
433 && other.getCropFactor() == srcImg.getCropFactor()
443 if (other.getSize() == srcImg.getSize() )
456 if (matchingLensNr != -1)
470 if (oldImgCount == 0)
473 message = _(
"Hugin has image stacks detected in the added images and will assign corresponding stack numbers to the images.");
477 message = _(
"Hugin has image stacks detected in the whole project. Stack numbers will be re-assigned on base of this detection. Existing stack assignments will be overwritten.");
479 message.append(wxT(
"\n"));
480 message.append(_(
"Should the position of images in each stack be linked?"));
481 wxMessageDialog dialog(wxGetActiveWindow(), message,
487 wxICON_EXCLAMATION | wxYES_NO | wxCANCEL);
488 dialog.SetExtendedMessage(_(
"When shooting bracketed image stacks from a sturdy tripod the position of the images in each stack can be linked to help Hugin to process the panorama. But if the images in each stack require a fine tune of the position (e. g. when shooting hand held), then don't link the position."));
489 if (oldImgCount == 0)
491 dialog.SetYesNoCancelLabels(_(
"Link position"), _(
"Don't link position"), _(
"Don't assign stacks"));
495 dialog.SetYesNoCancelLabels(_(
"Link position"), _(
"Don't link position"), _(
"Keep existing stacks"));
497 switch (dialog.ShowModal())
510 bool hasStacks =
false;
513 if (pano.
getImage(i).StackisLinked())
519 wxConfigBase* config = wxConfigBase::Get();
520 bool showExposureWarning = config->Read(wxT(
"/ShowExposureWarning"), 1l) == 1l;
524 wxXmlResource::Get()->LoadDialog(&dlg, NULL, wxT(
"warning_exposure_dlg"));
525 if (dlg.ShowModal() == wxID_OK)
527 if (XRCCTRL(dlg,
"dont_show_again_checkbox", wxCheckBox)->GetValue())
529 config->Write(wxT(
"/ShowExposureWarning"), 0l);
533 config->Write(wxT(
"/ShowExposureWarning"), 1l);
571 bool autopanoSiftFile=
false;
574 for (
unsigned int i = 0; i < nImg; i++) {
576 while (! fname.FileExists()){
578 if (basedir != wxT(
"")) {
581 DEBUG_DEBUG(
"Old filename, without path): " << fn);
585 fname.AssignDir(basedir);
586 fname.SetFullName(newname);
587 DEBUG_TRACE(
"filename with new path: " << fname.GetFullPath().mb_str(wxConvLocal));
588 if (fname.FileExists()) {
590 DEBUG_TRACE(
"New filename set: " << fname.GetFullPath().mb_str(wxConvLocal));
596 wxMessageBox(wxString::Format(_(
"The project file \"%s\" refers to image \"%s\" which was not found.\nPlease manually select the correct image."),
filename, fname.GetFullPath()), _(
"Image file not found"));
598 if (basedir == wxT(
"")) {
599 basedir = fname.GetPath();
603 wxFileDialog dlg(wxGetActiveWindow(), wxString::Format(_(
"Select image %s"), fname.GetFullName()),
604 basedir, fname.GetFullName(),
606 dlg.SetDirectory(basedir);
607 if (dlg.ShowModal() == wxID_OK) {
610 basedir = dlg.GetDirectory();
611 DEBUG_INFO(
"basedir is: " << basedir.mb_str(wxConvLocal));
618 fname.Assign(dlg.GetPath());
623 vigra::ImageImportInfo imginfo(srcImg.getFilename().c_str());
624 if (srcImg.getSize() != imginfo.size()) {
626 srcImg.
resize(imginfo.size(), &vars[i]);
629 double hfov = pano.
getImage(i).getHFOV();
631 && hfov >= 180 && autopanoSiftFile ==
false)
633 autopanoSiftFile =
true;
642 autopanoSiftRefImg = srcImg;
648 if (autopanoSiftFile)
651 srcImg.setHFOV(autopanoSiftRefImg.getHFOV());
657 pano.
setNrOfBands(imginfo.numBands() - imginfo.numExtraBands());
664 for (
unsigned i = 0; i < nImg; ++i)
666 if (!vars[i].empty())
673 for (
size_t i = 0; i < imgSetLens.size(); ++i)
676 if (imgLens.size()>1)
678 HuginBase::UIntSet::const_iterator it = imgLens.begin();
679 const size_t img1 = *it;
683 pano.linkImageVariableProjection(img1, *it);
685 }
while (it != imgLens.end());
700 int bad_cp_count = 0;
701 for (HuginBase::CPVector::const_iterator it = oldCPs.begin();
702 it != oldCPs.end(); ++it)
707 if (0 > point.
x1 || point.
x1 > img1.getSize().x ||
708 0 > point.
y1 || point.
y1 > img1.getSize().y ||
709 0 > point.
x2 || point.
x2 > img2.getSize().x ||
710 0 > point.
y2 || point.
y2 > img2.getSize().y)
715 goodCPs.push_back(point);
719 if (bad_cp_count > 0)
721 wxString errMsg = wxString::Format(_(
"%d invalid control point(s) found.\n\nPress OK to remove."), bad_cp_count);
722 wxMessageBox(errMsg, _(
"Error Detected"), wxICON_ERROR);
743 wxConfigBase* config = wxConfigBase::Get();
786 opts.
verdandiOptions = config->Read(wxT(
"/VerdandiDefaultArgs"), wxEmptyString).mb_str(wxConvLocal);
802 wxConfigBase* config = wxConfigBase::Get();
806 wxString path = config->Read(wxT(
"actualPath"), wxT(
""));
807 wxFileDialog dlg(wxGetActiveWindow(), _(
"Add images"),
810 dlg.SetDirectory(path);
814 if (config->HasEntry(wxT(
"lastImageType"))){
815 img_ext = config->Read(wxT(
"lastImageType")).c_str();
817 if (img_ext == wxT(
"all images"))
818 dlg.SetFilterIndex(0);
819 else if (img_ext == wxT(
"jpg"))
820 dlg.SetFilterIndex(1);
821 else if (img_ext == wxT(
"tiff"))
822 dlg.SetFilterIndex(2);
823 else if (img_ext == wxT(
"png"))
824 dlg.SetFilterIndex(3);
825 else if (img_ext == wxT(
"hdr"))
826 dlg.SetFilterIndex(4);
827 else if (img_ext == wxT(
"exr"))
828 dlg.SetFilterIndex(5);
829 else if (img_ext == wxT(
"all files"))
830 dlg.SetFilterIndex(6);
831 DEBUG_INFO (
"Image extention: " << img_ext.mb_str(wxConvLocal) );
834 if (dlg.ShowModal() == wxID_OK) {
836 wxArrayString Pathnames;
837 dlg.GetPaths(Pathnames);
842 config->Write(wxT(
"/actualPath"), wxPathOnly(Pathnames[0]));
844 config->Write(wxT(
"/actualPath"), dlg.GetDirectory());
846 DEBUG_INFO ( wxString::Format(wxT(
"img_ext: %d"), dlg.GetFilterIndex()).mb_str(wxConvLocal) );
848 switch ( dlg.GetFilterIndex() ) {
849 case 0: config->Write(wxT(
"lastImageType"), wxT(
"all images"));
break;
850 case 1: config->Write(wxT(
"lastImageType"), wxT(
"jpg"));
break;
851 case 2: config->Write(wxT(
"lastImageType"), wxT(
"tiff"));
break;
852 case 3: config->Write(wxT(
"lastImageType"), wxT(
"png"));
break;
853 case 4: config->Write(wxT(
"lastImageType"), wxT(
"hdr"));
break;
854 case 5: config->Write(wxT(
"lastImageType"), wxT(
"exr"));
break;
855 case 6: config->Write(wxT(
"lastImageType"), wxT(
"all files"));
break;
861 for (
unsigned int i=0; i< Pathnames.GetCount(); i++) {
863 vigra::ImageImportInfo inf(filename.c_str());
865 img.setFilename(filename);
868 img.applyEXIFValues();
886 if (nOldImg != nNewImg) {
887 wxString errMsg = wxString::Format(_(
"Error, template expects %d images,\ncurrent project contains %d images\n"), nNewImg, nOldImg);
888 wxMessageBox(errMsg, _(
"Could not apply template"), wxICON_ERROR);
894 for (
unsigned int i = 0; i < nNewImg; i++) {
901 DEBUG_DEBUG(
"apply template fn:" << newSrcImg.getFilename() <<
" real fn: " << oldSrcImg.getFilename());
902 newSrcImg.setFilename(oldSrcImg.getFilename());
903 if (oldSrcImg.getSize() != newSrcImg.getSize()) {
905 newSrcImg.
resize(oldSrcImg.getSize(), &(vars[i]));
912 for (
unsigned int i = 0; i < nNewImg; ++i)
914 if (!vars[i].empty())
929 wxMessageBox(_(
"Error loading project file"), _(
"Could not apply template"), wxICON_ERROR);
938 std::cout <<
"run python script: " << m_scriptFile.c_str() << std::endl;
941 "HuginBase::Panorama*" , &pano ) ;
944 wxMessageBox(wxString::Format(wxT(
"Script returned %d"),success),_(
"Result"), wxICON_INFORMATION);
945 std::cout <<
"Python interface returned " << success << endl ;
std::string GetICCDesc(const vigra::ImageImportInfo::ICCProfile &iccProfile)
returns description of given icc profile
bool FileExists(const std::string &filename)
checks if file exists
void imageChanged(unsigned int imgNr)
mark image for change notification.
std::vector< UIntSet > UIntSetVector
const int getNrOfBands() const
return number of bands of first image (without alpha channel) so it can be 1 for grayscale or 3 for r...
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
#define HUGIN_NONA_CROPPEDIMAGES
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
void setMemento(const PanoramaMemento &memento)
set the internal state
unsigned int getPartNumber(unsigned int imageNr) const
Get a part number from an image number.
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
std::vector< std::string > files
BlendingMechanism
blenders
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
#define HUGIN_JPEG_QUALITY
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Somewhere to specify what variables belong to what.
std::string GetICCProfileNameChecked(const std::string &iccName)
return name of icc profile with same checks for comparision
Functor class to compare two objects with the "Alphanum Algorithm".
std::string enfuseOptions
include file for the hugin project
#define HUGIN_GUI_SORT_NEW_IMG_ON_ADD
const CPVector & getCtrlPoints() const
get all control point of this Panorama
int getHeight() const
Get the height of the image in pixels.
std::string outputImageTypeCompression
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
const std::string filename
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
represents a control point
virtual void updateVariables(const VariableMapVector &vars)
Set the variables.
unsigned int colorReferenceImage
bool readProjectionFromDB(const bool ignoreFovRectilinear=true)
tries to read projection and crop area from lens database you need to call SrcPanoImage::readEXIF bef...
const bool markAsOptimized
std::set< unsigned int > UIntSet
std::vector< VariableMap > VariableMapVector
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
empirical model of response
C++ call interface to hpi.
UIntSetVector getPartsSet() const
return a vector which contains a HuginBase::UIntSet for each group with the corresponding images numb...
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
bool loadPTScript(std::istream &i, int &ptoVersion, const std::string &prefix="")
load a Hugin file
some definitions to work with optimizer master switches
std::size_t getNrOfImages() const
number of images.
void combineTwoImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void setCtrlPoints(const CPVector &points)
set all control points (Ippei: Is this supposed to be 'add' method?)
vigra_ext::Interpolator interpolator
int callhpi(const char *plugin_name, int argc,...)
simplified call interface to the Python Plugin facility.
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
int getWidth() const
Get the width of the image in pixels.
bool getLensDataFromUser(wxWindow *parent, HuginBase::SrcPanoImage &srcImg)
void resize(const vigra::Size2D &size, VariableMap *potentialLinkedVars)
"resize" image, adjusts all distortion coefficients for usage with a source image of size size potent...
ImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
#define HUGIN_NONA_INTERPOLATOR
#define HUGIN_TIFF_COMPRESSION
wxwindows specific panorama commands
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
#define HUGIN_NONA_USEGPU
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
#define HUGIN_HDRMERGE_ARGS
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
#define HUGIN_LDR_OUTPUT_FORMAT
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
const std::string getICCProfileDesc() const
return description of icc profile used for pano
std::string tiffCompression
std::string verdandiOptions
Same as above, but use a non const panorama.
PanoramaMemento getMemento() const
get the internal state
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
include file for the hugin project
std::string enblendOptions
void setICCProfileDesc(const std::string &newDesc)
sets the icc profile description for check of same profile
const PanoramaOptions & getOptions() const
returns the options for this panorama
HDRMergeType hdrMergeMode
void markAsOptimized(bool optimized=true)
Memento class for a Panorama object.
void updatePartNumbers()
Update the part numbers, call this when the panorama changes.
#define HUGIN_ENFUSE_ARGS
bool readEXIF()
try to fill out information about the image, by examining the exif data
std::string outputImageType
void setSize(vigra::Size2D val)
Set the image size in pixels.
void update()
Update part numbers for each variable group.
std::vector< ControlPoint > CPVector
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
void applyColorBalanceValue(HuginBase::SrcPanoImage &srcImg, HuginBase::Panorama &pano)
static void info(const char *fmt,...)
std::string hdrmergeOptions
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
void setNrOfBands(const int nrBands)
sets the number of bands
Interpolator
enum with all interpolation methods
HuginBase::SrcPanoImage GetSrcImage()
All variables of a source image.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
void switchParts(unsigned int ImageNr, unsigned int partNr)
switch a given image to a different part number.
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
#define HUGIN_ENBLEND_ARGS
const double getMaxExposureDifference() const
returns the maximum exposure value difference of all images in the project
#define HUGIN_DEFAULT_BLENDER
bool readCropfactorFromDB()
tries to read cropfactor from lens database you need to call SrcPanoImage::readEXIF before to fill so...
void reset()
clear the internal state.
BlendingMechanism blendMode
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
double outputExposureValue
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().