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     image->readMetadata();
00355 
00356     // look into XMP metadata
00357     Exiv2::XmpData& xmpData = image->xmpData();
00358     if (!xmpData.empty())
00359     {
00360         // we need to catch exceptions in case file does not contain any GPano tags
00361         try
00362         {
00363             Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.ProjectionType"));
00364             FileMetaData metaData = getFileMetadata();
00365             if (pos != xmpData.end())
00366             {
00367                 if (hugin_utils::tolower(pos->toString()) == "equirectangular")
00368                 {
00369                     long croppedWidth = 0;
00370                     long croppedHeight = 0;
00371                     pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageWidthPixels"));
00372                     if (pos != xmpData.end())
00373                     {
00374                         croppedWidth = pos->toLong();
00375                     }
00376                     else
00377                     {
00378                         // tag is required
00379                         throw std::logic_error("Required tag CroppedAreaImageWidthPixels missing");
00380                     };
00381                     pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaImageHeightPixels"));
00382                     if (pos != xmpData.end())
00383                     {
00384                         croppedHeight = pos->toLong();
00385                     }
00386                     else
00387                     {
00388                         // tag is required
00389                         throw std::logic_error("Required tag CroppedAreaImageHeightPixels missing");
00390                     };
00391                     // check if sizes matches, if not ignore all tags
00392                     if (getWidth() == croppedWidth && getHeight() == croppedHeight)
00393                     {
00394                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoWidthPixels"));
00395                         double hfov = 0;
00396                         if (pos != xmpData.end())
00397                         {
00398                             hfov = 360 * croppedWidth / (double)pos->toLong();
00399                         }
00400                         else
00401                         {
00402                             // tag is required
00403                             throw std::logic_error("Required tag FullPanoWidthPixels missing");
00404                         };
00405                         long fullHeight = 0;
00406                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.FullPanoHeightPixels"));
00407                         if (pos != xmpData.end())
00408                         {
00409                             fullHeight = pos->toLong();
00410                         }
00411                         else
00412                         {
00413                             // tag is required
00414                             throw std::logic_error("Required tag FullPanoHeightPixels missing");
00415                         };
00416                         long cropTop = 0;
00417                         pos = xmpData.findKey(Exiv2::XmpKey("Xmp.GPano.CroppedAreaTopPixels"));
00418                         if (pos != xmpData.end())
00419                         {
00420                             cropTop = pos->toLong();
00421                         }
00422                         else
00423                         {
00424                             // tag is required
00425                             throw std::logic_error("Required tag CroppedAreaTopPixels missing");
00426                         };
00427 
00428                         // all found, remember for later
00429                         metaData["projection"] = "equirectangular";
00430                         metaData["HFOV"] = hugin_utils::doubleToString(hfov, 3);
00431                         metaData["e"] = hugin_utils::doubleToString(-cropTop - ((getHeight() - fullHeight) / 2.0), 4);
00432                         setFileMetadata(metaData);
00433                     };
00434                 };
00435             };
00436         }
00437         catch (std::exception& e)
00438         {
00439             // just to catch error when image contains no GPano tags
00440             std::cerr << "Error reading GPano tags from " << filename << "(" << e.what() << ")" << std::endl;
00441         };
00442     };
00443 
00444     Exiv2::ExifData &exifData = image->exifData();
00445     if (exifData.empty()) {
00446         std::cerr << "Unable to read EXIF data from opened file:" << filename << std::endl;
00447         return !getFileMetadata().empty();
00448     }
00449 
00450     setExifExposureTime(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::exposureTime(exifData)));
00451     setExifAperture(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::fNumber(exifData)));
00452     
00453     //read exposure mode
00454     setExifExposureMode(Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Photo.ExposureMode"));
00455 
00456     // read ISO from EXIF or makernotes
00457     setExifISO(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::isoSpeed(exifData)));
00458 
00459     setExifMake(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::make(exifData)));
00460     setExifModel(Exiv2Helper::getExiv2ValueString(exifData, Exiv2::model(exifData)));
00461 
00462     //reading lens
00463     setExifLens(Exiv2Helper::getLensName(exifData));
00464 
00465     long orientation = Exiv2Helper::getExiv2ValueLong(exifData, "Exif.Image.Orientation");
00466     if (orientation>0 && trustExivOrientation())
00467     {
00468         switch (orientation) {
00469             case 3:  // rotate 180
00470                 roll = 180;
00471                 break;
00472             case 6: // rotate 90
00473                 roll = 90;
00474                 break;
00475             case 8: // rotate 270
00476                 roll = 270;
00477                 break;
00478             default:
00479                 break;
00480         }
00481     }
00482 
00483     long pixXdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelXDimension");
00484     long pixYdim = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.PixelYDimension");
00485 
00486     if (pixXdim !=0 && pixYdim !=0 )
00487     {
00488         double ratioExif = pixXdim/(double)pixYdim;
00489         double ratioImage = getWidth()/(double)getHeight();
00490         if (fabs( ratioExif - ratioImage) > 0.1)
00491         {
00492             // Image has been modified without adjusting exif tags.
00493             // Assume user has rotated to upright pose
00494             roll = 0;
00495         }
00496     }
00497     // save for later
00498     setExifOrientation(roll);
00499     
00500     double cropFactor = 0;
00501     DEBUG_DEBUG("cropFactor: " << cropFactor);
00502 
00503     float eFocalLength = Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::focalLength(exifData));
00504     float eFocalLength35 = Exiv2Helper::getExiv2ValueLong(exifData,"Exif.Photo.FocalLengthIn35mmFilm");
00505     float focalLength=0;
00506     //The various methods to detmine crop factor
00507     if (eFocalLength35 > 0 && eFocalLength > 0)
00508     {
00509         cropFactor = eFocalLength35 / eFocalLength;
00510         focalLength = eFocalLength;
00511     }
00512     else
00513     {
00514         if (eFocalLength35 > 0)
00515         {
00516             // 35 mm equiv focal length available, crop factor unknown.
00517             // do not ask for crop factor, assume 1.  Probably a full frame sensor
00518             cropFactor = 1;
00519             focalLength = eFocalLength35;
00520         }
00521         else
00522         {
00523             focalLength = (eFocalLength > 0) ? eFocalLength : 0;
00524             // alternative way to calculate crop factor
00525             cropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
00526             // check result
00527             if (cropFactor < 0.1)
00528             {
00529                 cropFactor = 0;
00530             };
00531         };
00532     };
00533  
00534     setExifFocalLength(focalLength);
00535     setExifFocalLength35(eFocalLength35);
00536     setExifCropFactor(cropFactor);
00537 
00538     setExifDistance(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::subjectDistance(exifData)));
00539     setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
00540 
00541     double redBalance, blueBalance;
00542     Exiv2Helper::readRedBlueBalance(exifData, redBalance, blueBalance);
00543     setExifRedBalance(redBalance);
00544     setExifBlueBalance(blueBalance);
00545 
00546     DEBUG_DEBUG("Results for:" << filename);
00547     DEBUG_DEBUG("Focal Length: " << getExifFocalLength());
00548     DEBUG_DEBUG("Crop Factor:  " << getCropFactor());
00549     DEBUG_DEBUG("Roll:         " << getExifOrientation());
00550 
00551     return true;
00552 }
00553 
00554 bool SrcPanoImage::applyEXIFValues(bool applyEVValue)
00555 {
00556     setRoll(getExifOrientation());
00557     if(applyEVValue)
00558     {
00559         setExposureValue(calcExifExposureValue());
00560     };
00561     // special handling for GPano tags
00562     FileMetaData metaData = getFileMetadata();
00563     if (!metaData.empty())
00564     {
00565         FileMetaData::const_iterator pos = metaData.find("projection");
00566         if (pos != metaData.end())
00567         {
00568             if (pos->second == "equirectangular")
00569             {
00570                 pos = metaData.find("HFOV");
00571                 if (pos != metaData.end())
00572                 {
00573                     double hfov = 0;
00574                     hugin_utils::stringToDouble(pos->second, hfov);
00575                     double e = 0;
00576                     pos = metaData.find("e");
00577                     if (pos != metaData.end())
00578                     {
00579                         hugin_utils::stringToDouble(pos->second, e);
00580                     };
00581                     if (hfov != 0)
00582                     {
00583                         setProjection(EQUIRECTANGULAR);
00584                         setHFOV(hfov);
00585                         setCropFactor(1.0);
00586                         hugin_utils::FDiff2D p = getRadialDistortionCenterShift();
00587                         p.y = e;
00588                         setRadialDistortionCenterShift(p);
00589                         return true;
00590                     };
00591                 };
00592             };
00593         };
00594     };
00595     double cropFactor=getExifCropFactor();
00596     double focalLength=getExifFocalLength();
00597     if(cropFactor>0.1)
00598     {
00599         setCropFactor(cropFactor);
00600     };
00601     if (focalLength > 0 && cropFactor > 0.1)
00602     {
00603         setHFOV(calcHFOV(getProjection(), focalLength, cropFactor, getSize()));
00604         DEBUG_DEBUG("HFOV:         " << getHFOV());
00605         return true;
00606     }
00607     else
00608     {
00609         return false;
00610     }
00611 }
00612 
00613 bool SrcPanoImage::readCropfactorFromDB()
00614 {
00615     // finally search in lens database
00616     if(getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
00617     {
00618         double dbCrop=0;
00619         if(LensDB::LensDB::GetSingleton().GetCropFactor(getExifMake(),getExifModel(),dbCrop))
00620         {
00621             if(dbCrop>0.1)
00622             {
00623                 setCropFactor(dbCrop);
00624                 setExifCropFactor(dbCrop);
00625                 if (getExifFocalLength() > 0)
00626                 {
00627                     setHFOV(calcHFOV(getProjection(), getExifFocalLength(), dbCrop, getSize()));
00628                 };
00629                 return true;
00630             };
00631         };
00632     };
00633     return false;
00634 };
00635 
00636 std::string SrcPanoImage::getDBLensName() const
00637 {
00638     std::string lens(getExifLens());
00639     if (!lens.empty())
00640     {
00641         return lens;
00642     }
00643     lens = getExifMake();
00644     if (!lens.empty())
00645     {
00646         if (!getExifModel().empty())
00647         {
00648             lens.append("|");
00649             lens.append(getExifModel());
00650             return lens;
00651         };
00652     };
00653     return std::string();
00654 };
00655 
00656 bool SrcPanoImage::readProjectionFromDB()
00657 {
00658     bool success=false;
00659     const std::string lensname = getDBLensName();
00660     const double focal = getExifFocalLength();
00661     if (!lensname.empty())
00662     {
00663         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00664         Projection dbProjection;
00665         if(lensDB.GetProjection(lensname, dbProjection))
00666         {
00667             setProjection(dbProjection);
00668             success=true;
00669         };
00670         if (focal>0)
00671         {
00672             double fov;
00673             if (lensDB.GetFov(lensname, focal, fov))
00674             {
00675                 // calculate FOV for given image, take different aspect ratios into account
00676                 const double newFocal = calcFocalLength(getProjection(), fov, getCropFactor(), vigra::Size2D(3000,2000));
00677                 const double newFov = calcHFOV(getProjection(), newFocal, getCropFactor(), getSize());
00678                 setHFOV(newFov);
00679             };
00680             vigra::Rect2D dbCropRect;
00681             if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
00682             {
00683                 setCropMode(isCircularCrop() ? CROP_CIRCLE : CROP_RECTANGLE);
00684                 setCropRect(dbCropRect);
00685             };
00686         };
00687     };
00688     return success;
00689 };
00690 
00691 bool SrcPanoImage::readDistortionFromDB()
00692 {
00693     const std::string lensname = getDBLensName();
00694     const double focal = getExifFocalLength();
00695     if (!lensname.empty() && focal > 0)
00696     {
00697         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00698         std::vector<double> dist;
00699         if(lensDB.GetDistortion(lensname, focal, dist))
00700         {
00701             if(dist.size()==3)
00702             {
00703                 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
00704                 setRadialDistortion(dist);
00705                 return true;
00706             };
00707         };
00708     };
00709     return false;
00710 };
00711 
00712 bool SrcPanoImage::readVignettingFromDB()
00713 {
00714     const std::string lensname = getDBLensName();
00715     const double focal = getExifFocalLength();
00716     if (!lensname.empty() && focal > 0)
00717     {
00718         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00719         std::vector<double> vig;
00720         if(lensDB.GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
00721         {
00722             if (vig.size() == 4)
00723             {
00724                 setRadialVigCorrCoeff(vig);
00725                 return true;
00726             };
00727         };
00728     };
00729     return false;
00730 };
00731 
00732 double SrcPanoImage::calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
00733 {
00734     // calculate diagonal of film
00735     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00736     double r = (double)imageSize.x / imageSize.y;
00737     
00738     // calculate the sensor width and height that fit the ratio
00739     // the ratio is determined by the size of our image.
00740     hugin_utils::FDiff2D sensorSize;
00741     sensorSize.x = d / sqrt(1 + 1/(r*r));
00742     sensorSize.y = sensorSize.x / r;
00743     
00744     double hfov = 360;
00745     
00746     switch (proj) {
00747         case SrcPanoImage::RECTILINEAR:
00748             hfov = 2*atan((sensorSize.x/2.0)/fl)  * 180.0/M_PI;
00749             break;
00750         case SrcPanoImage::CIRCULAR_FISHEYE:
00751         case SrcPanoImage::FULL_FRAME_FISHEYE:
00752             hfov = sensorSize.x / fl * 180/M_PI;
00753             break;
00754         case SrcPanoImage::EQUIRECTANGULAR:
00755         case SrcPanoImage::PANORAMIC:
00756             hfov = (sensorSize.x / fl) / M_PI * 180;
00757             break;
00758         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00759             {
00760                 double val=(sensorSize.x/2.0)/fl;
00761                 double n;
00762                 double frac=modf(val, &n);
00763                 hfov = 2 * asin(frac) * 180.0/M_PI + n * 180.0;
00764             }
00765             break;
00766         case SrcPanoImage::FISHEYE_EQUISOLID:
00767             hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/M_PI;
00768             break;
00769         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00770             hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/M_PI;
00771             break;
00772         case SrcPanoImage::FISHEYE_THOBY:
00773             hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/M_PI/0.713;
00774             break;
00775         default:
00776             hfov = 360;
00777             // TODO: add formulas for other projections
00778             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00779     }
00780     return hfov;
00781 }
00782 
00783 double SrcPanoImage::calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
00784 {
00785     // calculate diagonal of film
00786     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00787     double r = (double)imageSize.x / imageSize.y;
00788     
00789     // calculate the sensor width and height that fit the ratio
00790     // the ratio is determined by the size of our image.
00791     hugin_utils::FDiff2D sensorSize;
00792     sensorSize.x = d / sqrt(1 + 1/(r*r));
00793     sensorSize.y = sensorSize.x / r;
00794     
00795     switch (proj)
00796     {
00797         case SrcPanoImage::RECTILINEAR:
00798             return (sensorSize.x/2.0) / tan(hfov/180.0*M_PI/2);
00799             break;
00800         case SrcPanoImage::CIRCULAR_FISHEYE:
00801         case SrcPanoImage::FULL_FRAME_FISHEYE:
00802             // same projection equation for both fisheye types,
00803             // assume equal area projection.
00804             return sensorSize.x / (hfov/180*M_PI);
00805             break;
00806         case SrcPanoImage::EQUIRECTANGULAR:
00807         case SrcPanoImage::PANORAMIC:
00808             return  (sensorSize.x / (hfov/180*M_PI));
00809             break;
00810         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00811             {
00812                 int t=(int)ceil((hfov-180)/360);
00813                 return (sensorSize.x /2.0) / (2 * t + pow ( -1.0, t) * sin(hfov/180.0*M_PI/2.0));
00814             };
00815         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00816             return (sensorSize.x/4.0) / tan(hfov/180.0*M_PI/4.0);
00817         case SrcPanoImage::FISHEYE_EQUISOLID:
00818             return (sensorSize.x/4.0) / sin(hfov/180.0*M_PI/4.0);
00819         case SrcPanoImage::FISHEYE_THOBY:
00820             return (sensorSize.x/2.0) / (1.47 * sin(hfov/180.0*M_PI * 0.713 / 2.0));
00821         default:
00822             // TODO: add formulas for other projections
00823             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00824             return 0;
00825     }
00826 }
00827 
00828 double SrcPanoImage::calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
00829 {
00830     // calculate diagonal of film
00831     double r = (double)imageSize.x / imageSize.y;
00832 
00833     double x = 36;
00834     switch (proj)
00835     {
00836         case SrcPanoImage::RECTILINEAR:
00837             x = focalLength * tan(hfov/180.0*M_PI/2);
00838             break;
00839         case SrcPanoImage::CIRCULAR_FISHEYE:
00840         case SrcPanoImage::FULL_FRAME_FISHEYE:
00841         case SrcPanoImage::EQUIRECTANGULAR:
00842         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00843         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00844         case SrcPanoImage::FISHEYE_EQUISOLID:
00845         case SrcPanoImage::FISHEYE_THOBY:
00846         case SrcPanoImage::PANORAMIC:
00847             // same projection equation for both fisheye types,
00848             // assume equal area projection.
00849             x = focalLength * (hfov/180*M_PI);
00850             break;
00851         default:
00852             // TODO: add formulas for other projections
00853             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00854             return 0;
00855     }
00856     // diagonal of sensor
00857     double diag = x * sqrt(1+ 1/(r*r));
00858     return sqrt(36.0*36.0 + 24.0*24.0) / diag;
00859 }
00860 
00861 double SrcPanoImage::calcExifExposureValue()
00862 {
00863     double ev=0;
00864     double photoFNumber=getExifAperture();
00865     if(photoFNumber==0)
00866     {
00867         //if no F-number was found in EXIF data assume a f stop of 3.5 to get
00868         //a reasonable ev value if shutter time, e. g. for manual lenses is found
00869         photoFNumber=3.5;
00870     };
00871     if (getExifExposureTime() > 0)
00872     {
00873         double gain = 1;
00874         if (getExifISO()> 0)
00875         {
00876             gain = getExifISO() / 100.0;
00877         }
00878         ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
00879     };
00880     return ev;
00881 };
00882 
00883 void SrcPanoImage::updateFocalLength(double newFocalLength)
00884 {
00885     double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
00886     if(newHFOV!=0)
00887     {
00888         setHFOV(newHFOV);
00889     };
00890 };
00891 
00892 void SrcPanoImage::updateCropFactor(double focalLength, double newCropFactor)
00893 {
00894     double newHFOV=calcHFOV(getProjection(),focalLength,newCropFactor,getSize());
00895     if(newHFOV!=0)
00896     {
00897         setHFOV(newHFOV);
00898     };
00899     setCropFactor(newCropFactor);
00900 };
00901 
00902 // mask handling stuff
00903 void SrcPanoImage::addMask(MaskPolygon newMask)
00904 {
00905     MaskPolygonVector newMasks=m_Masks.getData();
00906     newMasks.push_back(newMask);
00907     setMasks(newMasks);
00908 };
00909 
00910 void SrcPanoImage::addActiveMask(MaskPolygon newMask)
00911 {
00912     MaskPolygonVector newMasks=m_ActiveMasks.getData();
00913     newMasks.push_back(newMask);
00914     setActiveMasks(newMasks);
00915 };
00916 
00917 void SrcPanoImage::clearActiveMasks()
00918 {
00919     MaskPolygonVector emptyMaskVector;
00920     m_ActiveMasks.setData(emptyMaskVector);
00921 };
00922 
00923 bool SrcPanoImage::hasMasks() const
00924 {
00925     return m_Masks.getData().size()>0;
00926 };
00927 
00928 bool SrcPanoImage::hasPositiveMasks() const
00929 {
00930     MaskPolygonVector masks=m_Masks.getData();
00931     if(masks.size()>0)
00932     {
00933         for(unsigned int i=0;i<masks.size();i++)
00934         {
00935             if(masks[i].isPositive())
00936             {
00937                 return true;
00938             };
00939         };
00940     };
00941     return false;
00942 };
00943 
00944 bool SrcPanoImage::hasActiveMasks() const
00945 {
00946     return m_ActiveMasks.getData().size()>0;
00947 };
00948  
00949 void SrcPanoImage::printMaskLines(std::ostream &o, unsigned int newImgNr) const
00950 {
00951     if(m_Masks.getData().size()>0)
00952         for(unsigned int i=0;i<m_Masks.getData().size();i++)
00953             m_Masks.getData()[i].printPolygonLine(o, newImgNr);
00954 };
00955 
00956 void SrcPanoImage::changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
00957 {
00958     if(index<m_Masks.getData().size())
00959     {
00960         MaskPolygonVector editedMasks=m_Masks.getData();
00961         editedMasks[index].setMaskType(newType);
00962         m_Masks.setData(editedMasks);
00963     };
00964 };
00965 
00966 void SrcPanoImage::deleteMask(unsigned int index)
00967 {
00968     if(index<m_Masks.getData().size())
00969     {
00970         MaskPolygonVector oldMasks=m_Masks.getData();
00971         oldMasks.erase(oldMasks.begin()+index);
00972         m_Masks.setData(oldMasks);
00973     };
00974 };
00975 
00976 void SrcPanoImage::deleteAllMasks()
00977 {
00978     MaskPolygonVector emptyMaskVector;
00979     m_Masks.setData(emptyMaskVector);
00980 };
00981 
00982 bool SrcPanoImage::isInsideMasks(vigra::Point2D p) const
00983 {
00984     if(!hasActiveMasks())
00985         return false;
00986     bool insideMask=false;
00987     unsigned int i=0;
00988     while(!insideMask && i<m_ActiveMasks.getData().size())
00989     {
00990         insideMask=m_ActiveMasks.getData()[i].isInside(p);
00991         i++;
00992     };
00993     return insideMask;
00994 };
00995 
01002 bool SrcPanoImage::trustExivOrientation()
01003 {
01004     if(getSize().width() < getSize().height())
01005         return false;
01006 
01007     return true;
01008 }
01009 
01010 const int SrcPanoImage::getExifDateTime(struct tm* datetime) const
01011 {
01012     //initialize struct
01013     std::memset(datetime, 0x0, sizeof(*datetime));
01014     //ignore daylight saving flag because it is not saved in EXIF date time format
01015     datetime->tm_isdst=-1;
01016     return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
01017 };
01018 
01019 } // namespace

Generated on 29 Aug 2016 for Hugintrunk by  doxygen 1.4.7