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                         };
00456                         break;
00457                     case vigra::ImageImportInfo::UINT16:
00458                         // special variant for unsigned 16 bit images
00459                         {
00460                             vigra::UInt16RGBImage* rgbImage = new vigra::UInt16RGBImage(aImageInfo.size());
00461                             vigra::BImage* mask = NULL;
00462                             // load image
00463                             if (aImageInfo.numExtraBands() == 1)
00464                             {
00465                                 mask = new vigra::BImage(aImageInfo.size());
00466                                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
00467                             }
00468                             else
00469                             {
00470                                 vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
00471                             };
00472                             // apply icc profile
00473                             if (!aImageInfo.getICCProfile().empty())
00474                             {
00475                                 TRACE_IMG("Applying icc profile...");
00476                                 HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_16);
00477                             };
00478                             vigra::UInt16RGBImage* scaled = NULL;
00479                             if (ioImgInfo._needsremap)
00480                             {
00481                                 // remap image
00482                                 TRACE_IMG("Remapping image...");
00483                                 RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00484                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask,
00485                                     vigra_ext::PassThroughFunctor<vigra::RGBValue<vigra::UInt16> >(),
00486                                     scaled, final_mask);
00487                             }
00488                             else
00489                             {
00490                                 if (iPanoDetector._downscale)
00491                                 {
00492                                     TRACE_IMG("Downscale image...");
00493                                 };
00494                                 HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
00495                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, iPanoDetector._downscale,
00496                                     scaled, final_mask);
00497                             };
00498                             if (iPanoDetector.getCeleste())
00499                             {
00500                                 TRACE_IMG("Mask areas with clouds...");
00501                                 vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *scaled, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
00502 #ifdef DEBUG_LOADING_REMAPPING
00503                                 // DEBUG: export celeste mask
00504                                 std::ostringstream maskfilename;
00505                                 maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
00506                                 vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00507                                 vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
00508 #endif
00509                                 if (final_mask)
00510                                 {
00511                                     vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
00512                                 }
00513                                 else
00514                                 {
00515                                     final_mask = celeste_mask;
00516                                 };
00517                             };
00518                             // scale to greyscale
00519                             TRACE_IMG("Convert to greyscale double...");
00520                             final_img = new vigra::DImage(scaled->size());
00521                             // keypoint finder expext 0..255 range
00522                             vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt16> >()),
00523                                 vigra::destImage(*final_img), vigra::functor::Arg1() / vigra::functor::Param(255.0));
00524                         };
00525                         break;
00526                     default:
00527                         // double variant for all other cases
00528                         {
00529                             vigra::DRGBImage* rgbImage = new vigra::DRGBImage(aImageInfo.size());
00530                             vigra::BImage* mask = NULL;
00531                             // load image
00532                             if (aImageInfo.numExtraBands() == 1)
00533                             {
00534                                 mask = new vigra::BImage(aImageInfo.size());
00535                                 vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
00536                             }
00537                             else
00538                             {
00539                                 vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
00540                             };
00541                             // range adaption
00542                             double minVal = 0;
00543                             double maxVal;
00544                             if (aImageInfo.getPixelType() == std::string("FLOAT") || aImageInfo.getPixelType() == std::string("DOUBLE"))
00545                             {
00546                                 vigra::FindMinMax<float> minmax;   // init functor
00547                                 vigra::inspectImage(vigra::srcImageRange(*rgbImage, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), minmax);
00548                                 minVal = minmax.min;
00549                                 maxVal = minmax.max;
00550                             }
00551                             else
00552                             {
00553                                 maxVal = vigra_ext::getMaxValForPixelType(aImageInfo.getPixelType());
00554                             };
00555                             bool range255 = (fabs(maxVal - 255) < 0.01 && fabs(minVal) < 0.01);
00556                             if (aImageInfo.getICCProfile().empty())
00557                             {
00558                                 // no icc profile, cpfind expects images in 0 ..255 range
00559                                 TRACE_IMG("Rescale range...");
00560                                 if (!range255)
00561                                 {
00562                                     vigra_ext::applyMapping(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage), minVal, maxVal, 0);
00563                                 };
00564                                 range255 = true;
00565                             }
00566                             else
00567                             {
00568                                 // apply ICC profile
00569                                 TRACE_IMG("Applying icc profile...");
00570                                 // lcms expects for double datatype all values between 0 and 1
00571                                 vigra::transformImage(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage),
00572                                     vigra_ext::LinearTransform<vigra::RGBValue<double> >(1.0 / maxVal - minVal, -minVal));
00573                                 range255 = false;
00574                                 HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_DBL);
00575                             };
00576                             vigra::DRGBImage* scaled;
00577                             if (ioImgInfo._needsremap)
00578                             {
00579                                 // remap image
00580                                 TRACE_IMG("Remapping image...");
00581                                 RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
00582                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask, vigra_ext::PassThroughFunctor<double>(),
00583                                     scaled, final_mask);
00584                             }
00585                             else
00586                             {
00587                                 TRACE_IMG("Transform to suitable grayscale...");
00588                                 HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
00589                                     ioImgInfo._detectWidth, ioImgInfo._detectHeight, iPanoDetector._downscale,
00590                                     scaled, final_mask);
00591                             };
00592                             if (iPanoDetector.getCeleste())
00593                             {
00594                                 TRACE_IMG("Mask areas with clouds...");
00595                                 vigra::UInt16RGBImage* image16 = new vigra::UInt16RGBImage(scaled->size());
00596                                 if (range255)
00597                                 {
00598                                     vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
00599                                         vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
00600                                 }
00601                                 else
00602                                 {
00603                                     vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
00604                                         vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(65535));
00605                                 };
00606                                 vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *image16, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
00607 #ifdef DEBUG_LOADING_REMAPPING
00608                                 // DEBUG: export celeste mask
00609                                 std::ostringstream maskfilename;
00610                                 maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
00611                                 vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00612                                 vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
00613 #endif
00614                                 delete image16;
00615                                 if (final_mask)
00616                                 {
00617                                     vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
00618                                 }
00619                                 else
00620                                 {
00621                                     final_mask = celeste_mask;
00622                                 };
00623                             };
00624                             // scale to greyscale
00625                             TRACE_IMG("Convert to greyscale double...");
00626                             final_img = new vigra::DImage(scaled->size());
00627                             // keypoint finder expext 0..255 range
00628                             if (range255)
00629                             {
00630                                 vigra::copyImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), vigra::destImage(*final_img));
00631                             }
00632                             else
00633                             {
00634                                 vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()),
00635                                     vigra::destImage(*final_img), vigra::functor::Arg1() * vigra::functor::Param(255.0));
00636                             };
00637                         };
00638                         break;
00639                 };
00640             }
00641             else
00642             {
00643                 TRACE_INFO("Cpfind works only with grayscale or RGB images");
00644                 ioImgInfo._loadFail = true;
00645                 return false;
00646             };
00647         };
00648 
00649 #ifdef DEBUG_LOADING_REMAPPING
00650         // DEBUG: export remapped
00651         std::ostringstream filename;
00652         filename << ioImgInfo._name << "_grey.JPG";
00653         vigra::ImageExportInfo exinfo(filename.str().c_str());
00654         vigra::exportImage(vigra::srcImageRange(*final_img), exinfo);
00655 #endif
00656 
00657         // Build integral image
00658         TRACE_IMG("Build integral image...");
00659         ioImgInfo._ii.init(*final_img);
00660         delete final_img;
00661 
00662         // compute distance map
00663         if(final_mask)
00664         {
00665             TRACE_IMG("Build distance map...");
00666             //apply threshold, in case loaded mask contains other values than 0 and 255
00667             vigra::transformImage(vigra::srcImageRange(*final_mask), vigra::destImage(*final_mask),
00668                                   vigra::Threshold<vigra::BImage::PixelType, vigra::BImage::PixelType>(1, 255, 0, 255));
00669             ioImgInfo._distancemap.resize(final_mask->width(), final_mask->height(), 0);
00670             vigra::distanceTransform(vigra::srcImageRange(*final_mask), vigra::destImage(ioImgInfo._distancemap), 255, 2);
00671 #ifdef DEBUG_LOADING_REMAPPING
00672             std::ostringstream maskfilename;
00673             maskfilename << ioImgInfo._name << "_mask.JPG";
00674             vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
00675             vigra::exportImage(vigra::srcImageRange(*final_mask), maskexinfo);
00676             std::ostringstream distfilename;
00677             distfilename << ioImgInfo._name << "_distancemap.JPG";
00678             vigra::ImageExportInfo distexinfo(distfilename.str().c_str());
00679             vigra::exportImage(vigra::srcImageRange(ioImgInfo._distancemap), distexinfo);
00680 #endif
00681             delete final_mask;
00682         };
00683     }
00684     catch (std::exception& e)
00685     {
00686         TRACE_INFO("An error happened while loading image : caught exception: " << e.what() << std::endl);
00687         ioImgInfo._loadFail=true;
00688         return false;
00689     }
00690 
00691     return true;
00692 }
00693 
00694 
00695 bool PanoDetector::FindKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00696 {
00697     TRACE_IMG("Find keypoints...");
00698 
00699     // setup the detector
00700     KeyPointDetector aKP;
00701 
00702     // detect the keypoints
00703     KeyPointVectInsertor aInsertor(ioImgInfo._kp);
00704     aKP.detectKeypoints(ioImgInfo._ii, aInsertor);
00705 
00706     TRACE_IMG("Found "<< ioImgInfo._kp.size() << " interest points.");
00707 
00708     return true;
00709 }
00710 
00711 bool PanoDetector::FilterKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00712 {
00713     TRACE_IMG("Filtering keypoints...");
00714 
00715     lfeat::Sieve<lfeat::KeyPointPtr, lfeat::KeyPointPtrSort > aSieve(iPanoDetector.getSieve1Width(),
00716             iPanoDetector.getSieve1Height(),
00717             iPanoDetector.getSieve1Size());
00718 
00719     // insert the points in the Sieve if they are not masked
00720     double aXF = (double)iPanoDetector.getSieve1Width() / (double)ioImgInfo._detectWidth;
00721     double aYF = (double)iPanoDetector.getSieve1Height() / (double)ioImgInfo._detectHeight;
00722 
00723     const bool distmap_valid=(ioImgInfo._distancemap.width()>0 && ioImgInfo._distancemap.height()>0);
00724     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00725     {
00726         lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00727         if(distmap_valid)
00728         {
00729             if(aK->_x > 0 && aK->_x < ioImgInfo._distancemap.width() && aK->_y > 0 && aK->_y < ioImgInfo._distancemap.height()
00730                     && ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) >aK->_scale*8)
00731             {
00732                 //cout << " dist from border:" << ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) << " required dist: " << aK->_scale*12 << std::endl;
00733                 aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
00734             }
00735         }
00736         else
00737         {
00738             aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
00739         };
00740     }
00741 
00742     // pull remaining values from the sieve
00743     ioImgInfo._kp.clear();
00744 
00745     // make an extractor and pull the points
00746     SieveExtractorKP aSieveExt(ioImgInfo._kp);
00747     aSieve.extract(aSieveExt);
00748 
00749     TRACE_IMG("Kept " << ioImgInfo._kp.size() << " interest points.");
00750 
00751     return true;
00752 
00753 }
00754 
00755 bool PanoDetector::MakeKeyPointDescriptorsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00756 {
00757     TRACE_IMG("Make keypoint descriptors...");
00758 
00759     // build a keypoint descriptor
00760     lfeat::CircularKeyPointDescriptor aKPD(ioImgInfo._ii);
00761 
00762     // vector for keypoints with more than one orientation
00763     lfeat::KeyPointVect_t kp_new_ori;
00764     for (size_t j = 0; j < ioImgInfo._kp.size(); ++j)
00765     {
00766         lfeat::KeyPointPtr& aK = ioImgInfo._kp[j];
00767         double angles[4];
00768         int nAngles = aKPD.assignOrientation(*aK, angles);
00769         for (int i=0; i < nAngles; i++)
00770         {
00771             // duplicate Keypoint with additional angles
00772             lfeat::KeyPointPtr aKn = lfeat::KeyPointPtr ( new lfeat::KeyPoint ( *aK ) );
00773             aKn->_ori = angles[i];
00774             kp_new_ori.push_back(aKn);
00775         }
00776     }
00777     ioImgInfo._kp.insert(ioImgInfo._kp.end(), kp_new_ori.begin(), kp_new_ori.end());
00778 
00779     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00780     {
00781         aKPD.makeDescriptor(*(ioImgInfo._kp[i]));
00782     }
00783     // store the descriptor length
00784     ioImgInfo._descLength = aKPD.getDescriptorLength();
00785     return true;
00786 }
00787 
00788 bool PanoDetector::RemapBackKeypoints(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00789 {
00790 
00791     double scale=iPanoDetector._downscale ? 2.0:1.0;
00792 
00793     if (!ioImgInfo._needsremap)
00794     {
00795         if(scale != 1.0)
00796         {
00797             for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00798             {
00799                 lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00800                 aK->_x *= scale;
00801                 aK->_y *= scale;
00802                 aK->_scale *= scale;
00803             }
00804         };
00805     }
00806     else
00807     {
00808         TRACE_IMG("Remapping back keypoints...");
00809         HuginBase::PTools::Transform trafo1;
00810         trafo1.createTransform(iPanoDetector._panoramaInfoCopy.getSrcImage(ioImgInfo._number),
00811                                ioImgInfo._projOpts);
00812 
00813         int dx1 = ioImgInfo._projOpts.getROI().left();
00814         int dy1 = ioImgInfo._projOpts.getROI().top();
00815 
00816         for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00817         {
00818             lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
00819             double xout, yout;
00820             if(trafo1.transformImgCoord(xout, yout, aK->_x + dx1, aK->_y+ dy1))
00821             {
00822                 // downscaling is take care of by the remapping transform
00823                 // no need for multiplying the scale factor...
00824                 aK->_x=xout;
00825                 aK->_y=yout;
00826                 aK->_scale *= scale;
00827             }
00828         }
00829     }
00830     return true;
00831 }
00832 
00833 bool PanoDetector::BuildKDTreesInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00834 {
00835     TRACE_IMG("Build KDTree...");
00836 
00837     if(ioImgInfo._kp.size()==0)
00838     {
00839         return false;
00840     };
00841     // build a vector of KDElemKeyPointPtr
00842 
00843     // create feature vector matrix for flann
00844     ioImgInfo._flann_descriptors = flann::Matrix<double>(new double[ioImgInfo._kp.size()*ioImgInfo._descLength],
00845                                    ioImgInfo._kp.size(), ioImgInfo._descLength);
00846     for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
00847     {
00848         memcpy(ioImgInfo._flann_descriptors[i], ioImgInfo._kp[i]->_vec, sizeof(double)*ioImgInfo._descLength);
00849     }
00850 
00851     // build query structure
00852     ioImgInfo._flann_index = new flann::Index<flann::L2<double> > (ioImgInfo._flann_descriptors, flann::KDTreeIndexParams(4));
00853     ioImgInfo._flann_index->buildIndex();
00854 
00855     return true;
00856 }
00857 
00858 bool PanoDetector::FreeMemoryInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
00859 {
00860     TRACE_IMG("Freeing memory...");
00861 
00862     ioImgInfo._ii.clean();
00863     ioImgInfo._distancemap.resize(0,0);
00864 
00865     return true;
00866 }
00867 
00868 
00869 bool PanoDetector::FindMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00870 {
00871     TRACE_PAIR("Find Matches...");
00872 
00873     // retrieve the KDTree of image 2
00874     flann::Index<flann::L2<double> > * index2 = ioMatchData._i2->_flann_index;
00875 
00876     // retrieve query points from image 1
00877     flann::Matrix<double> & query = ioMatchData._i1->_flann_descriptors;
00878 
00879     // storage for sorted 2 best matches
00880     int nn = 2;
00881     flann::Matrix<int> indices(new int[query.rows*nn], query.rows, nn);
00882     flann::Matrix<double> dists(new double[query.rows*nn], query.rows, nn);
00883 
00884     // perform matching using flann
00885     index2->knnSearch(query, indices, dists, nn, flann::SearchParams(iPanoDetector.getKDTreeSearchSteps()));
00886 
00887     //typedef KDTreeSpace::BestMatch<KDElemKeyPoint>            BM_t;
00888     //std::set<BM_t, std::greater<BM_t> >       aBestMatches;
00889 
00890     // store the matches already found to avoid 2 points in image1
00891     // match the same point in image2
00892     // both matches will be removed.
00893     std::set<int> aAlreadyMatched;
00894     std::set<int> aBadMatch;
00895 
00896     // unfiltered vector of matches
00897     typedef std::pair<lfeat::KeyPointPtr, int> TmpPair_t;
00898     std::vector<TmpPair_t>      aUnfilteredMatches;
00899 
00900     //PointMatchVector_t aMatches;
00901 
00902     // go through all the keypoints of image 1
00903     for (unsigned aKIt = 0; aKIt < query.rows; ++aKIt)
00904     {
00905         // accept the match if the second match is far enough
00906         // put a lower value for stronger matching default 0.15
00907         if (dists[aKIt][0] > iPanoDetector.getKDTreeSecondDistance()  * dists[aKIt][1])
00908         {
00909             continue;
00910         }
00911 
00912         // check if the kdtree match number is already in the already matched set
00913         if (aAlreadyMatched.find(indices[aKIt][0]) != aAlreadyMatched.end())
00914         {
00915             // add to delete list and continue
00916             aBadMatch.insert(indices[aKIt][0]);
00917             continue;
00918         }
00919 
00920         // TODO: add check for duplicate matches (can happen if a keypoint gets multiple orientations)
00921 
00922         // add the match number in already matched set
00923         aAlreadyMatched.insert(indices[aKIt][0]);
00924 
00925         // add the match to the unfiltered list
00926         aUnfilteredMatches.push_back(TmpPair_t(ioMatchData._i1->_kp[aKIt], indices[aKIt][0]));
00927     }
00928 
00929     // now filter and fill the vector of matches
00930     for (size_t i = 0; i < aUnfilteredMatches.size(); ++i)
00931     {
00932         TmpPair_t& aP = aUnfilteredMatches[i];
00933         // if the image2 match number is in the badmatch set, skip it.
00934         if (aBadMatch.find(aP.second) != aBadMatch.end())
00935         {
00936             continue;
00937         }
00938 
00939         // add the match in the output vector
00940         ioMatchData._matches.push_back(lfeat::PointMatchPtr( new lfeat::PointMatch(aP.first, ioMatchData._i2->_kp[aP.second])));
00941     }
00942 
00943     TRACE_PAIR("Found " << ioMatchData._matches.size() << " matches.");
00944     return true;
00945 }
00946 
00947 bool PanoDetector::RansacMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00948 {
00949     // Use panotools model for wide angle lenses
00950     HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
00951     if (rmode == HuginBase::RANSACOptimizer::HOMOGRAPHY ||
00952         (rmode == HuginBase::RANSACOptimizer::AUTO && iPanoDetector._panoramaInfo->getImage(ioMatchData._i1->_number).getHFOV() < 65 &&
00953              iPanoDetector._panoramaInfo->getImage(ioMatchData._i2->_number).getHFOV() < 65))
00954     {
00955         return RansacMatchesInPairHomography(ioMatchData, iPanoDetector);
00956     }
00957     else
00958     {
00959         return RansacMatchesInPairCam(ioMatchData, iPanoDetector);
00960     }
00961 }
00962 
00963 // new code with fisheye aware ransac
00964 bool PanoDetector::RansacMatchesInPairCam(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
00965 {
00966     TRACE_PAIR("RANSAC Filtering with Panorama model...");
00967 
00968     if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
00969     {
00970         TRACE_PAIR("Too few matches ... removing all of them.");
00971         ioMatchData._matches.clear();
00972         return true;
00973     }
00974 
00975     if (ioMatchData._matches.size() < 6)
00976     {
00977         TRACE_PAIR("Not enough matches for RANSAC filtering.");
00978         return true;
00979     }
00980 
00981     // setup a panorama project with the two images.
00982     // is this threadsafe (is this read only access?)
00983     HuginBase::UIntSet imgs;
00984     int pano_i1 = ioMatchData._i1->_number;
00985     int pano_i2 = ioMatchData._i2->_number;
00986     imgs.insert(pano_i1);
00987     imgs.insert(pano_i2);
00988     int pano_local_i1 = 0;
00989     int pano_local_i2 = 1;
00990     if (pano_i1 > pano_i2)
00991     {
00992         pano_local_i1 = 1;
00993         pano_local_i2 = 0;
00994     }
00995 
00996     // perform ransac matching.
00997     // ARGH the panotools optimizer uses global variables is not reentrant
00998     std::vector<int> inliers;
00999 #pragma omp critical
01000     {
01001         HuginBase::PanoramaData* panoSubset = iPanoDetector._panoramaInfo->getNewSubset(imgs);
01002 
01003         // create control point vector
01004         HuginBase::CPVector controlPoints(ioMatchData._matches.size());
01005         for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01006         {
01007             lfeat::PointMatchPtr& aM=ioMatchData._matches[i];
01008             controlPoints[i] = HuginBase::ControlPoint(pano_local_i1, aM->_img1_x, aM->_img1_y,
01009                                             pano_local_i2, aM->_img2_x, aM->_img2_y);
01010         }
01011         panoSubset->setCtrlPoints(controlPoints);
01012 
01013 
01014         PT_setProgressFcn(ptProgress);
01015         PT_setInfoDlgFcn(ptinfoDlg);
01016 
01017         HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
01018         if (rmode == HuginBase::RANSACOptimizer::AUTO)
01019         {
01020             rmode = HuginBase::RANSACOptimizer::RPY;
01021         }
01022         inliers = HuginBase::RANSACOptimizer::findInliers(*panoSubset, pano_local_i1, pano_local_i2,
01023                   iPanoDetector.getRansacDistanceThreshold(), rmode);
01024         PT_setProgressFcn(NULL);
01025         PT_setInfoDlgFcn(NULL);
01026         delete panoSubset;
01027     }
01028 
01029     TRACE_PAIR("Removed " << ioMatchData._matches.size() - inliers.size() << " matches. " << inliers.size() << " remaining.");
01030     if (inliers.size() < 0.5 * ioMatchData._matches.size())
01031     {
01032         // more than 50% of matches were removed, ignore complete pair...
01033         TRACE_PAIR("RANSAC found more than 50% outliers, removing all matches");
01034         ioMatchData._matches.clear();
01035         return true;
01036     }
01037 
01038 
01039     if (inliers.size() < (unsigned int)iPanoDetector.getMinimumMatches())
01040     {
01041         TRACE_PAIR("Too few matches ... removing all of them.");
01042         ioMatchData._matches.clear();
01043         return true;
01044     }
01045 
01046     // keep only inlier matches
01047     lfeat::PointMatchVector_t aInlierMatches;
01048     aInlierMatches.reserve(inliers.size());
01049 
01050     for (size_t i = 0; i < inliers.size(); ++i)
01051     {
01052         aInlierMatches.push_back(ioMatchData._matches[inliers[i]]);
01053     }
01054     ioMatchData._matches = aInlierMatches;
01055 
01056     /*
01057     if (iPanoDetector.getTest())
01058         TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
01059                                                                 aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
01060     */
01061 
01062     return true;
01063 }
01064 
01065 // homography based ransac matching
01066 bool PanoDetector::RansacMatchesInPairHomography(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
01067 {
01068     TRACE_PAIR("RANSAC Filtering...");
01069 
01070     if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
01071     {
01072         TRACE_PAIR("Too few matches ... removing all of them.");
01073         ioMatchData._matches.clear();
01074         return true;
01075     }
01076 
01077     if (ioMatchData._matches.size() < 6)
01078     {
01079         TRACE_PAIR("Not enough matches for RANSAC filtering.");
01080         return true;
01081     }
01082 
01083     lfeat::PointMatchVector_t aRemovedMatches;
01084 
01085     lfeat::Ransac aRansacFilter;
01086     aRansacFilter.setIterations(iPanoDetector.getRansacIterations());
01087     int thresholdDistance=iPanoDetector.getRansacDistanceThreshold();
01088     //increase RANSAC distance if the image were remapped to not exclude
01089     //too much points in this case
01090     if(ioMatchData._i1->_needsremap || ioMatchData._i2->_needsremap)
01091     {
01092         thresholdDistance*=5;
01093     }
01094     aRansacFilter.setDistanceThreshold(thresholdDistance);
01095     aRansacFilter.filter(ioMatchData._matches, aRemovedMatches);
01096 
01097 
01098     TRACE_PAIR("Removed " << aRemovedMatches.size() << " matches. " << ioMatchData._matches.size() << " remaining.");
01099 
01100     if (aRemovedMatches.size() > ioMatchData._matches.size())
01101     {
01102         // more than 50% of matches were removed, ignore complete pair...
01103         TRACE_PAIR("More than 50% outliers, removing all matches");
01104         ioMatchData._matches.clear();
01105         return true;
01106     }
01107 
01108     if (iPanoDetector.getTest())
01109         TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
01110                                     aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
01111 
01112     return true;
01113 
01114 }
01115 
01116 
01117 bool PanoDetector::FilterMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
01118 {
01119     TRACE_PAIR("Clustering matches...");
01120 
01121     if (ioMatchData._matches.size() < 2)
01122     {
01123         return true;
01124     }
01125 
01126     // compute min,max of x,y for image1
01127 
01128     double aMinX = std::numeric_limits<double>::max();
01129     double aMinY = std::numeric_limits<double>::max();
01130     double aMaxX = -std::numeric_limits<double>::max();
01131     double aMaxY = -std::numeric_limits<double>::max();
01132 
01133     for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01134     {
01135         lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
01136         if (aM->_img1_x < aMinX)
01137         {
01138             aMinX = aM->_img1_x;
01139         }
01140         if (aM->_img1_x > aMaxX)
01141         {
01142             aMaxX = aM->_img1_x;
01143         }
01144 
01145         if (aM->_img1_y < aMinY)
01146         {
01147             aMinY = aM->_img1_y;
01148         }
01149         if (aM->_img1_y > aMaxY)
01150         {
01151             aMaxY = aM->_img1_y;
01152         }
01153     }
01154 
01155     double aSizeX = aMaxX - aMinX + 2; // add 2 so max/aSize is strict < 1
01156     double aSizeY = aMaxY - aMinY + 2;
01157 
01158     //
01159 
01160     lfeat::Sieve<lfeat::PointMatchPtr, lfeat::PointMatchPtrSort> aSieve(iPanoDetector.getSieve2Width(),
01161             iPanoDetector.getSieve2Height(),
01162             iPanoDetector.getSieve2Size());
01163 
01164     // insert the points in the Sieve
01165     double aXF = (double)iPanoDetector.getSieve2Width() / aSizeX;
01166     double aYF = (double)iPanoDetector.getSieve2Height() / aSizeY;
01167     for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
01168     {
01169         lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
01170         aSieve.insert(aM, (int)((aM->_img1_x - aMinX) * aXF), (int)((aM->_img1_y - aMinY) * aYF));
01171     }
01172 
01173     // pull remaining values from the sieve
01174     ioMatchData._matches.clear();
01175 
01176     // make an extractor and pull the points
01177     SieveExtractorMatch aSieveExt(ioMatchData._matches);
01178     aSieve.extract(aSieveExt);
01179 
01180     TRACE_PAIR("Kept " << ioMatchData._matches.size() << " matches.");
01181     return true;
01182 }
01183 
01184 void PanoDetector::writeOutput()
01185 {
01186     // Write output pto file
01187 
01188     std::ofstream aOut(_outputFile.c_str(), std::ios_base::trunc);
01189     if( !aOut )
01190     {
01191         std::cerr << "ERROR : "
01192              << "Couldn't open file '" << _outputFile << "'!" << std::endl; //STS
01193         return;
01194     }
01195 
01196     aOut << "# pto project file generated by Hugin's cpfind" << std::endl << std::endl;
01197 
01198     _panoramaInfo->removeDuplicateCtrlPoints();
01199     AppBase::DocumentData::ReadWriteError err = _panoramaInfo->writeData(aOut);
01200     if (err != AppBase::DocumentData::SUCCESSFUL)
01201     {
01202         std::cerr << "ERROR couldn't write to output file '" << _outputFile << "'!" << std::endl;
01203         return;
01204     }
01205 }
01206 
01207 void PanoDetector::writeKeyfile(ImgData& imgInfo)
01208 {
01209     // Write output keyfile
01210 
01211     std::ofstream aOut(imgInfo._keyfilename.c_str(), std::ios_base::trunc);
01212 
01213     lfeat::SIFTFormatWriter writer(aOut);
01214 
01215     int origImgWidth =  _panoramaInfo->getImage(imgInfo._number).getSize().width();
01216     int origImgHeight =  _panoramaInfo->getImage(imgInfo._number).getSize().height();
01217 
01218     lfeat::ImageInfo img_info(imgInfo._name, origImgWidth, origImgHeight);
01219 
01220     writer.writeHeader ( img_info, imgInfo._kp.size(), imgInfo._descLength );
01221 
01222     for(size_t i=0; i<imgInfo._kp.size(); ++i)
01223     {
01224         lfeat::KeyPointPtr& aK=imgInfo._kp[i];
01225         writer.writeKeypoint ( aK->_x, aK->_y, aK->_scale, aK->_ori, aK->_score,
01226                                imgInfo._descLength, aK->_vec );
01227     }
01228     writer.writeFooter();
01229 }
01230 

Generated on 10 Feb 2016 for Hugintrunk by  doxygen 1.4.7