Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PanoDetectorLogic.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 ; tab-width: 4 -*-
2 /*
3 * Copyright (C) 2007-2008 Anael Orlinski
4 *
5 * This file is part of Panomatic.
6 *
7 * Panomatic is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * Panomatic is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Panomatic; if not, write to the Free Software
19 * <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "ImageImport.h"
23 
24 #include "PanoDetector.h"
25 #include <iostream>
26 #include <fstream>
27 #include <vigra/distancetransform.hxx>
28 #include "vigra_ext/impexalpha.hxx"
29 #include "vigra_ext/cms.h"
30 
31 #include <localfeatures/Sieve.h>
36 
37 /*
38 #include "KDTree.h"
39 #include "KDTreeImpl.h"
40 */
41 #include "Utils.h"
42 #include "hugin_utils/stl_utils.h"
43 #include "Tracer.h"
44 
47 #include <nona/RemappedPanoImage.h>
48 #include <nona/ImageRemapper.h>
49 
50 #include <time.h>
51 
52 #define TRACE_IMG(X) {if (iPanoDetector.getVerbose() > 1) { TRACE_INFO("i" << ioImgInfo._number << " : " << X << std::endl);} }
53 #define TRACE_PAIR(X) {if (iPanoDetector.getVerbose() > 1){ TRACE_INFO("i" << ioMatchData._i1->_number << " <> " \
54  "i" << ioMatchData._i2->_number << " : " << X << std::endl)}}
55 
56 // define a Keypoint insertor
58 {
59 public:
60  explicit KeyPointVectInsertor(lfeat::KeyPointVect_t& iVect) : _v(iVect) {};
61  inline virtual void operator()(const lfeat::KeyPoint& k)
62  {
63  _v.push_back(lfeat::KeyPointPtr(new lfeat::KeyPoint(k)));
64  }
65 
66 private:
68 
69 };
70 
71 
72 // define a sieve extractor
73 class SieveExtractorKP : public lfeat::SieveExtractor<lfeat::KeyPointPtr>
74 {
75 public:
76  explicit SieveExtractorKP(lfeat::KeyPointVect_t& iV) : _v(iV) {};
77  inline virtual void operator()(const lfeat::KeyPointPtr& k)
78  {
79  _v.push_back(k);
80  }
81 private:
83 };
84 
85 class SieveExtractorMatch : public lfeat::SieveExtractor<lfeat::PointMatchPtr>
86 {
87 public:
89  inline virtual void operator()(const lfeat::PointMatchPtr& m)
90  {
91  _m.push_back(m);
92  }
93 private:
95 };
96 
97 bool PanoDetector::LoadKeypoints(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
98 {
99  TRACE_IMG("Loading keypoints...");
100 
102  ioImgInfo._loadFail = (info.filename.empty());
103 
104  // update ImgData
105  if(ioImgInfo.NeedsRemapping())
106  {
107  ioImgInfo._detectWidth = std::max(info.width,info.height);
108  ioImgInfo._detectHeight = std::max(info.width,info.height);
109  ioImgInfo._projOpts.setWidth(ioImgInfo._detectWidth);
110  ioImgInfo._projOpts.setHeight(ioImgInfo._detectHeight);
111  }
112  else
113  {
114  ioImgInfo._detectWidth = info.width;
115  ioImgInfo._detectHeight = info.height;
116  };
117  ioImgInfo._descLength = info.dimensions;
118 
119  return true;
120 }
121 
123 template <class SrcImageIterator, class SrcAccessor>
124 void applyMaskAndCrop(vigra::triple<SrcImageIterator, SrcImageIterator, SrcAccessor> img, const HuginBase::SrcPanoImage& SrcImg)
125 {
126  vigra::Diff2D imgSize = img.second - img.first;
127 
128  // create dest y iterator
129  SrcImageIterator yd(img.first);
130  // loop over the image and transform
131  for(int y=0; y < imgSize.y; ++y, ++yd.y)
132  {
133  // create x iterators
134  SrcImageIterator xd(yd);
135  for(int x=0; x < imgSize.x; ++x, ++xd.x)
136  {
137  if(!SrcImg.isInside(vigra::Point2D(x,y)))
138  {
139  *xd=0;
140  };
141  }
142  }
143 }
144 
146 template <class T>
148 {
149  typedef T result_type;
150  explicit ScaleFunctor(double scale) { m_scale = scale; };
151 
152  T operator()(const T & a) const
153  {
154  return m_scale*a;
155  }
156 
157  template <class T2>
158  T2 operator()(const T2 & a, const hugin_utils::FDiff2D & p) const
159  {
160  return m_scale*a;
161  }
162 
163  template <class T2, class A>
164  A hdrWeight(T2 v, A a) const
165  {
166  return a;
167  }
168 
169 private:
170  double m_scale;
171 };
172 
177 template <class ImageType, class PixelTransform>
179  size_t detectWidth, size_t detectHeight,
180  ImageType*& image, vigra::BImage*& mask,
181  const PixelTransform& pixelTransform,
182  ImageType*& finalImage, vigra::BImage*& finalMask)
183 {
186  transform.createTransform(srcImage, options);
187  finalImage = new ImageType(detectWidth, detectHeight);
188  finalMask = new vigra::BImage(detectWidth, detectHeight, vigra::UInt8(0));
189  if (srcImage.hasActiveMasks() || (srcImage.getCropMode() != HuginBase::SrcPanoImage::NO_CROP && !srcImage.getCropRect().isEmpty()))
190  {
191  if (!mask)
192  {
193  // image has no mask, create full mask
194  mask = new vigra::BImage(image->size(), vigra::UInt8(255));
195  };
196  applyMaskAndCrop(vigra::destImageRange(*mask), srcImage);
197  };
198  if (mask)
199  {
201  options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
202  delete mask;
203  mask = NULL;
204  }
205  else
206  {
208  options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
209  };
210  delete image;
211  image = NULL;
212 }
213 
216 template <class ImageType>
217 void HandleDownscaleImage(const HuginBase::SrcPanoImage& srcImage, ImageType*& image, vigra::BImage*& mask,
218  size_t detectWidth, size_t detectHeight, bool downscale,
219  ImageType*& finalImage, vigra::BImage*& finalMask)
220 {
221  if (srcImage.hasActiveMasks() || (srcImage.getCropMode() != HuginBase::SrcPanoImage::NO_CROP && !srcImage.getCropRect().isEmpty()))
222  {
223  if (!mask)
224  {
225  // image has no mask, create full mask
226  mask = new vigra::BImage(image->size(), vigra::UInt8(255));
227  };
228  //copy mask and crop from pto file into alpha layer
229  applyMaskAndCrop(vigra::destImageRange(*mask), srcImage);
230  };
231  if (downscale)
232  {
233  // Downscale image
234  finalImage = new ImageType(detectWidth, detectHeight);
235  vigra::resizeImageNoInterpolation(vigra::srcImageRange(*image), vigra::destImageRange(*finalImage));
236  delete image;
237  image = NULL;
238  //downscale mask
239  if (mask)
240  {
241  finalMask = new vigra::BImage(detectWidth, detectHeight);
242  vigra::resizeImageNoInterpolation(vigra::srcImageRange(*mask), vigra::destImageRange(*finalMask));
243  delete mask;
244  mask = NULL;
245  };
246  }
247  else
248  {
249  // simply copy pointer instead of copying the whole image data
250  finalImage = image;
251  if (mask)
252  {
253  finalMask = mask;
254  };
255  };
256 };
257 
258 // save some intermediate images to disc if defined
259 // #define DEBUG_LOADING_REMAPPING
260 bool PanoDetector::AnalyzeImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
261 {
262  vigra::DImage* final_img = NULL;
263  vigra::BImage* final_mask = NULL;
264 
265  try
266  {
267  ioImgInfo._loadFail=false;
268 
269  TRACE_IMG("Load image...");
270  vigra::ImageImportInfo aImageInfo(ioImgInfo._name.c_str());
271  if (aImageInfo.numExtraBands() > 1)
272  {
273  TRACE_INFO("Image with multiple alpha channels are not supported");
274  ioImgInfo._loadFail = true;
275  return false;
276  };
277  // remark: it would be possible to handle all cases with the same code
278  // but this would mean that in some cases there are unnecessary
279  // range conversions and image data copying actions needed
280  // so we use specialed code for several cases to reduce memory usage
281  // and prevent unnecessary range adaptions
282  if (aImageInfo.isGrayscale())
283  {
284  // gray scale image
285  vigra::DImage* image = new vigra::DImage(aImageInfo.size());
286  vigra::BImage* mask = NULL;
287  // load gray scale image
288  if (aImageInfo.numExtraBands() == 1)
289  {
290  mask=new vigra::BImage(aImageInfo.size());
291  vigra::importImageAlpha(aImageInfo, vigra::destImage(*image), vigra::destImage(*mask));
292  }
293  else
294  {
295  vigra::importImage(aImageInfo, vigra::destImage(*image));
296  };
297  // adopt range
298  double minVal = 0;
299  double maxVal;
300  if (aImageInfo.getPixelType() == std::string("FLOAT") || aImageInfo.getPixelType() == std::string("DOUBLE") ||
301  aImageInfo.getPixelType() == std::string("UINT32") || aImageInfo.getPixelType() == std::string("INT32"))
302  {
303  vigra::FindAverageAndVariance<float> mean; // init functor
304  vigra::inspectImage(vigra::srcImageRange(*image), mean);
305  minVal = std::max(mean.average() - 3 * sqrt(mean.variance()), 1e-6f);
306  maxVal = mean.average() + 3 * sqrt(mean.variance());;
307  }
308  else
309  {
310  maxVal = vigra_ext::getMaxValForPixelType(aImageInfo.getPixelType());
311  };
312  bool range255 = (fabs(maxVal - 255) < 0.01 && fabs(minVal) < 0.01);
313  if (aImageInfo.getICCProfile().empty())
314  {
315  // no icc profile, cpfind expects images in 0 ..255 range
316  TRACE_IMG("Rescale range...");
317  if (!range255)
318  {
320  vigra::linearRangeMapping(minVal, maxVal, 0.0, 255.0));
321  };
322  range255 = true;
323  }
324  else
325  {
326  // apply ICC profile
327  TRACE_IMG("Applying icc profile...");
328  // lcms expects for double datatype all values between 0 and 1
330  vigra::linearRangeMapping(minVal, maxVal, 0.0, 1.0));
331  range255 = false;
332  HuginBase::Color::ApplyICCProfile(*image, aImageInfo.getICCProfile(), TYPE_GRAY_DBL);
333  };
334  if (ioImgInfo.NeedsRemapping())
335  {
336  // remap image
337  TRACE_IMG("Remapping image...");
338  if (range255)
339  {
340  RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
341  ioImgInfo._detectWidth, ioImgInfo._detectHeight, image, mask, vigra_ext::PassThroughFunctor<double>(),
342  final_img, final_mask);
343  }
344  else
345  {
346  // images has been scaled to 0..1 range before, scale back to 0..255 range
347  RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
348  ioImgInfo._detectWidth, ioImgInfo._detectHeight, image, mask, ScaleFunctor<double>(255.0),
349  final_img, final_mask);
350  };
351  }
352  else
353  {
354  if (range255)
355  {
356  TRACE_IMG("Downscale and transform to suitable grayscale...");
357  HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), image, mask,
358  ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
359  final_img, final_mask);
360  }
361  else
362  {
363  TRACE_IMG("Transform to suitable grayscale...");
364  HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), image, mask,
365  ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
366  final_img, final_mask);
367  vigra::transformImage(vigra::srcImageRange(*final_img), vigra::destImage(*final_img), vigra::linearRangeMapping(0, 1, 0, 255));
368  };
369  };
370  if (iPanoDetector.getCeleste())
371  {
372  TRACE_IMG("Celeste does not work with grayscale images. Skipping...");
373  };
374  }
375  else
376  {
377  if (aImageInfo.isColor())
378  {
379  // rgb images
380  // prepare radius parameter for celeste
381  int radius = 1;
382  if (iPanoDetector.getCeleste())
383  {
384  radius = iPanoDetector.getCelesteRadius();
385  if (iPanoDetector._downscale)
386  {
387  radius >>= 1;
388  };
389  if (radius < 2)
390  {
391  radius = 2;
392  };
393  };
394  switch (aImageInfo.pixelType())
395  {
396  case vigra::ImageImportInfo::UINT8:
397  // special variant for unsigned 8 bit images
398  {
399  vigra::BRGBImage* rgbImage=new vigra::BRGBImage(aImageInfo.size());
400  vigra::BImage* mask = NULL;
401  // load image
402  if (aImageInfo.numExtraBands() == 1)
403  {
404  mask=new vigra::BImage(aImageInfo.size());
405  vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
406  }
407  else
408  {
409  vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
410  };
411  // apply icc profile
412  if (!aImageInfo.getICCProfile().empty())
413  {
414  TRACE_IMG("Applying icc profile...");
415  HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_8);
416  };
417  vigra::BRGBImage* scaled = NULL;
418  if (ioImgInfo.NeedsRemapping())
419  {
420  // remap image
421  TRACE_IMG("Remapping image...");
422  RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
423  ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask,
425  scaled, final_mask);
426  }
427  else
428  {
429  if (ioImgInfo.IsDownscale())
430  {
431  TRACE_IMG("Downscale image...");
432  };
433  HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
434  ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
435  scaled, final_mask);
436  };
437  if (iPanoDetector.getCeleste())
438  {
439  TRACE_IMG("Mask areas with clouds...");
440  vigra::UInt16RGBImage* image16=new vigra::UInt16RGBImage(scaled->size());
442  vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
443  vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *image16, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
444 #ifdef DEBUG_LOADING_REMAPPING
445  // DEBUG: export celeste mask
446  std::ostringstream maskfilename;
447  maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
448  vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
449  vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
450 #endif
451  delete image16;
452  if (final_mask)
453  {
454  vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
455  }
456  else
457  {
458  final_mask = celeste_mask;
459  };
460  };
461  // scale to greyscale
462  TRACE_IMG("Convert to greyscale double...");
463  final_img = new vigra::DImage(scaled->size());
464  vigra::copyImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >()),
465  vigra::destImage(*final_img));
466  delete scaled;
467  };
468  break;
469  case vigra::ImageImportInfo::UINT16:
470  // special variant for unsigned 16 bit images
471  {
472  vigra::UInt16RGBImage* rgbImage = new vigra::UInt16RGBImage(aImageInfo.size());
473  vigra::BImage* mask = NULL;
474  // load image
475  if (aImageInfo.numExtraBands() == 1)
476  {
477  mask = new vigra::BImage(aImageInfo.size());
478  vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
479  }
480  else
481  {
482  vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
483  };
484  // apply icc profile
485  if (!aImageInfo.getICCProfile().empty())
486  {
487  TRACE_IMG("Applying icc profile...");
488  HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_16);
489  };
490  vigra::UInt16RGBImage* scaled = NULL;
491  if (ioImgInfo.NeedsRemapping())
492  {
493  // remap image
494  TRACE_IMG("Remapping image...");
495  RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
496  ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask,
498  scaled, final_mask);
499  }
500  else
501  {
502  if (ioImgInfo.IsDownscale())
503  {
504  TRACE_IMG("Downscale image...");
505  };
506  HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
507  ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
508  scaled, final_mask);
509  };
510  if (iPanoDetector.getCeleste())
511  {
512  TRACE_IMG("Mask areas with clouds...");
513  vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *scaled, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
514 #ifdef DEBUG_LOADING_REMAPPING
515  // DEBUG: export celeste mask
516  std::ostringstream maskfilename;
517  maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
518  vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
519  vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
520 #endif
521  if (final_mask)
522  {
523  vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
524  }
525  else
526  {
527  final_mask = celeste_mask;
528  };
529  };
530  // scale to greyscale
531  TRACE_IMG("Convert to greyscale double...");
532  final_img = new vigra::DImage(scaled->size());
533  // keypoint finder expext 0..255 range
534  vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt16> >()),
535  vigra::destImage(*final_img), vigra::functor::Arg1() / vigra::functor::Param(255.0));
536  delete scaled;
537  };
538  break;
539  default:
540  // double variant for all other cases
541  {
542  vigra::DRGBImage* rgbImage = new vigra::DRGBImage(aImageInfo.size());
543  vigra::BImage* mask = NULL;
544  // load image
545  if (aImageInfo.numExtraBands() == 1)
546  {
547  mask = new vigra::BImage(aImageInfo.size());
548  vigra::importImageAlpha(aImageInfo, vigra::destImage(*rgbImage), vigra::destImage(*mask));
549  }
550  else
551  {
552  vigra::importImage(aImageInfo, vigra::destImage(*rgbImage));
553  };
554  // range adaption
555  double minVal = 0;
556  double maxVal;
557  const bool isDouble = aImageInfo.getPixelType() == std::string("FLOAT") || aImageInfo.getPixelType() == std::string("DOUBLE") ||
558  aImageInfo.getPixelType() == std::string("UINT32") || aImageInfo.getPixelType() == std::string("INT32");
559  if (isDouble)
560  {
561  vigra::FindAverageAndVariance<float> mean; // init functor
562  vigra::inspectImage(vigra::srcImageRange(*rgbImage, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), mean);
563  minVal = std::max(mean.average() - 3 * sqrt(mean.variance()), 1e-6f);
564  maxVal = mean.average() + 3 * sqrt(mean.variance());;
565  }
566  else
567  {
568  maxVal = vigra_ext::getMaxValForPixelType(aImageInfo.getPixelType());
569  };
570  bool range255 = (fabs(maxVal - 255) < 0.01 && fabs(minVal) < 0.01);
571  if (aImageInfo.getICCProfile().empty())
572  {
573  // no icc profile, cpfind expects images in 0 ..255 range
574  TRACE_IMG("Rescale range...");
575  if (!range255)
576  {
577  int mapping = 0;
578  if (isDouble && iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number).getResponseType() == HuginBase::BaseSrcPanoImage::RESPONSE_LINEAR)
579  {
580  // switch to log mapping for double/float images with linear response type
581  mapping = 1;
582  };
583  vigra_ext::applyMapping(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage), minVal, maxVal, mapping);
584  };
585  range255 = true;
586  }
587  else
588  {
589  // apply ICC profile
590  TRACE_IMG("Applying icc profile...");
591  // lcms expects for double datatype all values between 0 and 1
593  vigra_ext::LinearTransform<vigra::RGBValue<double> >(1.0 / maxVal - minVal, -minVal));
594  range255 = false;
595  HuginBase::Color::ApplyICCProfile(*rgbImage, aImageInfo.getICCProfile(), TYPE_RGB_DBL);
596  };
597  vigra::DRGBImage* scaled;
598  if (ioImgInfo.NeedsRemapping())
599  {
600  // remap image
601  TRACE_IMG("Remapping image...");
602  RemapImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), ioImgInfo._projOpts,
603  ioImgInfo._detectWidth, ioImgInfo._detectHeight, rgbImage, mask, vigra_ext::PassThroughFunctor<double>(),
604  scaled, final_mask);
605  }
606  else
607  {
608  TRACE_IMG("Transform to suitable grayscale...");
609  HandleDownscaleImage(iPanoDetector._panoramaInfoCopy.getImage(ioImgInfo._number), rgbImage, mask,
610  ioImgInfo._detectWidth, ioImgInfo._detectHeight, ioImgInfo.IsDownscale(),
611  scaled, final_mask);
612  };
613  if (iPanoDetector.getCeleste())
614  {
615  TRACE_IMG("Mask areas with clouds...");
616  vigra::UInt16RGBImage* image16 = new vigra::UInt16RGBImage(scaled->size());
617  if (range255)
618  {
620  vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
621  }
622  else
623  {
625  vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(65535));
626  };
627  vigra::BImage* celeste_mask = celeste::getCelesteMask(iPanoDetector.svmModel, *image16, radius, iPanoDetector.getCelesteThreshold(), 800, true, false);
628 #ifdef DEBUG_LOADING_REMAPPING
629  // DEBUG: export celeste mask
630  std::ostringstream maskfilename;
631  maskfilename << ioImgInfo._name << "_celeste_mask.JPG";
632  vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
633  vigra::exportImage(vigra::srcImageRange(*celeste_mask), maskexinfo);
634 #endif
635  delete image16;
636  if (final_mask)
637  {
638  vigra::copyImageIf(vigra::srcImageRange(*celeste_mask), vigra::srcImage(*final_mask), vigra::destImage(*final_mask));
639  }
640  else
641  {
642  final_mask = celeste_mask;
643  };
644  };
645  // scale to greyscale
646  TRACE_IMG("Convert to greyscale double...");
647  final_img = new vigra::DImage(scaled->size());
648  // keypoint finder expext 0..255 range
649  if (range255)
650  {
651  vigra::copyImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()), vigra::destImage(*final_img));
652  }
653  else
654  {
655  vigra::transformImage(vigra::srcImageRange(*scaled, vigra::RGBToGrayAccessor<vigra::RGBValue<double> >()),
656  vigra::destImage(*final_img), vigra::functor::Arg1() * vigra::functor::Param(255.0));
657  };
658  delete scaled;
659  };
660  break;
661  };
662  }
663  else
664  {
665  TRACE_INFO("Cpfind works only with grayscale or RGB images");
666  ioImgInfo._loadFail = true;
667  return false;
668  };
669  };
670 
671 #ifdef DEBUG_LOADING_REMAPPING
672  // DEBUG: export remapped
673  std::ostringstream filename;
674  filename << ioImgInfo._name << "_grey.JPG";
675  vigra::ImageExportInfo exinfo(filename.str().c_str());
676  vigra::exportImage(vigra::srcImageRange(*final_img), exinfo);
677 #endif
678 
679  // Build integral image
680  TRACE_IMG("Build integral image...");
681  ioImgInfo._ii.init(*final_img);
682  delete final_img;
683 
684  // compute distance map
685  if(final_mask)
686  {
687  TRACE_IMG("Build distance map...");
688  //apply threshold, in case loaded mask contains other values than 0 and 255
690  vigra::Threshold<vigra::BImage::PixelType, vigra::BImage::PixelType>(1, 255, 0, 255));
691  ioImgInfo._distancemap.resize(final_mask->width(), final_mask->height(), 0);
692  vigra::distanceTransform(vigra::srcImageRange(*final_mask), vigra::destImage(ioImgInfo._distancemap), 255, 2);
693 #ifdef DEBUG_LOADING_REMAPPING
694  std::ostringstream maskfilename;
695  maskfilename << ioImgInfo._name << "_mask.JPG";
696  vigra::ImageExportInfo maskexinfo(maskfilename.str().c_str());
697  vigra::exportImage(vigra::srcImageRange(*final_mask), maskexinfo);
698  std::ostringstream distfilename;
699  distfilename << ioImgInfo._name << "_distancemap.JPG";
700  vigra::ImageExportInfo distexinfo(distfilename.str().c_str());
701  vigra::exportImage(vigra::srcImageRange(ioImgInfo._distancemap), distexinfo);
702 #endif
703  delete final_mask;
704  };
705  }
706  catch (std::exception& e)
707  {
708  TRACE_INFO("An error happened while loading image : caught exception: " << e.what() << std::endl);
709  ioImgInfo._loadFail=true;
710  return false;
711  }
712 
713  return true;
714 }
715 
716 
717 bool PanoDetector::FindKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
718 {
719  TRACE_IMG("Find keypoints...");
720 
721  // setup the detector
722  KeyPointDetector aKP;
723 
724  // detect the keypoints
725  KeyPointVectInsertor aInsertor(ioImgInfo._kp);
726  aKP.detectKeypoints(ioImgInfo._ii, aInsertor);
727 
728  TRACE_IMG("Found "<< ioImgInfo._kp.size() << " interest points.");
729 
730  return true;
731 }
732 
733 bool PanoDetector::FilterKeyPointsInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
734 {
735  TRACE_IMG("Filtering keypoints...");
736 
738  iPanoDetector.getSieve1Height(),
739  iPanoDetector.getSieve1Size());
740 
741  // insert the points in the Sieve if they are not masked
742  double aXF = (double)iPanoDetector.getSieve1Width() / (double)ioImgInfo._detectWidth;
743  double aYF = (double)iPanoDetector.getSieve1Height() / (double)ioImgInfo._detectHeight;
744 
745  const bool distmap_valid=(ioImgInfo._distancemap.width()>0 && ioImgInfo._distancemap.height()>0);
746  for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
747  {
748  lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
749  if(distmap_valid)
750  {
751  if(aK->_x > 0 && aK->_x < ioImgInfo._distancemap.width() && aK->_y > 0 && aK->_y < ioImgInfo._distancemap.height()
752  && ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) >aK->_scale*8)
753  {
754  //cout << " dist from border:" << ioImgInfo._distancemap((int)(aK->_x),(int)(aK->_y)) << " required dist: " << aK->_scale*12 << std::endl;
755  aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
756  }
757  }
758  else
759  {
760  aSieve.insert(aK, (int)(aK->_x * aXF), (int)(aK->_y * aYF));
761  };
762  }
763 
764  // pull remaining values from the sieve
765  ioImgInfo._kp.clear();
766 
767  // make an extractor and pull the points
768  SieveExtractorKP aSieveExt(ioImgInfo._kp);
769  aSieve.extract(aSieveExt);
770 
771  TRACE_IMG("Kept " << ioImgInfo._kp.size() << " interest points.");
772 
773  return true;
774 
775 }
776 
778 {
779  TRACE_IMG("Make keypoint descriptors...");
780 
781  // build a keypoint descriptor
782  lfeat::CircularKeyPointDescriptor aKPD(ioImgInfo._ii);
783 
784  // vector for keypoints with more than one orientation
785  lfeat::KeyPointVect_t kp_new_ori;
786  for (size_t j = 0; j < ioImgInfo._kp.size(); ++j)
787  {
788  lfeat::KeyPointPtr& aK = ioImgInfo._kp[j];
789  double angles[4];
790  int nAngles = aKPD.assignOrientation(*aK, angles);
791  for (int i=0; i < nAngles; i++)
792  {
793  // duplicate Keypoint with additional angles
795  aKn->_ori = angles[i];
796  kp_new_ori.push_back(aKn);
797  }
798  }
799  ioImgInfo._kp.insert(ioImgInfo._kp.end(), kp_new_ori.begin(), kp_new_ori.end());
800 
801  for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
802  {
803  aKPD.makeDescriptor(*(ioImgInfo._kp[i]));
804  }
805  // store the descriptor length
806  ioImgInfo._descLength = aKPD.getDescriptorLength();
807  return true;
808 }
809 
810 bool PanoDetector::RemapBackKeypoints(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
811 {
812  if (ioImgInfo.IsDownscale())
813  {
814  for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
815  {
816  lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
817  aK->_x *= 2.0;
818  aK->_y *= 2.0;
819  aK->_scale *= 2.0;
820  };
821  }
822  else
823  {
824  if (ioImgInfo.NeedsRemapping())
825  {
826  TRACE_IMG("Remapping back keypoints...");
828  trafo1.createTransform(iPanoDetector._panoramaInfoCopy.getSrcImage(ioImgInfo._number),
829  ioImgInfo._projOpts);
830 
831  int dx1 = ioImgInfo._projOpts.getROI().left();
832  int dy1 = ioImgInfo._projOpts.getROI().top();
833 
834  for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
835  {
836  lfeat::KeyPointPtr& aK = ioImgInfo._kp[i];
837  double xout, yout;
838  if (trafo1.transformImgCoord(xout, yout, aK->_x + dx1, aK->_y + dy1))
839  {
840  // downscaling is take care of by the remapping transform
841  // no need for multiplying the scale factor...
842  aK->_x = xout;
843  aK->_y = yout;
844  };
845  };
846  };
847  };
848  return true;
849 }
850 
851 bool PanoDetector::BuildKDTreesInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
852 {
853  TRACE_IMG("Build KDTree...");
854 
855  if(ioImgInfo._kp.empty())
856  {
857  return false;
858  };
859  // build a vector of KDElemKeyPointPtr
860 
861  // create feature vector matrix for flann
862  ioImgInfo._flann_descriptors = flann::Matrix<double>(new double[ioImgInfo._kp.size()*ioImgInfo._descLength],
863  ioImgInfo._kp.size(), ioImgInfo._descLength);
864  for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
865  {
866  memcpy(ioImgInfo._flann_descriptors[i], ioImgInfo._kp[i]->_vec, sizeof(double)*ioImgInfo._descLength);
867  }
868 
869  // build query structure
870  ioImgInfo._flann_index = new flann::Index<flann::L2<double> > (ioImgInfo._flann_descriptors, flann::KDTreeIndexParams(4));
871  ioImgInfo._flann_index->buildIndex();
872 
873  return true;
874 }
875 
876 bool PanoDetector::FreeMemoryInImage(ImgData& ioImgInfo, const PanoDetector& iPanoDetector)
877 {
878  TRACE_IMG("Freeing memory...");
879 
880  ioImgInfo._ii.clean();
881  ioImgInfo._distancemap.resize(0,0);
882 
883  return true;
884 }
885 
886 
887 bool PanoDetector::FindMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
888 {
889  TRACE_PAIR("Find Matches...");
890 
891  // retrieve the KDTree of image 2
892  flann::Index<flann::L2<double> > * index2 = ioMatchData._i2->_flann_index;
893 
894  // retrieve query points from image 1
895  flann::Matrix<double> & query = ioMatchData._i1->_flann_descriptors;
896 
897  // storage for sorted 2 best matches
898  int nn = 2;
899  flann::Matrix<int> indices(new int[query.rows*nn], query.rows, nn);
900  flann::Matrix<double> dists(new double[query.rows*nn], query.rows, nn);
901 
902  // perform matching using flann
903  index2->knnSearch(query, indices, dists, nn, flann::SearchParams(iPanoDetector.getKDTreeSearchSteps()));
904 
905  //typedef KDTreeSpace::BestMatch<KDElemKeyPoint> BM_t;
906  //std::set<BM_t, std::greater<BM_t> > aBestMatches;
907 
908  // store the matches already found to avoid 2 points in image1
909  // match the same point in image2
910  // both matches will be removed.
911  std::set<int> aAlreadyMatched;
912  std::set<int> aBadMatch;
913 
914  // unfiltered vector of matches
915  typedef std::pair<lfeat::KeyPointPtr, int> TmpPair_t;
916  std::vector<TmpPair_t> aUnfilteredMatches;
917 
918  //PointMatchVector_t aMatches;
919 
920  // go through all the keypoints of image 1
921  for (unsigned aKIt = 0; aKIt < query.rows; ++aKIt)
922  {
923  // accept the match if the second match is far enough
924  // put a lower value for stronger matching default 0.15
925  if (dists[aKIt][0] > iPanoDetector.getKDTreeSecondDistance() * dists[aKIt][1])
926  {
927  continue;
928  }
929 
930  // check if the kdtree match number is already in the already matched set
931  if (aAlreadyMatched.find(indices[aKIt][0]) != aAlreadyMatched.end())
932  {
933  // add to delete list and continue
934  aBadMatch.insert(indices[aKIt][0]);
935  continue;
936  }
937 
938  // TODO: add check for duplicate matches (can happen if a keypoint gets multiple orientations)
939 
940  // add the match number in already matched set
941  aAlreadyMatched.insert(indices[aKIt][0]);
942 
943  // add the match to the unfiltered list
944  aUnfilteredMatches.push_back(TmpPair_t(ioMatchData._i1->_kp[aKIt], indices[aKIt][0]));
945  }
946 
947  // now filter and fill the vector of matches
948  for (size_t i = 0; i < aUnfilteredMatches.size(); ++i)
949  {
950  TmpPair_t& aP = aUnfilteredMatches[i];
951  // if the image2 match number is in the badmatch set, skip it.
952  if (aBadMatch.find(aP.second) != aBadMatch.end())
953  {
954  continue;
955  }
956 
957  // add the match in the output vector
958  ioMatchData._matches.push_back(lfeat::PointMatchPtr( new lfeat::PointMatch(aP.first, ioMatchData._i2->_kp[aP.second])));
959  }
960 
961  delete[] indices.ptr();
962  delete[] dists.ptr();
963  TRACE_PAIR("Found " << ioMatchData._matches.size() << " matches.");
964  return true;
965 }
966 
967 bool PanoDetector::RansacMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
968 {
969  // Use panotools model for wide angle lenses
970  HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
972  (rmode == HuginBase::RANSACOptimizer::AUTO && iPanoDetector._panoramaInfo->getImage(ioMatchData._i1->_number).getHFOV() < 65 &&
973  iPanoDetector._panoramaInfo->getImage(ioMatchData._i2->_number).getHFOV() < 65))
974  {
975  return RansacMatchesInPairHomography(ioMatchData, iPanoDetector);
976  }
977  else
978  {
979  return RansacMatchesInPairCam(ioMatchData, iPanoDetector);
980  }
981 }
982 
983 // new code with fisheye aware ransac
984 bool PanoDetector::RansacMatchesInPairCam(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
985 {
986  TRACE_PAIR("RANSAC Filtering with Panorama model...");
987 
988  if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
989  {
990  TRACE_PAIR("Too few matches ... removing all of them.");
991  ioMatchData._matches.clear();
992  return true;
993  }
994 
995  if (ioMatchData._matches.size() < 6)
996  {
997  TRACE_PAIR("Not enough matches for RANSAC filtering.");
998  return true;
999  }
1000 
1001  // setup a panorama project with the two images.
1002  // is this threadsafe (is this read only access?)
1003  HuginBase::UIntSet imgs;
1004  int pano_i1 = ioMatchData._i1->_number;
1005  int pano_i2 = ioMatchData._i2->_number;
1006  imgs.insert(pano_i1);
1007  imgs.insert(pano_i2);
1008  int pano_local_i1 = 0;
1009  int pano_local_i2 = 1;
1010  if (pano_i1 > pano_i2)
1011  {
1012  pano_local_i1 = 1;
1013  pano_local_i2 = 0;
1014  }
1015 
1016  // perform ransac matching.
1017  // ARGH the panotools optimizer uses global variables is not reentrant
1018  std::vector<int> inliers;
1019 #pragma omp critical
1020  {
1021  HuginBase::PanoramaData* panoSubset = iPanoDetector._panoramaInfo->getNewSubset(imgs);
1022 
1023  // create control point vector
1024  HuginBase::CPVector controlPoints(ioMatchData._matches.size());
1025  for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
1026  {
1027  lfeat::PointMatchPtr& aM=ioMatchData._matches[i];
1028  controlPoints[i] = HuginBase::ControlPoint(pano_local_i1, aM->_img1_x, aM->_img1_y,
1029  pano_local_i2, aM->_img2_x, aM->_img2_y);
1030  }
1031  panoSubset->setCtrlPoints(controlPoints);
1032 
1033 
1034  PT_setProgressFcn(ptProgress);
1035  PT_setInfoDlgFcn(ptinfoDlg);
1036 
1037  HuginBase::RANSACOptimizer::Mode rmode = iPanoDetector._ransacMode;
1038  if (rmode == HuginBase::RANSACOptimizer::AUTO)
1039  {
1041  }
1042  // the RANSAC uses the distance in the image for determination of valid parameter
1043  // so make the threshold depending on the image size, use the given pixel distance relative to a 12 MPix image with 4000x3000 pixel
1044  const double threshold = iPanoDetector.getRansacDistanceThreshold() / 5000.0 * hypot(panoSubset->getImage(pano_local_i2).getWidth(), panoSubset->getImage(pano_local_i2).getHeight());
1045  inliers = HuginBase::RANSACOptimizer::findInliers(*panoSubset, pano_local_i1, pano_local_i2,
1046  threshold, rmode);
1047  PT_setProgressFcn(NULL);
1048  PT_setInfoDlgFcn(NULL);
1049  delete panoSubset;
1050  }
1051 
1052  TRACE_PAIR("Removed " << ioMatchData._matches.size() - inliers.size() << " matches. " << inliers.size() << " remaining.");
1053  if (inliers.size() < 0.5 * ioMatchData._matches.size())
1054  {
1055  // more than 50% of matches were removed, ignore complete pair...
1056  TRACE_PAIR("RANSAC found more than 50% outliers, removing all matches");
1057  ioMatchData._matches.clear();
1058  return true;
1059  }
1060 
1061 
1062  if (inliers.size() < (unsigned int)iPanoDetector.getMinimumMatches())
1063  {
1064  TRACE_PAIR("Too few matches ... removing all of them.");
1065  ioMatchData._matches.clear();
1066  return true;
1067  }
1068 
1069  // keep only inlier matches
1070  lfeat::PointMatchVector_t aInlierMatches;
1071  aInlierMatches.reserve(inliers.size());
1072 
1073  for (size_t i = 0; i < inliers.size(); ++i)
1074  {
1075  aInlierMatches.push_back(ioMatchData._matches[inliers[i]]);
1076  }
1077  ioMatchData._matches = aInlierMatches;
1078 
1079  /*
1080  if (iPanoDetector.getTest())
1081  TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
1082  aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
1083  */
1084 
1085  return true;
1086 }
1087 
1088 // homography based ransac matching
1090 {
1091  TRACE_PAIR("RANSAC Filtering...");
1092 
1093  if (ioMatchData._matches.size() < (unsigned int)iPanoDetector.getMinimumMatches())
1094  {
1095  TRACE_PAIR("Too few matches ... removing all of them.");
1096  ioMatchData._matches.clear();
1097  return true;
1098  }
1099 
1100  if (ioMatchData._matches.size() < 6)
1101  {
1102  TRACE_PAIR("Not enough matches for RANSAC filtering.");
1103  return true;
1104  }
1105 
1106  lfeat::PointMatchVector_t aRemovedMatches;
1107 
1108  lfeat::Ransac aRansacFilter;
1109  aRansacFilter.setIterations(iPanoDetector.getRansacIterations());
1110  int thresholdDistance=iPanoDetector.getRansacDistanceThreshold();
1111  //increase RANSAC distance if the image were remapped to not exclude
1112  //too much points in this case
1113  if(ioMatchData._i1->NeedsRemapping() || ioMatchData._i2->NeedsRemapping())
1114  {
1115  thresholdDistance*=5;
1116  }
1117  aRansacFilter.setDistanceThreshold(thresholdDistance);
1118  aRansacFilter.filter(ioMatchData._matches, aRemovedMatches);
1119 
1120 
1121  TRACE_PAIR("Removed " << aRemovedMatches.size() << " matches. " << ioMatchData._matches.size() << " remaining.");
1122 
1123  if (aRemovedMatches.size() > ioMatchData._matches.size())
1124  {
1125  // more than 50% of matches were removed, ignore complete pair...
1126  TRACE_PAIR("More than 50% outliers, removing all matches");
1127  ioMatchData._matches.clear();
1128  return true;
1129  }
1130 
1131  if (iPanoDetector.getTest())
1132  TestCode::drawRansacMatches(ioMatchData._i1->_name, ioMatchData._i2->_name, ioMatchData._matches,
1133  aRemovedMatches, aRansacFilter, iPanoDetector.getDownscale());
1134 
1135  return true;
1136 
1137 }
1138 
1139 
1140 bool PanoDetector::FilterMatchesInPair(MatchData& ioMatchData, const PanoDetector& iPanoDetector)
1141 {
1142  TRACE_PAIR("Clustering matches...");
1143 
1144  if (ioMatchData._matches.size() < 2)
1145  {
1146  return true;
1147  }
1148 
1149  // compute min,max of x,y for image1
1150 
1151  double aMinX = std::numeric_limits<double>::max();
1152  double aMinY = std::numeric_limits<double>::max();
1153  double aMaxX = -std::numeric_limits<double>::max();
1154  double aMaxY = -std::numeric_limits<double>::max();
1155 
1156  for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
1157  {
1158  lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
1159  if (aM->_img1_x < aMinX)
1160  {
1161  aMinX = aM->_img1_x;
1162  }
1163  if (aM->_img1_x > aMaxX)
1164  {
1165  aMaxX = aM->_img1_x;
1166  }
1167 
1168  if (aM->_img1_y < aMinY)
1169  {
1170  aMinY = aM->_img1_y;
1171  }
1172  if (aM->_img1_y > aMaxY)
1173  {
1174  aMaxY = aM->_img1_y;
1175  }
1176  }
1177 
1178  double aSizeX = aMaxX - aMinX + 2; // add 2 so max/aSize is strict < 1
1179  double aSizeY = aMaxY - aMinY + 2;
1180 
1181  //
1182 
1184  iPanoDetector.getSieve2Height(),
1185  iPanoDetector.getSieve2Size());
1186 
1187  // insert the points in the Sieve
1188  double aXF = (double)iPanoDetector.getSieve2Width() / aSizeX;
1189  double aYF = (double)iPanoDetector.getSieve2Height() / aSizeY;
1190  for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
1191  {
1192  lfeat::PointMatchPtr& aM = ioMatchData._matches[i];
1193  HuginBase::ControlPoint cp(ioMatchData._i1->_number, aM->_img1_x, aM->_img1_y,
1194  ioMatchData._i2->_number, aM->_img2_x, aM->_img2_y);
1195  if (set_contains(iPanoDetector._cpsHashSet, cp.getCPString()))
1196  {
1197  // match already exits, skipping
1198  continue;
1199  }
1200  aSieve.insert(aM, (int)((aM->_img1_x - aMinX) * aXF), (int)((aM->_img1_y - aMinY) * aYF));
1201  };
1202 
1203  // pull remaining values from the sieve
1204  ioMatchData._matches.clear();
1205 
1206  // make an extractor and pull the points
1207  SieveExtractorMatch aSieveExt(ioMatchData._matches);
1208  aSieve.extract(aSieveExt);
1209 
1210  TRACE_PAIR("Kept " << ioMatchData._matches.size() << " matches.");
1211  return true;
1212 }
1213 
1215 {
1216  // Write output pto file
1218  {
1219  std::cerr << "ERROR couldn't write to output file '" << _outputFile << "'!" << std::endl;
1220  }
1221 }
1222 
1224 {
1225  // Write output keyfile
1226 
1227  std::ofstream aOut(imgInfo._keyfilename.c_str(), std::ios_base::trunc);
1228 
1229  lfeat::SIFTFormatWriter writer(aOut);
1230 
1231  int origImgWidth = _panoramaInfo->getImage(imgInfo._number).getSize().width();
1232  int origImgHeight = _panoramaInfo->getImage(imgInfo._number).getSize().height();
1233 
1234  lfeat::ImageInfo img_info(imgInfo._name, origImgWidth, origImgHeight);
1235 
1236  writer.writeHeader ( img_info, imgInfo._kp.size(), imgInfo._descLength );
1237 
1238  for(size_t i=0; i<imgInfo._kp.size(); ++i)
1239  {
1240  lfeat::KeyPointPtr& aK=imgInfo._kp[i];
1241  writer.writeKeypoint ( aK->_x, aK->_y, aK->_scale, aK->_ori, aK->_score,
1242  imgInfo._descLength, aK->_vec );
1243  }
1244  writer.writeFooter();
1245 }
1246 
int getSieve2Width() const
Definition: PanoDetector.h:207
int getRansacIterations() const
Definition: PanoDetector.h:182
Dummy progress display, without output.
lfeat::PointMatchVector_t & _m
double getMaxValForPixelType(const std::string &v)
Definition: utils.h:89
int getSieve2Size() const
Definition: PanoDetector.h:215
std::vector< PointMatchPtr > PointMatchVector_t
Definition: PointMatch.h:53
void HandleDownscaleImage(const HuginBase::SrcPanoImage &srcImage, ImageType *&image, vigra::BImage *&mask, size_t detectWidth, size_t detectHeight, bool downscale, ImageType *&finalImage, vigra::BImage *&finalMask)
downscale image if requested, optimized code for non-downscale version to prevent unnecessary copying...
vigra::BImage _distancemap
Definition: PanoDetector.h:399
std::vector< KeyPointPtr > KeyPointVect_t
Definition: KeyPoint.h:111
void setHeight(unsigned int h)
set panorama height
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
Definition: Panorama.cpp:1620
virtual void setCtrlPoints(const CPVector &points)=0
set all control points (Ippei: Is this supposed to be &#39;add&#39; method?)
vigra::BImage * getCelesteMask(struct svm_model *model, vigra::UInt16RGBImage &input, int radius, float threshold, int resize_dimension, bool adaptThreshold, bool verbose)
calculates the mask using SVM
Definition: Celeste.cpp:313
PanoramaData * getNewSubset(const UIntSet &imgs) const
Definition: Panorama.h:183
int getMinimumMatches() const
Definition: PanoDetector.h:178
void copyImageIf(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:316
static const double A(-0.75)
static bool LoadKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool MakeKeyPointDescriptorsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void makeDescriptor(KeyPoint &ioKeyPoint) const
HuginBase::PanoramaOptions _projOpts
Definition: PanoDetector.h:401
bool hasActiveMasks() const
returns true, if image has active masks
static std::vector< int > findInliers(PanoramaData &pano, int i1, int i2, double maxError, Mode mode=RPY)
T2 operator()(const T2 &a, const hugin_utils::FDiff2D &p) const
void setDistanceThreshold(int iDT)
Contains functions to transform whole images.
bool getTest() const
Definition: PanoDetector.h:304
static int ptinfoDlg(int command, char *argument)
Definition: icpfind.cpp:103
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition: stl_utils.h:74
static int ptProgress(int command, char *argument)
Definition: icpfind.cpp:99
int getHeight() const
Get the height of the image in pixels.
Definition: SrcPanoImage.h:276
void setIterations(int iIters)
bool isInside(vigra::Point2D p, bool ignoreMasks=false) const
check if a coordinate is inside the source image
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
Definition: ROIImage.h:300
void applyMapping(vigra::triple< SrcIterator, SrcIterator, SrcAccessor > img, vigra::pair< DestIterator, DestAccessor > dest, T min, T max, int mapping)
Definition: utils.h:685
represents a control point
Definition: ControlPoint.h:38
lfeat::KeyPointVect_t & _v
functor to scale image on the fly during other operations
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
#define TRACE_PAIR(X)
const std::string getCPString() const
returns string which contains all features of a control point used for detecting duplicate control po...
static bool RansacMatchesInPairCam(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
const vigra::Rect2D & getROI() const
static bool FilterKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
int getSieve1Height() const
Definition: PanoDetector.h:136
bool IsDownscale() const
Definition: PanoDetector.h:439
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
int getSieve1Size() const
Definition: PanoDetector.h:140
void filter(std::vector< PointMatchPtr > &ioMatches, std::vector< PointMatchPtr > &ioRemovedMatches)
vigra::FRGBImage ImageType
std::set< std::string > _cpsHashSet
Definition: PanoDetector.h:369
int getWidth() const
Get the width of the image in pixels.
Definition: SrcPanoImage.h:266
static bool FindMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
lfeat::KeyPointVect_t & _v
static bool RansacMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
virtual void operator()(const lfeat::PointMatchPtr &m)
#define TRACE_IMG(X)
Model for a panorama.
Definition: PanoramaData.h:81
void RemapImage(const HuginBase::SrcPanoImage &srcImage, const HuginBase::PanoramaOptions &options, size_t detectWidth, size_t detectHeight, ImageType *&image, vigra::BImage *&mask, const PixelTransform &pixelTransform, ImageType *&finalImage, vigra::BImage *&finalMask)
helper function to remap image to given projection, you can supply a pixelTransform, which will be applied during remapping, this is intended for scaling a image during remapping, but this means also, that no photometric corrections are applied, if this is wanted you need to supply a suitable pixelTransform
virtual const SrcPanoImage & getImage(std::size_t nr) const =0
get a panorama image, counting starts with 0
static bool RemapBackKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
void init(vigra::DImage &img)
Definition: Image.cpp:34
static void drawRansacMatches(std::string &i1, std::string &i2, lfeat::PointMatchVector_t &iOK, lfeat::PointMatchVector_t &iNOK, lfeat::Ransac &iRansac, bool iHalf)
Definition: TestCode.cpp:82
bool NeedsRemapping() const
Definition: PanoDetector.h:440
static bool FreeMemoryInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
!! from PTOptimise.h 1951
lfeat::PointMatchVector_t _matches
Definition: PanoDetector.h:452
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition: threshold.h:41
ScaleFunctor(double scale)
flann::Matrix< double > _flann_descriptors
Definition: PanoDetector.h:411
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
#define TRACE_INFO(x)
Definition: Tracer.h:26
HuginBase::Panorama _panoramaInfoCopy
Definition: PanoDetector.h:368
T operator()(const T &a) const
struct celeste::svm_model * svmModel
Definition: PanoDetector.h:478
HuginBase::RANSACOptimizer::Mode _ransacMode
Definition: PanoDetector.h:337
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
int getKDTreeSearchSteps() const
Definition: PanoDetector.h:153
static bool FilterMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
lfeat::KeyPointVect_t _kp
Definition: PanoDetector.h:406
void transformImageAlpha(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, std::pair< SrcAlphaIterator, SrcAlphaAccessor > srcAlpha, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform image, and respect a possible alpha channel.
static bool AnalyzeImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
virtual void operator()(const lfeat::KeyPointPtr &k)
KeyPointVectInsertor(lfeat::KeyPointVect_t &iVect)
std::string _keyfilename
Definition: PanoDetector.h:404
std::string _outputFile
Definition: PanoDetector.h:362
bool getDownscale() const
Definition: PanoDetector.h:237
static T max(T x, T y)
Definition: svm.cpp:65
Holds transformations for Image -&gt; Pano and the other way.
flann::Index< flann::L2< double > > * _flann_index
Definition: PanoDetector.h:412
double getCelesteThreshold() const
Definition: PanoDetector.h:284
int getSieve2Height() const
Definition: PanoDetector.h:211
SieveExtractorKP(lfeat::KeyPointVect_t &iV)
void applyMaskAndCrop(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, const HuginBase::SrcPanoImage &SrcImg)
apply the mask and the crop of the given SrcImg to given mask image
std::shared_ptr< KeyPoint > KeyPointPtr
Definition: KeyPoint.h:110
SieveExtractorMatch(lfeat::PointMatchVector_t &iM)
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
double getKDTreeSecondDistance() const
Definition: PanoDetector.h:157
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:312
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:305
int assignOrientation(KeyPoint &ioKeyPoint, double angles[4]) const
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
static void info(const char *fmt,...)
Definition: svm.cpp:95
bool getCeleste() const
Definition: PanoDetector.h:276
HuginBase::Panorama * _panoramaInfo
Definition: PanoDetector.h:367
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
virtual void operator()(const lfeat::KeyPoint &k)
int getCelesteRadius() const
Definition: PanoDetector.h:292
A hdrWeight(T2 v, A a) const
void clean()
Definition: Image.cpp:47
void writeKeyfile(ImgData &imgInfo)
int getSieve1Width() const
Definition: PanoDetector.h:132
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
std::shared_ptr< PointMatch > PointMatchPtr
Definition: PointMatch.h:52
functions to handle icc profiles in images
ImageInfo loadKeypoints(const std::string &filename, KeyPointVect_t &vec)
Definition: KeyPointIO.cpp:85
void detectKeypoints(Image &iImage, KeyPointInsertor &iInsertor)
static bool FindKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void createTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
initialize pano-&gt;image transformation
std::string filename
Definition: KeyPointIO.h:44
void ApplyICCProfile(ImageType &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsUInt32Number imageFormat)
converts given image with iccProfile to sRGB/gray space, need to give pixel type in lcms2 format work...
Definition: cms.h:37
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
static bool BuildKDTreesInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool RansacMatchesInPairHomography(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
int getRansacDistanceThreshold() const
Definition: PanoDetector.h:186