Hugin trunk 0.1
Loading...
Searching...
No Matches
SrcPanoImage.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
13/*
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This software is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public
25 * License along with this software. If not, see
26 * <http://www.gnu.org/licenses/>.
27 *
28 */
29
30// for debugging
31#include <iostream>
32#include <stdio.h>
33#include <stdexcept>
34//#include <wx/wxprec.h>
35
36#include "SrcPanoImage.h"
37
38#include <iostream>
39#include <vector>
40#include <vigra/diff2d.hxx>
41#include <vigra/imageinfo.hxx>
42#include <hugin_utils/utils.h>
43#include <exiv2/exiv2.hpp>
44#include <lensdb/LensDB.h>
45#include "Exiv2Helper.h"
46
47#ifdef __FreeBSD__
48#define log2(x) (log(x) / M_LN2)
49#endif /* __FreeBSD__ */
50
52
53namespace HuginBase {
54
56{
57 // TODO: check if images have the same orientation.
58 // calculate scaling ratio
59 const double scale = (double) sz.x / m_Size.getData().x;
60
61 // center shift
62 if (potentialLinkedVars != nullptr)
63 {
64 // pointer to VariableMap given, can be used to update linked variables later
65 // otherwise linked variables could not be correct updated, e.g. in apply template
66 potentialLinkedVars->insert(std::make_pair("d", Variable("d", m_RadialDistortionCenterShift.getData().x * scale)));
67 potentialLinkedVars->insert(std::make_pair("e", Variable("e", m_RadialDistortionCenterShift.getData().y * scale)));
68 potentialLinkedVars->insert(std::make_pair("g", Variable("g", m_Shear.getData().x * scale)));
69 potentialLinkedVars->insert(std::make_pair("t", Variable("t", m_Shear.getData().y * scale)));
70 }
71 else
72 {
73 // directly update the image variables
75 m_Shear.setData(m_Shear.getData() * scale);
76 };
77
78 // crop
79 // ensure the scaled rectangle is inside the new image size
80 switch (m_CropMode.getData())
81 {
82 case NO_CROP:
83 m_CropRect.setData(vigra::Rect2D(sz));
84 break;
85 case CROP_RECTANGLE:
86 {
87 vigra::Rect2D rect(m_CropRect.getData());
88 rect *= scale;
89 rect &= vigra::Rect2D(sz);
90 m_CropRect.setData(rect);
91 }
92 break;
93 case CROP_CIRCLE:
94 {
95 vigra::Rect2D rect(m_CropRect.getData());
96 rect *= scale;
97 m_CropRect.setData(rect);
98 }
99 break;
100 }
101
102 m_Size.setData(sz);
103 // vignetting correction
104 if (potentialLinkedVars != nullptr)
105 {
106 // see comment under center shift above
107 potentialLinkedVars->insert(std::make_pair("Vx", Variable("Vx", m_RadialVigCorrCenterShift.getData().x * scale)));
108 potentialLinkedVars->insert(std::make_pair("Vy", Variable("Vy", m_RadialVigCorrCenterShift.getData().y * scale)));
109 }
110 else
111 {
113 };
114 // resize masks
116 for(unsigned int i=0;i<scaledMasks.size();i++)
117 scaledMasks[i].scale(scale);
118 m_Masks.setData(scaledMasks);
119 scaledMasks.clear();
120 scaledMasks=m_ActiveMasks.getData();
121 for(unsigned int i=0;i<scaledMasks.size();i++)
122 scaledMasks[i].scale(scale);
123 m_ActiveMasks.setData(scaledMasks);
124}
125
127{
128 switch (m_Projection.getData())
129 {
130 case PANORAMIC:
131 case EQUIRECTANGULAR:
132 if (m_HFOV.getData() == 360) return true;
134 case CIRCULAR_FISHEYE:
135 case RECTILINEAR:
139 case FISHEYE_THOBY:
140 default:
141 break;
142 }
143 return false;
144}
145
146bool SrcPanoImage::isInside(vigra::Point2D p, bool ignoreMasks) const
147{
148 bool insideCrop=false;
149 switch(m_CropMode.getData()) {
150 case NO_CROP:
151 case CROP_RECTANGLE:
152 insideCrop = m_CropRect.getData().contains(p);
153 break;
154 case CROP_CIRCLE:
155 {
156 if (0 > p.x || 0 > p.y || p.x >= m_Size.getData().x || p.y >= m_Size.getData().y) {
157 // outside image
158 return false;
159 }
161 cropCenter.x = m_CropRect.getData().left() + m_CropRect.getData().width()/2.0;
162 cropCenter.y = m_CropRect.getData().top() + m_CropRect.getData().height()/2.0;
163 double radius2 = std::min(m_CropRect.getData().width()/2.0, m_CropRect.getData().height()/2.0);
166 insideCrop = (radius2 > pf.x*pf.x+pf.y*pf.y );
167 }
168 }
169 if(insideCrop && !ignoreMasks)
170 return !(isInsideMasks(p));
171 else
172 return insideCrop;
173}
174
176{
178 return (projection==CIRCULAR_FISHEYE || projection==FISHEYE_THOBY || projection==FISHEYE_ORTHOGRAPHIC);
179};
180
182{
183 bool nr = (m_RadialDistortionRed.getData()[0] == 0.0 && m_RadialDistortionRed.getData()[1] == 0.0 &&
184 m_RadialDistortionRed.getData()[2] == 0.0 && m_RadialDistortionRed.getData()[3] == 1);
185 bool nb = (m_RadialDistortionBlue.getData()[0] == 0.0 && m_RadialDistortionBlue.getData()[1] == 0.0 &&
186 m_RadialDistortionBlue.getData()[2] == 0.0 && m_RadialDistortionBlue.getData()[3] == 1);
187 return !(nr && nb);
188}
189
190
195
196
201
203{
204 m_CropMode.setData(val);
205 if (val == NO_CROP) {
206 m_CropRect.setData(vigra::Rect2D(m_Size.getData()));
207 }
208}
209
210void SrcPanoImage::setSize(vigra::Size2D val)
211{
212 m_Size.setData(val);
213 if (m_CropMode.getData() == NO_CROP) {
214 m_CropRect.setData(vigra::Rect2D(val));
215 }
216}
217
219{ return 1.0/pow(2.0, m_ExposureValue.getData()); }
220
221void SrcPanoImage::setExposure(const double & val)
222{ m_ExposureValue.setData(log2(1/val)); }
223
224
226{
227 DEBUG_TRACE("");
228 return (
229#define image_variable( name, type, default_value ) \
230 m_##name.getData() == other.m_##name.getData() &&
231#include "image_variables.h"
232#undef image_variable
233 true // All the variable checks above end with && so we need this.
234 );
235}
236
237// convinience functions to extract a set of variables
238double SrcPanoImage::getVar(const std::string & code) const
239{
240 DEBUG_TRACE("");
241 assert(!code.empty());
242#define image_variable( name, type, default_value ) \
243 if (PTOVariableConverterFor##name::checkApplicability(code)) \
244 return PTOVariableConverterFor##name::getValueFromVariable(code, m_##name );\
245 else
246#include "image_variables.h"
247#undef image_variable
248 {// this is for the final else.
249 DEBUG_ERROR("Unknown variable " << code);
250 }
251 return 0;
252}
253
254void SrcPanoImage::setVar(const std::string & code, double val)
255{
256 DEBUG_TRACE("Var:" << code << " value: " << val);
257 assert(!code.empty());
258#define image_variable( name, type, default_value ) \
259 if (PTOVariableConverterFor##name::checkApplicability(code)) \
260 {PTOVariableConverterFor##name::setValueFromVariable(code, m_##name, val);}\
261 else
262#include "image_variables.h"
263#undef image_variable
264 {// this is for the final else.
265 DEBUG_ERROR("Unknown variable " << code);
266 }
267}
268
270{
271 // make a variable map vector
272
273 // fill variable map with details about this image.
274 // position
275 DEBUG_TRACE("");
276
277 VariableMap vars;
278#define image_variable( name, type, default_value ) \
279 PTOVariableConverterFor##name::addToVariableMap(m_##name, vars);
280#include "image_variables.h"
281#undef image_variable
282
283 return vars;
284}
285
287{
288 if(getWidth()<=0 || getHeight()<=0)
289 {
290 try
291 {
292 vigra::ImageImportInfo info(getFilename().c_str());
293 setSize(info.size());
294 // save pixeltype for later, so we don't need to parse the file again
295 const std::string pixeltype(info.getPixelType());
297 metaData["pixeltype"] = pixeltype;
299 }
300 catch(std::exception & )
301 {
302 return false;
303 }
304 };
305 return true;
306
307};
308
310{
311 std::string filename = getFilename();
312 double roll = 0;
313 // clear all old values
318 setExifISO(0);
319 setExifMake(std::string(""));
320 setExifModel(std::string(""));
321 setExifLens(std::string(""));
327 setExifDate(std::string(""));
330
332 {
333 return false;
334 };
335
336 // if width==2*height assume equirectangular image
337 if (getWidth() == 2 * getHeight())
338 {
340 metaData["projection"] = "equirectangular";
341 metaData["HFOV"] = "360";
343 };
344
345#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
346 Exiv2::Image::UniquePtr image;
347#else
348 Exiv2::Image::AutoPtr image;
349#endif
350 try {
351 image = Exiv2::ImageFactory::open(filename.c_str());
352 }
353 catch (const Exiv2::Error& e)
354 {
355 std::cerr << "Exiv2: Error reading metadata (" << e.what() << ")" << std::endl;
356 return false;
357 }
358
359 try
360 {
361 image->readMetadata();
362 }
363 catch (const Exiv2::Error& e)
364 {
365 std::cerr << "Caught Exiv2 exception '" << e.what() << "' for file " << filename << std::endl;
366 return false;
367 }
368
369 // look into XMP metadata
370 Exiv2::XmpData& xmpData = image->xmpData();
371 if (!xmpData.empty())
372 {
373 // we need to catch exceptions in case file does not contain any GPano tags
374 try
375 {
376 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.ProjectionType"));
378 if (pos != xmpData.end())
379 {
380 if (hugin_utils::tolower(pos->toString()) == "equirectangular")
381 {
382 long croppedWidth = 0;
383 long croppedHeight = 0;
384 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageWidthPixels"));
385 if (pos != xmpData.end())
386 {
387#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
388 croppedWidth = pos->toInt64();
389#else
390 croppedWidth = pos->toLong();
391#endif
392 }
393 else
394 {
395 // tag is required
396 throw std::logic_error("Required tag CroppedAreaImageWidthPixels missing");
397 };
398 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageHeightPixels"));
399 if (pos != xmpData.end())
400 {
401#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
402 croppedHeight = pos->toInt64();
403#else
404 croppedHeight = pos->toLong();
405#endif
406 }
407 else
408 {
409 // tag is required
410 throw std::logic_error("Required tag CroppedAreaImageHeightPixels missing");
411 };
412 // check if sizes matches, if not ignore all tags
414 {
415 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoWidthPixels"));
416 double hfov = 0;
417 if (pos != xmpData.end())
418 {
419#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
420 hfov = 360 * croppedWidth / (double)pos->toInt64();
421#else
422 hfov = 360 * croppedWidth / (double)pos->toLong();
423#endif
424 }
425 else
426 {
427 // tag is required
428 throw std::logic_error("Required tag FullPanoWidthPixels missing");
429 };
430 long fullHeight = 0;
431 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoHeightPixels"));
432 if (pos != xmpData.end())
433 {
434#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
435 fullHeight = pos->toInt64();
436#else
437 fullHeight = pos->toLong();
438#endif
439 }
440 else
441 {
442 // tag is required
443 throw std::logic_error("Required tag FullPanoHeightPixels missing");
444 };
445 long cropTop = 0;
446 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaTopPixels"));
447 if (pos != xmpData.end())
448 {
449#if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
450 cropTop = pos->toInt64();
451#else
452 cropTop = pos->toLong();
453#endif
454 }
455 else
456 {
457 // tag is required
458 throw std::logic_error("Required tag CroppedAreaTopPixels missing");
459 };
460
461 // all found, remember for later
462 metaData["projection"] = "equirectangular";
463 metaData["HFOV"] = hugin_utils::doubleToString(hfov, 3);
466 };
467 };
468 };
469 }
470 catch (std::exception& e)
471 {
472 // just to catch error when image contains no GPano tags
473 std::cerr << "Error reading GPano tags from " << filename << "(" << e.what() << ")" << std::endl;
474 };
475 };
476
477 Exiv2::ExifData &exifData = image->exifData();
478 if (exifData.empty()) {
479 std::cerr << "Unable to read EXIF data from opened file:" << filename << std::endl;
480 return !getFileMetadata().empty();
481 }
482
485
486 //read exposure mode
488
489 // read ISO from EXIF or makernotes
491
494
495 //reading lens
497
498 long orientation = Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Image.Orientation");
500 {
501 switch (orientation) {
502 case 3: // rotate 180
503 roll = 180;
504 break;
505 case 6: // rotate 90
506 roll = 90;
507 break;
508 case 8: // rotate 270
509 roll = 270;
510 break;
511 default:
512 break;
513 }
514 }
515
516 long pixXdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelXDimension");
517 long pixYdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelYDimension");
518
519 if (pixXdim !=0 && pixYdim !=0 )
520 {
522 double ratioImage = getWidth()/(double)getHeight();
523 if (fabs( ratioExif - ratioImage) > 0.1)
524 {
525 // Image has been modified without adjusting exif tags.
526 // Assume user has rotated to upright pose
527 roll = 0;
528 }
529 }
530 // save for later
531 setExifOrientation(roll);
532
533 double cropFactor = 0;
534 DEBUG_DEBUG("cropFactor: " << cropFactor);
535
537 float eFocalLength35 = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.FocalLengthIn35mmFilm");
538 // take also digital zoom into account for cell phone cameras
539 float digitalZoom = Exiv2Helper::getExiv2ValueDouble(exifData, "Exif.Photo.DigitalZoomRatio");
541 {
542 // Apple ignores the guidelines in EXIF standards and includes the digital zoom into FocalLength35mm
543 // so ignore the digital zoom in this case
544 digitalZoom = 0;
545 };
546 if (eFocalLength35 > 0 && digitalZoom > 1)
547 {
549 };
550 float focalLength=0;
551 //The various methods to detmine crop factor
552 if (eFocalLength35 > 0 && eFocalLength > 0)
553 {
554 cropFactor = eFocalLength35 / eFocalLength;
556 }
557 else
558 {
559 if (eFocalLength35 > 0)
560 {
561 // 35 mm equiv focal length available, crop factor unknown.
562 // do not ask for crop factor, assume 1. Probably a full frame sensor
563 cropFactor = 1;
565 }
566 else
567 {
569 // alternative way to calculate crop factor
571 // check result
572 if (cropFactor < 0.1)
573 {
574 cropFactor = 0;
575 };
576 };
577 };
578 // check results, if 35 mm focal length is too small reset crop factor to 0
579 if (focalLength > 0 && cropFactor > 0 && focalLength*cropFactor < 6)
580 {
581 cropFactor = 0;
582 // check alternative way to calculate crop factor, e.g. when focal length and focal length in 35 mm are given
583 // and are the same, but not a full frame camera
585 if (newCropFactor > 0)
586 {
587 if (focalLength*newCropFactor >= 6)
588 {
589 cropFactor = newCropFactor;
590 }
591 };
592 };
593
596 setExifCropFactor(cropFactor);
597
599 setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
600
601 double redBalance, blueBalance;
605
606 double gpsCoord;
608 {
612 };
614 {
618 };
619
620 DEBUG_DEBUG("Results for:" << filename);
621 DEBUG_DEBUG("Focal Length: " << getExifFocalLength());
622 DEBUG_DEBUG("Crop Factor: " << getCropFactor());
623 DEBUG_DEBUG("Roll: " << getExifOrientation());
624
625 return true;
626}
627
629{
631 if(applyEVValue)
632 {
634 };
635 // special handling for GPano tags
637 if (!metaData.empty())
638 {
639 FileMetaData::const_iterator pos = metaData.find("projection");
640 if (pos != metaData.end())
641 {
642 if (pos->second == "equirectangular")
643 {
644 pos = metaData.find("HFOV");
645 if (pos != metaData.end())
646 {
647 double hfov = 0;
648 hugin_utils::stringToDouble(pos->second, hfov);
649 double e = 0;
650 pos = metaData.find("e");
651 if (pos != metaData.end())
652 {
654 };
655 if (hfov != 0)
656 {
657 setProjection(EQUIRECTANGULAR);
658 setHFOV(hfov);
659 setCropFactor(1.0);
661 p.y = e;
663 return true;
664 };
665 };
666 };
667 };
668 };
669 double cropFactor=getExifCropFactor();
671 if(cropFactor>0.1)
672 {
673 setCropFactor(cropFactor);
674 };
675 if (focalLength > 0 && cropFactor > 0.1)
676 {
677 setHFOV(calcHFOV(getProjection(), focalLength, cropFactor, getSize()));
678 DEBUG_DEBUG("HFOV: " << getHFOV());
679 return true;
680 }
681 else
682 {
683 return false;
684 }
685}
686
688{
689 // finally search in lens database
690 if(getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
691 {
692 double dbCrop=0;
694 {
695 if(dbCrop>0.1)
696 {
697 setCropFactor(dbCrop);
699 if (getExifFocalLength() > 0)
700 {
701 setHFOV(calcHFOV(getProjection(), getExifFocalLength(), dbCrop, getSize()));
702 };
703 return true;
704 };
705 };
706 };
707 return false;
708};
709
711{
712 std::string lens(getExifLens());
713 if (!lens.empty())
714 {
715 return lens;
716 }
717 lens = getExifMake();
718 if (!lens.empty())
719 {
720 if (!getExifModel().empty())
721 {
722 lens.append("|");
723 lens.append(getExifModel());
724 return lens;
725 };
726 };
727 return std::string();
728};
729
731{
732 switch (proj)
733 {
740 return true;
741 default:
742 return false;
743 };
744 return false;
745};
746
748{
749 bool success=false;
750 double oldFocal = 0;
751 const std::string lensname = getDBLensName();
752 const double focal = getExifFocalLength();
753 if (!lensname.empty())
754 {
757 if(lensDB.GetProjection(lensname, dbProjection))
758 {
759 oldFocal = calcFocalLength(getProjection(), getHFOV(), getCropFactor(), getSize());
760 setProjection(dbProjection);
761 success = true;
762 };
763 if (focal>0)
764 {
765 double fov;
766 // read fov only for non rectilinear images
767 // for these relay on the EXIF data, because often user manage to store
768 // wrong values in the database, so ignore them for rectilinear images
769 if ((getProjection() != RECTILINEAR || !ignoreFovRectilinear) && lensDB.GetFov(lensname, focal, fov))
770 {
771 // calculate FOV for given image, take different aspect ratios into account
772 const double newFocal = calcFocalLength(getProjection(), fov, getCropFactor(), vigra::Size2D(3000, 2000));
773 const double newFov = calcHFOV(getProjection(), newFocal, getCropFactor(), getSize());
774 setHFOV(newFov);
775 oldFocal = 0;
776 // for fisheye lenses read also automatically the distortions parameters from lens db
777 // because fisheye often don't follow exactly one of the projection models and need
778 // the distortion parameters to model the real projection of the used fisheye lens
779 if(isFisheye(getProjection()))
780 {
781 std::vector<double> dist;
782 if (lensDB.GetDistortion(lensname, focal, dist))
783 {
784 if (dist.size() == 3)
785 {
786 dist.push_back(1.0 - dist[0] - dist[1] - dist[2]);
788 };
789 };
790 };
791 };
792 vigra::Rect2D dbCropRect;
793 if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
794 {
797 };
798 };
799 // updated fov after changing projection, if not already done with value from database
800 if (success && oldFocal > 0)
801 {
802 const double newFov = calcHFOV(getProjection(), oldFocal, getCropFactor(), getSize());
803 setHFOV(newFov);
804 };
805 };
806 // store information about reading from database in FileMetadata map
807 if (success)
808 {
810 metaData["readProjectionFromDB"] = "true";
812 };
813
814 return success;
815};
816
818{
819 const std::string lensname = getDBLensName();
820 const double focal = getExifFocalLength();
821 if (!lensname.empty() && focal > 0)
822 {
824 std::vector<double> dist;
825 if(lensDB.GetDistortion(lensname, focal, dist))
826 {
827 if(dist.size()==3)
828 {
829 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
831 return true;
832 };
833 };
834 };
835 return false;
836};
837
839{
840 const std::string lensname = getDBLensName();
841 const double focal = getExifFocalLength();
842 if (!lensname.empty() && focal > 0)
843 {
845 std::vector<double> vig;
846 if(lensDB.GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
847 {
848 if (vig.size() == 4)
849 {
851 return true;
852 };
853 };
854 };
855 return false;
856};
857
858double SrcPanoImage::calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
859{
860 // calculate diagonal of film
861 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
862 double r = (double)imageSize.x / imageSize.y;
863
864 // calculate the sensor width and height that fit the ratio
865 // the ratio is determined by the size of our image.
867 sensorSize.x = d / sqrt(1 + 1/(r*r));
868 sensorSize.y = sensorSize.x / r;
869
870 double hfov = 360;
871
872 switch (proj) {
874 hfov = 2*atan((sensorSize.x/2.0)/fl) * 180.0/M_PI;
875 break;
878 hfov = sensorSize.x / fl * 180/M_PI;
879 break;
882 hfov = (sensorSize.x / fl) / M_PI * 180;
883 break;
885 {
886 double val=(sensorSize.x/2.0)/fl;
887 double n;
888 double frac=modf(val, &n);
889 hfov = 2 * asin(frac) * 180.0/M_PI + n * 180.0;
890 }
891 break;
893 hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/M_PI;
894 break;
896 hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/M_PI;
897 break;
899 hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/M_PI/0.713;
900 break;
901 default:
902 hfov = 360;
903 // TODO: add formulas for other projections
904 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
905 }
906 return hfov;
907}
908
909double SrcPanoImage::calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
910{
911 // calculate diagonal of film
912 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
913 double r = (double)imageSize.x / imageSize.y;
914
915 // calculate the sensor width and height that fit the ratio
916 // the ratio is determined by the size of our image.
918 sensorSize.x = d / sqrt(1 + 1/(r*r));
919 sensorSize.y = sensorSize.x / r;
920
921 switch (proj)
922 {
924 return (sensorSize.x/2.0) / tan(hfov/180.0*M_PI/2);
925 break;
928 // same projection equation for both fisheye types,
929 // assume equal area projection.
930 return sensorSize.x / (hfov/180*M_PI);
931 break;
934 return (sensorSize.x / (hfov/180*M_PI));
935 break;
937 {
938 int t=(int)ceil((hfov-180)/360);
939 return (sensorSize.x /2.0) / (2 * t + pow ( -1.0, t) * sin(hfov/180.0*M_PI/2.0));
940 };
942 return (sensorSize.x/4.0) / tan(hfov/180.0*M_PI/4.0);
944 return (sensorSize.x/4.0) / sin(hfov/180.0*M_PI/4.0);
946 return (sensorSize.x/2.0) / (1.47 * sin(hfov/180.0*M_PI * 0.713 / 2.0));
947 default:
948 // TODO: add formulas for other projections
949 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
950 return 0;
951 }
952}
953
954double SrcPanoImage::calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
955{
956 // calculate diagonal of film
957 double r = (double)imageSize.x / imageSize.y;
958
959 double x = 36;
960 switch (proj)
961 {
963 x = focalLength * tan(hfov/180.0*M_PI/2);
964 break;
973 // same projection equation for both fisheye types,
974 // assume equal area projection.
975 x = focalLength * (hfov/180*M_PI);
976 break;
977 default:
978 // TODO: add formulas for other projections
979 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
980 return 0;
981 }
982 // diagonal of sensor
983 double diag = x * sqrt(1+ 1/(r*r));
984 return sqrt(36.0*36.0 + 24.0*24.0) / diag;
985}
986
988{
989 double ev=0;
991 if(photoFNumber==0)
992 {
993 //if no F-number was found in EXIF data assume a f stop of 3.5 to get
994 //a reasonable ev value if shutter time, e. g. for manual lenses is found
995 photoFNumber=3.5;
996 };
997 if (getExifExposureTime() > 0)
998 {
999 double gain = 1;
1000 if (getExifISO()> 0)
1001 {
1002 gain = getExifISO() / 100.0;
1003 }
1005 };
1006 return ev;
1007};
1008
1010{
1011 double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
1012 if(newHFOV!=0)
1013 {
1014 setHFOV(newHFOV);
1015 };
1016};
1017
1019{
1020 double newHFOV=calcHFOV(getProjection(),focalLength,newCropFactor,getSize());
1021 if(newHFOV!=0)
1022 {
1023 setHFOV(newHFOV);
1024 };
1025 setCropFactor(newCropFactor);
1026};
1027
1028// mask handling stuff
1035
1042
1048
1050{
1051 return !m_Masks.getData().empty();
1052};
1053
1055{
1056 MaskPolygonVector masks=m_Masks.getData();
1057 if(!masks.empty())
1058 {
1059 for(unsigned int i=0;i<masks.size();i++)
1060 {
1061 if(masks[i].isPositive())
1062 {
1063 return true;
1064 };
1065 };
1066 };
1067 return false;
1068};
1069
1071{
1072 return !m_ActiveMasks.getData().empty();
1073};
1074
1075void SrcPanoImage::printMaskLines(std::ostream &o, unsigned int newImgNr) const
1076{
1077 if(!m_Masks.getData().empty())
1078 for(unsigned int i=0;i<m_Masks.getData().size();i++)
1079 m_Masks.getData()[i].printPolygonLine(o, newImgNr);
1080};
1081
1083{
1084 if(index<m_Masks.getData().size())
1085 {
1087 editedMasks[index].setMaskType(newType);
1088 m_Masks.setData(editedMasks);
1089 };
1090};
1091
1092void SrcPanoImage::deleteMask(unsigned int index)
1093{
1094 if(index<m_Masks.getData().size())
1095 {
1097 oldMasks.erase(oldMasks.begin()+index);
1098 m_Masks.setData(oldMasks);
1099 };
1100};
1101
1107
1108bool SrcPanoImage::isInsideMasks(vigra::Point2D p) const
1109{
1110 if(!hasActiveMasks())
1111 return false;
1112 bool insideMask=false;
1113 unsigned int i=0;
1114 while(!insideMask && i<m_ActiveMasks.getData().size())
1115 {
1116 insideMask=m_ActiveMasks.getData()[i].isInside(p);
1117 i++;
1118 };
1119 return insideMask;
1120};
1121
1129{
1130 if(getSize().width() < getSize().height())
1131 return false;
1132
1133 return true;
1134}
1135
1137{
1138 //initialize struct
1139 std::memset(datetime, 0x0, sizeof(*datetime));
1140 //ignore daylight saving flag because it is not saved in EXIF date time format
1141 datetime->tm_isdst=-1;
1142 return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
1143};
1144
1145} // namespace
options getSize().area()) int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1
helper functions to work with Exif data via the exiv2 library
#define M_PI
Convenience functions for SrcPanoImage to use on the image variables.
class to access Hugins camera and lens database
Base class containing all the variables, but missing some of the other important functions and with s...
bool operator==(const BaseSrcPanoImage &other) const
Check that the variables match.
main database class
Definition LensDB.h:45
static LensDB & GetSingleton()
returns the static LensDB instance
Definition LensDB.cpp:2001
base class, which stores one mask polygon
Definition Mask.h:53
MaskType
enumeration with type of possible masks
Definition Mask.h:57
void changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
changes type of mask with index to given newType
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
bool checkImageSizeKnown()
check if the image size is known, if try to load the information from the file
bool readDistortionFromDB()
tries to read distortion data from lens database you need to call SrcPanoImage::readEXIF before to fi...
void addMask(MaskPolygon newMask)
add newMask to list of masks
static double calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
calcualte focal length, given crop factor and hfov
bool hasPositiveMasks() const
returns true, if image has positive masks
hugin_utils::FDiff2D getRadialVigCorrCenter() const
double getVar(const std::string &name) const
bool readVignettingFromDB()
tries to read vignetting data from lens database you need to call SrcPanoImage::readEXIF before to fi...
std::string getDBLensName() const
constructs the lens name for the database it is the lensname if known, for compact cameras it is cons...
void setVar(const std::string &name, double val)
bool isCircularCrop() const
returns true, if projection requires cicular crop
double calcExifExposureValue()
calculate exposure value
const int getExifDateTime(struct tm *datetime) const
try to convert Exif date time string to struct tm
bool readCropfactorFromDB()
tries to read cropfactor from lens database you need to call SrcPanoImage::readEXIF before to fill so...
int getWidth() const
Get the width of the image in pixels.
hugin_utils::FDiff2D getRadialDistortionCenter() const
bool trustExivOrientation()
Check if Exiv orientation tag can be trusted.
void setSize(vigra::Size2D val)
Set the image size in pixels.
bool hasMasks() const
returns true, if image has masks associated
bool isInside(vigra::Point2D p, bool ignoreMasks=false) const
check if a coordinate is inside the source image
void clearActiveMasks()
clears list of active masks
void setCropMode(CropMode val)
Set the crop mode.
void updateFocalLength(double newFocalLength)
updates the focal length, changes the hfov to reflect thew newFocalLength
int getHeight() const
Get the height of the image in pixels.
void setExposure(const double &val)
void updateCropFactor(double focalLength, double newCropFactor)
updates the crop factor, the hfov is calculates so that focal length remains the same
VariableMap getVariableMap() const
Return all the image variables in a variable map.
void addActiveMask(MaskPolygon newMask)
add newMask to list of active masks
void resize(const vigra::Size2D &size, VariableMap *potentialLinkedVars)
"resize" image, adjusts all distortion coefficients for usage with a source image of size size potent...
static double calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
calculate hfov of an image given focal length, image size and crop factor
bool readProjectionFromDB(const bool ignoreFovRectilinear=true)
tries to read projection and crop area from lens database you need to call SrcPanoImage::readEXIF bef...
bool hasActiveMasks() const
returns true, if image has active masks
bool readEXIF()
try to fill out information about the image, by examining the exif data
static double calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
calculate crop factor, given focal length and hfov
void deleteAllMasks()
delete all masks
void printMaskLines(std::ostream &o, unsigned int newImgNr) const
writes all mask lines to stream, using given image number
bool isInsideMasks(vigra::Point2D p) const
returns true, if point p is inside of one mask polygon
void deleteMask(unsigned int index)
delete mask at index
a variable has a value and a name.
#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_WARN(msg)
Definition utils.h:74
This file specifies what image variables SrcPanoImg should have.
const std::string getExiv2ValueString(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
bool getExiv2GPSLongitude(Exiv2::ExifData &exifData, double &longitude)
const double getCropFactor(Exiv2::ExifData &exifData, long width, long height)
bool getExiv2GPSLatitude(Exiv2::ExifData &exifData, double &latitude)
const double getExiv2ValueDouble(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
const long getExiv2ValueLong(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
const std::string getLensName(Exiv2::ExifData &exifData)
bool readRedBlueBalance(Exiv2::ExifData &exifData, double &redBalance, double &blueBalance)
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
bool isFisheye(const BaseSrcPanoImage::Projection &proj)
std::map< std::string, Variable > VariableMap
std::map< std::string, std::string > FileMetaData
typedef for general map for storing metadata in files
bool StringContainsCaseInsensitive(const std::string &s1, const std::string &s2)
check if s1 contains s2 using case insensitive comparison
Definition utils.cpp:335
TDiff2D< double > FDiff2D
Definition hugin_math.h:162
std::string doubleToString(double d, int digits)
convert a double to a string, suitable for display within a GUI.
Definition utils.cpp:228
std::string tolower(const std::string &s)
convert a string to lowercase
Definition stl_utils.h:49
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition utils.h:114
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