PanoDetectorLogic.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 ; tab-width: 4 -*-
00002 /*
00003 * Copyright (C) 2007-2008 Anael Orlinski
00004 *
00005 * This file is part of Panomatic.
00006 *
00007 * Panomatic is free software; you can redistribute it and/or modify
00008 * it under the terms of the GNU General Public License as published by
00009 * the Free Software Foundation; either version 2 of the License, or
00010 * (at your option) any later version.
00011 *
00012 * Panomatic is distributed in the hope that it will be useful,
00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 * GNU General Public License for more details.
00016 *
00017 * You should have received a copy of the GNU General Public License
00018 * along with Panomatic; if not, write to the Free Software
00019 * <http://www.gnu.org/licenses/>.
00020 */
00021 
00022 #include "ImageImport.h"
00023 
00024 #include "PanoDetector.h"
00025 #include <iostream>
00026 #include <fstream>
00027 #include <vigra/distancetransform.hxx>
00028 #include "vigra_ext/impexalpha.hxx"
00029 #include "vigra_ext/cms.h"
00030 
00031 #include <localfeatures/Sieve.h>
00032 #include <localfeatures/PointMatch.h>
00033 #include <localfeatures/RansacFiltering.h>
00034 #include <localfeatures/KeyPointIO.h>
00035 #include <localfeatures/CircularKeyPointDescriptor.h>
00036 
00037 /*
00038 #include "KDTree.h"
00039 #include "KDTreeImpl.h"
00040 */
00041 #include "Utils.h"
00042 #include "Tracer.h"
00043 
00044 #include <algorithms/nona/ComputeImageROI.h>
00045 #include <algorithms/optimizer/PTOptimizer.h>
00046 #include <nona/RemappedPanoImage.h>
00047 #include <nona/ImageRemapper.h>
00048 
00049 #include <time.h>
00050 
00051 #define TRACE_IMG(X) {if (iPanoDetector.getVerbose() > 1) { TRACE_INFO("i" << ioImgInfo._number << " : " << X << std::endl);} }
00052 #define TRACE_PAIR(X) {if (iPanoDetector.getVerbose() > 1){ TRACE_INFO("i" << ioMatchData._i1->_number << " <> " \
00053                 "i" << ioMatchData._i2->_number << " : " << X << std::endl)}}
00054 
00055 // define a Keypoint insertor
00056 class KeyPointVectInsertor : public lfeat::KeyPointInsertor
00057 {
00058 public:
00059     explicit KeyPointVectInsertor(lfeat::KeyPointVect_t& iVect) : _v(iVect) {};
00060     inline virtual void operator()(const lfeat::KeyPoint& k)
00061     {
00062         _v.push_back(lfeat::KeyPointPtr(new lfeat::KeyPoint(k)));
00063     }
00064 
00065 private:
00066     lfeat::KeyPointVect_t& _v;
00067 
00068 };
00069 
00070 
00071 // define a sieve extractor
00072 class SieveExtractorKP : public lfeat::SieveExtractor<lfeat::KeyPointPtr>
00073 {
00074 public:
00075     explicit SieveExtractorKP(lfeat::KeyPointVect_t& iV) : _v(iV) {};
00076     inline virtual void operator()(const lfeat::KeyPointPtr& k)
00077     {
00078         _v.push_back(k);
00079     }
00080 private:
00081     lfeat::KeyPointVect_t& _v;
00082 };
00083 
00084 class SieveExtractorMatch : public lfeat::SieveExtractor<lfeat::PointMatchPtr>
00085 {
00086 public:
00087     explicit SieveExtractorMatch(lfeat::PointMatchVector_t& iM) : _m(iM) {};
00088     inline virtual void operator()(const lfeat::PointMatchPtr& m)
00089     {
00090         _m.push_back(m);
00091     }
00092 private:
00093     lfeat::PointMatchVector_t& _m;
00094 };
00095 
00096 bool PanoDetector::LoadKeypoints(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00097 {
00098     TRACE_IMG("Loading keypoints...");
00099 
00100     lfeat::ImageInfo info = lfeat::loadKeypoints(ioImgInfo._keyfilename, ioImgInfo._kp);
00101     ioImgInfo._loadFail = (info.filename.size() == 0);
00102 
00103     // update ImgData
00104     if(ioImgInfo.NeedsRemapping())
00105     {
00106         ioImgInfo._detectWidth = std::max(info.width,info.height);
00107         ioImgInfo._detectHeight = std::max(info.width,info.height);
00108         ioImgInfo._projOpts.setWidth(ioImgInfo._detectWidth);
00109         ioImgInfo._projOpts.setHeight(ioImgInfo._detectHeight);
00110     }
00111     else
00112     {
00113         ioImgInfo._detectWidth = info.width;
00114         ioImgInfo._detectHeight = info.height;
00115     };
00116     ioImgInfo._descLength = info.dimensions;
00117 
00118     return true;
00119 }
00120 
00122 template <class SrcImageIterator, class SrcAccessor>
00123 void applyMaskAndCrop(vigra::triple<SrcImageIterator, SrcImageIterator, SrcAccessor> img, const HuginBase::SrcPanoImage& SrcImg)
00124 {
00125     vigra::Diff2D imgSize = img.second - img.first;
00126 
00127     // create dest y iterator
00128     SrcImageIterator yd(img.first);
00129     // loop over the image and transform
00130     for(int y=0; y < imgSize.y; ++y, ++yd.y)
00131     {
00132         // create x iterators
00133         SrcImageIterator xd(yd);
00134         for(int x=0; x < imgSize.x; ++x, ++xd.x)
00135         {
00136             if(!SrcImg.isInside(vigra::Point2D(x,y)))
00137             {
00138                 *xd=0;
00139             };
00140         }
00141     }
00142 }
00143 
00145 template <class T>
00146 struct ScaleFunctor
00147 {
00148     typedef T result_type;
00149     explicit ScaleFunctor(double scale) { m_scale = scale; };
00150 
00151     T operator()(const T & a) const
00152     {
00153         return m_scale*a;
00154     }
00155 
00156     template <class T2>
00157     T2 operator()(const T2 & a, const hugin_utils::FDiff2D & p) const
00158     {
00159         return m_scale*a;
00160     }
00161 
00162     template <class T2, class A>
00163     A hdrWeight(T2 v, A a) const
00164     {
00165         return a;
00166     }
00167 
00168 private:
00169     double m_scale;
00170 };
00171 
00176 template <class ImageType, class PixelTransform>
00177 void RemapImage(const HuginBase::SrcPanoImage& srcImage, const HuginBase::PanoramaOptions& options,
00178     size_t detectWidth, size_t detectHeight,
00179     ImageType*& image, vigra::BImage*& mask,
00180     const PixelTransform& pixelTransform,
00181     ImageType*& finalImage, vigra::BImage*& finalMask)
00182 {
00183     AppBase::DummyProgressDisplay dummy;
00184     HuginBase::PTools::Transform transform;
00185     transform.createTransform(srcImage, options);
00186     finalImage = new ImageType(detectWidth, detectHeight);
00187     finalMask = new vigra::BImage(detectWidth, detectHeight, vigra::UInt8(0));
00188     if (srcImage.hasActiveMasks() || (srcImage.getCropMode() != HuginBase::SrcPanoImage::NO_CROP && !srcImage.getCropRect().isEmpty()))
00189     {
00190         if (!mask)
00191         {
00192             // image has no mask, create full mask
00193             mask = new vigra::BImage(image->size(), vigra::UInt8(255));
00194         };
00195         applyMaskAndCrop(vigra::destImageRange(*mask), srcImage);
00196     };
00197     if (mask)
00198     {
00199         vigra_ext::transformImageAlpha(vigra::srcImageRange(*image), vigra::srcImage(*mask), vigra::destImageRange(*finalImage), vigra::destImage(*finalMask),
00200             options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
00201         delete mask;
00202         mask = NULL;
00203     }
00204     else
00205     {
00206         vigra_ext::transformImage(vigra::srcImageRange(*image), vigra::destImageRange(*finalImage), vigra::destImage(*finalMask),
00207             options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
00208     };
00209     delete image;
00210     image = NULL;
00211 }
00212 
00215 template <class ImageType>
00216 void HandleDownscaleImage(const HuginBase::SrcPanoImage& srcImage, ImageType*& image, vigra::BImage*& mask, 
00217     size_t detectWidth, size_t detectHeight, bool downscale,
00218     ImageType*& finalImage, vigra::BImage*& finalMask)
00219 {
00220     if (srcImage.hasActiveMasks() || (srcImage.getCropMode() != HuginBase::SrcPanoImage::NO_CROP && !srcImage.getCropRect().isEmpty()))
00221     {
00222         if (!mask)
00223         {
00224             // image has no mask, create full mask
00225             mask = new vigra::BImage(image->size(), vigra::UInt8(255));
00226         };
00227         //copy mask and crop from pto file into alpha layer
00228         applyMaskAndCrop(vigra::destImageRange(*mask), srcImage);
00229     };
00230     if (downscale)
00231     {
00232         // Downscale image
00233         finalImage = new ImageType(detectWidth, detectHeight);
00234         vigra::resizeImageNoInterpolation(vigra::srcImageRange(*image), vigra::destImageRange(*finalImage));
00235         delete image;
00236         image = NULL;
00237         //downscale mask
00238         if (mask)
00239         {
00240             finalMask = new vigra::BImage(detectWidth, detectHeight);
00241             vigra::resizeImageNoInterpolation(vigra::srcImageRange(*mask), vigra::destImageRange(*finalMask));
00242             delete mask;
00243             mask = NULL;
00244         };
00245     }
00246     else
00247     {
00248         // simply copy pointer instead of copying the whole image data
00249         finalImage = image;
00250         if (mask)
00251         {
00252             finalMask = mask;
00253         };
00254     };
00255 };
00256 
00257 // save some intermediate images to disc if defined
00258 // #define DEBUG_LOADING_REMAPPING
00259 bool PanoDetector::AnalyzeImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00260 {
00261     vigra::DImage* final_img = NULL;
00262     vigra::BImage* final_mask = NULL;
00263 
00264     try
00265     {
00266         ioImgInfo._loadFail=false;
00267 
00268         TRACE_IMG("Load image...");
00269         vigra::ImageImportInfo aImageInfo(ioImgInfo._name.c_str());
00270         if (aImageInfo.numExtraBands() > 1)
00271         {
00272             TRACE_INFO("Image with multiple alpha channels are not supported");
00273             ioImgInfo._loadFail = true;
00274             return false;
00275         };
00276         // remark: it would be possible to handle all cases with the same code
00277         // but this would mean that in some cases there are unnecessary
00278         // range conversions and image data copying actions needed
00279         // so we use specialed code for several cases to reduce memory usage
00280         // and prevent unnecessary range adaptions
00281         if (aImageInfo.isGrayscale())
00282         {
00283             // gray scale image
00284             vigra::DImage* image = new vigra::DImage(aImageInfo.size());
00285             vigra::BImage* mask = NULL;
00286             // load gray scale image
00287             if (aImageInfo.numExtraBands() == 1)
00288             {
00289                 mask=new vigra::BImage(aImageInfo.size());
00290                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*image), vigra::destImage(*mask));
00291             }
00292             else
00293             {
00294                 vigra::importImage(aImageInfo, vigra::destImage(*image));
00295             };
00296             // adopt range
00297             double minVal = 0;
00298             double maxVal;
00299             if (aImageInfo.getPixelType() == std::string("FLOAT") || aImageInfo.getPixelType() == std::string("DOUBLE"))
00300             {
00301                 vigra::FindMinMax<float> minmax;   // init functor
00302                 vigra::inspectImage(vigra::srcImageRange(*image), minmax);
00303                 minVal = minmax.min;
00304                 maxVal = minmax.max;
00305             }
00306             else
00307             {
00308                 maxVal = vigra_ext::getMaxValForPixelType(aImageInfo.getPixelType());
00309             };
00310             bool range255 = (fabs(maxVal - 255) < 0.01 && fabs(minVal) < 0.01);
00311             if (aImageInfo.getICCProfile().empty())
00312             {
00313                 // no icc profile, cpfind expects images in 0 ..255 range
00314                 TRACE_IMG("Rescale range...");
00315                 if (!range255)
00316                 {
00317                     vigra::transformImage(vigra::srcImageRange(*image), vigra::destImage(*image),
00318                         vigra::linearRangeMapping(minVal, maxVal, 0.0, 255.0));
00319                 };
00320                 range255 = true;
00321             }
00322             else
00323             {
00324                 // apply ICC profile
00325                 TRACE_IMG("Applying icc profile...");
00326                 // lcms expects for double datatype all values between 0 and 1
00327                 vigra::transformImage(vigra::srcImageRange(*image), vigra::destImage(*image),
00328                     vigra::linearRangeMapping(minVal, maxVal, 0.0, 1.0));
00329                 range255 = false;
00330                 HuginBase::Color::ApplyICCProfile(*image, aImageInfo.getICCProfile(), TYPE_GRAY_DBL);
00331             };
00332             if (ioImgInfo.NeedsRemapping())
00333             {
00334                 // remap image
00335                 TRACE_IMG("Remapping image...");
00336                 if (range255)
00337                 {
00338                     RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00339                         ioImgInfo._detectWidth, ioImgInfo._detectHeight, image, mask, vigra_ext::PassThroughFunctor<double>(),
00340                         final_img, final_mask);
00341                 }
00342                 else
00343                 {
00344                     // images has been scaled to 0..1 range before, scale back to 0..255 range
00345                     RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00346                         ioImgInfo._detectWidth, ioImgInfo._detectHeight, image, mask, ScaleFunctor<double>(255.0),
00347                         final_img, final_mask);
00348                 };
00349             }
00350             else
00351             {
00352                 if (range255)
00353                 {
00354                     TRACE_IMG("Downscale and transform to suitable grayscale...");
00355                     HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), image, mask,
00356                         ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
00357                         final_img, final_mask);
00358                 }
00359                 else
00360                 {
00361                     TRACE_IMG("Transform to suitable grayscale...");
00362                     HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), image, mask,
00363                         ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
00364                         final_img, final_mask);
00365                     vigra::transformImage(vigra::srcImageRange(*final_img), vigra::destImage(*final_img), vigra::linearRangeMapping(0, 1, 0, 255));
00366                 };
00367             };
00368             if (iPanoDetector.getCeleste())
00369             {
00370                 TRACE_IMG("Celeste does not work with grayscale images. Skipping...");
00371             };
00372         }
00373         else
00374         {
00375             if (aImageInfo.isColor())
00376             {
00377                 // rgb images
00378                 // prepare radius parameter for celeste
00379                 int radius = 1;
00380                 if (iPanoDetector.getCeleste())
00381                 {
00382                     radius = iPanoDetector.getCelesteRadius();
00383                     if (iPanoDetector._downscale)
00384                     {
00385                         radius >>= 1;
00386                     };
00387                     if (radius < 2)
00388                     {
00389                         radius = 2;
00390                     };
00391                 };
00392                 switch (aImageInfo.pixelType())
00393                 {
00394                     case vigra::ImageImportInfo::UINT8:
00395                         // special variant for unsigned 8 bit images
00396                         {
00397                             vigra::BRGBImage* rgbImage=new vigra::BRGBImage(aImageInfo.size());
00398                             vigra::BImage* mask = NULL;
00399                             // load image
00400                             if (aImageInfo.numExtraBands() == 1)
00401                             {
00402                                 mask=new vigra::BImage(aImageInfo.size());
00403                                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
00404                             }
00405                             else
00406                             {
00407                                 vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
00408                             };
00409                             // apply icc profile
00410                             if (!aImageInfo.getICCProfile().empty())
00411                             {
00412                                 TRACE_IMG("Applying icc profile...");
00413                                 HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_8);
00414                             };
00415                             vigra::BRGBImage* scaled = NULL;
00416                             if (ioImgInfo.NeedsRemapping())
00417                             {
00418                                 // remap image
00419                                 TRACE_IMG("Remapping image...");
00420                                 RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00421                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask, 
00422                                     vigra_ext::PassThroughFunctor<vigra::RGBValue<vigra::UInt8> >(),
00423                                     scaled, final_mask);
00424                             }
00425                             else
00426                             {
00427                                 if (ioImgInfo.IsDownscale())
00428                                 {
00429                                     TRACE_IMG("Downscale image...");
00430                                 };
00431                                 HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
00432                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
00433                                     scaled, final_mask);
00434                             };
00435                             if (iPanoDetector.getCeleste())
00436                             {
00437                                 TRACE_IMG("Mask areas with clouds...");
00438                                 vigra::UInt16RGBImage* image16=new vigra::UInt16RGBImage(scaled->size());
00439                                 vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
00440                                     vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
00441                                 vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *image16, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
00442 #ifdef DEBUG_LOADING_REMAPPING
00443                                 // DEBUG: export celeste mask
00444                                 std::ostringstream maskfilename;
00445                                 maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
00446                                 vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00447                                 vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
00448 #endif
00449                                 delete image16;
00450                                 if (final_mask)
00451                                 {
00452                                     vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
00453                                 }
00454                                 else
00455                                 {
00456                                     final_mask = celeste_mask;
00457                                 };
00458                             };
00459                             // scale to greyscale
00460                             TRACE_IMG("Convert to greyscale double...");
00461                             final_img = new vigra::DImage(scaled->size());
00462                             vigra::copyImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >()),
00463                                 vigra::destImage(*final_img));
00464                             delete scaled;
00465                         };
00466                         break;
00467                     case vigra::ImageImportInfo::UINT16:
00468                         // special variant for unsigned 16 bit images
00469                         {
00470                             vigra::UInt16RGBImage* rgbImage = new vigra::UInt16RGBImage(aImageInfo.size());
00471                             vigra::BImage* mask = NULL;
00472                             // load image
00473                             if (aImageInfo.numExtraBands() == 1)
00474                             {
00475                                 mask = new vigra::BImage(aImageInfo.size());
00476                                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
00477                             }
00478                             else
00479                             {
00480                                 vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
00481                             };
00482                             // apply icc profile
00483                             if (!aImageInfo.getICCProfile().empty())
00484                             {
00485                                 TRACE_IMG("Applying icc profile...");
00486                                 HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_16);
00487                             };
00488                             vigra::UInt16RGBImage* scaled = NULL;
00489                             if (ioImgInfo.NeedsRemapping())
00490                             {
00491                                 // remap image
00492                                 TRACE_IMG("Remapping image...");
00493                                 RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00494                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask,
00495                                     vigra_ext::PassThroughFunctor<vigra::RGBValue<vigra::UInt16> >(),
00496                                     scaled, final_mask);
00497                             }
00498                             else
00499                             {
00500                                 if (ioImgInfo.IsDownscale())
00501                                 {
00502                                     TRACE_IMG("Downscale image...");
00503                                 };
00504                                 HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
00505                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
00506                                     scaled, final_mask);
00507                             };
00508                             if (iPanoDetector.getCeleste())
00509                             {
00510                                 TRACE_IMG("Mask areas with clouds...");
00511                                 vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *scaled, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
00512 #ifdef DEBUG_LOADING_REMAPPING
00513                                 // DEBUG: export celeste mask
00514                                 std::ostringstream maskfilename;
00515                                 maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
00516                                 vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00517                                 vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
00518 #endif
00519                                 if (final_mask)
00520                                 {
00521                                     vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
00522                                 }
00523                                 else
00524                                 {
00525                                     final_mask = celeste_mask;
00526                                 };
00527                             };
00528                             // scale to greyscale
00529                             TRACE_IMG("Convert to greyscale double...");
00530                             final_img = new vigra::DImage(scaled->size());
00531                             // keypoint finder expext 0..255 range
00532                             vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt16> >()),
00533                                 vigra::destImage(*final_img), vigra::functor::Arg1() / vigra::functor::Param(255.0));
00534                             delete scaled;
00535                         };
00536                         break;
00537                     default:
00538                         // double variant for all other cases
00539                         {
00540                             vigra::DRGBImage* rgbImage = new vigra::DRGBImage(aImageInfo.size());
00541                             vigra::BImage* mask = NULL;
00542                             // load image
00543                             if (aImageInfo.numExtraBands() == 1)
00544                             {
00545                                 mask = new vigra::BImage(aImageInfo.size());
00546                                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
00547                             }
00548                             else
00549                             {
00550                                 vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
00551                             };
00552                             // range adaption
00553                             double minVal = 0;
00554                             double maxVal;
00555                             const bool isDouble = aImageInfo.getPixelType() == std::string("FLOAT") || aImageInfo.getPixelType() == std::string("DOUBLE");
00556                             if (isDouble)
00557                             {
00558                                 vigra::FindMinMax<float> minmax;   // init functor
00559                                 vigra::inspectImage(vigra::srcImageRange(*rgbImage, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), minmax);
00560                                 minVal = minmax.min;
00561                                 maxVal = minmax.max;
00562                             }
00563                             else
00564                             {
00565                                 maxVal = vigra_ext::getMaxValForPixelType(aImageInfo.getPixelType());
00566                             };
00567                             bool range255 = (fabs(maxVal - 255) < 0.01 && fabs(minVal) < 0.01);
00568                             if (aImageInfo.getICCProfile().empty())
00569                             {
00570                                 // no icc profile, cpfind expects images in 0 ..255 range
00571                                 TRACE_IMG("Rescale range...");
00572                                 if (!range255)
00573                                 {
00574                                     int mapping = 0;
00575                                     if (isDouble && iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number).getResponseType() == HuginBase::BaseSrcPanoImage::RESPONSE_LINEAR)
00576                                     {
00577                                         // switch to log mapping for double/float images with linear response type
00578                                         mapping = 1;
00579                                     };
00580                                     vigra_ext::applyMapping(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage), minVal, maxVal, mapping);
00581                                 };
00582                                 range255 = true;
00583                             }
00584                             else
00585                             {
00586                                 // apply ICC profile
00587                                 TRACE_IMG("Applying icc profile...");
00588                                 // lcms expects for double datatype all values between 0 and 1
00589                                 vigra::transformImage(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage),
00590                                     vigra_ext::LinearTransform<vigra::RGBValue<double> >(1.0 / maxVal - minVal, -minVal));
00591                                 range255 = false;
00592                                 HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_DBL);
00593                             };
00594                             vigra::DRGBImage* scaled;
00595                             if (ioImgInfo.NeedsRemapping())
00596                             {
00597                                 // remap image
00598                                 TRACE_IMG("Remapping image...");
00599                                 RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00600                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask, vigra_ext::PassThroughFunctor<double>(),
00601                                     scaled, final_mask);
00602                             }
00603                             else
00604                             {
00605                                 TRACE_IMG("Transform to suitable grayscale...");
00606                                 HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
00607                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
00608                                     scaled, final_mask);
00609                             };
00610                             if (iPanoDetector.getCeleste())
00611                             {
00612                                 TRACE_IMG("Mask areas with clouds...");
00613                                 vigra::UInt16RGBImage* image16 = new vigra::UInt16RGBImage(scaled->size());
00614                                 if (range255)
00615                                 {
00616                                     vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
00617                                         vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
00618                                 }
00619                                 else
00620                                 {
00621                                     vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
00622                                         vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(65535));
00623                                 };
00624                                 vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *image16, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
00625 #ifdef DEBUG_LOADING_REMAPPING
00626                                 // DEBUG: export celeste mask
00627                                 std::ostringstream maskfilename;
00628                                 maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
00629                                 vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00630                                 vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
00631 #endif
00632                                 delete image16;
00633                                 if (final_mask)
00634                                 {
00635                                     vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
00636                                 }
00637                                 else
00638                                 {
00639                                     final_mask = celeste_mask;
00640                                 };
00641                             };
00642                             // scale to greyscale
00643                             TRACE_IMG("Convert to greyscale double...");
00644                             final_img = new vigra::DImage(scaled->size());
00645                             // keypoint finder expext 0..255 range
00646                             if (range255)
00647                             {
00648                                 vigra::copyImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), vigra::destImage(*final_img));
00649                             }
00650                             else
00651                             {
00652                                 vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()),
00653                                     vigra::destImage(*final_img), vigra::functor::Arg1() * vigra::functor::Param(255.0));
00654                             };
00655                             delete scaled;
00656                         };
00657                         break;
00658                 };
00659             }
00660             else
00661             {
00662                 TRACE_INFO("Cpfind works only with grayscale or RGB images");
00663                 ioImgInfo._loadFail = true;
00664                 return false;
00665             };
00666         };
00667 
00668 #ifdef DEBUG_LOADING_REMAPPING
00669         // DEBUG: export remapped
00670         std::ostringstream filename;
00671         filename << ioImgInfo._name << "_grey.JPG";
00672         vigra::ImageExportInfo exinfo(filename.str().c_str());
00673         vigra::exportImage(vigra::srcImageRange(*final_img), exinfo);
00674 #endif
00675 
00676         // Build integral image
00677         TRACE_IMG("Build integral image...");
00678         ioImgInfo._ii.init(*final_img);
00679         delete final_img;
00680 
00681         // compute distance map
00682         if(final_mask)
00683         {
00684             TRACE_IMG("Build distance map...");
00685             //apply threshold, in case loaded mask contains other values than 0 and 255
00686             vigra::transformImage(vigra::srcImageRange(*final_mask), vigra::destImage(*final_mask),
00687                                   vigra::Threshold<vigra::BImage::PixelType, vigra::BImage::PixelType>(1, 255, 0, 255));
00688             ioImgInfo._distancemap.resize(final_mask->width(), final_mask->height(), 0);
00689             vigra::distanceTransform(vigra::srcImageRange(*final_mask), vigra::destImage(ioImgInfo._distancemap), 255, 2);
00690 #ifdef DEBUG_LOADING_REMAPPING
00691             std::ostringstream maskfilename;
00692             maskfilename << ioImgInfo._name << "_mask.JPG";
00693             vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00694             vigra::exportImage(vigra::srcImageRange(*final_mask), maskexinfo);
00695             std::ostringstream distfilename;
00696             distfilename << ioImgInfo._name << "_distancemap.JPG";
00697             vigra::ImageExportInfo distexinfo(distfilename.str().c_str());
00698             vigra::exportImage(vigra::srcImageRange(ioImgInfo._distancemap), distexinfo);
00699 #endif
00700             delete final_mask;
00701         };
00702     }
00703     catch (std::exception& e)
00704     {
00705         TRACE_INFO("An error happened while loading image : caught exception: " << e.what() << std::endl);
00706         ioImgInfo._loadFail=true;
00707         return false;
00708     }
00709 
00710     return true;
00711 }
00712 
00713 
00714 bool PanoDetector::FindKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00715 {
00716     TRACE_IMG("Find keypoints...");
00717 
00718     // setup the detector
00719     KeyPointDetector aKP;
00720 
00721     // detect the keypoints
00722     KeyPointVectInsertor aInsertor(ioImgInfo._kp);
00723     aKP.detectKeypoints(ioImgInfo._ii, aInsertor);
00724 
00725     TRACE_IMG("Found "<< ioImgInfo._kp.size() << " interest points.");
00726 
00727     return true;
00728 }
00729 
00730 bool PanoDetector::FilterKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00731 {
00732     TRACE_IMG("Filtering keypoints...");
00733 
00734     lfeat::Sieve<lfeat::KeyPointPtr, lfeat::KeyPointPtrSort > aSieve(iPanoDetector.getSieve1Width(),
00735             iPanoDetector.getSieve1Height(),
00736             iPanoDetector.getSieve1Size());
00737 
00738     // insert the points in the Sieve if they are not masked
00739     double aXF = (double)iPanoDetector.getSieve1Width() / (double)ioImgInfo._detectWidth;
00740     double aYF = (double)iPanoDetector.getSieve1Height() / (double)ioImgInfo._detectHeight;
00741 
00742     const bool distmap_valid=(ioImgInfo._distancemap.width()>0 && ioImgInfo._distancemap.height()>0);
00743     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00744     {
00745         lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00746         if(distmap_valid)
00747         {
00748             if(aK->_x > 0 && aK->_x < ioImgInfo._distancemap.width() && aK->_y > 0 && aK->_y < ioImgInfo._distancemap.height()
00749                     && ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) >aK->_scale*8)
00750             {
00751                 //cout << " dist from border:" << ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) << " required dist: " << aK->_scale*12 << std::endl;
00752                 aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
00753             }
00754         }
00755         else
00756         {
00757             aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
00758         };
00759     }
00760 
00761     // pull remaining values from the sieve
00762     ioImgInfo._kp.clear();
00763 
00764     // make an extractor and pull the points
00765     SieveExtractorKP aSieveExt(ioImgInfo._kp);
00766     aSieve.extract(aSieveExt);
00767 
00768     TRACE_IMG("Kept " << ioImgInfo._kp.size() << " interest points.");
00769 
00770     return true;
00771 
00772 }
00773 
00774 bool PanoDetector::MakeKeyPointDescriptorsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00775 {
00776     TRACE_IMG("Make keypoint descriptors...");
00777 
00778     // build a keypoint descriptor
00779     lfeat::CircularKeyPointDescriptor aKPD(ioImgInfo._ii);
00780 
00781     // vector for keypoints with more than one orientation
00782     lfeat::KeyPointVect_t kp_new_ori;
00783     for (size_t j = 0; j < ioImgInfo._kp.size(); ++j)
00784     {
00785         lfeat::KeyPointPtr& aK = ioImgInfo._kp[j];
00786         double angles[4];
00787         int nAngles = aKPD.assignOrientation(*aK, angles);
00788         for (int i=0; i < nAngles; i++)
00789         {
00790             // duplicate Keypoint with additional angles
00791             lfeat::KeyPointPtr aKn = lfeat::KeyPointPtr ( new lfeat::KeyPoint ( *aK ) );
00792             aKn->_ori = angles[i];
00793             kp_new_ori.push_back(aKn);
00794         }
00795     }
00796     ioImgInfo._kp.insert(ioImgInfo._kp.end(), kp_new_ori.begin(), kp_new_ori.end());
00797 
00798     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00799     {
00800         aKPD.makeDescriptor(*(ioImgInfo._kp[i]));
00801     }
00802     // store the descriptor length
00803     ioImgInfo._descLength = aKPD.getDescriptorLength();
00804     return true;
00805 }
00806 
00807 bool PanoDetector::RemapBackKeypoints(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00808 {
00809     if (ioImgInfo.IsDownscale())
00810     {
00811         for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00812         {
00813             lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00814             aK->_x *= 2.0;
00815             aK->_y *= 2.0;
00816             aK->_scale *= 2.0;
00817         };
00818     }
00819     else
00820     {
00821         if (ioImgInfo.NeedsRemapping())
00822         {
00823             TRACE_IMG("Remapping back keypoints...");
00824             HuginBase::PTools::Transform trafo1;
00825             trafo1.createTransform(iPanoDetector._panoramaInfoCopy.getSrcImage(ioImgInfo._number),
00826                 ioImgInfo._projOpts);
00827 
00828             int dx1 = ioImgInfo._projOpts.getROI().left();
00829             int dy1 = ioImgInfo._projOpts.getROI().top();
00830 
00831             for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00832             {
00833                 lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00834                 double xout, yout;
00835                 if (trafo1.transformImgCoord(xout, yout, aK->_x + dx1, aK->_y + dy1))
00836                 {
00837                     // downscaling is take care of by the remapping transform
00838                     // no need for multiplying the scale factor...
00839                     aK->_x = xout;
00840                     aK->_y = yout;
00841                 };
00842             };
00843         };
00844     };
00845     return true;
00846 }
00847 
00848 bool PanoDetector::BuildKDTreesInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00849 {
00850     TRACE_IMG("Build KDTree...");
00851 
00852     if(ioImgInfo._kp.size()==0)
00853     {
00854         return false;
00855     };
00856     // build a vector of KDElemKeyPointPtr
00857 
00858     // create feature vector matrix for flann
00859     ioImgInfo._flann_descriptors = flann::Matrix<double>(new double[ioImgInfo._kp.size()*ioImgInfo._descLength],
00860                                    ioImgInfo._kp.size(), ioImgInfo._descLength);
00861     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00862     {
00863         memcpy(ioImgInfo._flann_descriptors[i], ioImgInfo._kp[i]->_vec, sizeof(double)*ioImgInfo._descLength);
00864     }
00865 
00866     // build query structure
00867     ioImgInfo._flann_index = new flann::Index<flann::L2<double> > (ioImgInfo._flann_descriptors, flann::KDTreeIndexParams(4));
00868     ioImgInfo._flann_index->buildIndex();
00869 
00870     return true;
00871 }
00872 
00873 bool PanoDetector::FreeMemoryInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00874 {
00875     TRACE_IMG("Freeing memory...");
00876 
00877     ioImgInfo._ii.clean();
00878     ioImgInfo._distancemap.resize(0,0);
00879 
00880     return true;
00881 }
00882 
00883 
00884 bool PanoDetector::FindMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00885 {
00886     TRACE_PAIR("Find Matches...");
00887 
00888     // retrieve the KDTree of image 2
00889     flann::Index<flann::L2<double> > * index2 = ioMatchData._i2->_flann_index;
00890 
00891     // retrieve query points from image 1
00892     flann::Matrix<double> & query = ioMatchData._i1->_flann_descriptors;
00893 
00894     // storage for sorted 2 best matches
00895     int nn = 2;
00896     flann::Matrix<int> indices(new int[query.rows*nn], query.rows, nn);
00897     flann::Matrix<double> dists(new double[query.rows*nn], query.rows, nn);
00898 
00899     // perform matching using flann
00900     index2->knnSearch(query, indices, dists, nn, flann::SearchParams(iPanoDetector.getKDTreeSearchSteps()));
00901 
00902     //typedef KDTreeSpace::BestMatch<KDElemKeyPoint>            BM_t;
00903     //std::set<BM_t, std::greater<BM_t> >       aBestMatches;
00904 
00905     // store the matches already found to avoid 2 points in image1
00906     // match the same point in image2
00907     // both matches will be removed.
00908     std::set<int> aAlreadyMatched;
00909     std::set<int> aBadMatch;
00910 
00911     // unfiltered vector of matches
00912     typedef std::pair<lfeat::KeyPointPtr, int> TmpPair_t;
00913     std::vector<TmpPair_t>      aUnfilteredMatches;
00914 
00915     //PointMatchVector_t aMatches;
00916 
00917     // go through all the keypoints of image 1
00918     for (unsigned aKIt = 0; aKIt < query.rows; ++aKIt)
00919     {
00920         // accept the match if the second match is far enough
00921         // put a lower value for stronger matching default 0.15
00922         if (dists[aKIt][0] > iPanoDetector.getKDTreeSecondDistance()  * dists[aKIt][1])
00923         {
00924             continue;
00925         }
00926 
00927         // check if the kdtree match number is already in the already matched set
00928         if (aAlreadyMatched.find(indices[aKIt][0]) != aAlreadyMatched.end())
00929         {
00930             // add to delete list and continue
00931             aBadMatch.insert(indices[aKIt][0]);
00932             continue;
00933         }
00934 
00935         // TODO: add check for duplicate matches (can happen if a keypoint gets multiple orientations)
00936 
00937         // add the match number in already matched set
00938         aAlreadyMatched.insert(indices[aKIt][0]);
00939 
00940         // add the match to the unfiltered list
00941         aUnfilteredMatches.push_back(TmpPair_t(ioMatchData._i1->_kp[aKIt], indices[aKIt][0]));
00942     }
00943 
00944     // now filter and fill the vector of matches
00945     for (size_t i = 0; i < aUnfilteredMatches.size(); ++i)
00946     {
00947         TmpPair_t& aP = aUnfilteredMatches[i];
00948         // if the image2 match number is in the badmatch set, skip it.
00949         if (aBadMatch.find(aP.second) != aBadMatch.end())
00950         {
00951             continue;
00952         }
00953 
00954         // add the match in the output vector
00955         ioMatchData._matches.push_back(lfeat::PointMatchPtr( new lfeat::PointMatch(aP.first, ioMatchData._i2->_kp[aP.second])));
00956     }
00957 
00958     delete[] indices.ptr();
00959     delete[] dists.ptr();
00960     TRACE_PAIR("Found " << ioMatchData._matches.size() << " matches.");
00961     return true;
00962 }
00963 
00964 bool PanoDetector::RansacMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00965 {
00966     // Use panotools model for wide angle lenses
00967     HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
00968     if (rmode == HuginBase::RANSACOptimizer::HOMOGRAPHY ||
00969         (rmode == HuginBase::RANSACOptimizer::AUTO && iPanoDetector._panoramaInfo->getImage(ioMatchData._i1->_number).getHFOV() < 65 &&
00970              iPanoDetector._panoramaInfo->getImage(ioMatchData._i2->_number).getHFOV() < 65))
00971     {
00972         return RansacMatchesInPairHomography(ioMatchData, iPanoDetector);
00973     }
00974     else
00975     {
00976         return RansacMatchesInPairCam(ioMatchData, iPanoDetector);
00977     }
00978 }
00979 
00980 // new code with fisheye aware ransac
00981 bool PanoDetector::RansacMatchesInPairCam(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00982 {
00983     TRACE_PAIR("RANSAC Filtering with Panorama model...");
00984 
00985     if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
00986     {
00987         TRACE_PAIR("Too few matches ... removing all of them.");
00988         ioMatchData._matches.clear();
00989         return true;
00990     }
00991 
00992     if (ioMatchData._matches.size() < 6)
00993     {
00994         TRACE_PAIR("Not enough matches for RANSAC filtering.");
00995         return true;
00996     }
00997 
00998     // setup a panorama project with the two images.
00999     // is this threadsafe (is this read only access?)
01000     HuginBase::UIntSet imgs;
01001     int pano_i1 = ioMatchData._i1->_number;
01002     int pano_i2 = ioMatchData._i2->_number;
01003     imgs.insert(pano_i1);
01004     imgs.insert(pano_i2);
01005     int pano_local_i1 = 0;
01006     int pano_local_i2 = 1;
01007     if (pano_i1 > pano_i2)
01008     {
01009         pano_local_i1 = 1;
01010         pano_local_i2 = 0;
01011     }
01012 
01013     // perform ransac matching.
01014     // ARGH the panotools optimizer uses global variables is not reentrant
01015     std::vector<int> inliers;
01016 #pragma omp critical
01017     {
01018         HuginBase::PanoramaData* panoSubset = iPanoDetector._panoramaInfo->getNewSubset(imgs);
01019 
01020         // create control point vector
01021         HuginBase::CPVector controlPoints(ioMatchData._matches.size());
01022         for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01023         {
01024             lfeat::PointMatchPtr& aM=ioMatchData._matches[i];
01025             controlPoints[i] = HuginBase::ControlPoint(pano_local_i1, aM->_img1_x, aM->_img1_y,
01026                                             pano_local_i2, aM->_img2_x, aM->_img2_y);
01027         }
01028         panoSubset->setCtrlPoints(controlPoints);
01029 
01030 
01031         PT_setProgressFcn(ptProgress);
01032         PT_setInfoDlgFcn(ptinfoDlg);
01033 
01034         HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
01035         if (rmode == HuginBase::RANSACOptimizer::AUTO)
01036         {
01037             rmode = HuginBase::RANSACOptimizer::RPY;
01038         }
01039         inliers = HuginBase::RANSACOptimizer::findInliers(*panoSubset, pano_local_i1, pano_local_i2,
01040                   iPanoDetector.getRansacDistanceThreshold(), rmode);
01041         PT_setProgressFcn(NULL);
01042         PT_setInfoDlgFcn(NULL);
01043         delete panoSubset;
01044     }
01045 
01046     TRACE_PAIR("Removed " << ioMatchData._matches.size() - inliers.size() << " matches. " << inliers.size() << " remaining.");
01047     if (inliers.size() < 0.5 * ioMatchData._matches.size())
01048     {
01049         // more than 50% of matches were removed, ignore complete pair...
01050         TRACE_PAIR("RANSAC found more than 50% outliers, removing all matches");
01051         ioMatchData._matches.clear();
01052         return true;
01053     }
01054 
01055 
01056     if (inliers.size() < (unsigned int)iPanoDetector.getMinimumMatches())
01057     {
01058         TRACE_PAIR("Too few matches ... removing all of them.");
01059         ioMatchData._matches.clear();
01060         return true;
01061     }
01062 
01063     // keep only inlier matches
01064     lfeat::PointMatchVector_t aInlierMatches;
01065     aInlierMatches.reserve(inliers.size());
01066 
01067     for (size_t i = 0; i < inliers.size(); ++i)
01068     {
01069         aInlierMatches.push_back(ioMatchData._matches[inliers[i]]);
01070     }
01071     ioMatchData._matches = aInlierMatches;
01072 
01073     /*
01074     if (iPanoDetector.getTest())
01075         TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
01076                                                                 aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
01077     */
01078 
01079     return true;
01080 }
01081 
01082 // homography based ransac matching
01083 bool PanoDetector::RansacMatchesInPairHomography(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
01084 {
01085     TRACE_PAIR("RANSAC Filtering...");
01086 
01087     if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
01088     {
01089         TRACE_PAIR("Too few matches ... removing all of them.");
01090         ioMatchData._matches.clear();
01091         return true;
01092     }
01093 
01094     if (ioMatchData._matches.size() < 6)
01095     {
01096         TRACE_PAIR("Not enough matches for RANSAC filtering.");
01097         return true;
01098     }
01099 
01100     lfeat::PointMatchVector_t aRemovedMatches;
01101 
01102     lfeat::Ransac aRansacFilter;
01103     aRansacFilter.setIterations(iPanoDetector.getRansacIterations());
01104     int thresholdDistance=iPanoDetector.getRansacDistanceThreshold();
01105     //increase RANSAC distance if the image were remapped to not exclude
01106     //too much points in this case
01107     if(ioMatchData._i1->NeedsRemapping() || ioMatchData._i2->NeedsRemapping())
01108     {
01109         thresholdDistance*=5;
01110     }
01111     aRansacFilter.setDistanceThreshold(thresholdDistance);
01112     aRansacFilter.filter(ioMatchData._matches, aRemovedMatches);
01113 
01114 
01115     TRACE_PAIR("Removed " << aRemovedMatches.size() << " matches. " << ioMatchData._matches.size() << " remaining.");
01116 
01117     if (aRemovedMatches.size() > ioMatchData._matches.size())
01118     {
01119         // more than 50% of matches were removed, ignore complete pair...
01120         TRACE_PAIR("More than 50% outliers, removing all matches");
01121         ioMatchData._matches.clear();
01122         return true;
01123     }
01124 
01125     if (iPanoDetector.getTest())
01126         TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
01127                                     aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
01128 
01129     return true;
01130 
01131 }
01132 
01133 
01134 bool PanoDetector::FilterMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
01135 {
01136     TRACE_PAIR("Clustering matches...");
01137 
01138     if (ioMatchData._matches.size() < 2)
01139     {
01140         return true;
01141     }
01142 
01143     // compute min,max of x,y for image1
01144 
01145     double aMinX = std::numeric_limits<double>::max();
01146     double aMinY = std::numeric_limits<double>::max();
01147     double aMaxX = -std::numeric_limits<double>::max();
01148     double aMaxY = -std::numeric_limits<double>::max();
01149 
01150     for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01151     {
01152         lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
01153         if (aM->_img1_x < aMinX)
01154         {
01155             aMinX = aM->_img1_x;
01156         }
01157         if (aM->_img1_x > aMaxX)
01158         {
01159             aMaxX = aM->_img1_x;
01160         }
01161 
01162         if (aM->_img1_y < aMinY)
01163         {
01164             aMinY = aM->_img1_y;
01165         }
01166         if (aM->_img1_y > aMaxY)
01167         {
01168             aMaxY = aM->_img1_y;
01169         }
01170     }
01171 
01172     double aSizeX = aMaxX - aMinX + 2; // add 2 so max/aSize is strict < 1
01173     double aSizeY = aMaxY - aMinY + 2;
01174 
01175     //
01176 
01177     lfeat::Sieve<lfeat::PointMatchPtr, lfeat::PointMatchPtrSort> aSieve(iPanoDetector.getSieve2Width(),
01178             iPanoDetector.getSieve2Height(),
01179             iPanoDetector.getSieve2Size());
01180 
01181     // insert the points in the Sieve
01182     double aXF = (double)iPanoDetector.getSieve2Width() / aSizeX;
01183     double aYF = (double)iPanoDetector.getSieve2Height() / aSizeY;
01184     for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01185     {
01186         lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
01187         aSieve.insert(aM, (int)((aM->_img1_x - aMinX) * aXF), (int)((aM->_img1_y - aMinY) * aYF));
01188     }
01189 
01190     // pull remaining values from the sieve
01191     ioMatchData._matches.clear();
01192 
01193     // make an extractor and pull the points
01194     SieveExtractorMatch aSieveExt(ioMatchData._matches);
01195     aSieve.extract(aSieveExt);
01196 
01197     TRACE_PAIR("Kept " << ioMatchData._matches.size() << " matches.");
01198     return true;
01199 }
01200 
01201 void PanoDetector::writeOutput()
01202 {
01203     // Write output pto file
01204 
01205     std::ofstream aOut(_outputFile.c_str(), std::ios_base::trunc);
01206     if( !aOut )
01207     {
01208         std::cerr << "ERROR : "
01209              << "Couldn't open file '" << _outputFile << "'!" << std::endl; //STS
01210         return;
01211     }
01212 
01213     aOut << "# pto project file generated by Hugin's cpfind" << std::endl << std::endl;
01214 
01215     _panoramaInfo->removeDuplicateCtrlPoints();
01216     AppBase::DocumentData::ReadWriteError err = _panoramaInfo->writeData(aOut);
01217     if (err != AppBase::DocumentData::SUCCESSFUL)
01218     {
01219         std::cerr << "ERROR couldn't write to output file '" << _outputFile << "'!" << std::endl;
01220         return;
01221     }
01222 }
01223 
01224 void PanoDetector::writeKeyfile(ImgData& imgInfo)
01225 {
01226     // Write output keyfile
01227 
01228     std::ofstream aOut(imgInfo._keyfilename.c_str(), std::ios_base::trunc);
01229 
01230     lfeat::SIFTFormatWriter writer(aOut);
01231 
01232     int origImgWidth =  _panoramaInfo->getImage(imgInfo._number).getSize().width();
01233     int origImgHeight =  _panoramaInfo->getImage(imgInfo._number).getSize().height();
01234 
01235     lfeat::ImageInfo img_info(imgInfo._name, origImgWidth, origImgHeight);
01236 
01237     writer.writeHeader ( img_info, imgInfo._kp.size(), imgInfo._descLength );
01238 
01239     for(size_t i=0; i<imgInfo._kp.size(); ++i)
01240     {
01241         lfeat::KeyPointPtr& aK=imgInfo._kp[i];
01242         writer.writeKeypoint ( aK->_x, aK->_y, aK->_scale, aK->_ori, aK->_score,
01243                                imgInfo._descLength, aK->_vec );
01244     }
01245     writer.writeFooter();
01246 }
01247 

Generated on 26 May 2017 for Hugintrunk by  doxygen 1.4.7