Hugin trunk 0.1
Loading...
Searching...
No Matches
stacker.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
11/* This program is free software; you can redistribute it and/or
12* modify it under the terms of the GNU General Public
13* License as published by the Free Software Foundation; either
14* version 2 of the License, or (at your option) any later version.
15*
16* This software is distributed in the hope that it will be useful,
17* but WITHOUT ANY WARRANTY; without even the implied warranty of
18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19* General Public License for more details.
20*
21* You should have received a copy of the GNU General Public
22* License along with this software. If not, see
23* <http://www.gnu.org/licenses/>.
24*
25*/
26
27#include <stdio.h>
28#include <iostream>
29#include <fstream>
30#include <getopt.h>
31#include <hugin_utils/utils.h>
33#include <vigra/imageinfo.hxx>
34#include <vigra/codec.hxx>
35#include <vigra/stdimage.hxx>
37#include <vigra/tiff.hxx>
38#include <vigra_ext/tiffUtils.h>
40
42void SetCompression(vigra::ImageExportInfo& output, const std::string& compression)
43{
44 const std::string ext(hugin_utils::toupper(hugin_utils::getExtension(output.getFileName())));
45 if (!compression.empty())
46 {
47 if (ext == "JPEG" || ext == "JPG")
48 {
49 output.setCompression(std::string("JPEG QUALITY=" + compression).c_str());
50 }
51 else
52 {
53 output.setCompression(compression.c_str());
54 };
55 };
56};
57
59template <class ImageType, class MaskType>
60bool SaveImage(ImageType& image, MaskType& mask, vigra::ImageExportInfo& exportImageInfo, std::string filetype, std::string pixelType)
61{
62 typedef typename vigra::NumericTraits<typename ImageType::value_type>::isScalar scalar;
63 const int numberChannels = scalar().asBool ? 1 : 3;
64 exportImageInfo.setPixelType(pixelType.c_str());
65 if (vigra::isBandNumberSupported(filetype, numberChannels +1))
66 {
67 try
68 {
69 vigra::exportImageAlpha(vigra::srcImageRange(image), vigra::srcImage(mask), exportImageInfo);
70 }
71 catch (std::exception& e)
72 {
73 std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
74 << "Cause: " << e.what() << std::endl;
75 return false;
76 }
77 return true;
78 }
79 else
80 {
81 if (vigra::isBandNumberSupported(filetype, numberChannels))
82 {
83 std::cout << "Warning: Filetype " << filetype << " does not support alpha channels." << std::endl
84 << "Saving image without alpha channel." << std::endl;
85 try
86 {
87 vigra::exportImage(vigra::srcImageRange(image), exportImageInfo);
88 }
89 catch (std::exception& e)
90 {
91 std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
92 << "Cause: " << e.what() << std::endl;
93 return false;
94 };
95 return true;
96 }
97 else
98 {
99 std::cerr << "ERROR: Output filetype " << filetype << " does not support " << numberChannels << " channels." << std::endl
100 << "Can't save image." << std::endl;
101 };
102 };
103 return false;
104};
105
107template <class ImageType, class MaskType>
108bool SaveFinalImage(ImageType& image, MaskType& mask, const std::string& inputPixelType, vigra::ImageExportInfo& output)
109{
111 if (vigra::isPixelTypeSupported(encoder->getFileType(), inputPixelType))
112 {
113 return SaveImage(image, mask, output, encoder->getFileType(), inputPixelType);
114 }
115 else
116 {
117 if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT8"))
118 {
119 // transform to UINT8
120 output.setForcedRangeMapping(0, vigra::NumericTraits<typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::max(), 0, 255);
121 return SaveImage(image, mask, output, encoder->getFileType(), "UINT8");
122 }
123 else
124 {
125 std::cerr << "ERROR: Output file type " << encoder->getFileType() << " does not support" << std::endl
126 << "requested pixeltype " << inputPixelType << "." << std::endl
127 << "Save output in other file format." << std::endl;
128 };
129 };
130 return false;
131};
132
134static void usage(const char* name)
135{
136 std::cout << name << ": stack images" << std::endl
137 << name << " version " << hugin_utils::GetHuginVersion() << std::endl
138 << std::endl
139 << "Usage: " << name << " [options] images" << std::endl
140 << std::endl
141 << " --output=FILE Set the filename for the output file." << std::endl
142 << " --compression=value Compression of the output files" << std::endl
143 << " For jpeg output: 0-100" << std::endl
144 << " For tiff output: PACKBITS, DEFLATE, LZW" << std::endl
145 << " --mode=STRING Select the mode for stacking" << std::endl
146 << " Possible names are described below." << std::endl
147 << std::endl
148 << " min|minimum|darkest Select the darkest pixel" << std::endl
149 << " max|maximum|brightest Select the brightest pixel" << std::endl
150 << " avg|average|mean Calculate the mean for each position" << std::endl
151 << " median Calculate the median for each position" << std::endl
152 << " winsor Calculate the Winsor trimmed mean" << std::endl
153 << " for each position. The parameter can be" << std::endl
154 << " set with --winsor-trim=NUMBER (default: 0.2)" << std::endl
155 << " sigma Calculate the sigma clipped mean for" << std::endl
156 << " each position. Fine-tune with" << std::endl
157 << " --max-sigma=NUMBER (default: 2) and" << std::endl
158 << " --max-iterations=NUMBER (default: 5)" << std::endl
159 << std::endl
160 << " --mask-input Mask input images" << std::endl
161 << " Only pixel which differ more than" << std::endl
162 << " mask-sigma * standard deviation" << std::endl
163 << " are visible in output" << std::endl
164 << " available for modes median, winsor or sigma" << std::endl
165 << " --mask-suffix=STRING Suffix for the masked input images" << std::endl
166 << " (default: _mask)" << std::endl
167 << " --mask-sigma=NUMBER Sigma parameter for input images masking" << std::endl
168 << " (default: 2)" << std::endl
169 << " --multi-layer-output Output layered TIFF instead of single images" << std::endl
170 << " (has only effect with --mask-input)" << std::endl
171 << " --bigtiff Write output in BigTIFF format" << std::endl
172 << " (only with TIFF output)" << std::endl
173 << " -h, --help Shows this help" << std::endl
174 << std::endl
175 << "The images to process can also be read from a text file. In this case specify the name" << std::endl
176 << "of the text file after an at sign + space (@ FILE.TXT)." << std::endl
177 << std::endl;
178};
179
180static struct GeneralParameters
181{
182public:
183 // for output file
184 std::string outputFilename;
185 std::string compression;
186 // stacking mode
187 std::string stackMode;
188 // for sigma clipping
189 double sigma = 2;
191 // for Winsor trimmed mean
192 double winsorTrim = 0.2;
193 // for masking input images
194 double maskSigma = 2;
195 // should masked input images saved
196 bool maskInput = false;
197 std::string maskSuffix = "_mask";
198 bool multiLayer = false;
199 bool useBigTIFF = false;
201
203{
204public:
205 explicit InputImage(const std::string filename) : m_info(filename.c_str())
206 {
207 m_filename = filename;
208 m_canvassize=m_info.getCanvasSize();
209 if (m_canvassize.area() == 0)
210 {
211 // not all images contains the canvas size/full image size
212 // in this case take also the position into account to get full image size
213 m_canvassize =vigra::Size2D(m_info.width(), m_info.height());
214 };
215 m_offsetX = m_info.getPosition().x;
216 m_offsetY = m_info.getPosition().y;
217 m_decoder=vigra::decoder(m_info);
218 m_hasAlpha = m_info.numExtraBands() == 1;
219 m_width=m_decoder->getWidth();
220 m_height=m_decoder->getHeight();
221 m_bands=m_decoder->getNumBands();
222 m_offset=m_decoder->getOffset();
223 m_x = 0;
224 m_y = 0;
225 m_noData = true;
226 };
228 {
229 m_decoder->abort();
230 };
231 const std::string getPixelType() const { return m_info.getPixelType(); };
232 const bool isColor() const { return m_info.isColor(); };
233 const bool isGrayscale() const { return m_info.isGrayscale(); };
234 const int numBands() const { return m_bands; };
235 const int numExtraBands() const { return m_info.numExtraBands(); }
236 const int numPixelSamples() const { return m_bands - m_info.numExtraBands(); };
237 const float getXResolution() const { return m_info.getXResolution(); };
238 const float getYResolution() const { return m_info.getYResolution(); };
239 const vigra::ImageImportInfo::ICCProfile getICCProfile() const { return m_info.getICCProfile(); };
240 const std::string getFilename() const { return m_filename; };
241 const vigra::Rect2D getROI() const { return vigra::Rect2D(vigra::Point2D(m_offsetX,m_offsetY), vigra::Size2D(m_width, m_height)); };
242 const vigra::Size2D getCanvasSize() const { return m_canvassize; };
243 const std::string getMaskFilename() const { return hugin_utils::stripExtension(m_filename) + Parameters.maskSuffix + ".tif"; };
244 const vigra::ImageImportInfo& getImageImportInfo() const { return m_info; };
245 void readLine(const int y)
246 {
247 if (y < m_offsetY + static_cast<int>(m_height))
248 {
249 if (y < m_offsetY)
250 {
251 m_noData = true;
252 }
253 else
254 {
255 m_noData = false;
256 m_decoder->nextScanline();
257 ++m_y;
258 while (y+1 < static_cast<int>(m_y) + m_offsetY)
259 {
260 m_decoder->nextScanline();
261 ++m_y;
262 }
263 }
264 }
265 else
266 {
267 m_noData = true;
268 };
269 };
270 template<class ValueType>
271 void getValue(const int x, vigra::RGBValue<ValueType>& value, ValueType& mask)
272 {
273 if (m_noData)
274 {
275 mask = vigra::NumericTraits<ValueType>::zero();
276 }
277 else
278 {
279 if (x < m_offsetX)
280 {
281 mask = vigra::NumericTraits<ValueType>::zero();
282 }
283 else
284 {
285 if (x < m_offsetX + static_cast<int>(m_width))
286 {
287 const ValueType* band0= static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(0));
288 const ValueType* band1 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(1));
289 const ValueType* band2 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(2));
290 band0 += m_offset*(x - m_offsetX);
291 band1 += m_offset*(x - m_offsetX);
292 band2 += m_offset*(x - m_offsetX);
293 if (m_bands == 4)
294 {
295 const ValueType* band3 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(3));
296 band3 += m_offset*(x - m_offsetX);
297 mask = *band3;
298 }
299 else
300 {
301 mask = vigra::NumericTraits<ValueType>::max();
302 };
303 value = vigra::RGBValue<ValueType>(*band0, *band1, *band2);
304 }
305 else
306 {
307 mask = vigra::NumericTraits<ValueType>::zero();
308 };
309 };
310 };
311 };
312 template<class ValueType>
313 void getValue(const int x, ValueType& value, ValueType& mask)
314 {
315 if (m_noData)
316 {
317 mask = vigra::NumericTraits<ValueType>::zero();
318 }
319 else
320 {
321 if (x < m_offsetX)
322 {
323 mask = vigra::NumericTraits<ValueType>::zero();
324 }
325 else
326 {
327 if (x < m_offsetX + static_cast<int>(m_width))
328 {
329 const ValueType* band0 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(0));
330 band0 += m_offset*(x - m_offsetX);
331 if (m_bands == 2)
332 {
333 const ValueType* band1 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(1));
334 band1 += m_offset*(x - m_offsetX);
335 mask = *band1;
336 }
337 else
338 {
339 mask = vigra::NumericTraits<ValueType>::max();
340 };
341 value = *band0;
342 }
343 else
344 {
345 mask = vigra::NumericTraits<ValueType>::zero();
346 };
347 };
348 };
349 };
350
351private:
352 std::string m_filename;
353 vigra::ImageImportInfo m_info;
354 vigra::Size2D m_canvassize;
359};
360
361template<class ValueType>
362void getMean(const std::vector<ValueType>& values, ValueType& val)
363{
364 size_t n = 0;
365 typedef vigra::NumericTraits<ValueType> RealTraits;
366 typename RealTraits::RealPromote mean = RealTraits::zero();
367 for (auto& x : values)
368 {
369 ++n;
370 mean += (x - mean) / n;
371 };
372 val = RealTraits::fromRealPromote(mean);
373};
374
375template<class ValueType>
376void getMeanSigma(const std::vector<ValueType>& values, ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
377{
378 typedef vigra::NumericTraits<ValueType> RealTraits;
379 typedef typename RealTraits::RealPromote RealType;
380 size_t n = 0;
381 RealType mean = RealTraits::zero();
382 RealType m2 = RealTraits::zero();
383 for (auto& x : values)
384 {
385 ++n;
386 const RealType delta = x - mean;
387 mean += delta / n;
388 const RealType delta2 = x - mean;
389 m2 += delta*delta2;
390 };
391 val = RealTraits::fromRealPromote(mean);
392 if (n >= 2)
393 {
394 sigma = sqrt(m2 / n);
395 }
396 else
397 {
398 sigma = vigra::NumericTraits<RealType>::zero();
399 };
400};
401
402template<class ValueType>
404{
405public:
406 void reset() { m_min = vigra::NumericTraits<ValueType>::max(); };
407 void operator()(const ValueType& val, vigra::VigraTrueType) { if (val < m_min) m_min = val; };
408 void operator()(const ValueType& val, vigra::VigraFalseType) { if (val.luminance() < m_min.luminance()) m_min = val; }
409 void operator()(const ValueType& val)
410 {
411 typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
412 operator()(val, is_scalar());
413 };
414 void getResult(ValueType& val) { val = m_min; };
415 bool IsValid() { return m_min != vigra::NumericTraits<ValueType>::max(); };
416private:
417 ValueType m_min;
418};
419
420template<class ValueType>
422{
423public:
424 virtual void reset() { m_max = vigra::NumericTraits<ValueType>::min(); };
425 void operator()(const ValueType& val, vigra::VigraTrueType) { if (val > m_max) m_max = val; };
426 void operator()(const ValueType& val, vigra::VigraFalseType) { if (val.luminance() > m_max.luminance()) m_max = val;}
427 virtual void operator()(const ValueType& val)
428 {
429 typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
430 operator()(val, is_scalar());
431 };
432 virtual void getResult(ValueType& val) { val = m_max; };
433 virtual bool IsValid() { return m_max != vigra::NumericTraits<ValueType>::min(); };
434private:
435 ValueType m_max;
436};
437
438template<class ValueType>
440{
441public:
442 virtual void reset() { m_values.clear(); };
443 virtual void operator()(const ValueType& val) { m_values.push_back(val); };
444 virtual void getResult(ValueType& val) { getMean(m_values, val); };
445 virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma) { getMeanSigma(m_values, val, sigma); };
446 virtual bool IsValid() { return !m_values.empty(); };
447 const std::string getName() const { return "mean"; };
448protected:
449 std::vector<ValueType> m_values;
450};
451
452template<class ValueType>
453class MedianStacker : public AverageStacker<ValueType>
454{
455public:
456 virtual void getResult(ValueType& val) override
457 {
458 sort();
459 if (this->m_values.size() % 2 == 1)
460 {
461 val = this->m_values[(this->m_values.size() - 1) / 2];
462 }
463 else
464 {
465 const int index = this->m_values.size() / 2;
466 val = 0.5 * (this->m_values[index - 1] + this->m_values[index]);
467 };
468 };
469 virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma) override
470 {
471 getResult(val);
472 ValueType mean;
473 getMeanSigma(this->m_values, mean, sigma);
474 };
475 const std::string getName() const { return "median"; };
476protected:
477 // sort gray scale
478 void sort(vigra::VigraTrueType)
479 {
480 std::sort(this->m_values.begin(), this->m_values.end());
481 };
482 // sort color values
483 void sort(vigra::VigraFalseType)
484 {
485 std::sort(this->m_values.begin(), this->m_values.end(),
486 [](const ValueType & a, const ValueType & b) {return a.luminance() < b.luminance(); });
487 };
488 // generic sort
489 void sort()
490 {
491 typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
492 sort(is_scalar());
493 };
494};
495
496template<class ValueType>
497class WinsorMeanStacker : public MedianStacker<ValueType>
498{
499public:
500 virtual void getResult(ValueType& val) override
501 {
502 Trim();
503 getMean(this->m_values, val);
504 };
505 virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma) override
506 {
507 Trim();
508 getMeanSigma(this->m_values, val, sigma);
509 };
510 const std::string getName() const { return "Winsor clipped mean"; };
511private:
512 void Trim()
513 {
514 this->sort();
515 const int indexTrim = hugin_utils::floori(Parameters.winsorTrim * this->m_values.size());
516 for (size_t i = 0; i < indexTrim; ++i)
517 {
518 this->m_values[i] = this->m_values[indexTrim];
519 }
520 for (size_t i = this->m_values.size() - indexTrim; i < this->m_values.size(); ++i)
521 {
522 this->m_values[i] = this->m_values[this->m_values.size() - indexTrim - 1];
523 };
524 }
525};
526
527template<class ValueType>
529{
530public:
531 virtual void reset() { m_values.clear(); m_sortValues.clear(); };
532 void operator()(const ValueType& val, vigra::VigraTrueType) { m_values.push_back(val); m_sortValues.push_back(val); };
533 void operator()(const ValueType& val, vigra::VigraFalseType) { m_values.push_back(val); m_sortValues.push_back(val.luminance()); };
534 virtual void operator()(const ValueType& val)
535 {
536 typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
537 operator()(val, is_scalar());
538 };
539 virtual void getResult(ValueType& val)
540 {
541 SigmaClip();
542 getMean(m_values, val);
543 };
544 virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
545 {
546 SigmaClip();
548 };
549 virtual bool IsValid() { return !m_values.empty(); };
550 const std::string getName() const { return "sigma clipped mean"; };
551
552private:
554 {
555 size_t iteration = 0;
556 while (iteration < Parameters.maxIterations)
557 {
558 double mean, sigma;
560 const size_t oldSize = m_sortValues.size();
561 for (int i = m_sortValues.size() - 1; i >= 0 && m_sortValues.size() > 1; --i)
562 {
563 // check if values are in range
564 if (abs(m_sortValues[i] - mean) > Parameters.sigma * sigma)
565 {
566 m_sortValues.erase(m_sortValues.begin() + i);
567 m_values.erase(m_values.begin() + i);
568 };
569 };
570 if (m_sortValues.size() == oldSize)
571 {
572 // no values outside range, end while loop
573 break;
574 };
575 ++iteration;
576 };
577 };
578 std::vector<ValueType> m_values;
579 std::vector<double> m_sortValues;
580};
581
582bool CheckInput(const std::vector<InputImage*>& images, vigra::Rect2D& outputROI, vigra::Size2D& canvasSize)
583{
584 if (images.empty())
585 {
586 return false;
587 };
588 // get ROI and canvas size which contains all images
589 outputROI = images[0]->getROI();
590 canvasSize = images[0]->getCanvasSize();
591 for (size_t i = 1; i < images.size(); ++i)
592 {
593 outputROI |= images[i]->getROI();
594 if (images[i]->getCanvasSize().width() > canvasSize.width())
595 {
596 canvasSize.setWidth(images[i]->getCanvasSize().width());
597 };
598 if (images[i]->getCanvasSize().height() > canvasSize.height())
599 {
600 canvasSize.setHeight(images[i]->getCanvasSize().height());
601 };
602 };
603 if (outputROI.area() == 0)
604 {
605 std::cerr << "ERROR: You can't stack non-overlapping images." << std::endl;
606 return false;
607 };
608 return true;
609}
610
612template <class PixelType, class Functor>
613bool StackImages(std::vector<InputImage*>& images, Functor& stacker)
614{
615 typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
616 vigra::Rect2D outputROI;
617 vigra::Size2D canvasSize;
618 if (!CheckInput(images, outputROI, canvasSize))
619 {
620 return false;
621 }
622 // prepare output
623 vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
624 exportImageInfo.setXResolution(images[0]->getXResolution());
625 exportImageInfo.setYResolution(images[0]->getYResolution());
626 exportImageInfo.setPosition(outputROI.upperLeft());
627 exportImageInfo.setCanvasSize(canvasSize);
628 exportImageInfo.setICCProfile(images[0]->getICCProfile());
630 vigra::BasicImage<PixelType> output(outputROI.size());
631 vigra::BImage mask(output.size(),vigra::UInt8(0));
632 // loop over all lines
633 for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
634 {
635 // load next line
636#pragma omp parallel for
637 for (int i = 0; i < images.size(); ++i)
638 {
639 images[i]->readLine(y);
640 };
641 // process current line
642#pragma omp parallel for schedule(static, 100)
643 for (int x = outputROI.left(); x < outputROI.right(); ++x)
644 {
645 // we need a private copy for each thread
647 privateStacker.reset();
648 for (size_t i = 0; i < images.size(); ++i)
649 {
650 PixelType value;
652 images[i]->getValue(x, value, maskValue);
653 if (maskValue > 0)
654 {
655 privateStacker(value);
656 }
657 };
658 if (privateStacker.IsValid())
659 {
660 privateStacker.getResult(output(x - outputROI.left(), y - outputROI.top()));
661 mask(x-outputROI.left(), y-outputROI.top()) = 255;
662 };
663 };
664 };
665 std::cout << "Write result to " << Parameters.outputFilename << std::endl;
666 return SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo);
667};
668
669template <class PixelType>
671{
672public:
673 typedef vigra::TinyVector<PixelType, 2> first_argument_type;
674 typedef PixelType second_argument_type;
675 typedef vigra::UInt8 third_argument_type;
676 typedef vigra::UInt8 result_type;
677 vigra::UInt8 operator()(const vigra::TinyVector<PixelType, 2>& limits, const PixelType& color, const vigra::UInt8& mask, vigra::VigraFalseType) const
678 {
679 if (mask > 0 && (color.luminance() < limits[0].luminance() || color.luminance() > limits[1].luminance()))
680 {
681 return 255;
682 }
683 else
684 {
685 return 0;
686 };
687 };
688
689 vigra::UInt8 operator()(const vigra::TinyVector<PixelType, 2>& limits, const PixelType& gray, const vigra::UInt8& mask, vigra::VigraTrueType) const
690 {
691 if (mask > 0 && (gray < limits[0] || gray>limits[1]))
692 {
693 return 255;
694 }
695 else
696 {
697 return 0;
698 };
699 };
700
701 vigra::UInt8 operator()(const vigra::TinyVector<PixelType, 2>& limits, const PixelType& pixel, const vigra::UInt8& mask) const
702 {
703 typedef typename vigra::NumericTraits<PixelType>::isScalar is_scalar;
704 return (*this)(limits, pixel, mask, is_scalar());
705 }
706
707};
708
709template <class PixelType, class Functor>
710bool StackImagesAndMask(std::vector<InputImage*>& images, Functor& stacker)
711{
712 typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
713 vigra::Rect2D outputROI;
714 vigra::Size2D canvasSize;
715 if (!CheckInput(images, outputROI, canvasSize))
716 {
717 return false;
718 }
719 // prepare output
720 vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
721 exportImageInfo.setXResolution(images[0]->getXResolution());
722 exportImageInfo.setYResolution(images[0]->getYResolution());
723 exportImageInfo.setPosition(outputROI.upperLeft());
724 exportImageInfo.setCanvasSize(canvasSize);
725 exportImageInfo.setICCProfile(images[0]->getICCProfile());
727 // for multi-layer output
728 vigra::TiffImage* tiffImage;
729 vigra::BasicImage<PixelType> output(outputROI.size());
730 vigra::BImage mask(output.size(), vigra::UInt8(0));
731 vigra::BasicImage<vigra::TinyVector<PixelType, 2>> limits(output.size());
732 // loop over all lines
733 for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
734 {
735 // load next line
736#pragma omp parallel for
737 for (int i = 0; i < images.size(); ++i)
738 {
739 images[i]->readLine(y);
740 };
741 // process current line
742#pragma omp parallel for schedule(static, 100)
743 for (int x = outputROI.left(); x < outputROI.right(); ++x)
744 {
745 // we need a private copy for each thread
747 privateStacker.reset();
748 for (size_t i = 0; i < images.size(); ++i)
749 {
750 PixelType value;
752 images[i]->getValue(x, value, maskValue);
753 if (maskValue > 0)
754 {
755 privateStacker(value);
756 }
757 };
758 if (privateStacker.IsValid())
759 {
760 PixelType mean;
761 typename vigra::NumericTraits<PixelType>::RealPromote sigma;
762 privateStacker.getResultAndSigma(mean, sigma);
763 output(x - outputROI.left(), y - outputROI.top()) = mean;
764 mask(x - outputROI.left(), y - outputROI.top()) = 255;
765 limits(x - outputROI.left(), y - outputROI.top()) = vigra::TinyVector<PixelType, 2>(mean - Parameters.maskSigma*sigma, mean + Parameters.maskSigma*sigma);
766 };
767 };
768 };
769 std::cout << "Write result to " << Parameters.outputFilename << std::endl;
770 if (Parameters.multiLayer)
771 {
772 tiffImage = TIFFOpen(Parameters.outputFilename.c_str(), "w");
774 Parameters.compression.empty() ? "LZW" : Parameters.compression, 0, images.size() + 1,
775 outputROI.upperLeft(), canvasSize, images[0]->getICCProfile());
776 vigra_ext::createAlphaTiffImage(vigra::srcImageRange(output), vigra::maskImage(mask), tiffImage);
778 }
779 else
780 {
781 if (!SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo))
782 {
783 return false;
784 };
785 };
786 // we don't need median image any more
787 output.resize(0, 0);
788 std::cout << "Masking input images with sigma=" << Parameters.maskSigma;
789 if (Parameters.multiLayer)
790 {
791 std::cout << std::endl;
792 }
793 else
794 {
795 std::cout << " and suffix " << Parameters.maskSuffix << ".tif" << std::endl;
796 };
797 for (size_t i = 0; i < images.size(); ++i)
798 {
799 std::cout << "Masking " << images[i]->getFilename();
800 if (Parameters.multiLayer)
801 {
802 std::cout << std::endl;
803 }
804 else
805 {
806 std::cout << " -> " << images[i]->getMaskFilename() << std::endl;
807 };
808 vigra::BasicImage<PixelType> image(images[i]->getROI().size());
809 vigra::BImage mask(image.size(), 255);
810 if (images[i]->numExtraBands() == 1)
811 {
812 vigra::importImageAlpha(images[i]->getImageImportInfo(), vigra::destImage(image), vigra::destImage(mask));
813 }
814 else
815 {
816 vigra::importImage(images[i]->getImageImportInfo(), vigra::destImage(image));
817 };
818 vigra::Rect2D roi = images[i]->getROI();
819 roi.moveBy(-outputROI.upperLeft());
820 vigra::omp::combineThreeImages(vigra::srcImageRange(limits, roi), vigra::srcImage(image), vigra::srcImage(mask), vigra::destImage(mask), FilterMask<PixelType>());
821 if (Parameters.multiLayer)
822 {
823 vigra_ext::createTiffDirectory(tiffImage, images[i]->getFilename(), images[i]->getFilename(),
824 Parameters.compression.empty() ? "LZW" : Parameters.compression, i+1, images.size() + 1,
825 images[i]->getROI().upperLeft(), images[i]->getCanvasSize(), images[i]->getICCProfile());
826 vigra_ext::createAlphaTiffImage(vigra::srcImageRange(image), vigra::maskImage(mask), tiffImage);
828 }
829 else
830 {
831 if (hugin_utils::FileExists(images[i]->getMaskFilename()))
832 {
833 std::cout << "Masked file \"" << images[i]->getMaskFilename() << "\" already exists." << std::endl
834 << "Processing aborted." << std::endl;
835 return false;
836 };
837 vigra::ImageExportInfo exportMaskImage(images[i]->getMaskFilename().c_str(), Parameters.useBigTIFF ? "w8" : "w");
838 exportMaskImage.setXResolution(images[i]->getXResolution());
839 exportMaskImage.setYResolution(images[i]->getYResolution());
840 exportMaskImage.setPosition(images[i]->getROI().upperLeft());
841 exportMaskImage.setCanvasSize(images[i]->getCanvasSize());
842 exportMaskImage.setICCProfile(images[i]->getICCProfile());
843 exportMaskImage.setPixelType(images[i]->getPixelType().c_str());
844 exportMaskImage.setCompression("LZW");
845 try
846 {
847 vigra::exportImageAlpha(vigra::srcImageRange(image), vigra::srcImage(mask), exportMaskImage);
848 }
849 catch (std::exception& e)
850 {
851 std::cerr << "Could not save masked images \"" << exportMaskImage.getFileName() << "\"." << std::endl
852 << "Error code: " << e.what() << std::endl
853 << "Processing aborted." << std::endl;
854 return false;
855 }
856 };
857 };
858 if (Parameters.multiLayer)
859 {
861 }
862 return true;
863};
864
865void CleanUp(std::vector<InputImage*>& images)
866{
867 for (auto& img : images)
868 {
869 delete img;
870 };
871};
872
873template <class PixelType>
874bool main_stacker(std::vector<InputImage*>& images)
875{
876 if (Parameters.stackMode == "min" || Parameters.stackMode == "minimum" || Parameters.stackMode == "darkest")
877 {
878 std::cout << "Merging stack with minimum operator." << std::endl;
880 return StackImages<PixelType>(images, stacker);
881 }
882 else
883 {
884 if (Parameters.stackMode == "max" || Parameters.stackMode == "maximum" || Parameters.stackMode == "brightest")
885 {
886 std::cout << "Merging stack with maximum operator." << std::endl;
888 return StackImages<PixelType>(images, stacker);
889 }
890 else
891 {
892 if (Parameters.stackMode == "avg" || Parameters.stackMode == "average" || Parameters.stackMode == "mean")
893 {
894 std::cout << "Merging stack with mean operator." << std::endl;
896 if (Parameters.maskInput)
897 {
899 }
900 else
901 {
902 return StackImages<PixelType>(images, stacker);
903 };
904 }
905 else
906 {
907 if (Parameters.stackMode == "median")
908 {
910 std::cout << "Merging stack with median operator." << std::endl;
911 if (Parameters.maskInput)
912 {
914 }
915 else
916 {
917 return StackImages<PixelType>(images, stacker);
918 };
919 }
920 else
921 {
922 if (Parameters.stackMode == "winsor")
923 {
925 std::cout << "Merging stack with Winsor clipping operator (trim=" << Parameters.winsorTrim << ")." << std::endl;
926 if (Parameters.maskInput)
927 {
929 }
930 else
931 {
932 return StackImages<PixelType>(images, stacker);
933 };
934 }
935 else
936 {
937 if (Parameters.stackMode == "sigma")
938 {
940 std::cout << "Merging stack with sigma clipping operator (max sigma=" << Parameters.sigma << ", max " << Parameters.maxIterations << " iterations)." << std::endl;
941 if (Parameters.maskInput)
942 {
944 }
945 else
946 {
947 return StackImages<PixelType>(images, stacker);
948 };
949 }
950 else
951 {
952 if (Parameters.stackMode.empty())
953 {
954 std::cerr << "ERROR: No stacking mode given. Please specify a stacking mode with --mode=STACK_MODE" << std::endl
955 << " Allowed values for STACK_MODE are min|max|average|median|winsor|sigma" << std::endl;
956 }
957 else
958 {
959 std::cerr << "ERROR: " << "\"" << Parameters.stackMode << "\" is not a valid stack mode." << std::endl
960 << " Allowed values are min|max|average|median|winsor|sigma" << std::endl;
961 };
962 return false;
963 }
964 };
965 };
966 };
967 };
968 };
969};
970
971int main(int argc, char* argv[])
972{
973 // parse arguments
974 const char* optstring = "o:h";
975
976 enum
977 {
978 OPT_COMPRESSION = 1000,
988 };
989 static struct option longOptions[] =
990 {
991 { "output", required_argument, NULL, 'o' },
992 { "compression", required_argument, NULL, OPT_COMPRESSION },
994 { "winsor-trim", required_argument, NULL, OPT_WINSOR_TRIM},
995 { "max-sigma", required_argument, NULL, OPT_SIGMA_MAX},
996 { "max-iterations", required_argument, NULL, OPT_MAX_ITER},
997 { "mask-input", no_argument, NULL, OPT_MASK_INPUT},
998 { "mask-suffix", required_argument, NULL, OPT_MASK_SUFFIX},
999 { "mask-sigma", required_argument, NULL, OPT_MASK_SIGMA },
1000 { "multi-layer-output", no_argument, NULL, OPT_MULTILAYER },
1001 { "bigtiff", no_argument, NULL, OPT_BIGTIFF },
1002 { "help", no_argument, NULL, 'h' },
1003 0
1004 };
1005
1006 int c;
1007 while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
1008 {
1009 switch (c)
1010 {
1011 case 'o':
1012 Parameters.outputFilename = optarg;
1013 break;
1014 case 'h':
1016 return 0;
1017 break;
1018 case OPT_COMPRESSION:
1019 Parameters.compression = hugin_utils::toupper(optarg);
1020 break;
1021 case OPT_STACKMODE:
1022 Parameters.stackMode=optarg;
1023 Parameters.stackMode = hugin_utils::tolower(Parameters.stackMode);
1024 break;
1025 case OPT_WINSOR_TRIM:
1026 {
1027 std::string text(optarg);
1028 int pos = text.find("%");
1029 if (pos != std::string::npos)
1030 {
1031 text = text.substr(0, pos);
1033 {
1034 std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for Winsor trim factor given." << std::endl;
1035 return 1;
1036 };
1037 Parameters.winsorTrim /= 100.0;
1038 }
1039 else
1040 {
1042 {
1043 std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for Winsor trim factor given." << std::endl;
1044 return 1;
1045 };
1046 };
1047 if (Parameters.winsorTrim<0.01 || Parameters.winsorTrim>0.49)
1048 {
1049 std::cerr << hugin_utils::stripPath(argv[0]) << ": Winsor trim factor " << Parameters.winsorTrim << " not in valid range (0.01 - 0.49)." << std::endl;
1050 return 1;
1051 };
1052 };
1053 break;
1054 case OPT_SIGMA_MAX:
1055 {
1056 std::string text(optarg);
1058 {
1059 std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal sigma value." << std::endl;
1060 return 1;
1061 };
1062 if (Parameters.sigma<0.01)
1063 {
1064 std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal sigma value have to be positive." << std::endl;
1065 return 1;
1066 };
1067 };
1068 break;
1069 case OPT_MAX_ITER:
1070 {
1071 std::string text(optarg);
1072 if (!hugin_utils::stringToInt(text, Parameters.maxIterations))
1073 {
1074 std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal iterations." << std::endl;
1075 return 1;
1076 };
1077 if (Parameters.maxIterations<1)
1078 {
1079 std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal iterations values have to be at least 1." << std::endl;
1080 return 1;
1081 };
1082 };
1083 break;
1084 case OPT_MASK_INPUT:
1085 Parameters.maskInput = true;
1086 break;
1087 case OPT_MASK_SUFFIX:
1088 Parameters.maskSuffix = optarg;
1089 break;
1090 case OPT_MASK_SIGMA:
1091 {
1092 std::string text(optarg);
1094 {
1095 std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal sigma value." << std::endl;
1096 return 1;
1097 };
1098 if (Parameters.maskSigma<0.01)
1099 {
1100 std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal sigma value have to be positive." << std::endl;
1101 return 1;
1102 };
1103 };
1104 break;
1105 case OPT_MULTILAYER:
1106 Parameters.multiLayer = true;
1107 break;
1108 case OPT_BIGTIFF:
1109 Parameters.useBigTIFF = true;
1110 break;
1111 case ':':
1112 case '?':
1113 // missing argument or invalid switch
1114 return 1;
1115 break;
1116 default:
1117 // this should not happen
1118 abort();
1119 }
1120 };
1121
1122 unsigned nFiles = argc - optind;
1123 if (nFiles < 1)
1124 {
1125 std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one image need to be specified" << std::endl;
1126 return 1;
1127 }
1128
1129 // extract file names
1130 std::vector<std::string> files;
1131 size_t i = 0;
1132 bool isResponseFile = false;
1133 while (i < nFiles)
1134 {
1135 // @ indicates response file follows
1136 if (argv[optind + i][0] == '@')
1137 {
1138 ++i;
1139 isResponseFile = true;
1140 if (i == nFiles)
1141 {
1142 break;
1143 };
1144 };
1145 std::string currentFile(argv[optind + i]);
1146 if (isResponseFile)
1147 {
1148 // read response file
1149 std::ifstream textfile(currentFile);
1150 if (textfile.is_open())
1151 {
1152 std::string line;
1153 // read text file line for line
1154 while (std::getline(textfile, line))
1155 {
1156 const size_t pos = line.find_first_not_of(" \t");
1157 if (line.empty() || (pos != std::string::npos && line[pos] == '#'))
1158 {
1159 // ignore empty lines and lines beginning if "#"
1160 // these are regarded as comments
1161 continue;
1162 };
1163 // check if file exists and it is an image file
1164 if (hugin_utils::FileExists(line) && vigra::isImage(line.c_str()))
1165 {
1166 files.push_back(line);
1167 };
1168 };
1169 textfile.close();
1170 }
1171 else
1172 {
1173 std::cerr << "ERROR: Could not open file " << currentFile << std::endl;
1174 };
1175 isResponseFile = false;
1176 }
1177 else
1178 {
1179 // image file given, check file existence
1181 {
1182 if (vigra::isImage(currentFile.c_str()))
1183 {
1184 // it is an image file
1185 files.push_back(currentFile);
1186 }
1187 else
1188 {
1189 std::cerr << "ERROR: File " << currentFile << " is not a image file recognized by vigra." << std::endl;
1190 };
1191 }
1192 else
1193 {
1194 std::cerr << "ERROR: File " << currentFile << " does not exists." << std::endl;
1195 };
1196 };
1197 ++i;
1198 };
1199
1200 if (files.empty())
1201 {
1202 std::cerr << "ERROR: " << hugin_utils::stripPath(argv[0]) << " needs at least one image." << std::endl;
1203 return 1;
1204 };
1205
1206 if (Parameters.outputFilename.empty())
1207 {
1208 Parameters.outputFilename = "final.tif";
1209 };
1210 // if no extension is given assume TIF format
1211 hugin_utils::EnforceExtension(Parameters.outputFilename, "tif");
1212 if (!hugin_utils::IsFileTypeSupported(Parameters.outputFilename))
1213 {
1214 std::cerr << "ERROR: Extension \"" << hugin_utils::getExtension(Parameters.outputFilename) << "\" is unknown." << std::endl;
1215 return 1;
1216 };
1217 const std::string extension = hugin_utils::tolower(hugin_utils::getExtension(Parameters.outputFilename));
1218 if (Parameters.multiLayer && extension != "tif" && extension != "tiff")
1219 {
1220 std::cerr << "ERROR: Multi layer output expects a tiff file as output." << std::endl
1221 << " Other image formates are not compatible with this option." << std::endl;
1222 return 1;
1223 };
1224 bool success = false;
1225 std::vector<InputImage*> images;
1226 for (size_t i = 0; i < files.size(); ++i)
1227 {
1228 images.push_back(new InputImage(files[i]));
1229 if (images.back()->getROI().area() == 0)
1230 {
1231 std::cerr << "ERROR: Image " << images.back()->getFilename() << " has no valid data." << std::endl;
1232 CleanUp(images);
1233 return 1;
1234 };
1235 };
1236 if (!images[0]->isColor() && !images[0]->isGrayscale())
1237 {
1238 std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1239 << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numPixelSamples() << " channels per pixel." << std::endl;
1240 CleanUp(images);
1241 return 1;
1242 }
1243 if (images[0]->numExtraBands() > 1)
1244 {
1245 std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1246 << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numExtraBands() << " extra channels." << std::endl;
1247 CleanUp(images);
1248 return 1;
1249 }
1250 const std::string pixeltype(images[0]->getPixelType());
1251 //check, that image information matches
1252 for (size_t i = 1; i < files.size(); ++i)
1253 {
1254 if (!images[i]->isColor() && !images[i]->isGrayscale())
1255 {
1256 std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1257 << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numPixelSamples() << " channels per pixel." << std::endl;
1258 CleanUp(images);
1259 return 1;
1260 };
1261 if (images[i]->numExtraBands() > 1)
1262 {
1263 std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1264 << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numExtraBands() << " extra channels." << std::endl;
1265 CleanUp(images);
1266 return 1;
1267 };
1268 if (images[0]->isColor() != images[i]->isColor())
1269 {
1270 std::cerr << "ERROR: You can't merge color and grayscale images." << std::endl;
1271 CleanUp(images);
1272 return 1;
1273 };
1274 if (images[0]->numPixelSamples() != images[i]->numPixelSamples())
1275 {
1276 std::cerr << "ERROR: You can't merge image with different number of channels." << std::endl
1277 << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numBands() << " channels," << std::endl
1278 << " but image \"" << images[i]->getFilename() << "\" has " << images[i]->numBands() << " channels." << std::endl;
1279 CleanUp(images);
1280 return 1;
1281 };
1282 if (pixeltype!=images[i]->getPixelType())
1283 {
1284 std::cerr << "ERROR: You can't merge images with different pixel types." << std::endl
1285 << " Image \"" << images[0]->getFilename() << "\" has pixel type " << images[0]->getPixelType() << "," << std::endl
1286 << " but image \"" << images[i]->getFilename() << "\" has pixel type " << images[i]->getPixelType() << "." << std::endl;
1287 CleanUp(images);
1288 return 1;
1289 };
1290 };
1291
1292 if (images[0]->isColor())
1293 {
1294 if (pixeltype == "UINT8")
1295 {
1297 }
1298 else if (pixeltype == "UINT16")
1299 {
1301 }
1302 else if (pixeltype == "UINT32")
1303 {
1305 }
1306 else if (pixeltype == "FLOAT")
1307 {
1309 }
1310 else if (pixeltype == "DOUBLE")
1311 {
1313 }
1314 else
1315 {
1316 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1317 };
1318 }
1319 else
1320 {
1321 //grayscale images
1322 if (pixeltype == "UINT8")
1323 {
1325 }
1326 else if (pixeltype == "UINT16")
1327 {
1329 }
1330 else if (pixeltype == "UINT32")
1331 {
1333 }
1334 else if (pixeltype == "FLOAT")
1335 {
1336 success = main_stacker<float>(images);
1337 }
1338 else if (pixeltype == "DOUBLE")
1339 {
1340 success = main_stacker<double>(images);
1341 }
1342 else
1343 {
1344 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1345 };
1346 };
1347
1348 CleanUp(images);
1349 if (success)
1350 {
1351 return 0;
1352 };
1353 return 1;
1354}
static void usage()
Definition Main.cpp:32
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition stacker.cpp:445
virtual void reset()
Definition stacker.cpp:442
std::vector< ValueType > m_values
Definition stacker.cpp:449
virtual void operator()(const ValueType &val)
Definition stacker.cpp:443
virtual void getResult(ValueType &val)
Definition stacker.cpp:444
virtual bool IsValid()
Definition stacker.cpp:446
const std::string getName() const
Definition stacker.cpp:447
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &pixel, const vigra::UInt8 &mask) const
Definition stacker.cpp:701
vigra::UInt8 result_type
Definition stacker.cpp:676
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &gray, const vigra::UInt8 &mask, vigra::VigraTrueType) const
Definition stacker.cpp:689
PixelType second_argument_type
Definition stacker.cpp:674
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &color, const vigra::UInt8 &mask, vigra::VigraFalseType) const
Definition stacker.cpp:677
vigra::TinyVector< PixelType, 2 > first_argument_type
Definition stacker.cpp:673
vigra::UInt8 third_argument_type
Definition stacker.cpp:675
const std::string getPixelType() const
Definition stacker.cpp:231
VIGRA_UNIQUE_PTR< vigra::Decoder > m_decoder
Definition stacker.cpp:357
const int numPixelSamples() const
Definition stacker.cpp:236
const vigra::ImageImportInfo::ICCProfile getICCProfile() const
Definition stacker.cpp:239
const vigra::Rect2D getROI() const
Definition stacker.cpp:241
unsigned m_height
Definition stacker.cpp:356
std::string m_filename
Definition stacker.cpp:352
unsigned m_offset
Definition stacker.cpp:356
unsigned m_y
Definition stacker.cpp:356
vigra::ImageImportInfo m_info
Definition stacker.cpp:353
bool m_hasAlpha
Definition stacker.cpp:358
const vigra::Size2D getCanvasSize() const
Definition stacker.cpp:242
unsigned m_width
Definition stacker.cpp:356
const std::string getFilename() const
Definition stacker.cpp:240
const std::string getMaskFilename() const
Definition stacker.cpp:243
const bool isGrayscale() const
Definition stacker.cpp:233
const float getXResolution() const
Definition stacker.cpp:237
void readLine(const int y)
Definition stacker.cpp:245
const bool isColor() const
Definition stacker.cpp:232
void getValue(const int x, vigra::RGBValue< ValueType > &value, ValueType &mask)
Definition stacker.cpp:271
vigra::Size2D m_canvassize
Definition stacker.cpp:354
const int numExtraBands() const
Definition stacker.cpp:235
int m_offsetY
Definition stacker.cpp:355
int m_offsetX
Definition stacker.cpp:355
void getValue(const int x, ValueType &value, ValueType &mask)
Definition stacker.cpp:313
unsigned m_bands
Definition stacker.cpp:356
const vigra::ImageImportInfo & getImageImportInfo() const
Definition stacker.cpp:244
unsigned m_x
Definition stacker.cpp:356
InputImage(const std::string filename)
Definition stacker.cpp:205
bool m_noData
Definition stacker.cpp:358
const int numBands() const
Definition stacker.cpp:234
const float getYResolution() const
Definition stacker.cpp:238
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition stacker.cpp:426
virtual void reset()
Definition stacker.cpp:424
virtual void getResult(ValueType &val)
Definition stacker.cpp:432
virtual void operator()(const ValueType &val)
Definition stacker.cpp:427
virtual bool IsValid()
Definition stacker.cpp:433
ValueType m_max
Definition stacker.cpp:435
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition stacker.cpp:425
void sort(vigra::VigraFalseType)
Definition stacker.cpp:483
void sort(vigra::VigraTrueType)
Definition stacker.cpp:478
virtual void getResult(ValueType &val) override
Definition stacker.cpp:456
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma) override
Definition stacker.cpp:469
const std::string getName() const
Definition stacker.cpp:475
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition stacker.cpp:408
void operator()(const ValueType &val)
Definition stacker.cpp:409
void getResult(ValueType &val)
Definition stacker.cpp:414
ValueType m_min
Definition stacker.cpp:417
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition stacker.cpp:407
void reset()
Definition stacker.cpp:406
bool IsValid()
Definition stacker.cpp:415
virtual void reset()
Definition stacker.cpp:531
virtual bool IsValid()
Definition stacker.cpp:549
std::vector< ValueType > m_values
Definition stacker.cpp:578
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition stacker.cpp:532
virtual void operator()(const ValueType &val)
Definition stacker.cpp:534
const std::string getName() const
Definition stacker.cpp:550
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition stacker.cpp:533
virtual void getResult(ValueType &val)
Definition stacker.cpp:539
std::vector< double > m_sortValues
Definition stacker.cpp:579
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition stacker.cpp:544
virtual void getResult(ValueType &val) override
Definition stacker.cpp:500
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma) override
Definition stacker.cpp:505
const std::string getName() const
Definition stacker.cpp:510
static double sigma
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
class AlphaIterator class AlphaAccessor inline void exportImageAlpha(ImageIterator image_upper_left, ImageIterator image_lower_right, ImageAccessor image_accessor, AlphaIterator alpha_upper_left, AlphaAccessor alpha_accessor, const ImageExportInfo &export_info)
vigra::FRGBImage ImageType
std::string toupper(const std::string &s)
Definition stl_utils.h:59
std::string stripExtension(const std::string &basename2)
remove extension of a filename
Definition utils.cpp:130
std::string getExtension(const std::string &basename2)
Get extension of a filename.
Definition utils.cpp:99
int floori(double x)
Definition hugin_math.h:65
void EnforceExtension(std::string &filename, const std::string &defaultExtension)
check if filename contains extension, if not add default extension
Definition utils.cpp:411
std::string tolower(const std::string &s)
convert a string to lowercase
Definition stl_utils.h:49
bool IsFileTypeSupported(const std::string &filename)
return true, if file type by extension is supported by vigra
Definition utils.cpp:405
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
bool FileExists(const std::string &filename)
checks if file exists
Definition utils.cpp:362
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition utils.h:114
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
bool stringToInt(const std::string &s, int &val)
convert string to integer value, returns true, if sucessful
Definition utils.cpp:264
void combineThreeImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, SrcImageIterator3 src3_upperleft, SrcAccessor3 src3_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void createTiffDirectory(vigra::TiffImage *tiff, const std::string &pagename, const std::string &documentname, const std::string comp, uint16_t page, uint16_t nImg, vigra::Diff2D offset, vigra::Size2D fullSize, const vigra::ImageExportInfo::ICCProfile &icc)
write a new Tiff directory, for a new layer
Definition tiffUtils.h:58
void createAlphaTiffImage(ImageIterator upperleft, ImageIterator lowerright, ImageAccessor a, AlphaIterator alphaUpperleft, AlphaAccessor alphaA, vigra::TiffImage *tiff)
Definition tiffUtils.h:522
int main(int argc, char *argv[])
Definition stacker.cpp:971
bool CheckInput(const std::vector< InputImage * > &images, vigra::Rect2D &outputROI, vigra::Size2D &canvasSize)
Definition stacker.cpp:582
bool StackImagesAndMask(std::vector< InputImage * > &images, Functor &stacker)
Definition stacker.cpp:710
bool main_stacker(std::vector< InputImage * > &images)
Definition stacker.cpp:874
void getMeanSigma(const std::vector< ValueType > &values, ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition stacker.cpp:376
void getMean(const std::vector< ValueType > &values, ValueType &val)
Definition stacker.cpp:362
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
Definition stacker.cpp:42
bool SaveImage(ImageType &image, MaskType &mask, vigra::ImageExportInfo &exportImageInfo, std::string filetype, std::string pixelType)
save image, when possible with alpha channel, take care of formats which does not support alpha chann...
Definition stacker.cpp:60
bool SaveFinalImage(ImageType &image, MaskType &mask, const std::string &inputPixelType, vigra::ImageExportInfo &output)
save final image, take care of some supported pixel types and convert when necessary to smaller pixel...
Definition stacker.cpp:108
static struct GeneralParameters Parameters
void CleanUp(std::vector< InputImage * > &images)
Definition stacker.cpp:865
bool StackImages(std::vector< InputImage * > &images, Functor &stacker)
loads images line by line and merge into final image, save the result
Definition stacker.cpp:613
std::string outputFilename
Definition stacker.cpp:184
std::string maskSuffix
Definition stacker.cpp:197
std::string compression
Definition stacker.cpp:185
std::string stackMode
Definition stacker.cpp:187
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
Some functions to create tiff images with masks.