Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
51 #include "ImageVariableTranslate.h"
52 
53 namespace HuginBase {
54 
55 void SrcPanoImage::resize(const vigra::Size2D & sz, VariableMap* potentialLinkedVars)
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
74  m_RadialDistortionCenterShift.setData(m_RadialDistortionCenterShift.getData() * scale);
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  {
112  m_RadialVigCorrCenterShift.setData(m_RadialVigCorrCenterShift.getData() * scale);
113  };
114  // resize masks
115  MaskPolygonVector scaledMasks=m_Masks.getData();
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;
133  case FULL_FRAME_FISHEYE:
134  case CIRCULAR_FISHEYE:
135  case RECTILINEAR:
138  case FISHEYE_EQUISOLID:
139  case FISHEYE_THOBY:
140  default:
141  break;
142  }
143  return false;
144 }
145 
146 bool 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  }
160  hugin_utils::FDiff2D cropCenter;
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);
164  radius2 = radius2 * radius2;
165  hugin_utils::FDiff2D pf = hugin_utils::FDiff2D(p) - cropCenter;
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 {
177  HuginBase::BaseSrcPanoImage::Projection projection=m_Projection.getData();
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 
192 {
193  return hugin_utils::FDiff2D(m_Size.getData()) / 2.0 + m_RadialDistortionCenterShift.getData();
194 }
195 
196 
198 {
199  return (hugin_utils::FDiff2D(m_Size.getData()) - hugin_utils::FDiff2D(1, 1)) / 2.0 + m_RadialVigCorrCenterShift.getData();
200 }
201 
203 {
204  m_CropMode.setData(val);
205  if (val == NO_CROP) {
206  m_CropRect.setData(vigra::Rect2D(m_Size.getData()));
207  }
208 }
209 
210 void 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 
221 void SrcPanoImage::setExposure(const double & val)
222 { m_ExposureValue.setData(log2(1/val)); }
223 
224 
225 bool BaseSrcPanoImage::operator==(const BaseSrcPanoImage & other) const
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
238 double 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 
254 void 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());
296  FileMetaData metaData = getFileMetadata();
297  metaData["pixeltype"] = pixeltype;
298  setFileMetadata(metaData);
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
314  setFileMetadata(FileMetaData());
315  setExifExposureTime(0);
316  setExifAperture(0);
317  setExifExposureMode(0);
318  setExifISO(0);
319  setExifMake(std::string(""));
320  setExifModel(std::string(""));
321  setExifLens(std::string(""));
322  setExifOrientation(0);
323  setExifFocalLength(0);
324  setExifFocalLength35(0);
325  setExifCropFactor(0);
326  setExifDistance(0);
327  setExifDate(std::string(""));
328  setExifRedBalance(1);
329  setExifBlueBalance(1);
330 
331  if(!checkImageSizeKnown())
332  {
333  return false;
334  };
335 
336  // if width==2*height assume equirectangular image
337  if (getWidth() == 2 * getHeight())
338  {
339  FileMetaData metaData = getFileMetadata();
340  metaData["projection"] = "equirectangular";
341  metaData["HFOV"] = "360";
342  setFileMetadata(metaData);
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"));
377  FileMetaData metaData = getFileMetadata();
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
413  if (getWidth() == croppedWidth && getHeight() == croppedHeight)
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);
464  metaData["e"] = hugin_utils::doubleToString(-cropTop - ((getHeight() - fullHeight) / 2.0), 4);
465  setFileMetadata(metaData);
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 
483  setExifExposureTime(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::exposureTime(exifData)));
484  setExifAperture(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::fNumber(exifData)));
485 
486  //read exposure mode
487  setExifExposureMode(Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Photo.ExposureMode"));
488 
489  // read ISO from EXIF or makernotes
490  setExifISO(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::isoSpeed(exifData)));
491 
492  setExifMake(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::make(exifData)));
493  setExifModel(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::model(exifData)));
494 
495  //reading lens
496  setExifLens(Exiv2Helper::getLensName(exifData));
497 
498  long orientation = Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Image.Orientation");
499  if (orientation>0 && trustExivOrientation())
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  {
521  double ratioExif = pixXdim/(double)pixYdim;
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 
536  float eFocalLength = Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::focalLength(exifData));
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");
540  if (hugin_utils::StringContainsCaseInsensitive(getExifMake(), "Apple"))
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  {
548  eFocalLength35 *= digitalZoom;
549  };
550  float focalLength=0;
551  //The various methods to detmine crop factor
552  if (eFocalLength35 > 0 && eFocalLength > 0)
553  {
554  cropFactor = eFocalLength35 / eFocalLength;
555  focalLength = 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;
564  focalLength = eFocalLength35;
565  }
566  else
567  {
568  focalLength = (eFocalLength > 0) ? eFocalLength : 0;
569  // alternative way to calculate crop factor
570  cropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
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
584  const double newCropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
585  if (newCropFactor > 0)
586  {
587  if (focalLength*newCropFactor >= 6)
588  {
589  cropFactor = newCropFactor;
590  }
591  };
592  };
593 
594  setExifFocalLength(focalLength);
595  setExifFocalLength35(eFocalLength35);
596  setExifCropFactor(cropFactor);
597 
598  setExifDistance(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::subjectDistance(exifData)));
599  setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
600 
601  double redBalance, blueBalance;
602  Exiv2Helper::readRedBlueBalance(exifData, redBalance, blueBalance);
603  setExifRedBalance(redBalance);
604  setExifBlueBalance(blueBalance);
605 
606  double gpsCoord;
607  if (Exiv2Helper::getExiv2GPSLatitude(exifData, gpsCoord))
608  {
609  FileMetaData metaData = getFileMetadata();
610  metaData["latitude"] = hugin_utils::doubleToString(gpsCoord);
611  setFileMetadata(metaData);
612  };
613  if (Exiv2Helper::getExiv2GPSLongitude(exifData, gpsCoord))
614  {
615  FileMetaData metaData = getFileMetadata();
616  metaData["longitude"] = hugin_utils::doubleToString(gpsCoord);
617  setFileMetadata(metaData);
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 
628 bool SrcPanoImage::applyEXIFValues(bool applyEVValue)
629 {
630  setRoll(getExifOrientation());
631  if(applyEVValue)
632  {
633  setExposureValue(calcExifExposureValue());
634  };
635  // special handling for GPano tags
636  FileMetaData metaData = getFileMetadata();
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  {
653  hugin_utils::stringToDouble(pos->second, e);
654  };
655  if (hfov != 0)
656  {
657  setProjection(EQUIRECTANGULAR);
658  setHFOV(hfov);
659  setCropFactor(1.0);
660  hugin_utils::FDiff2D p = getRadialDistortionCenterShift();
661  p.y = e;
662  setRadialDistortionCenterShift(p);
663  return true;
664  };
665  };
666  };
667  };
668  };
669  double cropFactor=getExifCropFactor();
670  double focalLength=getExifFocalLength();
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;
693  if(LensDB::LensDB::GetSingleton().GetCropFactor(getExifMake(),getExifModel(),dbCrop))
694  {
695  if(dbCrop>0.1)
696  {
697  setCropFactor(dbCrop);
698  setExifCropFactor(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 
710 std::string SrcPanoImage::getDBLensName() const
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 
747 bool SrcPanoImage::readProjectionFromDB(const bool ignoreFovRectilinear)
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  {
756  Projection dbProjection;
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]);
787  setRadialDistortion(dist);
788  };
789  };
790  };
791  };
792  vigra::Rect2D dbCropRect;
793  if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
794  {
796  setCropRect(dbCropRect);
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  {
809  FileMetaData metaData = getFileMetadata();
810  metaData["readProjectionFromDB"] = "true";
811  setFileMetadata(metaData);
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]);
830  setRadialDistortion(dist);
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  {
850  setRadialVigCorrCoeff(vig);
851  return true;
852  };
853  };
854  };
855  return false;
856 };
857 
858 double 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.
866  hugin_utils::FDiff2D sensorSize;
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 
909 double 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.
917  hugin_utils::FDiff2D sensorSize;
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 
954 double 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;
990  double photoFNumber=getExifAperture();
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  }
1004  ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
1005  };
1006  return ev;
1007 };
1008 
1009 void SrcPanoImage::updateFocalLength(double newFocalLength)
1010 {
1011  double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
1012  if(newHFOV!=0)
1013  {
1014  setHFOV(newHFOV);
1015  };
1016 };
1017 
1018 void SrcPanoImage::updateCropFactor(double focalLength, double newCropFactor)
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
1030 {
1031  MaskPolygonVector newMasks=m_Masks.getData();
1032  newMasks.push_back(newMask);
1033  setMasks(newMasks);
1034 };
1035 
1037 {
1038  MaskPolygonVector newMasks=m_ActiveMasks.getData();
1039  newMasks.push_back(newMask);
1040  setActiveMasks(newMasks);
1041 };
1042 
1044 {
1045  MaskPolygonVector emptyMaskVector;
1046  m_ActiveMasks.setData(emptyMaskVector);
1047 };
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 
1075 void 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  {
1086  MaskPolygonVector editedMasks=m_Masks.getData();
1087  editedMasks[index].setMaskType(newType);
1088  m_Masks.setData(editedMasks);
1089  };
1090 };
1091 
1092 void SrcPanoImage::deleteMask(unsigned int index)
1093 {
1094  if(index<m_Masks.getData().size())
1095  {
1096  MaskPolygonVector oldMasks=m_Masks.getData();
1097  oldMasks.erase(oldMasks.begin()+index);
1098  m_Masks.setData(oldMasks);
1099  };
1100 };
1101 
1103 {
1104  MaskPolygonVector emptyMaskVector;
1105  m_Masks.setData(emptyMaskVector);
1106 };
1107 
1108 bool 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 
1136 const int SrcPanoImage::getExifDateTime(struct tm* datetime) const
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
const std::string getLensName(Exiv2::ExifData &exifData)
void setCropMode(CropMode val)
Set the crop mode.
bool trustExivOrientation()
Check if Exiv orientation tag can be trusted.
hugin_utils::FDiff2D getRadialDistortionCenter() const
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
void addMask(MaskPolygon newMask)
add newMask to list of masks
static LensDB & GetSingleton()
returns the static LensDB instance
Definition: LensDB.cpp:2001
bool isCircularCrop() const
returns true, if projection requires cicular crop
bool GetVignetting(const std::string &lens, const double focal, const double aperture, const double distance, std::vector< double > &vignetting) const
returns the vignetting parameters of the lens
Definition: LensDB.cpp:2205
#define DEBUG_TRACE(msg)
Definition: utils.h:67
static double calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
calculate crop factor, given focal length and hfov
void addActiveMask(MaskPolygon newMask)
add newMask to list of active masks
std::string getDBLensName() const
constructs the lens name for the database it is the lensname if known, for compact cameras it is cons...
bool operator==(const BaseSrcPanoImage &other) const
Check that the variables match.
bool checkImageSizeKnown()
check if the image size is known, if try to load the information from the file
hugin_utils::FDiff2D getRadialVigCorrCenter() const
bool hasActiveMasks() const
returns true, if image has active masks
bool GetProjection(const std::string &lens, BaseSrcPanoImage::Projection &projection) const
returns the projection of the lens
Definition: LensDB.cpp:2040
bool GetCrop(const std::string &lens, const double focal, const vigra::Size2D &imageSize, vigra::Rect2D &cropRect) const
returns the crop of the lens the information for landscape and portrait images are stored separately ...
Definition: LensDB.cpp:2073
void printMaskLines(std::ostream &o, unsigned int newImgNr) const
writes all mask lines to stream, using given image number
a variable has a value and a name.
double calcExifExposureValue()
calculate exposure value
const double getExiv2ValueDouble(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
int getHeight() const
Get the height of the image in pixels.
Definition: SrcPanoImage.h:276
bool isInside(vigra::Point2D p, bool ignoreMasks=false) const
check if a coordinate is inside the source image
bool GetDistortion(const std::string &lens, const double focal, std::vector< double > &distortion) const
returns the distortion parameters of the lens
Definition: LensDB.cpp:2162
std::string doubleToString(double d, int digits)
convert a double to a string, suitable for display within a GUI.
Definition: utils.cpp:228
void updateFocalLength(double newFocalLength)
updates the focal length, changes the hfov to reflect thew newFocalLength
bool readProjectionFromDB(const bool ignoreFovRectilinear=true)
tries to read projection and crop area from lens database you need to call SrcPanoImage::readEXIF bef...
bool hasMasks() const
returns true, if image has masks associated
main database class
Definition: LensDB.h:44
class to access Hugins camera and lens database
void updateCropFactor(double focalLength, double newCropFactor)
updates the crop factor, the hfov is calculates so that focal length remains the same ...
options getSize().area()) int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1
const long getExiv2ValueLong(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
#define DEBUG_WARN(msg)
Definition: utils.h:74
VariableMap getVariableMap() const
Return all the image variables in a variable map.
void setVar(const std::string &name, double val)
void clearActiveMasks()
clears list of active masks
const int getExifDateTime(struct tm *datetime) const
try to convert Exif date time string to struct tm
bool getCorrectTCA() const
const std::string getExiv2ValueString(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator it)
float pow(float a, double b)
Definition: utils.h:181
int getWidth() const
Get the width of the image in pixels.
Definition: SrcPanoImage.h:266
MaskType
enumeration with type of possible masks
Definition: Mask.h:56
void deleteMask(unsigned int index)
delete mask at index
void deleteAllMasks()
delete all masks
void resize(const vigra::Size2D &size, VariableMap *potentialLinkedVars)
&quot;resize&quot; image, adjusts all distortion coefficients for usage with a source image of size size potent...
bool getExiv2GPSLongitude(Exiv2::ExifData &exifData, double &longitude)
bool GetFov(const std::string &lens, const double focal, double &fov) const
returns the field of view of the lens the fov is always returned for a landscape image with aspect ra...
Definition: LensDB.cpp:2119
std::map< std::string, std::string > FileMetaData
typedef for general map for storing metadata in files
Definition: SrcPanoImage.h:55
Base class containing all the variables, but missing some of the other important functions and with s...
Definition: SrcPanoImage.h:67
double getVar(const std::string &name) const
TDiff2D< double > FDiff2D
Definition: hugin_math.h:150
static double calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
calcualte focal length, given crop factor and hfov
std::vector< MaskPolygon > MaskPolygonVector
Definition: Mask.h:147
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition: utils.h:114
#define DEBUG_ERROR(msg)
Definition: utils.h:76
std::map< std::string, Variable > VariableMap
bool StringContainsCaseInsensitive(const std::string &s1, const std::string &s2)
check if s1 contains s2 using case insensitive comparison
Definition: utils.cpp:335
double getExposure() const
const double getCropFactor(Exiv2::ExifData &exifData, long width, long height)
bool getExiv2GPSLatitude(Exiv2::ExifData &exifData, double &latitude)
bool readVignettingFromDB()
tries to read vignetting data from lens database you need to call SrcPanoImage::readEXIF before to fi...
Convenience functions for SrcPanoImage to use on the image variables.
bool readDistortionFromDB()
tries to read distortion data from lens database you need to call SrcPanoImage::readEXIF before to fi...
#define DEBUG_DEBUG(msg)
Definition: utils.h:68
bool isFisheye(const BaseSrcPanoImage::Projection &proj)
bool readEXIF()
try to fill out information about the image, by examining the exif data
void setExposure(const double &val)
void setSize(vigra::Size2D val)
Set the image size in pixels.
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
static void info(const char *fmt,...)
Definition: svm.cpp:95
helper functions to work with Exif data via the exiv2 library
bool isInsideMasks(vigra::Point2D p) const
returns true, if point p is inside of one mask polygon
#define M_PI
Definition: GaborFilter.cpp:34
This file specifies what image variables SrcPanoImg should have.
static T min(T x, T y)
Definition: svm.cpp:62
bool readCropfactorFromDB()
tries to read cropfactor from lens database you need to call SrcPanoImage::readEXIF before to fill so...
bool hasPositiveMasks() const
returns true, if image has positive masks
std::string tolower(const std::string &s)
convert a string to lowercase
Definition: stl_utils.h:49
bool readRedBlueBalance(Exiv2::ExifData &exifData, double &redBalance, double &blueBalance)
base class, which stores one mask polygon
Definition: Mask.h:52
void changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
changes type of mask with index to given newType