40 #include <vigra/diff2d.hxx>
41 #include <vigra/imageinfo.hxx>
43 #include <exiv2/exiv2.hpp>
48 #define log2(x) (log(x) / M_LN2)
59 const double scale = (double) sz.x / m_Size.getData().x;
62 if (potentialLinkedVars !=
nullptr)
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)));
74 m_RadialDistortionCenterShift.setData(m_RadialDistortionCenterShift.getData() * scale);
75 m_Shear.setData(m_Shear.getData() * scale);
80 switch (m_CropMode.getData())
83 m_CropRect.setData(vigra::Rect2D(sz));
87 vigra::Rect2D rect(m_CropRect.getData());
89 rect &= vigra::Rect2D(sz);
90 m_CropRect.setData(rect);
95 vigra::Rect2D rect(m_CropRect.getData());
97 m_CropRect.setData(rect);
104 if (potentialLinkedVars !=
nullptr)
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)));
112 m_RadialVigCorrCenterShift.setData(m_RadialVigCorrCenterShift.getData() * scale);
116 for(
unsigned int i=0;i<scaledMasks.size();i++)
117 scaledMasks[i].scale(scale);
118 m_Masks.setData(scaledMasks);
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);
128 switch (m_Projection.getData())
132 if (m_HFOV.getData() == 360)
return true;
148 bool insideCrop=
false;
149 switch(m_CropMode.getData()) {
152 insideCrop = m_CropRect.getData().contains(p);
156 if (0 > p.x || 0 > p.y || p.x >= m_Size.getData().x || p.y >= m_Size.getData().y) {
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;
166 insideCrop = (radius2 > pf.
x*pf.
x+pf.
y*pf.
y );
169 if(insideCrop && !ignoreMasks)
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);
204 m_CropMode.setData(val);
206 m_CropRect.setData(vigra::Rect2D(m_Size.getData()));
213 if (m_CropMode.getData() ==
NO_CROP) {
214 m_CropRect.setData(vigra::Rect2D(val));
219 {
return 1.0/
pow(2.0, m_ExposureValue.getData()); }
222 { m_ExposureValue.setData(log2(1/val)); }
229 #define image_variable( name, type, default_value ) \
230 m_##name.getData() == other.m_##name.getData() &&
232 #undef image_variable
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 );\
247 #undef image_variable
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);}\
263 #undef image_variable
278 #define image_variable( name, type, default_value ) \
279 PTOVariableConverterFor##name::addToVariableMap(m_##name, vars);
281 #undef image_variable
292 vigra::ImageImportInfo
info(getFilename().c_str());
295 const std::string pixeltype(info.getPixelType());
297 metaData[
"pixeltype"] = pixeltype;
298 setFileMetadata(metaData);
300 catch(std::exception & )
311 std::string filename = getFilename();
315 setExifExposureTime(0);
317 setExifExposureMode(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);
327 setExifDate(std::string(
""));
328 setExifRedBalance(1);
329 setExifBlueBalance(1);
340 metaData[
"projection"] =
"equirectangular";
341 metaData[
"HFOV"] =
"360";
342 setFileMetadata(metaData);
345 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
346 Exiv2::Image::UniquePtr image;
348 Exiv2::Image::AutoPtr image;
351 image = Exiv2::ImageFactory::open(filename.c_str());
353 catch (
const Exiv2::Error& e)
355 std::cerr <<
"Exiv2: Error reading metadata (" << e.what() <<
")" << std::endl;
361 image->readMetadata();
363 catch (
const Exiv2::Error& e)
365 std::cerr <<
"Caught Exiv2 exception '" << e.what() <<
"' for file " << filename << std::endl;
370 Exiv2::XmpData& xmpData = image->xmpData();
371 if (!xmpData.empty())
376 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.ProjectionType"));
378 if (pos != xmpData.end())
382 long croppedWidth = 0;
383 long croppedHeight = 0;
384 pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.CroppedAreaImageWidthPixels"));
385 if (pos != xmpData.end())
387 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
388 croppedWidth = pos->toInt64();
390 croppedWidth = pos->toLong();
396 throw std::logic_error(
"Required tag CroppedAreaImageWidthPixels missing");
398 pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.CroppedAreaImageHeightPixels"));
399 if (pos != xmpData.end())
401 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
402 croppedHeight = pos->toInt64();
404 croppedHeight = pos->toLong();
410 throw std::logic_error(
"Required tag CroppedAreaImageHeightPixels missing");
415 pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.FullPanoWidthPixels"));
417 if (pos != xmpData.end())
419 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
420 hfov = 360 * croppedWidth / (double)pos->toInt64();
422 hfov = 360 * croppedWidth / (double)pos->toLong();
428 throw std::logic_error(
"Required tag FullPanoWidthPixels missing");
431 pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.FullPanoHeightPixels"));
432 if (pos != xmpData.end())
434 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
435 fullHeight = pos->toInt64();
437 fullHeight = pos->toLong();
443 throw std::logic_error(
"Required tag FullPanoHeightPixels missing");
446 pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.GPano.CroppedAreaTopPixels"));
447 if (pos != xmpData.end())
449 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,28,0)
450 cropTop = pos->toInt64();
452 cropTop = pos->toLong();
458 throw std::logic_error(
"Required tag CroppedAreaTopPixels missing");
462 metaData[
"projection"] =
"equirectangular";
465 setFileMetadata(metaData);
470 catch (std::exception& e)
473 std::cerr <<
"Error reading GPano tags from " << filename <<
"(" << e.what() <<
")" << std::endl;
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();
501 switch (orientation) {
519 if (pixXdim !=0 && pixYdim !=0 )
521 double ratioExif = pixXdim/(double)pixYdim;
523 if (fabs( ratioExif - ratioImage) > 0.1)
531 setExifOrientation(roll);
533 double cropFactor = 0;
546 if (eFocalLength35 > 0 && digitalZoom > 1)
548 eFocalLength35 *= digitalZoom;
552 if (eFocalLength35 > 0 && eFocalLength > 0)
554 cropFactor = eFocalLength35 / eFocalLength;
555 focalLength = eFocalLength;
559 if (eFocalLength35 > 0)
564 focalLength = eFocalLength35;
568 focalLength = (eFocalLength > 0) ? eFocalLength : 0;
572 if (cropFactor < 0.1)
579 if (focalLength > 0 && cropFactor > 0 && focalLength*cropFactor < 6)
585 if (newCropFactor > 0)
587 if (focalLength*newCropFactor >= 6)
589 cropFactor = newCropFactor;
594 setExifFocalLength(focalLength);
595 setExifFocalLength35(eFocalLength35);
596 setExifCropFactor(cropFactor);
601 double redBalance, blueBalance;
603 setExifRedBalance(redBalance);
604 setExifBlueBalance(blueBalance);
611 setFileMetadata(metaData);
617 setFileMetadata(metaData);
621 DEBUG_DEBUG(
"Focal Length: " << getExifFocalLength());
630 setRoll(getExifOrientation());
637 if (!metaData.empty())
639 FileMetaData::const_iterator pos = metaData.find(
"projection");
640 if (pos != metaData.end())
642 if (pos->second ==
"equirectangular")
644 pos = metaData.find(
"HFOV");
645 if (pos != metaData.end())
650 pos = metaData.find(
"e");
651 if (pos != metaData.end())
662 setRadialDistortionCenterShift(p);
669 double cropFactor=getExifCropFactor();
670 double focalLength=getExifFocalLength();
673 setCropFactor(cropFactor);
675 if (focalLength > 0 && cropFactor > 0.1)
677 setHFOV(
calcHFOV(getProjection(), focalLength, cropFactor,
getSize()));
690 if(
getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
697 setCropFactor(dbCrop);
698 setExifCropFactor(dbCrop);
699 if (getExifFocalLength() > 0)
701 setHFOV(
calcHFOV(getProjection(), getExifFocalLength(), dbCrop,
getSize()));
712 std::string lens(getExifLens());
717 lens = getExifMake();
720 if (!getExifModel().empty())
723 lens.append(getExifModel());
727 return std::string();
752 const double focal = getExifFocalLength();
753 if (!lensname.empty())
760 setProjection(dbProjection);
769 if ((getProjection() !=
RECTILINEAR || !ignoreFovRectilinear) && lensDB.
GetFov(lensname, focal, fov))
781 std::vector<double> dist;
784 if (dist.size() == 3)
786 dist.push_back(1.0 - dist[0] - dist[1] - dist[2]);
787 setRadialDistortion(dist);
792 vigra::Rect2D dbCropRect;
796 setCropRect(dbCropRect);
800 if (success && oldFocal > 0)
810 metaData[
"readProjectionFromDB"] =
"true";
811 setFileMetadata(metaData);
820 const double focal = getExifFocalLength();
821 if (!lensname.empty() && focal > 0)
824 std::vector<double> dist;
829 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
830 setRadialDistortion(dist);
841 const double focal = getExifFocalLength();
842 if (!lensname.empty() && focal > 0)
845 std::vector<double> vig;
846 if(lensDB.
GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
850 setRadialVigCorrCoeff(vig);
861 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
862 double r = (double)imageSize.x / imageSize.y;
867 sensorSize.
x = d / sqrt(1 + 1/(r*r));
868 sensorSize.y = sensorSize.x / r;
874 hfov = 2*atan((sensorSize.x/2.0)/fl) * 180.0/
M_PI;
878 hfov = sensorSize.x / fl * 180/
M_PI;
882 hfov = (sensorSize.x / fl) /
M_PI * 180;
886 double val=(sensorSize.x/2.0)/fl;
888 double frac=modf(val, &n);
889 hfov = 2 * asin(frac) * 180.0/
M_PI + n * 180.0;
893 hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/
M_PI;
896 hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/
M_PI;
899 hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/
M_PI/0.713;
904 DEBUG_WARN(
"Focal length calculations only supported with rectilinear and fisheye images");
912 double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
913 double r = (double)imageSize.x / imageSize.y;
918 sensorSize.
x = d / sqrt(1 + 1/(r*r));
919 sensorSize.y = sensorSize.x / r;
924 return (sensorSize.x/2.0) / tan(hfov/180.0*
M_PI/2);
930 return sensorSize.x / (hfov/180*
M_PI);
934 return (sensorSize.x / (hfov/180*
M_PI));
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));
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));
949 DEBUG_WARN(
"Focal length calculations only supported with rectilinear and fisheye images");
957 double r = (double)imageSize.x / imageSize.y;
963 x = focalLength * tan(hfov/180.0*
M_PI/2);
975 x = focalLength * (hfov/180*
M_PI);
979 DEBUG_WARN(
"Focal length calculations only supported with rectilinear and fisheye images");
983 double diag = x * sqrt(1+ 1/(r*r));
984 return sqrt(36.0*36.0 + 24.0*24.0) / diag;
990 double photoFNumber=getExifAperture();
997 if (getExifExposureTime() > 0)
1000 if (getExifISO()> 0)
1002 gain = getExifISO() / 100.0;
1004 ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
1020 double newHFOV=
calcHFOV(getProjection(),focalLength,newCropFactor,
getSize());
1025 setCropFactor(newCropFactor);
1032 newMasks.push_back(newMask);
1039 newMasks.push_back(newMask);
1040 setActiveMasks(newMasks);
1046 m_ActiveMasks.setData(emptyMaskVector);
1051 return !m_Masks.getData().empty();
1059 for(
unsigned int i=0;i<masks.size();i++)
1061 if(masks[i].isPositive())
1072 return !m_ActiveMasks.getData().empty();
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);
1084 if(index<m_Masks.getData().size())
1087 editedMasks[index].setMaskType(newType);
1088 m_Masks.setData(editedMasks);
1094 if(index<m_Masks.getData().size())
1097 oldMasks.erase(oldMasks.begin()+index);
1098 m_Masks.setData(oldMasks);
1105 m_Masks.setData(emptyMaskVector);
1112 bool insideMask=
false;
1114 while(!insideMask && i<m_ActiveMasks.getData().size())
1116 insideMask=m_ActiveMasks.getData()[i].isInside(p);
1139 std::memset(datetime, 0x0,
sizeof(*datetime));
1141 datetime->tm_isdst=-1;
1142 return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
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
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
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
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 ...
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.
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
std::string doubleToString(double d, int digits)
convert a double to a string, suitable for display within a GUI.
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
bool horizontalWarpNeeded()
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)
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)
int getWidth() const
Get the width of the image in pixels.
MaskType
enumeration with type of possible masks
void deleteMask(unsigned int index)
delete mask at index
void deleteAllMasks()
delete all 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...
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...
std::map< std::string, std::string > FileMetaData
typedef for general map for storing metadata in files
Base class containing all the variables, but missing some of the other important functions and with s...
double getVar(const std::string &name) const
TDiff2D< double > FDiff2D
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
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
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
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...
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,...)
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
This file specifies what image variables SrcPanoImg should have.
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
bool readRedBlueBalance(Exiv2::ExifData &exifData, double &redBalance, double &blueBalance)
base class, which stores one mask polygon
void changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
changes type of mask with index to given newType