SrcPanoImage.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00013 /*
00014  *  This program is free software; you can redistribute it and/or
00015  *  modify it under the terms of the GNU General Public
00016  *  License as published by the Free Software Foundation; either
00017  *  version 2 of the License, or (at your option) any later version.
00018  *
00019  *  This software is distributed in the hope that it will be useful,
00020  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00022  *  General Public License for more details.
00023  *
00024  *  You should have received a copy of the GNU General Public
00025  *  License along with this software. If not, see
00026  *  <http://www.gnu.org/licenses/>.
00027  *
00028  */
00029 
00030 // for debugging
00031 #include <iostream>
00032 #include <stdio.h>
00033 #include <stdexcept>
00034 //#include <wx/wxprec.h>
00035 
00036 #include "SrcPanoImage.h"
00037 
00038 #include <iostream>
00039 #include <vector>
00040 #include <vigra/diff2d.hxx>
00041 #include <vigra/imageinfo.hxx>
00042 #include <hugin_utils/utils.h>
00043 #include <exiv2/exif.hpp>
00044 #include <exiv2/image.hpp>
00045 #include <exiv2/easyaccess.hpp>
00046 #include <lensdb/LensDB.h>
00047 #include "Exiv2Helper.h"
00048 
00049 #ifdef __FreeBSD__
00050 #define log2(x)        (log(x) / M_LN2)
00051 #endif /* __FreeBSD__ */
00052 
00053 #include "ImageVariableTranslate.h"
00054 
00055 namespace HuginBase {
00056 
00057 void SrcPanoImage::resize(const vigra::Size2D & sz)
00058 {
00059         // TODO: check if images have the same orientation.
00060         // calculate scaling ratio
00061         const double scale = (double) sz.x / m_Size.getData().x;
00062         
00063         // center shift
00064         m_RadialDistortionCenterShift.setData(m_RadialDistortionCenterShift.getData() * scale);
00065         m_Shear.setData(m_Shear.getData() * scale);
00066         
00067         // crop
00068         // ensure the scaled rectangle is inside the new image size
00069         switch (m_CropMode.getData())
00070         {
00071             case NO_CROP:
00072                 m_CropRect.setData(vigra::Rect2D(sz));
00073                 break;
00074             case CROP_RECTANGLE:
00075                 {
00076                     vigra::Rect2D rect(m_CropRect.getData());
00077                     rect *= scale;
00078                     rect &= vigra::Rect2D(sz);
00079                     m_CropRect.setData(rect);
00080                 }
00081                 break;
00082             case CROP_CIRCLE:
00083                 {
00084                     vigra::Rect2D rect(m_CropRect.getData());
00085                     rect *= scale;
00086                     m_CropRect.setData(rect);
00087                 }
00088                 break;
00089         }
00090         
00091         m_Size.setData(sz);
00092         // vignetting correction
00093         m_RadialVigCorrCenterShift.setData(m_RadialVigCorrCenterShift.getData() *scale);
00094         // resize masks
00095         MaskPolygonVector scaledMasks=m_Masks.getData();
00096         for(unsigned int i=0;i<scaledMasks.size();i++)
00097             scaledMasks[i].scale(scale);
00098         m_Masks.setData(scaledMasks);
00099         scaledMasks.clear();
00100         scaledMasks=m_ActiveMasks.getData();
00101         for(unsigned int i=0;i<scaledMasks.size();i++)
00102             scaledMasks[i].scale(scale);
00103         m_ActiveMasks.setData(scaledMasks);
00104 }
00105 
00106 bool SrcPanoImage::horizontalWarpNeeded()
00107 {
00108     switch (m_Projection.getData())
00109     {
00110         case PANORAMIC:
00111         case EQUIRECTANGULAR:
00112             if (m_HFOV.getData() == 360) return true;
00113         case FULL_FRAME_FISHEYE:
00114         case CIRCULAR_FISHEYE:
00115         case RECTILINEAR:
00116         case FISHEYE_ORTHOGRAPHIC:
00117         case FISHEYE_STEREOGRAPHIC:
00118         case FISHEYE_EQUISOLID:
00119         case FISHEYE_THOBY:
00120         default:
00121             break;
00122     }
00123     return false;
00124 }
00125 
00126 void BaseSrcPanoImage::setDefaults()
00127 {
00128     /* Some of the vectors are difficult to initalise with the variables list
00129      * header, so we make some local variables which are used in it.
00130      */
00131     // Radial Distortion defaults
00132     std::vector<double> distortion_default(4, 0.0);
00133     distortion_default[3] = 1;
00134     
00135     std::vector<double> RadialVigCorrCoeff_default(4, 0.0);
00136     RadialVigCorrCoeff_default[0] = 1;
00137     HuginBase::MaskPolygonVector defaultMaskVector;
00138 #define image_variable( name, type, default_value ) m_##name.setData(default_value);
00139 #include "image_variables.h"
00140 #undef image_variable
00141 }
00142 
00143 bool SrcPanoImage::isInside(vigra::Point2D p, bool ignoreMasks) const
00144 {
00145     bool insideCrop=false;
00146     switch(m_CropMode.getData()) {
00147         case NO_CROP:
00148         case CROP_RECTANGLE:
00149             insideCrop = m_CropRect.getData().contains(p);
00150             break;
00151         case CROP_CIRCLE:
00152         {
00153             if (0 > p.x || 0 > p.y || p.x >= m_Size.getData().x || p.y >= m_Size.getData().y) {
00154                 // outside image
00155                 return false;
00156             }
00157             hugin_utils::FDiff2D cropCenter;
00158             cropCenter.x = m_CropRect.getData().left() + m_CropRect.getData().width()/2.0;
00159             cropCenter.y = m_CropRect.getData().top() + m_CropRect.getData().height()/2.0;
00160             double radius2 = std::min(m_CropRect.getData().width()/2.0, m_CropRect.getData().height()/2.0);
00161             radius2 = radius2 * radius2;
00162             hugin_utils::FDiff2D pf = hugin_utils::FDiff2D(p) - cropCenter;
00163             insideCrop = (radius2 > pf.x*pf.x+pf.y*pf.y );
00164         }
00165     }
00166     if(insideCrop && !ignoreMasks)
00167         return !(isInsideMasks(p));
00168     else
00169         return insideCrop;
00170 }
00171 
00172 bool SrcPanoImage::isCircularCrop() const
00173 {
00174     HuginBase::BaseSrcPanoImage::Projection projection=m_Projection.getData();
00175     return (projection==CIRCULAR_FISHEYE || projection==FISHEYE_THOBY || projection==FISHEYE_ORTHOGRAPHIC);
00176 };
00177 
00178 bool SrcPanoImage::getCorrectTCA() const
00179 { 
00180     bool nr = (m_RadialDistortionRed.getData()[0] == 0.0 && m_RadialDistortionRed.getData()[1] == 0.0 &&
00181                m_RadialDistortionRed.getData()[2] == 0.0 && m_RadialDistortionRed.getData()[3] == 1);
00182     bool nb = (m_RadialDistortionBlue.getData()[0] == 0.0 && m_RadialDistortionBlue.getData()[1] == 0.0 &&
00183                m_RadialDistortionBlue.getData()[2] == 0.0 && m_RadialDistortionBlue.getData()[3] == 1);
00184     return !(nr && nb);
00185 }
00186 
00187 
00188 hugin_utils::FDiff2D SrcPanoImage::getRadialDistortionCenter() const
00189 {
00190     return hugin_utils::FDiff2D(m_Size.getData()) / 2.0 + m_RadialDistortionCenterShift.getData();
00191 }
00192 
00193 
00194 hugin_utils::FDiff2D SrcPanoImage::getRadialVigCorrCenter() const
00195 {
00196     return (hugin_utils::FDiff2D(m_Size.getData()) - hugin_utils::FDiff2D(1, 1)) / 2.0 + m_RadialVigCorrCenterShift.getData();
00197 }
00198 
00199 void SrcPanoImage::setCropMode(CropMode val)
00200 {
00201     m_CropMode.setData(val);
00202     if (val == NO_CROP) {
00203         m_CropRect.setData(vigra::Rect2D(m_Size.getData()));
00204     }
00205 }
00206 
00207 void SrcPanoImage::setSize(vigra::Size2D val)
00208 {
00209     m_Size.setData(val);
00210     if (m_CropMode.getData() == NO_CROP) {
00211         m_CropRect.setData(vigra::Rect2D(val));
00212     }
00213 }
00214 
00215 double SrcPanoImage::getExposure() const
00216 { return 1.0/pow(2.0, m_ExposureValue.getData()); }
00217 
00218 void SrcPanoImage::setExposure(const double & val)
00219 { m_ExposureValue.setData(log2(1/val)); }
00220 
00221 
00222 bool BaseSrcPanoImage::operator==(const BaseSrcPanoImage & other) const
00223 {
00224     DEBUG_TRACE("");
00225     return (
00226 #define image_variable( name, type, default_value ) \
00227     m_##name.getData() == other.m_##name.getData() &&
00228 #include "image_variables.h"
00229 #undef image_variable
00230     true // All the variable checks above end with && so we need this.
00231     );
00232 }
00233 
00234 // convinience functions to extract a set of variables
00235 double SrcPanoImage::getVar(const std::string & code) const
00236 {
00237     DEBUG_TRACE("");
00238     assert(code.size() > 0);
00239 #define image_variable( name, type, default_value ) \
00240     if (PTOVariableConverterFor##name::checkApplicability(code)) \
00241         return PTOVariableConverterFor##name::getValueFromVariable(code, m_##name );\
00242     else 
00243 #include "image_variables.h"
00244 #undef image_variable
00245     {// this is for the final else.
00246         DEBUG_ERROR("Unknown variable " << code);
00247     }
00248     return 0;
00249 }
00250 
00251 void SrcPanoImage::setVar(const std::string & code, double val)
00252 {
00253     DEBUG_TRACE("Var:" << code << " value: " << val);
00254     assert(code.size() > 0);
00255 #define image_variable( name, type, default_value ) \
00256     if (PTOVariableConverterFor##name::checkApplicability(code)) \
00257         {PTOVariableConverterFor##name::setValueFromVariable(code, m_##name, val);}\
00258     else 
00259 #include "image_variables.h"
00260 #undef image_variable
00261     {// this is for the final else.
00262         DEBUG_ERROR("Unknown variable " << code);
00263     }
00264 }
00265 
00266 VariableMap SrcPanoImage::getVariableMap() const
00267 {
00268     // make a variable map vector
00269     
00270     // fill variable map with details about this image.
00271     // position
00272     DEBUG_TRACE("");
00273 
00274     VariableMap vars;
00275 #define image_variable( name, type, default_value ) \
00276     PTOVariableConverterFor##name::addToVariableMap(m_##name, vars);
00277 #include "image_variables.h"
00278 #undef image_variable
00279 
00280     return vars;
00281 }
00282 
00283 bool SrcPanoImage::checkImageSizeKnown()
00284 {
00285     if(getWidth()<=0 || getHeight()<=0)
00286     {
00287         try
00288         {
00289             vigra::ImageImportInfo info(getFilename().c_str());
00290             setSize(info.size());
00291             // save pixeltype for later, so we don't need to parse the file again
00292             const std::string pixeltype(info.getPixelType());
00293             FileMetaData metaData = getFileMetadata();
00294             metaData["pixeltype"] = pixeltype;
00295             setFileMetadata(metaData);
00296         }
00297         catch(std::exception & )
00298         {
00299             return false;
00300         }
00301     };
00302     return true;
00303 
00304 };
00305 
00306 bool SrcPanoImage::readEXIF()
00307 {
00308     std::string filename = getFilename();
00309     double roll = 0;
00310     // clear all old values
00311     setFileMetadata(FileMetaData());
00312     setExifExposureTime(0);
00313     setExifAperture(0);
00314     setExifExposureMode(0);
00315     setExifISO(0);
00316     setExifMake(std::string(""));
00317     setExifModel(std::string(""));
00318     setExifLens(std::string(""));
00319     setExifOrientation(0);
00320     setExifFocalLength(0);
00321     setExifFocalLength35(0);
00322     setExifCropFactor(0);
00323     setExifDistance(0);
00324     setExifDate(std::string(""));
00325     setExifRedBalance(1);
00326     setExifBlueBalance(1);
00327 
00328     if(!checkImageSizeKnown())
00329     {
00330         return false;
00331     };
00332 
00333     // if width==2*height assume equirectangular image
00334     if (getWidth() == 2 * getHeight())
00335     {
00336         FileMetaData metaData = getFileMetadata();
00337         metaData["projection"] = "equirectangular";
00338         metaData["HFOV"] = "360";
00339         setFileMetadata(metaData);
00340     };
00341 
00342     Exiv2::Image::AutoPtr image;
00343     try {
00344         image = Exiv2::ImageFactory::open(filename.c_str());
00345     }catch(...) {
00346         std::cerr << __FILE__ << " " << __LINE__ << " Error opening file" << std::endl;
00347         return false;
00348     }
00349     if (image.get() == 0) {
00350         std::cerr << "Unable to open file to read EXIF data: " << filename << std::endl;
00351         return false;
00352     }
00353 
00354     try
00355     {
00356         image->readMetadata();
00357     }
00358     catch (const Exiv2::Error& e)
00359     {
00360         std::cerr << "Caught Exiv2 exception '" << e.what() << "' for file " << filename << std::endl;
00361         return false;
00362     }
00363 
00364     // look into XMP metadata
00365     Exiv2::XmpData& xmpData = image->xmpData();
00366     if (!xmpData.empty())
00367     {
00368         // we need to catch exceptions in case file does not contain any GPano tags
00369         try
00370         {
00371             Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.ProjectionType"));
00372             FileMetaData metaData = getFileMetadata();
00373             if (pos != xmpData.end())
00374             {
00375                 if (hugin_utils::tolower(pos->toString()) == "equirectangular")
00376                 {
00377                     long croppedWidth = 0;
00378                     long croppedHeight = 0;
00379                     pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageWidthPixels"));
00380                     if (pos != xmpData.end())
00381                     {
00382                         croppedWidth = pos->toLong();
00383                     }
00384                     else
00385                     {
00386                         // tag is required
00387                         throw std::logic_error("Required tag CroppedAreaImageWidthPixels missing");
00388                     };
00389                     pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageHeightPixels"));
00390                     if (pos != xmpData.end())
00391                     {
00392                         croppedHeight = pos->toLong();
00393                     }
00394                     else
00395                     {
00396                         // tag is required
00397                         throw std::logic_error("Required tag CroppedAreaImageHeightPixels missing");
00398                     };
00399                     // check if sizes matches, if not ignore all tags
00400                     if (getWidth() == croppedWidth && getHeight() == croppedHeight)
00401                     {
00402                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoWidthPixels"));
00403                         double hfov = 0;
00404                         if (pos != xmpData.end())
00405                         {
00406                             hfov = 360 * croppedWidth / (double)pos->toLong();
00407                         }
00408                         else
00409                         {
00410                             // tag is required
00411                             throw std::logic_error("Required tag FullPanoWidthPixels missing");
00412                         };
00413                         long fullHeight = 0;
00414                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoHeightPixels"));
00415                         if (pos != xmpData.end())
00416                         {
00417                             fullHeight = pos->toLong();
00418                         }
00419                         else
00420                         {
00421                             // tag is required
00422                             throw std::logic_error("Required tag FullPanoHeightPixels missing");
00423                         };
00424                         long cropTop = 0;
00425                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaTopPixels"));
00426                         if (pos != xmpData.end())
00427                         {
00428                             cropTop = pos->toLong();
00429                         }
00430                         else
00431                         {
00432                             // tag is required
00433                             throw std::logic_error("Required tag CroppedAreaTopPixels missing");
00434                         };
00435 
00436                         // all found, remember for later
00437                         metaData["projection"] = "equirectangular";
00438                         metaData["HFOV"] = hugin_utils::doubleToString(hfov, 3);
00439                         metaData["e"] = hugin_utils::doubleToString(-cropTop - ((getHeight() - fullHeight) / 2.0), 4);
00440                         setFileMetadata(metaData);
00441                     };
00442                 };
00443             };
00444         }
00445         catch (std::exception& e)
00446         {
00447             // just to catch error when image contains no GPano tags
00448             std::cerr << "Error reading GPano tags from " << filename << "(" << e.what() << ")" << std::endl;
00449         };
00450     };
00451 
00452     Exiv2::ExifData &exifData = image->exifData();
00453     if (exifData.empty()) {
00454         std::cerr << "Unable to read EXIF data from opened file:" << filename << std::endl;
00455         return !getFileMetadata().empty();
00456     }
00457 
00458     setExifExposureTime(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::exposureTime(exifData)));
00459     setExifAperture(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::fNumber(exifData)));
00460     
00461     //read exposure mode
00462     setExifExposureMode(Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Photo.ExposureMode"));
00463 
00464     // read ISO from EXIF or makernotes
00465     setExifISO(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::isoSpeed(exifData)));
00466 
00467     setExifMake(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::make(exifData)));
00468     setExifModel(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::model(exifData)));
00469 
00470     //reading lens
00471     setExifLens(Exiv2Helper::getLensName(exifData));
00472 
00473     long orientation = Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Image.Orientation");
00474     if (orientation>0 && trustExivOrientation())
00475     {
00476         switch (orientation) {
00477             case 3:  // rotate 180
00478                 roll = 180;
00479                 break;
00480             case 6: // rotate 90
00481                 roll = 90;
00482                 break;
00483             case 8: // rotate 270
00484                 roll = 270;
00485                 break;
00486             default:
00487                 break;
00488         }
00489     }
00490 
00491     long pixXdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelXDimension");
00492     long pixYdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelYDimension");
00493 
00494     if (pixXdim !=0 && pixYdim !=0 )
00495     {
00496         double ratioExif = pixXdim/(double)pixYdim;
00497         double ratioImage = getWidth()/(double)getHeight();
00498         if (fabs( ratioExif - ratioImage) > 0.1)
00499         {
00500             // Image has been modified without adjusting exif tags.
00501             // Assume user has rotated to upright pose
00502             roll = 0;
00503         }
00504     }
00505     // save for later
00506     setExifOrientation(roll);
00507     
00508     double cropFactor = 0;
00509     DEBUG_DEBUG("cropFactor: " << cropFactor);
00510 
00511     float eFocalLength = Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::focalLength(exifData));
00512     float eFocalLength35 = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.FocalLengthIn35mmFilm");
00513     float focalLength=0;
00514     //The various methods to detmine crop factor
00515     if (eFocalLength35 > 0 && eFocalLength > 0)
00516     {
00517         cropFactor = eFocalLength35 / eFocalLength;
00518         focalLength = eFocalLength;
00519     }
00520     else
00521     {
00522         if (eFocalLength35 > 0)
00523         {
00524             // 35 mm equiv focal length available, crop factor unknown.
00525             // do not ask for crop factor, assume 1.  Probably a full frame sensor
00526             cropFactor = 1;
00527             focalLength = eFocalLength35;
00528         }
00529         else
00530         {
00531             focalLength = (eFocalLength > 0) ? eFocalLength : 0;
00532             // alternative way to calculate crop factor
00533             cropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
00534             // check result
00535             if (cropFactor < 0.1)
00536             {
00537                 cropFactor = 0;
00538             };
00539         };
00540     };
00541  
00542     setExifFocalLength(focalLength);
00543     setExifFocalLength35(eFocalLength35);
00544     setExifCropFactor(cropFactor);
00545 
00546     setExifDistance(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::subjectDistance(exifData)));
00547     setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
00548 
00549     double redBalance, blueBalance;
00550     Exiv2Helper::readRedBlueBalance(exifData, redBalance, blueBalance);
00551     setExifRedBalance(redBalance);
00552     setExifBlueBalance(blueBalance);
00553 
00554     DEBUG_DEBUG("Results for:" << filename);
00555     DEBUG_DEBUG("Focal Length: " << getExifFocalLength());
00556     DEBUG_DEBUG("Crop Factor:  " << getCropFactor());
00557     DEBUG_DEBUG("Roll:         " << getExifOrientation());
00558 
00559     return true;
00560 }
00561 
00562 bool SrcPanoImage::applyEXIFValues(bool applyEVValue)
00563 {
00564     setRoll(getExifOrientation());
00565     if(applyEVValue)
00566     {
00567         setExposureValue(calcExifExposureValue());
00568     };
00569     // special handling for GPano tags
00570     FileMetaData metaData = getFileMetadata();
00571     if (!metaData.empty())
00572     {
00573         FileMetaData::const_iterator pos = metaData.find("projection");
00574         if (pos != metaData.end())
00575         {
00576             if (pos->second == "equirectangular")
00577             {
00578                 pos = metaData.find("HFOV");
00579                 if (pos != metaData.end())
00580                 {
00581                     double hfov = 0;
00582                     hugin_utils::stringToDouble(pos->second, hfov);
00583                     double e = 0;
00584                     pos = metaData.find("e");
00585                     if (pos != metaData.end())
00586                     {
00587                         hugin_utils::stringToDouble(pos->second, e);
00588                     };
00589                     if (hfov != 0)
00590                     {
00591                         setProjection(EQUIRECTANGULAR);
00592                         setHFOV(hfov);
00593                         setCropFactor(1.0);
00594                         hugin_utils::FDiff2D p = getRadialDistortionCenterShift();
00595                         p.y = e;
00596                         setRadialDistortionCenterShift(p);
00597                         return true;
00598                     };
00599                 };
00600             };
00601         };
00602     };
00603     double cropFactor=getExifCropFactor();
00604     double focalLength=getExifFocalLength();
00605     if(cropFactor>0.1)
00606     {
00607         setCropFactor(cropFactor);
00608     };
00609     if (focalLength > 0 && cropFactor > 0.1)
00610     {
00611         setHFOV(calcHFOV(getProjection(), focalLength, cropFactor, getSize()));
00612         DEBUG_DEBUG("HFOV:         " << getHFOV());
00613         return true;
00614     }
00615     else
00616     {
00617         return false;
00618     }
00619 }
00620 
00621 bool SrcPanoImage::readCropfactorFromDB()
00622 {
00623     // finally search in lens database
00624     if(getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
00625     {
00626         double dbCrop=0;
00627         if(LensDB::LensDB::GetSingleton().GetCropFactor(getExifMake(),getExifModel(),dbCrop))
00628         {
00629             if(dbCrop>0.1)
00630             {
00631                 setCropFactor(dbCrop);
00632                 setExifCropFactor(dbCrop);
00633                 if (getExifFocalLength() > 0)
00634                 {
00635                     setHFOV(calcHFOV(getProjection(), getExifFocalLength(), dbCrop, getSize()));
00636                 };
00637                 return true;
00638             };
00639         };
00640     };
00641     return false;
00642 };
00643 
00644 std::string SrcPanoImage::getDBLensName() const
00645 {
00646     std::string lens(getExifLens());
00647     if (!lens.empty())
00648     {
00649         return lens;
00650     }
00651     lens = getExifMake();
00652     if (!lens.empty())
00653     {
00654         if (!getExifModel().empty())
00655         {
00656             lens.append("|");
00657             lens.append(getExifModel());
00658             return lens;
00659         };
00660     };
00661     return std::string();
00662 };
00663 
00664 bool SrcPanoImage::readProjectionFromDB()
00665 {
00666     bool success=false;
00667     const std::string lensname = getDBLensName();
00668     const double focal = getExifFocalLength();
00669     if (!lensname.empty())
00670     {
00671         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00672         Projection dbProjection;
00673         if(lensDB.GetProjection(lensname, dbProjection))
00674         {
00675             setProjection(dbProjection);
00676             success=true;
00677         };
00678         if (focal>0)
00679         {
00680             double fov;
00681             if (lensDB.GetFov(lensname, focal, fov))
00682             {
00683                 // calculate FOV for given image, take different aspect ratios into account
00684                 const double newFocal = calcFocalLength(getProjection(), fov, getCropFactor(), vigra::Size2D(3000,2000));
00685                 const double newFov = calcHFOV(getProjection(), newFocal, getCropFactor(), getSize());
00686                 setHFOV(newFov);
00687             };
00688             vigra::Rect2D dbCropRect;
00689             if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
00690             {
00691                 setCropMode(isCircularCrop() ? CROP_CIRCLE : CROP_RECTANGLE);
00692                 setCropRect(dbCropRect);
00693             };
00694         };
00695     };
00696     return success;
00697 };
00698 
00699 bool SrcPanoImage::readDistortionFromDB()
00700 {
00701     const std::string lensname = getDBLensName();
00702     const double focal = getExifFocalLength();
00703     if (!lensname.empty() && focal > 0)
00704     {
00705         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00706         std::vector<double> dist;
00707         if(lensDB.GetDistortion(lensname, focal, dist))
00708         {
00709             if(dist.size()==3)
00710             {
00711                 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
00712                 setRadialDistortion(dist);
00713                 return true;
00714             };
00715         };
00716     };
00717     return false;
00718 };
00719 
00720 bool SrcPanoImage::readVignettingFromDB()
00721 {
00722     const std::string lensname = getDBLensName();
00723     const double focal = getExifFocalLength();
00724     if (!lensname.empty() && focal > 0)
00725     {
00726         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00727         std::vector<double> vig;
00728         if(lensDB.GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
00729         {
00730             if (vig.size() == 4)
00731             {
00732                 setRadialVigCorrCoeff(vig);
00733                 return true;
00734             };
00735         };
00736     };
00737     return false;
00738 };
00739 
00740 double SrcPanoImage::calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
00741 {
00742     // calculate diagonal of film
00743     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00744     double r = (double)imageSize.x / imageSize.y;
00745     
00746     // calculate the sensor width and height that fit the ratio
00747     // the ratio is determined by the size of our image.
00748     hugin_utils::FDiff2D sensorSize;
00749     sensorSize.x = d / sqrt(1 + 1/(r*r));
00750     sensorSize.y = sensorSize.x / r;
00751     
00752     double hfov = 360;
00753     
00754     switch (proj) {
00755         case SrcPanoImage::RECTILINEAR:
00756             hfov = 2*atan((sensorSize.x/2.0)/fl)  * 180.0/M_PI;
00757             break;
00758         case SrcPanoImage::CIRCULAR_FISHEYE:
00759         case SrcPanoImage::FULL_FRAME_FISHEYE:
00760             hfov = sensorSize.x / fl * 180/M_PI;
00761             break;
00762         case SrcPanoImage::EQUIRECTANGULAR:
00763         case SrcPanoImage::PANORAMIC:
00764             hfov = (sensorSize.x / fl) / M_PI * 180;
00765             break;
00766         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00767             {
00768                 double val=(sensorSize.x/2.0)/fl;
00769                 double n;
00770                 double frac=modf(val, &n);
00771                 hfov = 2 * asin(frac) * 180.0/M_PI + n * 180.0;
00772             }
00773             break;
00774         case SrcPanoImage::FISHEYE_EQUISOLID:
00775             hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/M_PI;
00776             break;
00777         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00778             hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/M_PI;
00779             break;
00780         case SrcPanoImage::FISHEYE_THOBY:
00781             hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/M_PI/0.713;
00782             break;
00783         default:
00784             hfov = 360;
00785             // TODO: add formulas for other projections
00786             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00787     }
00788     return hfov;
00789 }
00790 
00791 double SrcPanoImage::calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
00792 {
00793     // calculate diagonal of film
00794     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00795     double r = (double)imageSize.x / imageSize.y;
00796     
00797     // calculate the sensor width and height that fit the ratio
00798     // the ratio is determined by the size of our image.
00799     hugin_utils::FDiff2D sensorSize;
00800     sensorSize.x = d / sqrt(1 + 1/(r*r));
00801     sensorSize.y = sensorSize.x / r;
00802     
00803     switch (proj)
00804     {
00805         case SrcPanoImage::RECTILINEAR:
00806             return (sensorSize.x/2.0) / tan(hfov/180.0*M_PI/2);
00807             break;
00808         case SrcPanoImage::CIRCULAR_FISHEYE:
00809         case SrcPanoImage::FULL_FRAME_FISHEYE:
00810             // same projection equation for both fisheye types,
00811             // assume equal area projection.
00812             return sensorSize.x / (hfov/180*M_PI);
00813             break;
00814         case SrcPanoImage::EQUIRECTANGULAR:
00815         case SrcPanoImage::PANORAMIC:
00816             return  (sensorSize.x / (hfov/180*M_PI));
00817             break;
00818         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00819             {
00820                 int t=(int)ceil((hfov-180)/360);
00821                 return (sensorSize.x /2.0) / (2 * t + pow ( -1.0, t) * sin(hfov/180.0*M_PI/2.0));
00822             };
00823         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00824             return (sensorSize.x/4.0) / tan(hfov/180.0*M_PI/4.0);
00825         case SrcPanoImage::FISHEYE_EQUISOLID:
00826             return (sensorSize.x/4.0) / sin(hfov/180.0*M_PI/4.0);
00827         case SrcPanoImage::FISHEYE_THOBY:
00828             return (sensorSize.x/2.0) / (1.47 * sin(hfov/180.0*M_PI * 0.713 / 2.0));
00829         default:
00830             // TODO: add formulas for other projections
00831             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00832             return 0;
00833     }
00834 }
00835 
00836 double SrcPanoImage::calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
00837 {
00838     // calculate diagonal of film
00839     double r = (double)imageSize.x / imageSize.y;
00840 
00841     double x = 36;
00842     switch (proj)
00843     {
00844         case SrcPanoImage::RECTILINEAR:
00845             x = focalLength * tan(hfov/180.0*M_PI/2);
00846             break;
00847         case SrcPanoImage::CIRCULAR_FISHEYE:
00848         case SrcPanoImage::FULL_FRAME_FISHEYE:
00849         case SrcPanoImage::EQUIRECTANGULAR:
00850         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00851         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00852         case SrcPanoImage::FISHEYE_EQUISOLID:
00853         case SrcPanoImage::FISHEYE_THOBY:
00854         case SrcPanoImage::PANORAMIC:
00855             // same projection equation for both fisheye types,
00856             // assume equal area projection.
00857             x = focalLength * (hfov/180*M_PI);
00858             break;
00859         default:
00860             // TODO: add formulas for other projections
00861             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00862             return 0;
00863     }
00864     // diagonal of sensor
00865     double diag = x * sqrt(1+ 1/(r*r));
00866     return sqrt(36.0*36.0 + 24.0*24.0) / diag;
00867 }
00868 
00869 double SrcPanoImage::calcExifExposureValue()
00870 {
00871     double ev=0;
00872     double photoFNumber=getExifAperture();
00873     if(photoFNumber==0)
00874     {
00875         //if no F-number was found in EXIF data assume a f stop of 3.5 to get
00876         //a reasonable ev value if shutter time, e. g. for manual lenses is found
00877         photoFNumber=3.5;
00878     };
00879     if (getExifExposureTime() > 0)
00880     {
00881         double gain = 1;
00882         if (getExifISO()> 0)
00883         {
00884             gain = getExifISO() / 100.0;
00885         }
00886         ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
00887     };
00888     return ev;
00889 };
00890 
00891 void SrcPanoImage::updateFocalLength(double newFocalLength)
00892 {
00893     double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
00894     if(newHFOV!=0)
00895     {
00896         setHFOV(newHFOV);
00897     };
00898 };
00899 
00900 void SrcPanoImage::updateCropFactor(double focalLength, double newCropFactor)
00901 {
00902     double newHFOV=calcHFOV(getProjection(),focalLength,newCropFactor,getSize());
00903     if(newHFOV!=0)
00904     {
00905         setHFOV(newHFOV);
00906     };
00907     setCropFactor(newCropFactor);
00908 };
00909 
00910 // mask handling stuff
00911 void SrcPanoImage::addMask(MaskPolygon newMask)
00912 {
00913     MaskPolygonVector newMasks=m_Masks.getData();
00914     newMasks.push_back(newMask);
00915     setMasks(newMasks);
00916 };
00917 
00918 void SrcPanoImage::addActiveMask(MaskPolygon newMask)
00919 {
00920     MaskPolygonVector newMasks=m_ActiveMasks.getData();
00921     newMasks.push_back(newMask);
00922     setActiveMasks(newMasks);
00923 };
00924 
00925 void SrcPanoImage::clearActiveMasks()
00926 {
00927     MaskPolygonVector emptyMaskVector;
00928     m_ActiveMasks.setData(emptyMaskVector);
00929 };
00930 
00931 bool SrcPanoImage::hasMasks() const
00932 {
00933     return m_Masks.getData().size()>0;
00934 };
00935 
00936 bool SrcPanoImage::hasPositiveMasks() const
00937 {
00938     MaskPolygonVector masks=m_Masks.getData();
00939     if(masks.size()>0)
00940     {
00941         for(unsigned int i=0;i<masks.size();i++)
00942         {
00943             if(masks[i].isPositive())
00944             {
00945                 return true;
00946             };
00947         };
00948     };
00949     return false;
00950 };
00951 
00952 bool SrcPanoImage::hasActiveMasks() const
00953 {
00954     return m_ActiveMasks.getData().size()>0;
00955 };
00956  
00957 void SrcPanoImage::printMaskLines(std::ostream &o, unsigned int newImgNr) const
00958 {
00959     if(m_Masks.getData().size()>0)
00960         for(unsigned int i=0;i<m_Masks.getData().size();i++)
00961             m_Masks.getData()[i].printPolygonLine(o, newImgNr);
00962 };
00963 
00964 void SrcPanoImage::changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
00965 {
00966     if(index<m_Masks.getData().size())
00967     {
00968         MaskPolygonVector editedMasks=m_Masks.getData();
00969         editedMasks[index].setMaskType(newType);
00970         m_Masks.setData(editedMasks);
00971     };
00972 };
00973 
00974 void SrcPanoImage::deleteMask(unsigned int index)
00975 {
00976     if(index<m_Masks.getData().size())
00977     {
00978         MaskPolygonVector oldMasks=m_Masks.getData();
00979         oldMasks.erase(oldMasks.begin()+index);
00980         m_Masks.setData(oldMasks);
00981     };
00982 };
00983 
00984 void SrcPanoImage::deleteAllMasks()
00985 {
00986     MaskPolygonVector emptyMaskVector;
00987     m_Masks.setData(emptyMaskVector);
00988 };
00989 
00990 bool SrcPanoImage::isInsideMasks(vigra::Point2D p) const
00991 {
00992     if(!hasActiveMasks())
00993         return false;
00994     bool insideMask=false;
00995     unsigned int i=0;
00996     while(!insideMask && i<m_ActiveMasks.getData().size())
00997     {
00998         insideMask=m_ActiveMasks.getData()[i].isInside(p);
00999         i++;
01000     };
01001     return insideMask;
01002 };
01003 
01010 bool SrcPanoImage::trustExivOrientation()
01011 {
01012     if(getSize().width() < getSize().height())
01013         return false;
01014 
01015     return true;
01016 }
01017 
01018 const int SrcPanoImage::getExifDateTime(struct tm* datetime) const
01019 {
01020     //initialize struct
01021     std::memset(datetime, 0x0, sizeof(*datetime));
01022     //ignore daylight saving flag because it is not saved in EXIF date time format
01023     datetime->tm_isdst=-1;
01024     return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
01025 };
01026 
01027 } // namespace

Generated on 4 Dec 2016 for Hugintrunk by  doxygen 1.4.7