Hugin trunk 0.1
Loading...
Searching...
No Matches
wxPanoCommand.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
27#include "hugin_config.h"
28
29#include "panoinc_WX.h"
30#include "panoinc.h"
31#include <wx/window.h>
32#include "wxPlatform.h"
33#include "LensTools.h"
34
35#include "wxImageCache.h"
36#include "platform.h"
37#include "wxPanoCommand.h"
38#include "HFOVDialog.h"
39#include "wxutils.h"
41
42#include <vigra/cornerdetection.hxx>
43#include <vigra/localminmax.hxx>
45
47
48#ifdef HUGIN_HSI
50#endif
51
52namespace PanoCommand
53{
54
56{
57 // TODO: rewrite, and use same function for modular image creation.
58 const HuginBase::SrcPanoImage & i1 = pano.getImage(img1);
59 const HuginBase::SrcPanoImage & i2 = pano.getImage(img1);
60
61 // run both images through the harris corner detector
62 ImageCache::EntryPtr eptr = ImageCache::getInstance().getSmallImage(i1.getFilename());
63
64 vigra::BImage leftImg(eptr->get8BitImage()->size());
65
66 vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> > ga;
67 vigra::copyImage(srcImageRange(*(eptr->get8BitImage()), ga ),
68 destImage(leftImg));
69
70 double scale = i1.getSize().width() / (double) leftImg.width();
71
72 //const vigra::BImage & leftImg = ImageCache::getInstance().getPyramidImage(
73 // i1.getFilename(),1);
74
75 vigra::BImage leftCorners(leftImg.size());
76 vigra::FImage leftCornerResponse(leftImg.size());
77
78 // empty corner image
79 leftCorners.init(0);
80
81 DEBUG_DEBUG("running corner detector threshold: " << cornerThreshold << " scale: " << scale );
82
83 // find corner response at scale scale
84 vigra::cornerResponseFunction(srcImageRange(leftImg),
85 destImage(leftCornerResponse),
86 scale);
87
88 // saveScaledImage(leftCornerResponse,"corner_response.png");
89 DEBUG_DEBUG("finding local maxima");
90 // find local maxima of corner response, mark with 1
91 vigra::localMaxima(vigra::srcImageRange(leftCornerResponse), vigra::destImage(leftCorners), 255);
92
93// exportImage(srcImageRange(leftCorners), vigra::ImageExportInfo("c:/corner_response_maxima.png"));
94
95 DEBUG_DEBUG("thresholding corner response");
96 // threshold corner response to keep only strong corners (above 400.0)
97 vigra::transformImage(vigra::srcImageRange(leftCornerResponse), vigra::destImage(leftCornerResponse),
98 vigra::Threshold<double, double>(
99 cornerThreshold, DBL_MAX, 0.0, 1.0));
100
101 vigra::combineTwoImages(srcImageRange(leftCorners), srcImage(leftCornerResponse),
102 destImage(leftCorners), std::multiplies<float>());
103
104// exportImage(srcImageRange(leftCorners), vigra::ImageExportInfo("c:/corner_response_threshold.png"));
105
106 // create transform from img1 -> sphere
109
112 opts.setHFOV(360);
113 opts.setWidth(360);
114 opts.setVFOV(180);
115
116 img1ToSphere.createInvTransform(pano, img1, opts);
117 sphereToImg2.createTransform(pano, img2, opts);
118
119
120 int border = 5;
121 double sphx, sphy;
122 double img2x, img2y;
123 // need to scale the images.
124 // sample grid on img1 and try to add ctrl points
125 for (unsigned int x=0; x < (unsigned int)leftImg.width(); x++ ) {
126 for (unsigned int y=0; y < (unsigned int)leftImg.height(); y++) {
127 if (leftCorners(x,y) > 0) {
128 img1ToSphere.transformImgCoord(sphx, sphy, scale*x, scale*y);
129 sphereToImg2.transformImgCoord(img2x, img2y, sphx, sphy);
130 // check if it is inside..
131 if ( img2x > border && img2x < i2.getWidth() - border
132 && img2y > border && img2y < i2.getHeight() - border )
133 {
134 // add control point
136 pano.addCtrlPoint(p);
137 }
138 }
139 }
140 }
142 return true;
143}
144
146{
147 double redBal=1;
148 double blueBal=1;
149 if(pano.getNrOfImages()>=1)
150 {
152 // use EXIF Red/BlueBalance data only if image and anchor image are from the same camera
153 if(srcImg.getExifMake() == anchor.getExifMake() &&
154 srcImg.getExifModel() == anchor.getExifModel())
155 {
156 double redBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifRedBalance();
157 double blueBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifBlueBalance();
158 if(fabs(redBalanceAnchor)<1e-2)
159 {
161 };
162 if(fabs(blueBalanceAnchor)<1e-2)
163 {
165 };
166 redBal=fabs(srcImg.getExifRedBalance()/redBalanceAnchor);
167 blueBal=fabs(srcImg.getExifBlueBalance()/blueBalanceAnchor);
168 if(redBal<1e-2)
169 {
170 redBal=1;
171 };
172 if(blueBal<1e-2)
173 {
174 blueBal=1;
175 };
176 };
177 }
178 srcImg.setWhiteBalanceRed(redBal);
179 srcImg.setWhiteBalanceBlue(blueBal);
180};
181
183{
184 // display lens dialog
186 dlg.CenterOnParent();
187 int ret = dlg.ShowModal();
188 if (ret == wxID_OK)
189 {
190 // assume a cancel dialog.
191 srcImg = dlg.GetSrcImage();
192 if (dlg.GetCropFactor() <= 0)
193 {
194 srcImg.setCropFactor(1);
195 }
196 return true;
197 }
198 else {
199 return false;
200 }
201}
202
204std::string GetICCProfileNameChecked(const std::string& iccName)
205{
206 // if no icc profile given assume sRGB profile
207 if (iccName.empty())
208 {
209 return "sRGB";
210 };
211 // all profiles starting with sRGB are assumed the same
212 // even if there small? differences between them
213 if (iccName.compare(0, 4, "sRGB") == 0)
214 {
215 return "sRGB";
216 };
217 // otherwise return full name
218 return iccName;
219}
221{
222 // check if the files should be sorted by date
223 const long sort = wxConfigBase::Get()->Read("General/SortNewImgOnAdd", HUGIN_GUI_SORT_NEW_IMG_ON_ADD);
224
225 switch (sort) {
226 case 1:
227 // sort by filename
228 std::sort(files.begin(), files.end(), doj::alphanum_less());
229 break;
230 case 2:
231 // sort by date
232 std::sort(files.begin(), files.end(), FileIsNewer());
233 break;
234 default:
235 // no or unknown sort method
236 break;
237 }
238
241 const size_t oldImgCount = pano.getNrOfImages();
242
243 // load additional images...
244 for (const auto& filename: files)
245 {
247 wxString fname(filename.c_str(), HUGIN_CONV_FILENAME);
248
249 // try to read settings automatically.
250 srcImg.setFilename(filename);
251 try
252 {
253 vigra::ImageImportInfo info(filename.c_str());
254 if(info.width()==0 || info.height()==0)
255 {
256 hugin_utils::HuginMessageBox(wxString::Format(_("Could not decode image:\n%s\nAbort"), fname.c_str()), _("Hugin"), wxOK | wxICON_HAND, wxGetActiveWindow());
257 return false;
258 };
259 srcImg.setSize(info.size());
260 const std::string pixelType=info.getPixelType();
261 // refuse black/white images
262 if (pixelType == "BILEVEL")
263 {
264 hugin_utils::HuginMessageBox(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()),
266 continue;
267 }
268 // check if images is grayscale or RGB image, maybe with alpha channel
269 // reject CMYK or other images
270 const int bands = info.numBands();
271 const int extraBands = info.numExtraBands();
272 if (bands != 1 && bands != 3 && !(bands == 2 && extraBands == 1) && !(bands == 4 && extraBands == 1))
273 {
274 hugin_utils::HuginMessageBox(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),
276 continue;
277 };
278 if (pano.getNrOfImages() == 0)
279 {
280 pano.setNrOfBands(bands - extraBands);
281 };
282 if (pano.getNrOfBands() != bands - extraBands)
283 {
284 wxString s(_("Hugin supports only grayscale or RGB images (without and with alpha channel)."));
285 s.Append("\n");
286 if (pano.getNrOfBands() == 3)
287 {
288 s.Append(wxString::Format(_("File \"%s\" is a grayscale image, but other images in project are color images."), fname.c_str()));
289 }
290 else
291 {
292 s.Append(wxString::Format(_("File \"%s\" is a color image, but other images in project are grayscale images."), fname.c_str()));
293 };
294 s.Append("\n");
295 s.Append(_("Hugin does not support this mixing. Skipping this image.\nConvert this image to grayscale or RGB image respectively and try loading again."));
297 continue;
298 };
299 if ((pixelType == "UINT8") || (pixelType == "UINT16") || (pixelType == "INT16"))
300 {
301 if (hugin_utils::IsLinearICCProfile(info.getICCProfile()))
302 {
304 }
305 else
306 {
308 }
309 }
310 else
312 if (pano.getNrOfImages() > 0)
313 {
314 const std::string newICCProfileDesc = hugin_utils::GetICCDesc(info.getICCProfile());
316 {
317 // icc profiles does not match
319 if (newICCProfileDesc.empty())
320 {
321 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());
322 }
323 else
324 {
325 if (pano.getICCProfileDesc().empty())
326 {
327 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());
328 }
329 else
330 {
331 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());
332 }
333 }
334 warning.Append("\n");
335 warning.Append(_("Hugin expects all images in the same color profile.\nPlease convert all images to same color profile and try again."));
337 continue;
338 }
339 }
340 else
341 {
342 // remember icc profile name
343 if (!info.getICCProfile().empty())
344 {
345 pano.setICCProfileDesc(hugin_utils::GetICCDesc(info.getICCProfile()));
346 }
347 else
348 {
349 pano.setICCProfileDesc("");
350 };
351 }
352 }
353 catch(std::exception & e)
354 {
355 std::cerr << "ERROR: caught exception: " << e.what() << std::endl;
356 std::cerr << "Could not get pixel type for file " << filename << std::endl;
357 hugin_utils::HuginMessageBox(wxString::Format(_("Could not decode image:\n%s\nAbort"), fname.c_str()), _("Hugin"), wxOK | wxICON_EXCLAMATION, wxGetActiveWindow());
358 return false;
359 };
360 bool ok = srcImg.readEXIF();
361 if(ok)
362 {
363 ok = srcImg.applyEXIFValues();
364 // load crop factor from database if unknown
365 if (srcImg.getCropFactor()<0.1)
366 {
367 srcImg.readCropfactorFromDB();
368 ok=(srcImg.getExifFocalLength()>0 && srcImg.getCropFactor()>0.1);
369 };
371 {
372 // if projection is equirectangular, we loaded info from gpano tags
373 // in this case we don't need to look up the database
374 const bool ignoreFovRectilinear = wxConfigBase::Get()->Read("/General/IgnoreFovRectilinearOnAdd", 1l) == 1l;
375 srcImg.readProjectionFromDB(ignoreFovRectilinear);
376 };
377 };
378 // set preferred projection if requested
379 if (m_preferredLensType != -1)
380 {
382 };
383 // save EXIF data for later to prevent double loading of EXIF data
385 if (! ok ) {
386 // search for image with matching size and exif data
387 // and re-use it.
388 bool added = false;
389 for (unsigned int i=0; i < pano.getNrOfImages(); i++) {
391 if ( other.getSize() == srcImg.getSize() &&
392 other.getExifModel() == srcImg.getExifModel() &&
393 other.getExifMake() == srcImg.getExifMake() &&
394 other.getExifFocalLength() == srcImg.getExifFocalLength()
395 )
396 {
397 // add image
398 if (srcImg.getCropFactor() <= 0.1)
399 {
400 // set crop factor to 1 if not set
401 srcImg.setCropFactor(other.getCropFactor());
402 };
403 int imgNr = pano.addImage(srcImg);
404 variable_groups.update();
405 lenses.switchParts(imgNr, lenses.getPartNumber(i));
406 added=true;
407 break;
408 }
409 }
410 if (added) continue;
411 }
412 int matchingLensNr=-1;
413 // if no similar image found, ask user
414 if (! ok) {
416 // assume a standard lens
417 srcImg.setHFOV(50);
418 srcImg.setCropFactor(1);
419 }
420 }
421
422 // check the image hasn't disappeared on us since the HFOV dialog was
423 // opened
424 wxString fn(srcImg.getFilename().c_str(),HUGIN_CONV_FILENAME);
425 if (!wxFileName::FileExists(fn)) {
426 DEBUG_INFO("Image: " << fn.mb_str() << " has disappeared, skipping...");
427 continue;
428 }
429
430 // FIXME: check if the exif information
431 // indicates this image matches a already used lens
432 variable_groups.update();
433 double ev = 0;
434 bool set_exposure = false;
435 for (unsigned int i=0; i < pano.getNrOfImages(); i++) {
437 if (other.getExifFocalLength()>0) {
438 if (other.getSize() == srcImg.getSize()
439 && other.getExifModel() == srcImg.getExifModel()
440 && other.getExifMake() == srcImg.getExifMake()
441 && other.getExifFocalLength() == srcImg.getExifFocalLength()
442 // Smartphone cameras can have different crop factors, take also this case into account
443 && other.getCropFactor() == srcImg.getCropFactor()
444 )
445 {
446 matchingLensNr = lenses.getPartNumber(i);
447 break;
448 }
449 }
450 else
451 {
452 // no exiv information, just check image size.
453 if (other.getSize() == srcImg.getSize() )
454 {
455 matchingLensNr = lenses.getPartNumber(i);
456 break;
457 }
458 }
459 }
460
461 // If matchingLensNr == -1 still, we haven't found a good lens to use.
462 // We shouldn't attach the image to a lens in this case, it will have
463 // its own new lens.
464 int imgNr = pano.addImage(srcImg);
465 variable_groups.update();
466 if (matchingLensNr != -1)
467 {
468 lenses.switchParts(imgNr, matchingLensNr);
469 }
470 if (imgNr == 0) {
471 // get initial value for output exposure
473 opts.outputExposureValue = srcImg.getExposureValue();
474 pano.setOptions(opts);
475 }
476 }
477 if (pano.hasPossibleStacks())
478 {
480 if (oldImgCount == 0)
481 {
482 // all images added
483 message = _("Hugin has image stacks detected in the added images and will assign corresponding stack numbers to the images.");
484 }
485 else
486 {
487 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.");
488 };
489 message.append("\n");
490 message.append(_("Should the position of images in each stack be linked?"));
492 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."));
493 if (oldImgCount == 0)
494 {
495 dialog->SetYesNoCancelLabels(_("Link position"), _("Don't link position"), _("Don't assign stacks"));
496 }
497 else
498 {
499 dialog->SetYesNoCancelLabels(_("Link position"), _("Don't link position"), _("Keep existing stacks"));
500 };
501 switch (dialog->ShowModal())
502 {
503 case wxID_OK:
504 case wxID_YES:
505 pano.linkPossibleStacks(true);
506 break;
507 case wxID_NO:
508 pano.linkPossibleStacks(false);
509 break;
510 };
511 }
512 else
513 {
514 bool hasStacks = false;
515 for (size_t i = 0; i<pano.getNrOfImages(); i++)
516 {
517 if (pano.getImage(i).StackisLinked())
518 {
519 hasStacks = true;
520 break;
521 };
522 };
523 wxConfigBase* config = wxConfigBase::Get();
524 bool showExposureWarning = config->Read("/ShowExposureWarning", 1l) == 1l;
526 {
527 wxDialog dlg;
528 wxXmlResource::Get()->LoadDialog(&dlg, NULL, "warning_exposure_dlg");
529 if (dlg.ShowModal() == wxID_OK)
530 {
531 if (XRCCTRL(dlg, "dont_show_again_checkbox", wxCheckBox)->GetValue())
532 {
533 config->Write("/ShowExposureWarning", 0l);
534 }
535 else
536 {
537 config->Write("/ShowExposureWarning", 1l);
538 };
539 config->Flush();
540 }
541 };
542 };
543 return true;
544}
545
546
548{
550 int ptoVersion = 0;
551 std::ifstream in(filename.c_str());
552 if (newPano.loadPTScript(in, ptoVersion, prefix))
553 {
554 pano.setMemento(newPano);
556 // always reset to TIFF_m ...
558 // get enblend and enfuse options from preferences
559 if (ptoVersion < 2)
560 {
561 // no options stored in file, use default arguments in config file
562 opts.enblendOptions = wxConfigBase::Get()->Read("/Enblend/Args", HUGIN_ENBLEND_ARGS).mb_str(wxConvLocal);
563 opts.enfuseOptions = wxConfigBase::Get()->Read("/Enfuse/Args", HUGIN_ENFUSE_ARGS).mb_str(wxConvLocal);
564 }
565 // Set the nona gpu flag base on what is in preferences as it is not
566 // stored in the file.
567 opts.remapUsingGPU = wxConfigBase::Get()->Read("/Nona/UseGPU",HUGIN_NONA_USEGPU) == 1;
568 pano.setOptions(opts);
569
572
573 unsigned int nImg = pano.getNrOfImages();
575 bool autopanoSiftFile=false;
578 for (unsigned int i = 0; i < nImg; i++) {
579 wxFileName fname(wxString (pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
580 while (! fname.FileExists()){
581 // Is file in the new path
582 if (basedir != wxEmptyString) {
583 DEBUG_DEBUG("Old filename: " << pano.getImage(i).getFilename());
584 std::string fn = hugin_utils::stripPath(pano.getImage(i).getFilename());
585 DEBUG_DEBUG("Old filename, without path): " << fn);
587 // GetFullName does only work with local paths (not compatible across platforms)
588// wxString newname = fname.GetFullName();
589 fname.AssignDir(basedir);
590 fname.SetFullName(newname);
591 DEBUG_TRACE("filename with new path: " << fname.GetFullPath().mb_str(wxConvLocal));
592 if (fname.FileExists()) {
593 pano.setImageFilename(i, (const char *)fname.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
594 DEBUG_TRACE("New filename set: " << fname.GetFullPath().mb_str(wxConvLocal));
595 // TODO - set pano dirty flag so that new paths are saved
596 continue;
597 }
598 }
599
600 hugin_utils::HuginMessageBox(wxString::Format(_("The project file \"%s\" refers to image \"%s\" which was not found.\nPlease manually select the correct image."), filename, fname.GetFullPath()), _("Hugin"), wxOK | wxICON_INFORMATION, wxGetActiveWindow());
601
602 if (basedir == wxEmptyString) {
603 basedir = fname.GetPath();
604 }
605
606 // open file dialog
607 wxFileDialog dlg(wxGetActiveWindow(), wxString::Format(_("Select image %s"), fname.GetFullName()),
608 basedir, fname.GetFullName(),
610 dlg.SetDirectory(basedir);
611 if (dlg.ShowModal() == wxID_OK) {
612 pano.setImageFilename(i, (const char *)dlg.GetPath().mb_str(HUGIN_CONV_FILENAME));
613 // save used path
614 basedir = dlg.GetDirectory();
615 DEBUG_INFO("basedir is: " << basedir.mb_str(wxConvLocal));
616 } else {
618 pano.setMemento(emptyPano);
619 // set an empty panorama
620 return true;
621 }
622 fname.Assign(dlg.GetPath());
623 }
624 // check if image size is correct
626 //
627 vigra::ImageImportInfo imginfo(srcImg.getFilename().c_str());
628 if (srcImg.getSize() != imginfo.size()) {
629 // adjust size properly.
630 srcImg.resize(imginfo.size(), &vars[i]);
631 }
632 // check if script contains invalid HFOV
633 double hfov = pano.getImage(i).getHFOV();
634 if (pano.getImage(i).getProjection() == HuginBase::SrcPanoImage::RECTILINEAR
635 && hfov >= 180 && autopanoSiftFile == false)
636 {
637 autopanoSiftFile = true;
638 // something is wrong here, try to read from exif data (all images)
639 bool ok = srcImg.readEXIF();
640 if(ok) {
641 ok = srcImg.applyEXIFValues();
642 };
643 if (! ok) {
645 }
647 }
648 else
649 {
650 // load exif data
651 srcImg.readEXIF();
653 {
654 // need to copy the lens parameters from the first lens.
655 srcImg.setHFOV(autopanoSiftRefImg.getHFOV());
656 };
657 };
658 // remember icc profile, only from first image
659 if (i == 0)
660 {
661 pano.setNrOfBands(imginfo.numBands() - imginfo.numExtraBands());
663 }
664 pano.setSrcImage(i, srcImg);
665 }
666 // special update routine, when images size changed
667 // and some linked variables needs update after rescaling
668 for (unsigned i = 0; i < nImg; ++i)
669 {
670 if (!vars[i].empty())
671 {
672 pano.updateVariables(i, vars[i]);
673 };
674 };
675 // Link image projection across each lens, since it is not saved.
676 const HuginBase::UIntSetVector imgSetLens = lenses.getPartsSet();
677 for (size_t i = 0; i < imgSetLens.size(); ++i)
678 {
680 if (imgLens.size()>1)
681 {
682 HuginBase::UIntSet::const_iterator it = imgLens.begin();
683 const size_t img1 = *it;
684 ++it;
685 do
686 {
687 pano.linkImageVariableProjection(img1, *it);
688 ++it;
689 } while (it != imgLens.end());
690 };
691 };
692 } else {
693 DEBUG_ERROR("could not load panotools script");
694 in.close();
695 return false;
696 };
697 in.close();
698
699 // Verify control points are valid
700 // loop through entire list of points, confirming they are inside the
701 // bounding box of their images
704 int bad_cp_count = 0;
705 for (HuginBase::CPVector::const_iterator it = oldCPs.begin();
706 it != oldCPs.end(); ++it)
707 {
709 const HuginBase::SrcPanoImage & img1 = pano.getImage(point.image1Nr);
710 const HuginBase::SrcPanoImage & img2 = pano.getImage(point.image2Nr);
711 if (0 > point.x1 || point.x1 > img1.getSize().x ||
712 0 > point.y1 || point.y1 > img1.getSize().y ||
713 0 > point.x2 || point.x2 > img2.getSize().x ||
714 0 > point.y2 || point.y2 > img2.getSize().y)
715 {
716 bad_cp_count++;
717 } else
718 {
719 goodCPs.push_back(point);
720 }
721 }
722
723 if (bad_cp_count > 0)
724 {
725 wxString errMsg = wxString::Format(_("%d invalid control point(s) found.\n\nPress OK to remove."), bad_cp_count);
728 }
729
730 // check stacks and warn users in case
731 CheckLensStacks(&pano, false);
732 // Update control point error values
735 {
736 pano.markAsOptimized();
737 };
738 return true;
739}
740
742{
743 pano.reset();
744
745 // Setup pano with options from preferences
747 wxConfigBase* config = wxConfigBase::Get();
748 opts.quality = config->Read("/output/jpeg_quality",HUGIN_JPEG_QUALITY);
749 switch(config->Read("/output/tiff_compression", HUGIN_TIFF_COMPRESSION))
750 {
751 case 0:
752 default:
753 opts.outputImageTypeCompression = "NONE";
754 opts.tiffCompression = "NONE";
755 break;
756 case 1:
757 opts.outputImageTypeCompression = "PACKBITS";
758 opts.tiffCompression = "PACKBITS";
759 break;
760 case 2:
761 opts.outputImageTypeCompression = "LZW";
762 opts.tiffCompression = "LZW";
763 break;
764 case 3:
765 opts.outputImageTypeCompression = "DEFLATE";
766 opts.tiffCompression = "DEFLATE";
767 break;
768 }
769 switch (config->Read("/output/ldr_format", HUGIN_LDR_OUTPUT_FORMAT)) {
770 case 1:
771 opts.outputImageType ="jpg";
772 break;
773 case 2:
774 opts.outputImageType ="png";
775 break;
776 case 3:
777 opts.outputImageType ="exr";
778 break;
779 default:
780 case 0:
781 opts.outputImageType ="tif";
782 break;
783 }
784 // HDR disabled because there is no real choice at the moment: HDR TIFF is broken and there is only EXR
785 // opts.outputImageTypeHDR = config->Read("/output/hdr_format", HUGIN_HDR_OUTPUT_FORMAT);
787 opts.blendMode = static_cast<HuginBase::PanoramaOptions::BlendingMechanism>(config->Read("/default_blender", HUGIN_DEFAULT_BLENDER));
788 opts.enblendOptions = config->Read("Enblend/Args",HUGIN_ENBLEND_ARGS).mb_str(wxConvLocal);
789 opts.enfuseOptions = config->Read("Enfuse/Args",HUGIN_ENFUSE_ARGS).mb_str(wxConvLocal);
790 opts.verdandiOptions = config->Read("/VerdandiDefaultArgs", wxEmptyString).mb_str(wxConvLocal);
791 opts.interpolator = (vigra_ext::Interpolator)config->Read("Nona/Interpolator",HUGIN_NONA_INTERPOLATOR);
792 opts.remapUsingGPU = config->Read("Nona/useGPU",HUGIN_NONA_USEGPU)!=0;
793 opts.tiff_saveROI = config->Read("Nona/CroppedImages",HUGIN_NONA_CROPPEDIMAGES)!=0;
796 pano.setOptions(opts);
797
800 return true;
801}
802
803
805{
806 wxConfigBase* config = wxConfigBase::Get();
807
808 if (pano.getNrOfImages() == 0) {
809 // TODO: prompt for images!
810 wxString path = config->Read("actualPath", wxEmptyString);
811 wxFileDialog dlg(wxGetActiveWindow(), _("Add images"),
812 path, wxEmptyString,
814 dlg.SetDirectory(path);
815
816 // remember the image extension
818 if (config->HasEntry("lastImageType")){
819 img_ext = config->Read("lastImageType").c_str();
820 }
821 if (img_ext == "all images")
822 dlg.SetFilterIndex(0);
823 else if (img_ext == "jpg")
824 dlg.SetFilterIndex(1);
825 else if (img_ext == "tiff")
826 dlg.SetFilterIndex(2);
827 else if (img_ext == "png")
828 dlg.SetFilterIndex(3);
829 else if (img_ext == "hdr")
830 dlg.SetFilterIndex(4);
831 else if (img_ext == "exr")
832 dlg.SetFilterIndex(5);
833 else if (img_ext == "all files")
834 dlg.SetFilterIndex(6);
835 DEBUG_INFO ( "Image extention: " << img_ext.mb_str(wxConvLocal) );
836
837 // call the file dialog
838 if (dlg.ShowModal() == wxID_OK) {
839 // get the selections
841 dlg.GetPaths(Pathnames);
842
843 // save the current path to config
844#ifdef __WXGTK__
845 //workaround a bug in GTK, see https://bugzilla.redhat.com/show_bug.cgi?id=849692 and http://trac.wxwidgets.org/ticket/14525
846 config->Write("/actualPath", wxPathOnly(Pathnames[0]));
847#else
848 config->Write("/actualPath", dlg.GetDirectory());
849#endif
850 DEBUG_INFO ( wxString::Format("img_ext: %d", dlg.GetFilterIndex()).mb_str(wxConvLocal) );
851 // save the image extension
852 switch ( dlg.GetFilterIndex() ) {
853 case 0: config->Write("lastImageType", "all images"); break;
854 case 1: config->Write("lastImageType", "jpg"); break;
855 case 2: config->Write("lastImageType", "tiff"); break;
856 case 3: config->Write("lastImageType", "png"); break;
857 case 4: config->Write("lastImageType", "hdr"); break;
858 case 5: config->Write("lastImageType", "exr"); break;
859 case 6: config->Write("lastImageType", "all files"); break;
860 }
861
864 // add images.
865 for (unsigned int i=0; i< Pathnames.GetCount(); i++) {
866 std::string filename = (const char *)Pathnames[i].mb_str(HUGIN_CONV_FILENAME);
867 vigra::ImageImportInfo inf(filename.c_str());
869 img.setFilename(filename);
870 img.setSize(inf.size());
871 img.readEXIF();
872 img.applyEXIFValues();
873 int imgNr = pano.addImage(img);
874 lenses.updatePartNumbers();
875 if (i > 0) lenses.switchParts(imgNr, 0);
876 }
877
878 }
879 }
880
881 unsigned int nOldImg = pano.getNrOfImages();
883
884 int ptoVersion = 0;
885 if (newPanoMem.loadPTScript(in, ptoVersion, "")) {
886 HuginBase::Panorama newPano;
887 newPano.setMemento(newPanoMem);
888
889 unsigned int nNewImg = newPano.getNrOfImages();
890 if (nOldImg != nNewImg) {
891 wxString errMsg = wxString::Format(_("Error, template expects %d images,\ncurrent project contains %d images\n"), nNewImg, nOldImg);
893 return false;
894 }
895
896 // check image sizes, and correct parameters if required.
898 for (unsigned int i = 0; i < nNewImg; i++) {
899
900 // check if image size is correct
903
904 // just keep the file name
905 DEBUG_DEBUG("apply template fn:" << newSrcImg.getFilename() << " real fn: " << oldSrcImg.getFilename());
906 newSrcImg.setFilename(oldSrcImg.getFilename());
907 if (oldSrcImg.getSize() != newSrcImg.getSize()) {
908 // adjust size properly.
909 newSrcImg.resize(oldSrcImg.getSize(), &(vars[i]));
910 }
911 newPano.setSrcImage(i, newSrcImg);
912 }
913 // now update all possible linked variables
914 // this has to be done separately, otherwise the linked variables
915 // would be updated several times
916 for (unsigned int i = 0; i < nNewImg; ++i)
917 {
918 if (!vars[i].empty())
919 {
920 newPano.updateVariables(i, vars[i]);
921 };
922 };
923 // keep old control points.
924 newPano.setCtrlPoints(pano.getCtrlPoints());
925 newPanoMem = newPano.getMemento();
926 // remmember setting of image type and icc profile
927 const int bands = pano.getNrOfBands();
928 const std::string iccProfile = pano.getICCProfileDesc();
930 pano.setNrOfBands(bands);
931 pano.setICCProfileDesc(iccProfile);
932 } else {
933 hugin_utils::HuginMessageBox(_("Error loading project file"), _("Hugin"), wxOK | wxICON_ERROR, wxGetActiveWindow());
934 }
936 return true;
937}
938
939#ifdef HUGIN_HSI
940bool PythonScriptPanoCmd::processPanorama(HuginBase::Panorama& pano)
941{
942 std::cout << "run python script: " << m_scriptFile.c_str() << std::endl;
943
944 int success = hpi::callhpi ( m_scriptFile.c_str() , 1 ,
945 "HuginBase::Panorama*" , &pano ) ;
946
947 if(success!=0)
948 hugin_utils::HuginMessageBox(wxString::Format("Script returned %d",success),_("Hugin"), wxOK | wxICON_INFORMATION, wxGetActiveWindow());
949 std::cout << "Python interface returned " << success << endl ;
950 // notify other of change in panorama
951 if(pano.getNrOfImages()>0)
952 {
953 for(unsigned int i=0;i<pano.getNrOfImages();i++)
954 {
955 pano.imageChanged(i);
956 };
957 };
959 return true;
960}
961#endif
962
963} // namespace
964
bool CheckLensStacks(HuginBase::Panorama *pano, bool allowCancel)
check, if lens and stacks are correctly linked shows message box with short information if not
some helper classes for graphes
some definitions to work with optimizer master switches
Somewhere to specify what variables belong to what.
A dialog for HFOV.
Definition HFOVDialog.h:38
@ RESPONSE_LINEAR
linear response
@ RESPONSE_EMOR
empirical model of response
represents a control point
Same as above, but use a non const panorama.
Holds transformations for Image -> Pano and the other way.
Memento class for a Panorama object.
Definition Panorama.h:50
bool loadPTScript(std::istream &i, int &ptoVersion, const std::string &prefix="")
load a Hugin file
Panorama image options.
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
vigra_ext::Interpolator interpolator
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
void imageChanged(unsigned int imgNr)
mark image for change notification.
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
void setMemento(const PanoramaMemento &memento)
set the internal state
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...
void setCtrlPoints(const CPVector &points)
set all control points (Ippei: Is this supposed to be 'add' method?)
Definition Panorama.cpp:449
const double getMaxExposureDifference() const
returns the maximum exposure value difference of all images in the project
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition Panorama.cpp:381
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition Panorama.cpp:319
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
Definition Panorama.cpp:373
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 markAsOptimized(bool optimized=true)
Definition Panorama.h:618
const std::string getICCProfileDesc() const
return description of icc profile used for pano
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition Panorama.h:319
virtual void updateVariables(const VariableMapVector &vars)
Set the variables.
Definition Panorama.cpp:171
PanoramaMemento getMemento() const
get the internal state
Definition Panorama.h:602
void setNrOfBands(const int nrBands)
sets the number of bands
void setICCProfileDesc(const std::string &newDesc)
sets the icc profile description for check of same profile
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Definition Panorama.cpp:311
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
void reset()
clear the internal state.
Definition Panorama.cpp:69
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
Definition Panorama.cpp:303
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
int getWidth() const
Get the width of the image in pixels.
void setSize(vigra::Size2D val)
Set the image size in pixels.
int getHeight() const
Get the height of the image in pixels.
bool readEXIF()
try to fill out information about the image, by examining the exif data
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
std::vector< std::string > files
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
virtual bool processPanorama(HuginBase::Panorama &pano)
Called by execute().
#define HUGIN_JPEG_QUALITY
#define HUGIN_ENBLEND_ARGS
#define HUGIN_NONA_USEGPU
#define HUGIN_GUI_SORT_NEW_IMG_ON_ADD
#define HUGIN_NONA_CROPPEDIMAGES
#define HUGIN_TIFF_COMPRESSION
#define HUGIN_ENFUSE_ARGS
#define HUGIN_DEFAULT_BLENDER
#define HUGIN_NONA_INTERPOLATOR
#define HUGIN_HDRMERGE_ARGS
#define HUGIN_LDR_OUTPUT_FORMAT
C++ call interface to hpi.
#define HUGIN_CONV_FILENAME
Definition platform.h:40
platform/compiler specific stuff.
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_TRACE(msg)
Definition utils.h:67
#define DEBUG_INFO(msg)
Definition utils.h:69
void calcCtrlPointErrors(PanoramaData &pano)
Update the Ctrl Point errors without optimizing.
std::vector< VariableMap > VariableMapVector
std::vector< ControlPoint > CPVector
std::vector< UIntSet > UIntSetVector
std::set< unsigned int > UIntSet
bool getLensDataFromUser(wxWindow *parent, HuginBase::SrcPanoImage &srcImg)
std::string GetICCProfileNameChecked(const std::string &iccName)
return name of icc profile with same checks for comparision
void applyColorBalanceValue(HuginBase::SrcPanoImage &srcImg, HuginBase::Panorama &pano)
int callhpi(const char *plugin_name, int argc,...)
simplified call interface to the Python Plugin facility.
Definition hpi.cpp:55
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:176
std::unique_ptr< wxMessageDialogBase > MessageDialog
Definition wxutils.h:90
bool IsLinearICCProfile(const vigra::ImageImportInfo::ICCProfile &iccProfile)
return true if icc profile is linear one, otherwise return false
Definition utils.cpp:940
std::string GetICCDesc(const vigra::ImageImportInfo::ICCProfile &iccProfile)
returns description of given icc profile
Definition utils.cpp:912
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
MessageDialog GetMessageDialog(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:212
Interpolator
enum with all interpolation methods
include file for the hugin project
include file for the hugin project
wxString GetFileDialogImageFilters()
return filter for image files, needed by file open dialog it contains all image format vigra can read...
Definition platform.cpp:70
Functor class to compare two objects with the "Alphanum Algorithm".
Definition alphanum.h:66
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
wxwindows specific panorama commands
platform/compiler specific stuff.