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     // check results, if 35 mm focal length is too small reset crop factor to 0
00542     if (focalLength > 0 && cropFactor > 0 && focalLength*cropFactor < 8)
00543     {
00544         cropFactor = 0;
00545         // check alternative way to calculate crop factor, e.g. when focal length and focal length in 35 mm are given
00546         // and are the same, but not a full frame camera
00547         const double newCropFactor = Exiv2Helper::getCropFactor(exifData, getWidth(), getHeight());
00548         if (newCropFactor > 0)
00549         {
00550             if (focalLength*newCropFactor > 8)
00551             {
00552                 cropFactor = newCropFactor;
00553             }
00554         };
00555     };
00556  
00557     setExifFocalLength(focalLength);
00558     setExifFocalLength35(eFocalLength35);
00559     setExifCropFactor(cropFactor);
00560 
00561     setExifDistance(Exiv2Helper::getExiv2ValueDouble(exifData, Exiv2::subjectDistance(exifData)));
00562     setExifDate(Exiv2Helper::getExiv2ValueString(exifData, "Exif.Photo.DateTimeOriginal"));
00563 
00564     double redBalance, blueBalance;
00565     Exiv2Helper::readRedBlueBalance(exifData, redBalance, blueBalance);
00566     setExifRedBalance(redBalance);
00567     setExifBlueBalance(blueBalance);
00568 
00569     DEBUG_DEBUG("Results for:" << filename);
00570     DEBUG_DEBUG("Focal Length: " << getExifFocalLength());
00571     DEBUG_DEBUG("Crop Factor:  " << getCropFactor());
00572     DEBUG_DEBUG("Roll:         " << getExifOrientation());
00573 
00574     return true;
00575 }
00576 
00577 bool SrcPanoImage::applyEXIFValues(bool applyEVValue)
00578 {
00579     setRoll(getExifOrientation());
00580     if(applyEVValue)
00581     {
00582         setExposureValue(calcExifExposureValue());
00583     };
00584     // special handling for GPano tags
00585     FileMetaData metaData = getFileMetadata();
00586     if (!metaData.empty())
00587     {
00588         FileMetaData::const_iterator pos = metaData.find("projection");
00589         if (pos != metaData.end())
00590         {
00591             if (pos->second == "equirectangular")
00592             {
00593                 pos = metaData.find("HFOV");
00594                 if (pos != metaData.end())
00595                 {
00596                     double hfov = 0;
00597                     hugin_utils::stringToDouble(pos->second, hfov);
00598                     double e = 0;
00599                     pos = metaData.find("e");
00600                     if (pos != metaData.end())
00601                     {
00602                         hugin_utils::stringToDouble(pos->second, e);
00603                     };
00604                     if (hfov != 0)
00605                     {
00606                         setProjection(EQUIRECTANGULAR);
00607                         setHFOV(hfov);
00608                         setCropFactor(1.0);
00609                         hugin_utils::FDiff2D p = getRadialDistortionCenterShift();
00610                         p.y = e;
00611                         setRadialDistortionCenterShift(p);
00612                         return true;
00613                     };
00614                 };
00615             };
00616         };
00617     };
00618     double cropFactor=getExifCropFactor();
00619     double focalLength=getExifFocalLength();
00620     if(cropFactor>0.1)
00621     {
00622         setCropFactor(cropFactor);
00623     };
00624     if (focalLength > 0 && cropFactor > 0.1)
00625     {
00626         setHFOV(calcHFOV(getProjection(), focalLength, cropFactor, getSize()));
00627         DEBUG_DEBUG("HFOV:         " << getHFOV());
00628         return true;
00629     }
00630     else
00631     {
00632         return false;
00633     }
00634 }
00635 
00636 bool SrcPanoImage::readCropfactorFromDB()
00637 {
00638     // finally search in lens database
00639     if(getCropFactor()<0.1 && !getExifMake().empty() && !getExifModel().empty())
00640     {
00641         double dbCrop=0;
00642         if(LensDB::LensDB::GetSingleton().GetCropFactor(getExifMake(),getExifModel(),dbCrop))
00643         {
00644             if(dbCrop>0.1)
00645             {
00646                 setCropFactor(dbCrop);
00647                 setExifCropFactor(dbCrop);
00648                 if (getExifFocalLength() > 0)
00649                 {
00650                     setHFOV(calcHFOV(getProjection(), getExifFocalLength(), dbCrop, getSize()));
00651                 };
00652                 return true;
00653             };
00654         };
00655     };
00656     return false;
00657 };
00658 
00659 std::string SrcPanoImage::getDBLensName() const
00660 {
00661     std::string lens(getExifLens());
00662     if (!lens.empty())
00663     {
00664         return lens;
00665     }
00666     lens = getExifMake();
00667     if (!lens.empty())
00668     {
00669         if (!getExifModel().empty())
00670         {
00671             lens.append("|");
00672             lens.append(getExifModel());
00673             return lens;
00674         };
00675     };
00676     return std::string();
00677 };
00678 
00679 bool SrcPanoImage::readProjectionFromDB()
00680 {
00681     bool success=false;
00682     const std::string lensname = getDBLensName();
00683     const double focal = getExifFocalLength();
00684     if (!lensname.empty())
00685     {
00686         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00687         Projection dbProjection;
00688         if(lensDB.GetProjection(lensname, dbProjection))
00689         {
00690             setProjection(dbProjection);
00691             success=true;
00692         };
00693         if (focal>0)
00694         {
00695             double fov;
00696             if (lensDB.GetFov(lensname, focal, fov))
00697             {
00698                 // ignore values if fov is bigger than 120 deg
00699                 // then probably an invalid value was written earlier
00700                 if (getProjection() == RECTILINEAR && fov < 120)
00701                 {
00702                     // calculate FOV for given image, take different aspect ratios into account
00703                     const double newFocal = calcFocalLength(getProjection(), fov, getCropFactor(), vigra::Size2D(3000, 2000));
00704                     const double newFov = calcHFOV(getProjection(), newFocal, getCropFactor(), getSize());
00705                     setHFOV(newFov);
00706                 };
00707             };
00708             vigra::Rect2D dbCropRect;
00709             if (lensDB.GetCrop(lensname, focal, getSize(), dbCropRect))
00710             {
00711                 setCropMode(isCircularCrop() ? CROP_CIRCLE : CROP_RECTANGLE);
00712                 setCropRect(dbCropRect);
00713             };
00714         };
00715     };
00716     return success;
00717 };
00718 
00719 bool SrcPanoImage::readDistortionFromDB()
00720 {
00721     const std::string lensname = getDBLensName();
00722     const double focal = getExifFocalLength();
00723     if (!lensname.empty() && focal > 0)
00724     {
00725         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00726         std::vector<double> dist;
00727         if(lensDB.GetDistortion(lensname, focal, dist))
00728         {
00729             if(dist.size()==3)
00730             {
00731                 dist.push_back(1.0-dist[0]-dist[1]-dist[2]);
00732                 setRadialDistortion(dist);
00733                 return true;
00734             };
00735         };
00736     };
00737     return false;
00738 };
00739 
00740 bool SrcPanoImage::readVignettingFromDB()
00741 {
00742     const std::string lensname = getDBLensName();
00743     const double focal = getExifFocalLength();
00744     if (!lensname.empty() && focal > 0)
00745     {
00746         const LensDB::LensDB& lensDB=LensDB::LensDB::GetSingleton();
00747         std::vector<double> vig;
00748         if(lensDB.GetVignetting(lensname, focal, getExifAperture(), getExifDistance(), vig))
00749         {
00750             if (vig.size() == 4)
00751             {
00752                 setRadialVigCorrCoeff(vig);
00753                 return true;
00754             };
00755         };
00756     };
00757     return false;
00758 };
00759 
00760 double SrcPanoImage::calcHFOV(SrcPanoImage::Projection proj, double fl, double crop, vigra::Size2D imageSize)
00761 {
00762     // calculate diagonal of film
00763     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00764     double r = (double)imageSize.x / imageSize.y;
00765     
00766     // calculate the sensor width and height that fit the ratio
00767     // the ratio is determined by the size of our image.
00768     hugin_utils::FDiff2D sensorSize;
00769     sensorSize.x = d / sqrt(1 + 1/(r*r));
00770     sensorSize.y = sensorSize.x / r;
00771     
00772     double hfov = 360;
00773     
00774     switch (proj) {
00775         case SrcPanoImage::RECTILINEAR:
00776             hfov = 2*atan((sensorSize.x/2.0)/fl)  * 180.0/M_PI;
00777             break;
00778         case SrcPanoImage::CIRCULAR_FISHEYE:
00779         case SrcPanoImage::FULL_FRAME_FISHEYE:
00780             hfov = sensorSize.x / fl * 180/M_PI;
00781             break;
00782         case SrcPanoImage::EQUIRECTANGULAR:
00783         case SrcPanoImage::PANORAMIC:
00784             hfov = (sensorSize.x / fl) / M_PI * 180;
00785             break;
00786         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00787             {
00788                 double val=(sensorSize.x/2.0)/fl;
00789                 double n;
00790                 double frac=modf(val, &n);
00791                 hfov = 2 * asin(frac) * 180.0/M_PI + n * 180.0;
00792             }
00793             break;
00794         case SrcPanoImage::FISHEYE_EQUISOLID:
00795             hfov = 4 * asin(std::min<double>(1.0, (sensorSize.x/4.0)/fl)) * 180.0/M_PI;
00796             break;
00797         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00798             hfov = 4 * atan((sensorSize.x/4.0)/fl) * 180.0/M_PI;
00799             break;
00800         case SrcPanoImage::FISHEYE_THOBY:
00801             hfov = 2 * asin(std::min<double>(1.0, sensorSize.x/(2.0*fl*1.47))) * 180.0/M_PI/0.713;
00802             break;
00803         default:
00804             hfov = 360;
00805             // TODO: add formulas for other projections
00806             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00807     }
00808     return hfov;
00809 }
00810 
00811 double SrcPanoImage::calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
00812 {
00813     // calculate diagonal of film
00814     double d = sqrt(36.0*36.0 + 24.0*24.0) / crop;
00815     double r = (double)imageSize.x / imageSize.y;
00816     
00817     // calculate the sensor width and height that fit the ratio
00818     // the ratio is determined by the size of our image.
00819     hugin_utils::FDiff2D sensorSize;
00820     sensorSize.x = d / sqrt(1 + 1/(r*r));
00821     sensorSize.y = sensorSize.x / r;
00822     
00823     switch (proj)
00824     {
00825         case SrcPanoImage::RECTILINEAR:
00826             return (sensorSize.x/2.0) / tan(hfov/180.0*M_PI/2);
00827             break;
00828         case SrcPanoImage::CIRCULAR_FISHEYE:
00829         case SrcPanoImage::FULL_FRAME_FISHEYE:
00830             // same projection equation for both fisheye types,
00831             // assume equal area projection.
00832             return sensorSize.x / (hfov/180*M_PI);
00833             break;
00834         case SrcPanoImage::EQUIRECTANGULAR:
00835         case SrcPanoImage::PANORAMIC:
00836             return  (sensorSize.x / (hfov/180*M_PI));
00837             break;
00838         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00839             {
00840                 int t=(int)ceil((hfov-180)/360);
00841                 return (sensorSize.x /2.0) / (2 * t + pow ( -1.0, t) * sin(hfov/180.0*M_PI/2.0));
00842             };
00843         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00844             return (sensorSize.x/4.0) / tan(hfov/180.0*M_PI/4.0);
00845         case SrcPanoImage::FISHEYE_EQUISOLID:
00846             return (sensorSize.x/4.0) / sin(hfov/180.0*M_PI/4.0);
00847         case SrcPanoImage::FISHEYE_THOBY:
00848             return (sensorSize.x/2.0) / (1.47 * sin(hfov/180.0*M_PI * 0.713 / 2.0));
00849         default:
00850             // TODO: add formulas for other projections
00851             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00852             return 0;
00853     }
00854 }
00855 
00856 double SrcPanoImage::calcCropFactor(SrcPanoImage::Projection proj, double hfov, double focalLength, vigra::Size2D imageSize)
00857 {
00858     // calculate diagonal of film
00859     double r = (double)imageSize.x / imageSize.y;
00860 
00861     double x = 36;
00862     switch (proj)
00863     {
00864         case SrcPanoImage::RECTILINEAR:
00865             x = focalLength * tan(hfov/180.0*M_PI/2);
00866             break;
00867         case SrcPanoImage::CIRCULAR_FISHEYE:
00868         case SrcPanoImage::FULL_FRAME_FISHEYE:
00869         case SrcPanoImage::EQUIRECTANGULAR:
00870         case SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
00871         case SrcPanoImage::FISHEYE_STEREOGRAPHIC:
00872         case SrcPanoImage::FISHEYE_EQUISOLID:
00873         case SrcPanoImage::FISHEYE_THOBY:
00874         case SrcPanoImage::PANORAMIC:
00875             // same projection equation for both fisheye types,
00876             // assume equal area projection.
00877             x = focalLength * (hfov/180*M_PI);
00878             break;
00879         default:
00880             // TODO: add formulas for other projections
00881             DEBUG_WARN("Focal length calculations only supported with rectilinear and fisheye images");
00882             return 0;
00883     }
00884     // diagonal of sensor
00885     double diag = x * sqrt(1+ 1/(r*r));
00886     return sqrt(36.0*36.0 + 24.0*24.0) / diag;
00887 }
00888 
00889 double SrcPanoImage::calcExifExposureValue()
00890 {
00891     double ev=0;
00892     double photoFNumber=getExifAperture();
00893     if(photoFNumber==0)
00894     {
00895         //if no F-number was found in EXIF data assume a f stop of 3.5 to get
00896         //a reasonable ev value if shutter time, e. g. for manual lenses is found
00897         photoFNumber=3.5;
00898     };
00899     if (getExifExposureTime() > 0)
00900     {
00901         double gain = 1;
00902         if (getExifISO()> 0)
00903         {
00904             gain = getExifISO() / 100.0;
00905         }
00906         ev = log2(photoFNumber * photoFNumber / (gain * getExifExposureTime()));
00907     };
00908     return ev;
00909 };
00910 
00911 void SrcPanoImage::updateFocalLength(double newFocalLength)
00912 {
00913     double newHFOV=calcHFOV(getProjection(),newFocalLength,getCropFactor(),getSize());
00914     if(newHFOV!=0)
00915     {
00916         setHFOV(newHFOV);
00917     };
00918 };
00919 
00920 void SrcPanoImage::updateCropFactor(double focalLength, double newCropFactor)
00921 {
00922     double newHFOV=calcHFOV(getProjection(),focalLength,newCropFactor,getSize());
00923     if(newHFOV!=0)
00924     {
00925         setHFOV(newHFOV);
00926     };
00927     setCropFactor(newCropFactor);
00928 };
00929 
00930 // mask handling stuff
00931 void SrcPanoImage::addMask(MaskPolygon newMask)
00932 {
00933     MaskPolygonVector newMasks=m_Masks.getData();
00934     newMasks.push_back(newMask);
00935     setMasks(newMasks);
00936 };
00937 
00938 void SrcPanoImage::addActiveMask(MaskPolygon newMask)
00939 {
00940     MaskPolygonVector newMasks=m_ActiveMasks.getData();
00941     newMasks.push_back(newMask);
00942     setActiveMasks(newMasks);
00943 };
00944 
00945 void SrcPanoImage::clearActiveMasks()
00946 {
00947     MaskPolygonVector emptyMaskVector;
00948     m_ActiveMasks.setData(emptyMaskVector);
00949 };
00950 
00951 bool SrcPanoImage::hasMasks() const
00952 {
00953     return m_Masks.getData().size()>0;
00954 };
00955 
00956 bool SrcPanoImage::hasPositiveMasks() const
00957 {
00958     MaskPolygonVector masks=m_Masks.getData();
00959     if(masks.size()>0)
00960     {
00961         for(unsigned int i=0;i<masks.size();i++)
00962         {
00963             if(masks[i].isPositive())
00964             {
00965                 return true;
00966             };
00967         };
00968     };
00969     return false;
00970 };
00971 
00972 bool SrcPanoImage::hasActiveMasks() const
00973 {
00974     return m_ActiveMasks.getData().size()>0;
00975 };
00976  
00977 void SrcPanoImage::printMaskLines(std::ostream &o, unsigned int newImgNr) const
00978 {
00979     if(m_Masks.getData().size()>0)
00980         for(unsigned int i=0;i<m_Masks.getData().size();i++)
00981             m_Masks.getData()[i].printPolygonLine(o, newImgNr);
00982 };
00983 
00984 void SrcPanoImage::changeMaskType(unsigned int index, HuginBase::MaskPolygon::MaskType newType)
00985 {
00986     if(index<m_Masks.getData().size())
00987     {
00988         MaskPolygonVector editedMasks=m_Masks.getData();
00989         editedMasks[index].setMaskType(newType);
00990         m_Masks.setData(editedMasks);
00991     };
00992 };
00993 
00994 void SrcPanoImage::deleteMask(unsigned int index)
00995 {
00996     if(index<m_Masks.getData().size())
00997     {
00998         MaskPolygonVector oldMasks=m_Masks.getData();
00999         oldMasks.erase(oldMasks.begin()+index);
01000         m_Masks.setData(oldMasks);
01001     };
01002 };
01003 
01004 void SrcPanoImage::deleteAllMasks()
01005 {
01006     MaskPolygonVector emptyMaskVector;
01007     m_Masks.setData(emptyMaskVector);
01008 };
01009 
01010 bool SrcPanoImage::isInsideMasks(vigra::Point2D p) const
01011 {
01012     if(!hasActiveMasks())
01013         return false;
01014     bool insideMask=false;
01015     unsigned int i=0;
01016     while(!insideMask && i<m_ActiveMasks.getData().size())
01017     {
01018         insideMask=m_ActiveMasks.getData()[i].isInside(p);
01019         i++;
01020     };
01021     return insideMask;
01022 };
01023 
01030 bool SrcPanoImage::trustExivOrientation()
01031 {
01032     if(getSize().width() < getSize().height())
01033         return false;
01034 
01035     return true;
01036 }
01037 
01038 const int SrcPanoImage::getExifDateTime(struct tm* datetime) const
01039 {
01040     //initialize struct
01041     std::memset(datetime, 0x0, sizeof(*datetime));
01042     //ignore daylight saving flag because it is not saved in EXIF date time format
01043     datetime->tm_isdst=-1;
01044     return Exiv2::exifTime(m_ExifDate.getData().c_str(),datetime);
01045 };
01046 
01047 } // namespace

Generated on 16 Aug 2017 for Hugintrunk by  doxygen 1.4.7