1 // -*- c-basic-offset: 4 -*-
2
3 /** @file SrcPanoImage.h
4 *
5 * @brief
6 *
7 * @author Pablo d'Angelo <pablo.dangelo@web.de>
8 * James Legg
9 *
10 * !! from PanoImage.h 1970
11 *
12 */
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
51 #include "ImageVariableTranslate.h"
52
53 namespace HuginBase {
54
resize(const vigra::Size2D & sz)55 void SrcPanoImage::resize(const vigra::Size2D & sz)
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 m_RadialDistortionCenterShift.setData(m_RadialDistortionCenterShift.getData() * scale);
63 m_Shear.setData(m_Shear.getData() * scale);
64
65 // crop
66 // ensure the scaled rectangle is inside the new image size
67 switch (m_CropMode.getData())
68 {
69 case NO_CROP:
70 m_CropRect.setData(vigra::Rect2D(sz));
71 break;
72 case CROP_RECTANGLE:
73 {
74 vigra::Rect2D rect(m_CropRect.getData());
75 rect *= scale;
76 rect &= vigra::Rect2D(sz);
77 m_CropRect.setData(rect);
78 }
79 break;
80 case CROP_CIRCLE:
81 {
82 vigra::Rect2D rect(m_CropRect.getData());
83 rect *= scale;
84 m_CropRect.setData(rect);
85 }
86 break;
87 }
88
89 m_Size.setData(sz);
90 // vignetting correction
91 m_RadialVigCorrCenterShift.setData(m_RadialVigCorrCenterShift.getData() *scale);
92 // resize masks
93 MaskPolygonVector scaledMasks=m_Masks.getData();
94 for(unsigned int i=0;i<scaledMasks.size();i++)
95 scaledMasks[i].scale(scale);
96 m_Masks.setData(scaledMasks);
97 scaledMasks.clear();
98 scaledMasks=m_ActiveMasks.getData();
99 for(unsigned int i=0;i<scaledMasks.size();i++)
100 scaledMasks[i].scale(scale);
101 m_ActiveMasks.setData(scaledMasks);
102 }
103
horizontalWarpNeeded()104 bool SrcPanoImage::horizontalWarpNeeded()
105 {
106 switch (m_Projection.getData())
107 {
108 case PANORAMIC:
109 case EQUIRECTANGULAR:
110 if (m_HFOV.getData() == 360) return true;
111 case FULL_FRAME_FISHEYE:
112 case CIRCULAR_FISHEYE:
113 case RECTILINEAR:
114 case FISHEYE_ORTHOGRAPHIC:
115 case FISHEYE_STEREOGRAPHIC:
116 case FISHEYE_EQUISOLID:
117 case FISHEYE_THOBY:
118 default:
119 break;
120 }
121 return false;
122 }
123
isInside(vigra::Point2D p,bool ignoreMasks) const124 bool SrcPanoImage::isInside(vigra::Point2D p, bool ignoreMasks) const
125 {
126 bool insideCrop=false;
127 switch(m_CropMode.getData()) {
128 case NO_CROP:
129 case CROP_RECTANGLE:
130 insideCrop = m_CropRect.getData().contains(p);
131 break;
132 case CROP_CIRCLE:
133 {
134 if (0 > p.x || 0 > p.y || p.x >= m_Size.getData().x || p.y >= m_Size.getData().y) {
135 // outside image
136 return false;
137 }
138 hugin_utils::FDiff2D cropCenter;
139 cropCenter.x = m_CropRect.getData().left() + m_CropRect.getData().width()/2.0;
140 cropCenter.y = m_CropRect.getData().top() + m_CropRect.getData().height()/2.0;
141 double radius2 = std::min(m_CropRect.getData().width()/2.0, m_CropRect.getData().height()/2.0);
142 radius2 = radius2 * radius2;
143 hugin_utils::FDiff2D pf = hugin_utils::FDiff2D(p) - cropCenter;
144 insideCrop = (radius2 > pf.x*pf.x+pf.y*pf.y );
145 }
146 }
147 if(insideCrop && !ignoreMasks)
148 return !(isInsideMasks(p));
149 else
150 return insideCrop;
151 }
152
isCircularCrop() const153 bool SrcPanoImage::isCircularCrop() const
154 {
155 HuginBase::BaseSrcPanoImage::Projection projection=m_Projection.getData();
156 return (projection==CIRCULAR_FISHEYE || projection==FISHEYE_THOBY || projection==FISHEYE_ORTHOGRAPHIC);
157 };
158
getCorrectTCA() const159 bool SrcPanoImage::getCorrectTCA() const
160 {
161 bool nr = (m_RadialDistortionRed.getData()[0] == 0.0 && m_RadialDistortionRed.getData()[1] == 0.0 &&
162 m_RadialDistortionRed.getData()[2] == 0.0 && m_RadialDistortionRed.getData()[3] == 1);
163 bool nb = (m_RadialDistortionBlue.getData()[0] == 0.0 && m_RadialDistortionBlue.getData()[1] == 0.0 &&
164 m_RadialDistortionBlue.getData()[2] == 0.0 && m_RadialDistortionBlue.getData()[3] == 1);
165 return !(nr && nb);
166 }
167
168
getRadialDistortionCenter() const169 hugin_utils::FDiff2D SrcPanoImage::getRadialDistortionCenter() const
170 {
171 return hugin_utils::FDiff2D(m_Size.getData()) / 2.0 + m_RadialDistortionCenterShift.getData();
172 }
173
174
getRadialVigCorrCenter() const175 hugin_utils::FDiff2D SrcPanoImage::getRadialVigCorrCenter() const
176 {
177 return (hugin_utils::FDiff2D(m_Size.getData()) - hugin_utils::FDiff2D(1, 1)) / 2.0 + m_RadialVigCorrCenterShift.getData();
178 }
179
setCropMode(CropMode val)180 void SrcPanoImage::setCropMode(CropMode val)
181 {
182 m_CropMode.setData(val);
183 if (val == NO_CROP) {
184 m_CropRect.setData(vigra::Rect2D(m_Size.getData()));
185 }
186 }
187
setSize(vigra::Size2D val)188 void SrcPanoImage::setSize(vigra::Size2D val)
189 {
190 m_Size.setData(val);
191 if (m_CropMode.getData() == NO_CROP) {
192 m_CropRect.setData(vigra::Rect2D(val));
193 }
194 }
195
getExposure() const196 double SrcPanoImage::getExposure() const
197 { return 1.0/pow(2.0, m_ExposureValue.getData()); }
198
setExposure(const double & val)199 void SrcPanoImage::setExposure(const double & val)
200 { m_ExposureValue.setData(log2(1/val)); }
201
202
operator ==(const BaseSrcPanoImage & other) const203 bool BaseSrcPanoImage::operator==(const BaseSrcPanoImage & other) const
204 {
205 DEBUG_TRACE("");
206 return (
207 #define image_variable( name, type, default_value ) \
208 m_##name.getData() == other.m_##name.getData() &&
209 #include "image_variables.h"
210 #undef image_variable
211 true // All the variable checks above end with && so we need this.
212 );
213 }
214
215 // convinience functions to extract a set of variables
getVar(const std::string & code) const216 double SrcPanoImage::getVar(const std::string & code) const
217 {
218 DEBUG_TRACE("");
219 assert(!code.empty());
220 #define image_variable( name, type, default_value ) \
221 if (PTOVariableConverterFor##name::checkApplicability(code)) \
222 return PTOVariableConverterFor##name::getValueFromVariable(code, m_##name );\
223 else
224 #include "image_variables.h"
225 #undef image_variable
226 {// this is for the final else.
227 DEBUG_ERROR("Unknown variable " << code);
228 }
229 return 0;
230 }
231
setVar(const std::string & code,double val)232 void SrcPanoImage::setVar(const std::string & code, double val)
233 {
234 DEBUG_TRACE("Var:" << code << " value: " << val);
235 assert(!code.empty());
236 #define image_variable( name, type, default_value ) \
237 if (PTOVariableConverterFor##name::checkApplicability(code)) \
238 {PTOVariableConverterFor##name::setValueFromVariable(code, m_##name, val);}\
239 else
240 #include "image_variables.h"
241 #undef image_variable
242 {// this is for the final else.
243 DEBUG_ERROR("Unknown variable " << code);
244 }
245 }
246
getVariableMap() const247 VariableMap SrcPanoImage::getVariableMap() const
248 {
249 // make a variable map vector
250
251 // fill variable map with details about this image.
252 // position
253 DEBUG_TRACE("");
254
255 VariableMap vars;
256 #define image_variable( name, type, default_value ) \
257 PTOVariableConverterFor##name::addToVariableMap(m_##name, vars);
258 #include "image_variables.h"
259 #undef image_variable
260
261 return vars;
262 }
263
checkImageSizeKnown()264 bool SrcPanoImage::checkImageSizeKnown()
265 {
266 if(getWidth()<=0 || getHeight()<=0)
267 {
268 try
269 {
270 vigra::ImageImportInfo info(getFilename().c_str());
271 setSize(info.size());
272 // save pixeltype for later, so we don't need to parse the file again
273 const std::string pixeltype(info.getPixelType());
274 FileMetaData metaData = getFileMetadata();
275 metaData["pixeltype"] = pixeltype;
276 setFileMetadata(metaData);
277 }
278 catch(std::exception & )
279 {
280 return false;
281 }
282 };
283 return true;
284
285 };
286
readEXIF()287 bool SrcPanoImage::readEXIF()
288 {
289 std::string filename = getFilename();
290 double roll = 0;
291 // clear all old values
292 setFileMetadata(FileMetaData());
293 setExifExposureTime(0);
294 setExifAperture(0);
295 setExifExposureMode(0);
296 setExifISO(0);
297 setExifMake(std::string(""));
298 setExifModel(std::string(""));
299 setExifLens(std::string(""));
300 setExifOrientation(0);
301 setExifFocalLength(0);
302 setExifFocalLength35(0);
303 setExifCropFactor(0);
304 setExifDistance(0);
305 setExifDate(std::string(""));
306 setExifRedBalance(1);
307 setExifBlueBalance(1);
308
309 if(!checkImageSizeKnown())
310 {
311 return false;
312 };
313
314 // if width==2*height assume equirectangular image
315 if (getWidth() == 2 * getHeight())
316 {
317 FileMetaData metaData = getFileMetadata();
318 metaData["projection"] = "equirectangular";
319 metaData["HFOV"] = "360";
320 setFileMetadata(metaData);
321 };
322
323 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
324 Exiv2::Image::UniquePtr image;
325 #else
326 Exiv2::Image::AutoPtr image;
327 #endif
328 try {
329 image = Exiv2::ImageFactory::open(filename.c_str());
330 }
331 catch (const Exiv2::Error& e)
332 {
333 std::cerr << "Exiv2: Error reading metadata (" << e.what() << ")" << std::endl;
334 return false;
335 }
336
337 try
338 {
339 image->readMetadata();
340 }
341 catch (const Exiv2::Error& e)
342 {
343 std::cerr << "Caught Exiv2 exception '" << e.what() << "' for file " << filename << std::endl;
344 return false;
345 }
346
347 // look into XMP metadata
348 Exiv2::XmpData& xmpData = image->xmpData();
349 if (!xmpData.empty())
350 {
351 // we need to catch exceptions in case file does not contain any GPano tags
352 try
353 {
354 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.ProjectionType"));
355 FileMetaData metaData = getFileMetadata();
356 if (pos != xmpData.end())
357 {
358 if (hugin_utils::tolower(pos->toString()) == "equirectangular")
359 {
360 long croppedWidth = 0;
361 long croppedHeight = 0;
362 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageWidthPixels"));
363 if (pos != xmpData.end())
364 {
365 croppedWidth = pos->toLong();
366 }
367 else
368 {
369 // tag is required
370 throw std::logic_error("Required tag CroppedAreaImageWidthPixels missing");
371 };
372 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageHeightPixels"));
373 if (pos != xmpData.end())
374 {
375 croppedHeight = pos->toLong();
376 }
377 else
378 {
379 // tag is required
380 throw std::logic_error("Required tag CroppedAreaImageHeightPixels missing");
381 };
382 // check if sizes matches, if not ignore all tags
383 if (getWidth() == croppedWidth && getHeight() == croppedHeight)
384 {
385 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoWidthPixels"));
386 double hfov = 0;
387 if (pos != xmpData.end())
388 {
389 hfov = 360 * croppedWidth / (double)pos->toLong();
390 }
391 else
392 {
393 // tag is required
394 throw std::logic_error("Required tag FullPanoWidthPixels missing");
395 };
396 long fullHeight = 0;
397 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoHeightPixels"));
398 if (pos != xmpData.end())
399 {
400 fullHeight = pos->toLong();
401 }
402 else
403 {
404 // tag is required
405 throw std::logic_error("Required tag FullPanoHeightPixels missing");
406 };
407 long cropTop = 0;
408 pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaTopPixels"));
409 if (pos != xmpData.end())
410 {
411 cropTop = pos->toLong();
412 }
413 else
414 {
415 // tag is required
416 throw std::logic_error("Required tag CroppedAreaTopPixels missing");
417 };
418
419 // all found, remember for later
420 metaData["projection"] = "equirectangular";
421 metaData["HFOV"] = hugin_utils::doubleToString(hfov, 3);
422 metaData["e"] = hugin_utils::doubleToString(-cropTop - ((getHeight() - fullHeight) / 2.0), 4);
423 setFileMetadata(metaData);
424 };
425 };
426 };
427 }
428 catch (std::exception& e)
429 {
430 // just to catch error when image contains no GPano tags
431 std::cerr << "Error reading GPano tags from " << filename << "(" << e.what() << ")" << std::endl;
432 };
433 };
434
435 Exiv2::ExifData &exifData = image->exifData();
436 if (exifData.empty()) {
437 std::cerr << "Unable to read EXIF data from opened file:" << filename << std::endl;
438 return !getFileMetadata().empty();
439 }
440
441 setExifExposureTime(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::exposureTime(exifData)));
442 setExifAperture(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::fNumber(exifData)));
443
444 //read exposure mode
445 setExifExposureMode(Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Photo.ExposureMode"));
446
447 // read ISO from EXIF or makernotes
448 setExifISO(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::isoSpeed(exifData)));
449
450 setExifMake(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::make(exifData)));
451 setExifModel(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::model(exifData)));
452
453 //reading lens
454 setExifLens(Exiv2Helper::getLensName(exifData));
455
456 long orientation = Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Image.Orientation");
457 if (orientation>0 && trustExivOrientation())
458 {
459 switch (orientation) {
460 case 3: // rotate 180
461 roll = 180;
462 break;
463 case 6: // rotate 90
464 roll = 90;
465 break;
466 case 8: // rotate 270
467 roll = 270;
468 break;
469 default:
470 break;
471 }
472 }
473
474 long pixXdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelXDimension");
475 long pixYdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelYDimension");
476
477 if (pixXdim !=0 && pixYdim !=0 )
478 {
479 double ratioExif = pixXdim/(double)pixYdim;
480 double ratioImage = getWidth()/(double)getHeight();
481 if (fabs( ratioExif - ratioImage) > 0.1)
482 {
483 // Image has been modified without adjusting exif tags.
484 // Assume user has rotated to upright pose
485 roll = 0;
486 }
487 }
488 // save for later
489 setExifOrientation(roll);
490
491 double cropFactor = 0;
492 DEBUG_DEBUG("cropFactor: " << cropFactor);
493
494 float eFocalLength = Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::focalLength(exifData));
495 float eFocalLength35 = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.FocalLengthIn35mmFilm");
496 float focalLength=0;
497 //The various methods to detmine crop factor
498 if (eFocalLength35 > 0 && eFocalLength > 0)
499 {
500 cropFactor = eFocalLength35 / eFocalLength;
501 focalLength = eFocalLength;
502 }
503 else
504 {
505 if (eFocalLength35 > 0)
506 {
507 // 35 mm equiv focal length available, crop factor unknown.
508 // do not ask for crop factor, assume 1. Probably a full frame sensor
509 cropFactor = 1;
510 focalLength = eFocalLength35;
511 }
512 else
513 {
514 focalLength = (eFocalLength > 0) ? eFocalLength : 0;
515 // alternative way to calculate crop factor
516 cropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
517 // check result
518 if (cropFactor < 0.1)
519 {
520 cropFactor = 0;
521 };
522 };
523 };
524 // check results, if 35 mm focal length is too small reset crop factor to 0
525 if (focalLength > 0 && cropFactor > 0 && focalLength*cropFactor < 6)
526 {
527 cropFactor = 0;
528 // check alternative way to calculate crop factor, e.g. when focal length and focal length in 35 mm are given
529 // and are the same, but not a full frame camera
530 const double newCropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
531 if (newCropFactor > 0)
532 {
533 if (focalLength*newCropFactor >= 6)
534 {
535 cropFactor = newCropFactor;
536 }
537 };
538 };
539
540 setExifFocalLength(focalLength);
541 setExifFocalLength35(eFocalLength35);
542 setExifCropFactor(cropFactor);
543
544 setExifDistance(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::subjectDistance(exifData)));
545 setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
546
547 double redBalance, blueBalance;
548 Exiv2Helper::readRedBlueBalance(exifData, redBalance, blueBalance);
549 setExifRedBalance(redBalance);
550 setExifBlueBalance(blueBalance);
551
552 DEBUG_DEBUG("Results for:" << filename);
553 DEBUG_DEBUG("Focal Length: " << getExifFocalLength());
554 DEBUG_DEBUG("Crop Factor: " << getCropFactor());
555 DEBUG_DEBUG("Roll: " << getExifOrientation());
556
557 return true;
558 }
559
applyEXIFValues(bool applyEVValue)560 bool SrcPanoImage::applyEXIFValues(bool applyEVValue)
561 {
562 setRoll(getExifOrientation());
563 if(applyEVValue)
564 {
565 setExposureValue(calcExifExposureValue());
566 };
567 // special handling for GPano tags
568 FileMetaData metaData = getFileMetadata();
569 if (!metaData.empty())
570 {
571 FileMetaData::const_iterator pos = metaData.find("projection");
572 if (pos != metaData.end())
573 {
574 if (pos->second == "equirectangular")
575 {
576 pos = metaData.find("HFOV");
577 if (pos != metaData.end())
578 {
579 double hfov = 0;
580 hugin_utils::stringToDouble(pos->second, hfov);
581 double e = 0;
582 pos = metaData.find("e");
583 if (pos != metaData.end())
584 {
585 hugin_utils::stringToDouble(pos->second, e);
586 };
587 if (hfov != 0)
588 {
589 setProjection(EQUIRECTANGULAR);
590 setHFOV(hfov);
591 setCropFactor(1.0);
592 hugin_utils::FDiff2D p = getRadialDistortionCenterShift();
593 p.y = e;
594 setRadialDistortionCenterShift(p);
595 return true;
596 };
597 };
598 };
599 };
600 };
601 double cropFactor=getExifCropFactor();
602 double focalLength=getExifFocalLength();
603 if(cropFactor>0.1)
604 {
605 setCropFactor(cropFactor);
606 };
607 if (focalLength > 0 && cropFactor > 0.1)
608 {
609 setHFOV(calcHFOV(getProjection(), focalLength, cropFactor, getSize()));
610 DEBUG_DEBUG("HFOV: " << getHFOV());
611 return true;
612 }
613 else
614 {
615 return false;
616 }
617 }
618
readCropfactorFromDB()619 bool SrcPanoImage::readCropfactorFromDB()
620 {
621 // finally search in lens database
622 if(getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
623 {
624 double dbCrop=0;
625 if(LensDB::LensDB::GetSingleton().GetCropFactor(getExifMake(),getExifModel(),dbCrop))
626 {
627 if(dbCrop>0.1)
628 {
629 setCropFactor(dbCrop);
630 setExifCropFactor(dbCrop);
631 if (getExifFocalLength() > 0)
632 {
633 setHFOV(calcHFOV(getProjection(), getExifFocalLength(), dbCrop, getSize()));
634 };
635 return true;
636 };
637 };
638 };
639 return false;
640 };
641
getDBLensName() const642 std::string SrcPanoImage::getDBLensName() const
643 {
644 std::string lens(getExifLens());
645 if (!lens.empty())
646 {
647 return lens;
648 }
649 lens = getExifMake();
650 if (!lens.empty())
651 {
652 if (!getExifModel().empty())
653 {
654 lens.append("|");
655 lens.append(getExifModel());
656 return lens;
657 };
658 };
659 return std::string();
660 };
661
isFisheye(const BaseSrcPanoImage::Projection & proj)662 bool isFisheye(const BaseSrcPanoImage::Projection& proj)
663 {
664 switch (proj)
665 {
666 case BaseSrcPanoImage::CIRCULAR_FISHEYE:
667 case BaseSrcPanoImage::FULL_FRAME_FISHEYE:
668 case BaseSrcPanoImage::FISHEYE_ORTHOGRAPHIC:
669 case BaseSrcPanoImage::FISHEYE_STEREOGRAPHIC:
670 case BaseSrcPanoImage::FISHEYE_EQUISOLID:
671 case BaseSrcPanoImage::FISHEYE_THOBY:
672 return true;
673 default:
674 return false;
675 };
676 return false;
677 };
678
readProjectionFromDB(const bool ignoreFovRectilinear)679 bool SrcPanoImage::readProjectionFromDB(const bool ignoreFovRectilinear)
680 {
681 bool success=false;
682 double oldFocal = 0;
683 const std::string lensname = getDBLensName();
684 const double focal = getExifFocalLength();
685 if (!lensname.empty())
686 {
687 const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
688 Projection dbProjection;
689 if(lensDB.GetProjection(lensname, dbProjection))
690 {
691 oldFocal = calcFocalLength(getProjection(), getHFOV(), getCropFactor(), getSize());
692 setProjection(dbProjection);
693 success = true;
694 };
695 if (focal>0)
696 {
697 double fov;
698 // read fov only for non rectilinear images
699 // for these relay on the EXIF data, because often user manage to store
700 // wrong values in the database, so ignore them for rectilinear images
701 if ((getProjection() != RECTILINEAR || !ignoreFovRectilinear) && lensDB.GetFov(lensname, focal, fov))
702 {
703 // calculate FOV for given image, take different aspect ratios into account
704 const double newFocal = calcFocalLength(getProjection(), fov, getCropFactor(), vigra::Size2D(3000, 2000));
705 const double newFov = calcHFOV(getProjection(), newFocal, getCropFactor(), getSize());
706 setHFOV(newFov);
707 oldFocal = 0;
708 // for fisheye lenses read also automatically the distortions parameters from lens db
709 // because fisheye often don't follow exactly one of the projection models and need
710 // the distortion parameters to model the real projection of the used fisheye lens
711 if(isFisheye(getProjection()))
712 {
713 std::vector<double> dist;
714 if (lensDB.GetDistortion(lensname, focal, dist))
715 {
716 if (dist.size() == 3)
717 {
718 dist.push_back(1.0 - dist[0] - dist[1] - dist[2]);
719 setRadialDistortion(dist);
720 };
721 };
722 };
723 };
724 vigra::Rect2D dbCropRect;
725 if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
726 {
727 setCropMode(isCircularCrop() ? CROP_CIRCLE : CROP_RECTANGLE);
728 setCropRect(dbCropRect);
729 };
730 };
731 // updated fov after changing projection, if not already done with value from database
732 if (success && oldFocal > 0)
733 {
734 const double newFov = calcHFOV(getProjection(), oldFocal, getCropFactor(), getSize());
735 setHFOV(newFov);
736 };
737 };
738 // store information about reading from database in FileMetadata map
739 if (success)
740 {
741 FileMetaData metaData = getFileMetadata();
742 metaData["readProjectionFromDB"] = "true";
743 setFileMetadata(metaData);
744 };
745
746 return success;
747 };
748
readDistortionFromDB()749 bool SrcPanoImage::readDistortionFromDB()
750 {
751 const std::string lensname = getDBLensName();
752 const double focal = getExifFocalLength();
753 if (!lensname.empty() && focal > 0)
754 {
755 const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
756 std::vector<double> dist;
757 if(lensDB.GetDistortion(lensname, focal, dist))
758 {
759 if(dist.size()==3)
760 {
761 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
762 setRadialDistortion(dist);
763 return true;
764 };
765 };
766 };
767 return false;
768 };
769
readVignettingFromDB()770 bool SrcPanoImage::readVignettingFromDB()
771 {
772 const std::string lensname = getDBLensName();
773 const double focal = getExifFocalLength();
774 if (!lensname.empty() && focal > 0)
775 {
776 const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
777 std::vector<double> vig;
778 if(lensDB.GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
779 {
780 if (vig.size() == 4)
781 {
782 setRadialVigCorrCoeff(vig);
783 return true;
784 };
785 };
786 };
787 return false;
788 };
789
calcHFOV(SrcPanoImage::Projection proj,double fl,double crop,vigra::Size2D imageSize)790 double SrcPanoImage::calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
791 {
792 // calculate diagonal of film
793 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
794 double r = (double)imageSize.x / imageSize.y;
795
796 // calculate the sensor width and height that fit the ratio
797 // the ratio is determined by the size of our image.
798 hugin_utils::FDiff2D sensorSize;
799 sensorSize.x = d / sqrt(1 + 1/(r*r));
800 sensorSize.y = sensorSize.x / r;
801
802 double hfov = 360;
803
804 switch (proj) {
805 case SrcPanoImage::RECTILINEAR:
806 hfov = 2*atan((sensorSize.x/2.0)/fl) * 180.0/M_PI;
807 break;
808 case SrcPanoImage::CIRCULAR_FISHEYE:
809 case SrcPanoImage::FULL_FRAME_FISHEYE:
810 hfov = sensorSize.x / fl * 180/M_PI;
811 break;
812 case SrcPanoImage::EQUIRECTANGULAR:
813 case SrcPanoImage::PANORAMIC:
814 hfov = (sensorSize.x / fl) / M_PI * 180;
815 break;
816 case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
817 {
818 double val=(sensorSize.x/2.0)/fl;
819 double n;
820 double frac=modf(val, &n);
821 hfov = 2 * asin(frac) * 180.0/M_PI + n * 180.0;
822 }
823 break;
824 case SrcPanoImage::FISHEYE_EQUISOLID:
825 hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/M_PI;
826 break;
827 case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
828 hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/M_PI;
829 break;
830 case SrcPanoImage::FISHEYE_THOBY:
831 hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/M_PI/0.713;
832 break;
833 default:
834 hfov = 360;
835 // TODO: add formulas for other projections
836 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
837 }
838 return hfov;
839 }
840
calcFocalLength(SrcPanoImage::Projection proj,double hfov,double crop,vigra::Size2D imageSize)841 double SrcPanoImage::calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
842 {
843 // calculate diagonal of film
844 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
845 double r = (double)imageSize.x / imageSize.y;
846
847 // calculate the sensor width and height that fit the ratio
848 // the ratio is determined by the size of our image.
849 hugin_utils::FDiff2D sensorSize;
850 sensorSize.x = d / sqrt(1 + 1/(r*r));
851 sensorSize.y = sensorSize.x / r;
852
853 switch (proj)
854 {
855 case SrcPanoImage::RECTILINEAR:
856 return (sensorSize.x/2.0) / tan(hfov/180.0*M_PI/2);
857 break;
858 case SrcPanoImage::CIRCULAR_FISHEYE:
859 case SrcPanoImage::FULL_FRAME_FISHEYE:
860 // same projection equation for both fisheye types,
861 // assume equal area projection.
862 return sensorSize.x / (hfov/180*M_PI);
863 break;
864 case SrcPanoImage::EQUIRECTANGULAR:
865 case SrcPanoImage::PANORAMIC:
866 return (sensorSize.x / (hfov/180*M_PI));
867 break;
868 case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
869 {
870 int t=(int)ceil((hfov-180)/360);
871 return (sensorSize.x /2.0) / (2 * t + pow ( -1.0, t) * sin(hfov/180.0*M_PI/2.0));
872 };
873 case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
874 return (sensorSize.x/4.0) / tan(hfov/180.0*M_PI/4.0);
875 case SrcPanoImage::FISHEYE_EQUISOLID:
876 return (sensorSize.x/4.0) / sin(hfov/180.0*M_PI/4.0);
877 case SrcPanoImage::FISHEYE_THOBY:
878 return (sensorSize.x/2.0) / (1.47 * sin(hfov/180.0*M_PI * 0.713 / 2.0));
879 default:
880 // TODO: add formulas for other projections
881 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
882 return 0;
883 }
884 }
885
calcCropFactor(SrcPanoImage::Projection proj,double hfov,double focalLength,vigra::Size2D imageSize)886 double SrcPanoImage::calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
887 {
888 // calculate diagonal of film
889 double r = (double)imageSize.x / imageSize.y;
890
891 double x = 36;
892 switch (proj)
893 {
894 case SrcPanoImage::RECTILINEAR:
895 x = focalLength * tan(hfov/180.0*M_PI/2);
896 break;
897 case SrcPanoImage::CIRCULAR_FISHEYE:
898 case SrcPanoImage::FULL_FRAME_FISHEYE:
899 case SrcPanoImage::EQUIRECTANGULAR:
900 case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
901 case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
902 case SrcPanoImage::FISHEYE_EQUISOLID:
903 case SrcPanoImage::FISHEYE_THOBY:
904 case SrcPanoImage::PANORAMIC:
905 // same projection equation for both fisheye types,
906 // assume equal area projection.
907 x = focalLength * (hfov/180*M_PI);
908 break;
909 default:
910 // TODO: add formulas for other projections
911 DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
912 return 0;
913 }
914 // diagonal of sensor
915 double diag = x * sqrt(1+ 1/(r*r));
916 return sqrt(36.0*36.0 + 24.0*24.0) / diag;
917 }
918
calcExifExposureValue()919 double SrcPanoImage::calcExifExposureValue()
920 {
921 double ev=0;
922 double photoFNumber=getExifAperture();
923 if(photoFNumber==0)
924 {
925 //if no F-number was found in EXIF data assume a f stop of 3.5 to get
926 //a reasonable ev value if shutter time, e. g. for manual lenses is found
927 photoFNumber=3.5;
928 };
929 if (getExifExposureTime() > 0)
930 {
931 double gain = 1;
932 if (getExifISO()> 0)
933 {
934 gain = getExifISO() / 100.0;
935 }
936 ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
937 };
938 return ev;
939 };
940
updateFocalLength(double newFocalLength)941 void SrcPanoImage::updateFocalLength(double newFocalLength)
942 {
943 double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
944 if(newHFOV!=0)
945 {
946 setHFOV(newHFOV);
947 };
948 };
949
updateCropFactor(double focalLength,double newCropFactor)950 void SrcPanoImage::updateCropFactor(double focalLength, double newCropFactor)
951 {
952 double newHFOV=calcHFOV(getProjection(),focalLength,newCropFactor,getSize());
953 if(newHFOV!=0)
954 {
955 setHFOV(newHFOV);
956 };
957 setCropFactor(newCropFactor);
958 };
959
960 // mask handling stuff
addMask(MaskPolygon newMask)961 void SrcPanoImage::addMask(MaskPolygon newMask)
962 {
963 MaskPolygonVector newMasks=m_Masks.getData();
964 newMasks.push_back(newMask);
965 setMasks(newMasks);
966 };
967
addActiveMask(MaskPolygon newMask)968 void SrcPanoImage::addActiveMask(MaskPolygon newMask)
969 {
970 MaskPolygonVector newMasks=m_ActiveMasks.getData();
971 newMasks.push_back(newMask);
972 setActiveMasks(newMasks);
973 };
974
clearActiveMasks()975 void SrcPanoImage::clearActiveMasks()
976 {
977 MaskPolygonVector emptyMaskVector;
978 m_ActiveMasks.setData(emptyMaskVector);
979 };
980
hasMasks() const981 bool SrcPanoImage::hasMasks() const
982 {
983 return !m_Masks.getData().empty();
984 };
985
hasPositiveMasks() const986 bool SrcPanoImage::hasPositiveMasks() const
987 {
988 MaskPolygonVector masks=m_Masks.getData();
989 if(!masks.empty())
990 {
991 for(unsigned int i=0;i<masks.size();i++)
992 {
993 if(masks[i].isPositive())
994 {
995 return true;
996 };
997 };
998 };
999 return false;
1000 };
1001
hasActiveMasks() const1002 bool SrcPanoImage::hasActiveMasks() const
1003 {
1004 return !m_ActiveMasks.getData().empty();
1005 };
1006
printMaskLines(std::ostream & o,unsigned int newImgNr) const1007 void SrcPanoImage::printMaskLines(std::ostream &o, unsigned int newImgNr) const
1008 {
1009 if(!m_Masks.getData().empty())
1010 for(unsigned int i=0;i<m_Masks.getData().size();i++)
1011 m_Masks.getData()[i].printPolygonLine(o, newImgNr);
1012 };
1013
changeMaskType(unsigned int index,HuginBase::MaskPolygon::MaskType newType)1014 void SrcPanoImage::changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
1015 {
1016 if(index<m_Masks.getData().size())
1017 {
1018 MaskPolygonVector editedMasks=m_Masks.getData();
1019 editedMasks[index].setMaskType(newType);
1020 m_Masks.setData(editedMasks);
1021 };
1022 };
1023
deleteMask(unsigned int index)1024 void SrcPanoImage::deleteMask(unsigned int index)
1025 {
1026 if(index<m_Masks.getData().size())
1027 {
1028 MaskPolygonVector oldMasks=m_Masks.getData();
1029 oldMasks.erase(oldMasks.begin()+index);
1030 m_Masks.setData(oldMasks);
1031 };
1032 };
1033
deleteAllMasks()1034 void SrcPanoImage::deleteAllMasks()
1035 {
1036 MaskPolygonVector emptyMaskVector;
1037 m_Masks.setData(emptyMaskVector);
1038 };
1039
isInsideMasks(vigra::Point2D p) const1040 bool SrcPanoImage::isInsideMasks(vigra::Point2D p) const
1041 {
1042 if(!hasActiveMasks())
1043 return false;
1044 bool insideMask=false;
1045 unsigned int i=0;
1046 while(!insideMask && i<m_ActiveMasks.getData().size())
1047 {
1048 insideMask=m_ActiveMasks.getData()[i].isInside(p);
1049 i++;
1050 };
1051 return insideMask;
1052 };
1053
1054 /**
1055 * Decides if the Exiv Orientation Tag of an images is plausible.
1056 * Current checks:
1057 * - If width is smaller than height, image is probably already rotated, tag may be wrong.
1058 * @return true if plausible.
1059 */
trustExivOrientation()1060 bool SrcPanoImage::trustExivOrientation()
1061 {
1062 if(getSize().width() < getSize().height())
1063 return false;
1064
1065 return true;
1066 }
1067
getExifDateTime(struct tm * datetime) const1068 const int SrcPanoImage::getExifDateTime(struct tm* datetime) const
1069 {
1070 //initialize struct
1071 std::memset(datetime, 0x0, sizeof(*datetime));
1072 //ignore daylight saving flag because it is not saved in EXIF date time format
1073 datetime->tm_isdst=-1;
1074 return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
1075 };
1076
1077 } // namespace
1078