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

Generated on 26 Jul 2016 for Hugintrunk by  doxygen 1.4.7