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

Generated on 27 May 2016 for Hugintrunk by  doxygen 1.4.7