Hugin trunk 0.1
Loading...
Searching...
No Matches
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>
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"
43#include "Tracer.h"
44
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{
59public:
61 inline virtual void operator()(const lfeat::KeyPoint& k)
62 {
63 _v.push_back(lfeat::KeyPointPtr(new lfeat::KeyPoint(k)));
64 }
65
66private:
68
69};
70
71
72// define a sieve extractor
73class SieveExtractorKP : public lfeat::SieveExtractor<lfeat::KeyPointPtr>
74{
75public:
77 inline virtual void operator()(const lfeat::KeyPointPtr& k)
78 {
79 _v.push_back(k);
80 }
81private:
83};
84
85class SieveExtractorMatch : public lfeat::SieveExtractor<lfeat::PointMatchPtr>
86{
87public:
89 inline virtual void operator()(const lfeat::PointMatchPtr& m)
90 {
91 _m.push_back(m);
92 }
93private:
95};
96
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
123template <class SrcImageIterator, class SrcAccessor>
124void 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
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
146template <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
169private:
170 double m_scale;
171};
172
177template <class ImageType, class PixelTransform>
179 size_t detectWidth, size_t detectHeight,
180 ImageType*& image, vigra::BImage*& mask,
182 ImageType*& finalImage, vigra::BImage*& finalMask)
183{
186 transform.createTransform(srcImage, options);
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 {
200 vigra_ext::transformImageAlpha(vigra::srcImageRange(*image), vigra::srcImage(*mask), vigra::destImageRange(*finalImage), vigra::destImage(*finalMask),
201 options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
202 delete mask;
203 mask = NULL;
204 }
205 else
206 {
207 vigra_ext::transformImage(vigra::srcImageRange(*image), vigra::destImageRange(*finalImage), vigra::destImage(*finalMask),
208 options.getROI().upperLeft(), transform, pixelTransform, false, vigra_ext::INTERP_CUBIC, &dummy);
209 };
210 delete image;
211 image = NULL;
212}
213
216template <class ImageType>
217void 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
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
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 {
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 {
319 vigra::transformImage(vigra::srcImageRange(*image), vigra::destImage(*image),
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
329 vigra::transformImage(vigra::srcImageRange(*image), vigra::destImage(*image),
330 vigra::linearRangeMapping(minVal, maxVal, 0.0, 1.0));
331 range255 = false;
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>(),
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),
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(),
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(),
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...");
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,
424 vigra_ext::PassThroughFunctor<vigra::RGBValue<vigra::UInt8> >(),
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(),
436 };
437 if (iPanoDetector.getCeleste())
438 {
439 TRACE_IMG("Mask areas with clouds...");
440 vigra::UInt16RGBImage* image16=new vigra::UInt16RGBImage(scaled->size());
441 vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
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 {
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...");
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,
497 vigra_ext::PassThroughFunctor<vigra::RGBValue<vigra::UInt16> >(),
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(),
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 {
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 {
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
592 vigra::transformImage(vigra::srcImageRange(*rgbImage), vigra::destImage(*rgbImage),
593 vigra_ext::LinearTransform<vigra::RGBValue<double> >(1.0 / maxVal - minVal, -minVal));
594 range255 = false;
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>(),
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(),
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 {
619 vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
620 vigra::linearIntensityTransform<vigra::RGBValue<vigra::UInt16> >(255));
621 }
622 else
623 {
624 vigra::transformImage(vigra::srcImageRange(*scaled), vigra::destImage(*image16),
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 {
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
689 vigra::transformImage(vigra::srcImageRange(*final_mask), vigra::destImage(*final_mask),
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
718{
719 TRACE_IMG("Find keypoints...");
720
721 // setup the detector
723
724 // detect the keypoints
726 aKP.detectKeypoints(ioImgInfo._ii, aInsertor);
727
728 TRACE_IMG("Found "<< ioImgInfo._kp.size() << " interest points.");
729
730 return true;
731}
732
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 {
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
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
783
784 // vector for keypoints with more than one orientation
786 for (size_t j = 0; j < ioImgInfo._kp.size(); ++j)
787 {
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
811{
812 if (ioImgInfo.IsDownscale())
813 {
814 for (size_t i = 0; i < ioImgInfo._kp.size(); ++i)
815 {
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 {
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
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
877{
878 TRACE_IMG("Freeing memory...");
879
880 ioImgInfo._ii.clean();
881 ioImgInfo._distancemap.resize(0,0);
882
883 return true;
884}
885
886
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 {
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
968{
969 // Use panotools model for wide angle lenses
972 (rmode == HuginBase::RANSACOptimizer::AUTO && iPanoDetector._panoramaInfo->getImage(ioMatchData._i1->_number).getHFOV() < 65 &&
973 iPanoDetector._panoramaInfo->getImage(ioMatchData._i2->_number).getHFOV() < 65))
974 {
976 }
977 else
978 {
980 }
981}
982
983// new code with fisheye aware ransac
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 {
1022
1023 // create control point vector
1025 for (size_t i = 0; i < ioMatchData._matches.size(); ++i)
1026 {
1029 pano_local_i2, aM->_img2_x, aM->_img2_y);
1030 }
1031 panoSubset->setCtrlPoints(controlPoints);
1032
1033
1036
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());
1046 threshold, rmode);
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
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
1107
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 {
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,
1134
1135 return true;
1136
1137}
1138
1139
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
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
1230
1231 int origImgWidth = _panoramaInfo->getImage(imgInfo._number).getSize().width();
1232 int origImgHeight = _panoramaInfo->getImage(imgInfo._number).getSize().height();
1233
1235
1236 writer.writeHeader ( img_info, imgInfo._kp.size(), imgInfo._descLength );
1237
1238 for(size_t i=0; i<imgInfo._kp.size(); ++i)
1239 {
1241 writer.writeKeypoint ( aK->_x, aK->_y, aK->_scale, aK->_ori, aK->_score,
1242 imgInfo._descLength, aK->_vec );
1243 }
1244 writer.writeFooter();
1245}
1246
!! from PTOptimise.h 1951
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...
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
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,...
#define TRACE_PAIR(X)
#define TRACE_IMG(X)
static int ptProgress(int command, char *argument)
static int ptinfoDlg(int command, char *argument)
Contains functions to transform whole images.
#define TRACE_INFO(x)
Definition Tracer.h:26
Dummy progress display, without output.
@ RESPONSE_LINEAR
linear response
represents a control point
Holds transformations for Image -> Pano and the other way.
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->image transformation
Model for a panorama.
virtual PanoramaData * getNewSubset(const UIntSet &imgs) const =0
Panorama image options.
const vigra::Rect2D & getROI() const
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
static std::vector< int > findInliers(PanoramaData &pano, int i1, int i2, double maxError, Mode mode=RPY)
All variables of a source image.
virtual void operator()(const lfeat::KeyPoint &k)
lfeat::KeyPointVect_t & _v
KeyPointVectInsertor(lfeat::KeyPointVect_t &iVect)
static bool AnalyzeImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool MakeKeyPointDescriptorsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
void writeKeyfile(ImgData &imgInfo)
static bool FindMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
std::string _outputFile
static bool FindKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
HuginBase::Panorama * _panoramaInfo
static bool FilterKeyPointsInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool BuildKDTreesInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool RansacMatchesInPairHomography(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
static bool FreeMemoryInImage(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool RansacMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
static bool RansacMatchesInPairCam(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
static bool LoadKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool RemapBackKeypoints(ImgData &ioImgInfo, const PanoDetector &iPanoDetector)
static bool FilterMatchesInPair(MatchData &ioMatchData, const PanoDetector &iPanoDetector)
SieveExtractorKP(lfeat::KeyPointVect_t &iV)
lfeat::KeyPointVect_t & _v
virtual void operator()(const lfeat::KeyPointPtr &k)
SieveExtractorMatch(lfeat::PointMatchVector_t &iM)
virtual void operator()(const lfeat::PointMatchPtr &m)
lfeat::PointMatchVector_t & _m
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
void setIterations(int iIters)
functions to handle icc profiles in images
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
vigra::FRGBImage ImageType
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
std::vector< ControlPoint > CPVector
std::set< unsigned int > UIntSet
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
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
ImageInfo loadKeypoints(const std::string &filename, KeyPointVect_t &vec)
std::shared_ptr< KeyPoint > KeyPointPtr
Definition KeyPoint.h:110
std::shared_ptr< PointMatch > PointMatchPtr
Definition PointMatch.h:52
std::vector< PointMatchPtr > PointMatchVector_t
Definition PointMatch.h:53
std::vector< KeyPointPtr > KeyPointVect_t
Definition KeyPoint.h:111
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.
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.
double getMaxValForPixelType(const std::string &v)
Definition utils.h:89
void applyMapping(vigra::triple< SrcIterator, SrcIterator, SrcAccessor > img, vigra::pair< DestIterator, DestAccessor > dest, T min, T max, int mapping)
Definition utils.h:685
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
functor to scale image on the fly during other operations
T2 operator()(const T2 &a, const hugin_utils::FDiff2D &p) const
A hdrWeight(T2 v, A a) const
ScaleFunctor(double scale)
T operator()(const T &a) const
std::string filename
Definition KeyPointIO.h:44
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