Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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>
32 #include <hugin_utils/stl_utils.h>
33 #include <vigra/imageinfo.hxx>
34 #include <vigra/codec.hxx>
35 #include <vigra/stdimage.hxx>
36 #include <vigra_ext/impexalpha.hxx>
37 #include <vigra/tiff.hxx>
38 #include <vigra_ext/tiffUtils.h>
39 #include <vigra_ext/openmp_vigra.h>
40 
42 void 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 
59 template <class ImageType, class MaskType>
60 bool 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 
107 template <class ImageType, class MaskType>
108 bool SaveFinalImage(ImageType& image, MaskType& mask, const std::string& inputPixelType, vigra::ImageExportInfo& output)
109 {
110  VIGRA_UNIQUE_PTR<vigra::Encoder> encoder(vigra::encoder(output));
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 
134 static 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 
180 static struct GeneralParameters
181 {
182 public:
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;
190  int maxIterations = 5;
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;
200 } Parameters;
201 
203 {
204 public:
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  {
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  {
340  };
341  value = *band0;
342  }
343  else
344  {
345  mask = vigra::NumericTraits<ValueType>::zero();
346  };
347  };
348  };
349  };
350 
351 private:
352  std::string m_filename;
353  vigra::ImageImportInfo m_info;
354  vigra::Size2D m_canvassize;
357  VIGRA_UNIQUE_PTR<vigra::Decoder> m_decoder;
359 };
360 
361 template<class ValueType>
362 void 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 
375 template<class ValueType>
376 void 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 
402 template<class ValueType>
404 {
405 public:
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; };
416 private:
417  ValueType m_min;
418 };
419 
420 template<class ValueType>
422 {
423 public:
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(); };
434 private:
435  ValueType m_max;
436 };
437 
438 template<class ValueType>
440 {
441 public:
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"; };
448 protected:
449  std::vector<ValueType> m_values;
450 };
451 
452 template<class ValueType>
453 class MedianStacker : public AverageStacker<ValueType>
454 {
455 public:
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"; };
476 protected:
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 
496 template<class ValueType>
497 class WinsorMeanStacker : public MedianStacker<ValueType>
498 {
499 public:
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"; };
511 private:
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 
527 template<class ValueType>
529 {
530 public:
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  size_t iteration = 0;
542  while (iteration < Parameters.maxIterations)
543  {
544  double mean, sigma;
545  getMeanSigma(m_sortValues, mean, sigma);
546  const size_t oldSize = m_sortValues.size();
547  for (int i = m_sortValues.size() - 1; i >= 0 && m_sortValues.size() > 1; --i)
548  {
549  // check if values are in range
550  if (abs(m_sortValues[i] - mean) > Parameters.sigma * sigma)
551  {
552  m_sortValues.erase(m_sortValues.begin() + i);
553  m_values.erase(m_values.begin() + i);
554  };
555  };
556  if (m_sortValues.size() == oldSize)
557  {
558  // no values outside range, end while loop
559  break;
560  };
561  ++iteration;
562  };
563  getMean(m_values, val);
564  };
565  virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
566  {
567  size_t iteration = 0;
568  while (iteration < Parameters.maxIterations)
569  {
570  double grayMean, graySigma;
571  getMeanSigma(m_sortValues, grayMean, graySigma);
572  const size_t oldSize = m_sortValues.size();
573  for (int i = m_sortValues.size() - 1; i >= 0 && m_sortValues.size() > 1; --i)
574  {
575  // check if values are in range
576  if (abs(m_sortValues[i] - grayMean) > Parameters.sigma * graySigma)
577  {
578  m_sortValues.erase(m_sortValues.begin() + i);
579  m_values.erase(m_values.begin() + i);
580  };
581  };
582  if (m_sortValues.size() == oldSize)
583  {
584  // no values outside range, end loop
585  break;
586  };
587  ++iteration;
588  };
589  getMeanSigma(m_values, val, sigma);
590  };
591  virtual bool IsValid() { return !m_values.empty(); };
592  const std::string getName() const { return "sigma clipped mean"; };
593 
594 private:
595  std::vector<ValueType> m_values;
596  std::vector<double> m_sortValues;
597 };
598 
599 bool CheckInput(const std::vector<InputImage*>& images, vigra::Rect2D& outputROI, vigra::Size2D& canvasSize)
600 {
601  if (images.empty())
602  {
603  return false;
604  };
605  // get ROI and canvas size which contains all images
606  outputROI = images[0]->getROI();
607  canvasSize = images[0]->getCanvasSize();
608  for (size_t i = 1; i < images.size(); ++i)
609  {
610  outputROI |= images[i]->getROI();
611  if (images[i]->getCanvasSize().width() > canvasSize.width())
612  {
613  canvasSize.setWidth(images[i]->getCanvasSize().width());
614  };
615  if (images[i]->getCanvasSize().height() > canvasSize.height())
616  {
617  canvasSize.setHeight(images[i]->getCanvasSize().height());
618  };
619  };
620  if (outputROI.area() == 0)
621  {
622  std::cerr << "ERROR: You can't stack non-overlapping images." << std::endl;
623  return false;
624  };
625  return true;
626 }
627 
629 template <class PixelType, class Functor>
630 bool StackImages(std::vector<InputImage*>& images, Functor& stacker)
631 {
632  typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
633  vigra::Rect2D outputROI;
634  vigra::Size2D canvasSize;
635  if (!CheckInput(images, outputROI, canvasSize))
636  {
637  return false;
638  }
639  // prepare output
640  vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
641  exportImageInfo.setXResolution(images[0]->getXResolution());
642  exportImageInfo.setYResolution(images[0]->getYResolution());
643  exportImageInfo.setPosition(outputROI.upperLeft());
644  exportImageInfo.setCanvasSize(canvasSize);
645  exportImageInfo.setICCProfile(images[0]->getICCProfile());
646  SetCompression(exportImageInfo, Parameters.compression);
647  vigra::BasicImage<PixelType> output(outputROI.size());
648  vigra::BImage mask(output.size(),vigra::UInt8(0));
649  // loop over all lines
650  for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
651  {
652  // load next line
653 #pragma omp parallel for
654  for (int i = 0; i < images.size(); ++i)
655  {
656  images[i]->readLine(y);
657  };
658  // process current line
659 #pragma omp parallel for schedule(static, 100)
660  for (int x = outputROI.left(); x < outputROI.right(); ++x)
661  {
662  // we need a private copy for each thread
663  Functor privateStacker(stacker);
664  privateStacker.reset();
665  for (size_t i = 0; i < images.size(); ++i)
666  {
667  PixelType value;
668  ChannelType maskValue;
669  images[i]->getValue(x, value, maskValue);
670  if (maskValue > 0)
671  {
672  privateStacker(value);
673  }
674  };
675  if (privateStacker.IsValid())
676  {
677  privateStacker.getResult(output(x - outputROI.left(), y - outputROI.top()));
678  mask(x-outputROI.left(), y-outputROI.top()) = 255;
679  };
680  };
681  };
682  std::cout << "Write result to " << Parameters.outputFilename << std::endl;
683  return SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo);
684 };
685 
686 template <class PixelType>
688 {
689 public:
690  typedef typename vigra::NumericTraits<PixelType>::RealPromote realPixelType;
691  typedef vigra::TinyVector<realPixelType, 2> first_argument_type;
692  typedef PixelType second_argument_type;
693  typedef vigra::UInt8 third_argument_type;
694  typedef vigra::UInt8 result_type;
695  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& color, const vigra::UInt8& mask, vigra::VigraFalseType) const
696  {
697  if (mask > 0 && (color.red() < limits[0].red() || color.red()>limits[1].red() ||
698  color.green() < limits[0].green() || color.green() > limits[1].green() ||
699  color.blue() < limits[0].blue() || color.blue() > limits[1].blue()))
700  {
701  return 255;
702  }
703  else
704  {
705  return 0;
706  };
707  };
708 
709  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& gray, const vigra::UInt8& mask, vigra::VigraTrueType) const
710  {
711  if (mask > 0 && (gray < limits[0] || gray>limits[1]))
712  {
713  return 255;
714  }
715  else
716  {
717  return 0;
718  };
719  };
720 
721  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& pixel, const vigra::UInt8& mask) const
722  {
723  typedef typename vigra::NumericTraits<PixelType>::isScalar is_scalar;
724  return (*this)(limits, pixel, mask, is_scalar());
725  }
726 
727 };
728 
729 template <class PixelType, class Functor>
730 bool StackImagesAndMask(std::vector<InputImage*>& images, Functor& stacker)
731 {
732  typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
733  vigra::Rect2D outputROI;
734  vigra::Size2D canvasSize;
735  if (!CheckInput(images, outputROI, canvasSize))
736  {
737  return false;
738  }
739  // prepare output
740  vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
741  exportImageInfo.setXResolution(images[0]->getXResolution());
742  exportImageInfo.setYResolution(images[0]->getYResolution());
743  exportImageInfo.setPosition(outputROI.upperLeft());
744  exportImageInfo.setCanvasSize(canvasSize);
745  exportImageInfo.setICCProfile(images[0]->getICCProfile());
746  SetCompression(exportImageInfo, Parameters.compression);
747  // for multi-layer output
748  vigra::TiffImage* tiffImage;
749  vigra::BasicImage<PixelType> output(outputROI.size());
750  vigra::BImage mask(output.size(), vigra::UInt8(0));
751  vigra::BasicImage<vigra::TinyVector<typename vigra::NumericTraits<PixelType>::RealPromote, 2>> limits(output.size());
752  // loop over all lines
753  for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
754  {
755  // load next line
756 #pragma omp parallel for
757  for (int i = 0; i < images.size(); ++i)
758  {
759  images[i]->readLine(y);
760  };
761  // process current line
762 #pragma omp parallel for schedule(static, 100)
763  for (int x = outputROI.left(); x < outputROI.right(); ++x)
764  {
765  // we need a private copy for each thread
766  Functor privateStacker(stacker);
767  privateStacker.reset();
768  for (size_t i = 0; i < images.size(); ++i)
769  {
770  PixelType value;
771  ChannelType maskValue;
772  images[i]->getValue(x, value, maskValue);
773  if (maskValue > 0)
774  {
775  privateStacker(value);
776  }
777  };
778  if (privateStacker.IsValid())
779  {
780  PixelType mean;
781  typename vigra::NumericTraits<PixelType>::RealPromote sigma;
782  privateStacker.getResultAndSigma(mean, sigma);
783  output(x - outputROI.left(), y - outputROI.top()) = mean;
784  mask(x - outputROI.left(), y - outputROI.top()) = 255;
785  limits(x - outputROI.left(), y - outputROI.top()) = vigra::TinyVector<PixelType, 2>(mean - Parameters.maskSigma*sigma, mean + Parameters.maskSigma*sigma);
786  };
787  };
788  };
789  std::cout << "Write result to " << Parameters.outputFilename << std::endl;
790  if (Parameters.multiLayer)
791  {
792  tiffImage = TIFFOpen(Parameters.outputFilename.c_str(), "w");
793  vigra_ext::createTiffDirectory(tiffImage, stacker.getName(), stacker.getName(),
794  Parameters.compression.empty() ? "LZW" : Parameters.compression, 0, images.size() + 1,
795  outputROI.upperLeft(), canvasSize, images[0]->getICCProfile());
796  vigra_ext::createAlphaTiffImage(vigra::srcImageRange(output), vigra::maskImage(mask), tiffImage);
797  TIFFFlush(tiffImage);
798  }
799  else
800  {
801  if (!SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo))
802  {
803  return false;
804  };
805  };
806  // we don't need median image any more
807  output.resize(0, 0);
808  std::cout << "Masking input images with sigma=" << Parameters.maskSigma;
809  if (Parameters.multiLayer)
810  {
811  std::cout << std::endl;
812  }
813  else
814  {
815  std::cout << " and suffix " << Parameters.maskSuffix << ".tif" << std::endl;
816  };
817  for (size_t i = 0; i < images.size(); ++i)
818  {
819  std::cout << "Masking " << images[i]->getFilename();
820  if (Parameters.multiLayer)
821  {
822  std::cout << std::endl;
823  }
824  else
825  {
826  std::cout << " -> " << images[i]->getMaskFilename() << std::endl;
827  };
828  vigra::BasicImage<PixelType> image(images[i]->getROI().size());
829  vigra::BImage mask(image.size(), 255);
830  if (images[i]->numExtraBands() == 1)
831  {
832  vigra::importImageAlpha(images[i]->getImageImportInfo(), vigra::destImage(image), vigra::destImage(mask));
833  }
834  else
835  {
836  vigra::importImage(images[i]->getImageImportInfo(), vigra::destImage(image));
837  };
838  vigra::Rect2D roi = images[i]->getROI();
839  roi.moveBy(-outputROI.upperLeft());
841  if (Parameters.multiLayer)
842  {
843  vigra_ext::createTiffDirectory(tiffImage, images[i]->getFilename(), images[i]->getFilename(),
844  Parameters.compression.empty() ? "LZW" : Parameters.compression, i+1, images.size() + 1,
845  images[i]->getROI().upperLeft(), images[i]->getCanvasSize(), images[i]->getICCProfile());
846  vigra_ext::createAlphaTiffImage(vigra::srcImageRange(image), vigra::maskImage(mask), tiffImage);
847  TIFFFlush(tiffImage);
848  }
849  else
850  {
851  if (hugin_utils::FileExists(images[i]->getMaskFilename()))
852  {
853  std::cout << "Masked file \"" << images[i]->getMaskFilename() << "\" already exists." << std::endl
854  << "Processing aborted." << std::endl;
855  return false;
856  };
857  vigra::ImageExportInfo exportMaskImage(images[i]->getMaskFilename().c_str(), Parameters.useBigTIFF ? "w8" : "w");
858  exportMaskImage.setXResolution(images[i]->getXResolution());
859  exportMaskImage.setYResolution(images[i]->getYResolution());
860  exportMaskImage.setPosition(images[i]->getROI().upperLeft());
861  exportMaskImage.setCanvasSize(images[i]->getCanvasSize());
862  exportMaskImage.setICCProfile(images[i]->getICCProfile());
863  exportMaskImage.setPixelType(images[i]->getPixelType().c_str());
864  exportMaskImage.setCompression("LZW");
865  try
866  {
867  vigra::exportImageAlpha(vigra::srcImageRange(image), vigra::srcImage(mask), exportMaskImage);
868  }
869  catch (std::exception& e)
870  {
871  std::cerr << "Could not save masked images \"" << exportMaskImage.getFileName() << "\"." << std::endl
872  << "Error code: " << e.what() << std::endl
873  << "Processing aborted." << std::endl;
874  return false;
875  }
876  };
877  };
878  if (Parameters.multiLayer)
879  {
880  TIFFClose(tiffImage);
881  }
882  return true;
883 };
884 
885 void CleanUp(std::vector<InputImage*>& images)
886 {
887  for (auto& img : images)
888  {
889  delete img;
890  };
891 };
892 
893 template <class PixelType>
894 bool main_stacker(std::vector<InputImage*>& images)
895 {
896  if (Parameters.stackMode == "min" || Parameters.stackMode == "minimum" || Parameters.stackMode == "darkest")
897  {
898  std::cout << "Merging stack with minimum operator." << std::endl;
899  MinStacker<PixelType> stacker;
900  return StackImages<PixelType>(images, stacker);
901  }
902  else
903  {
904  if (Parameters.stackMode == "max" || Parameters.stackMode == "maximum" || Parameters.stackMode == "brightest")
905  {
906  std::cout << "Merging stack with maximum operator." << std::endl;
907  MaxStacker<PixelType> stacker;
908  return StackImages<PixelType>(images, stacker);
909  }
910  else
911  {
912  if (Parameters.stackMode == "avg" || Parameters.stackMode == "average" || Parameters.stackMode == "mean")
913  {
914  std::cout << "Merging stack with average operator." << std::endl;
916  if (Parameters.maskInput)
917  {
918  return StackImagesAndMask<PixelType>(images, stacker);
919  }
920  else
921  {
922  return StackImages<PixelType>(images, stacker);
923  };
924  }
925  else
926  {
927  if (Parameters.stackMode == "median")
928  {
929  MedianStacker<PixelType> stacker;
930  std::cout << "Merging stack with median operator." << std::endl;
931  if (Parameters.maskInput)
932  {
933  return StackImagesAndMask<PixelType>(images, stacker);
934  }
935  else
936  {
937  return StackImages<PixelType>(images, stacker);
938  };
939  }
940  else
941  {
942  if (Parameters.stackMode == "winsor")
943  {
945  std::cout << "Merging stack with Winsor clipping operator (trim=" << Parameters.winsorTrim << ")." << std::endl;
946  if (Parameters.maskInput)
947  {
948  return StackImagesAndMask<PixelType>(images, stacker);
949  }
950  else
951  {
952  return StackImages<PixelType>(images, stacker);
953  };
954  }
955  else
956  {
957  if (Parameters.stackMode == "sigma")
958  {
960  std::cout << "Merging stack with sigma clipping operator (max sigma=" << Parameters.sigma << ", max " << Parameters.maxIterations << " iterations)." << std::endl;
961  if (Parameters.maskInput)
962  {
963  return StackImagesAndMask<PixelType>(images, stacker);
964  }
965  else
966  {
967  return StackImages<PixelType>(images, stacker);
968  };
969  }
970  else
971  {
972  if (Parameters.stackMode.empty())
973  {
974  std::cerr << "ERROR: No stacking mode given. Please specify a stacking mode with --mode=STACK_MODE" << std::endl
975  << " Allowed values for STACK_MODE are min|max|average|median|winsor|sigma" << std::endl;
976  }
977  else
978  {
979  std::cerr << "ERROR: " << "\"" << Parameters.stackMode << "\" is not a valid stack mode." << std::endl
980  << " Allowed values are min|max|average|median|winsor|sigma" << std::endl;
981  };
982  return false;
983  }
984  };
985  };
986  };
987  };
988  };
989 };
990 
991 int main(int argc, char* argv[])
992 {
993  // parse arguments
994  const char* optstring = "o:h";
995 
996  enum
997  {
998  OPT_COMPRESSION = 1000,
999  OPT_STACKMODE,
1000  OPT_WINSOR_TRIM,
1001  OPT_SIGMA_MAX,
1002  OPT_MAX_ITER,
1003  OPT_MASK_INPUT,
1004  OPT_MASK_SUFFIX,
1005  OPT_MASK_SIGMA,
1006  OPT_MULTILAYER,
1007  OPT_BIGTIFF
1008  };
1009  static struct option longOptions[] =
1010  {
1011  { "output", required_argument, NULL, 'o' },
1012  { "compression", required_argument, NULL, OPT_COMPRESSION },
1013  { "mode", required_argument, NULL, OPT_STACKMODE },
1014  { "winsor-trim", required_argument, NULL, OPT_WINSOR_TRIM},
1015  { "max-sigma", required_argument, NULL, OPT_SIGMA_MAX},
1016  { "max-iterations", required_argument, NULL, OPT_MAX_ITER},
1017  { "mask-input", no_argument, NULL, OPT_MASK_INPUT},
1018  { "mask-suffix", required_argument, NULL, OPT_MASK_SUFFIX},
1019  { "mask-sigma", required_argument, NULL, OPT_MASK_SIGMA },
1020  { "multi-layer-output", no_argument, NULL, OPT_MULTILAYER },
1021  { "bigtiff", no_argument, NULL, OPT_BIGTIFF },
1022  { "help", no_argument, NULL, 'h' },
1023  0
1024  };
1025 
1026  int c;
1027  while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
1028  {
1029  switch (c)
1030  {
1031  case 'o':
1032  Parameters.outputFilename = optarg;
1033  break;
1034  case 'h':
1035  usage(hugin_utils::stripPath(argv[0]).c_str());
1036  return 0;
1037  break;
1038  case OPT_COMPRESSION:
1039  Parameters.compression = hugin_utils::toupper(optarg);
1040  break;
1041  case OPT_STACKMODE:
1042  Parameters.stackMode=optarg;
1043  Parameters.stackMode = hugin_utils::tolower(Parameters.stackMode);
1044  break;
1045  case OPT_WINSOR_TRIM:
1046  {
1047  std::string text(optarg);
1048  int pos = text.find("%");
1049  if (pos != std::string::npos)
1050  {
1051  text = text.substr(0, pos);
1052  if (!hugin_utils::stringToDouble(text, Parameters.winsorTrim))
1053  {
1054  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for Winsor trim factor given." << std::endl;
1055  return 1;
1056  };
1057  Parameters.winsorTrim /= 100.0;
1058  }
1059  else
1060  {
1061  if (!hugin_utils::stringToDouble(text, Parameters.winsorTrim))
1062  {
1063  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for Winsor trim factor given." << std::endl;
1064  return 1;
1065  };
1066  };
1067  if (Parameters.winsorTrim<0.01 || Parameters.winsorTrim>0.49)
1068  {
1069  std::cerr << hugin_utils::stripPath(argv[0]) << ": Winsor trim factor " << Parameters.winsorTrim << " not in valid range (0.01 - 0.49)." << std::endl;
1070  return 1;
1071  };
1072  };
1073  break;
1074  case OPT_SIGMA_MAX:
1075  {
1076  std::string text(optarg);
1077  if (!hugin_utils::stringToDouble(text, Parameters.sigma))
1078  {
1079  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal sigma value." << std::endl;
1080  return 1;
1081  };
1082  if (Parameters.sigma<0.01)
1083  {
1084  std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal sigma value have to be positive." << std::endl;
1085  return 1;
1086  };
1087  };
1088  break;
1089  case OPT_MAX_ITER:
1090  {
1091  std::string text(optarg);
1092  if (!hugin_utils::stringToInt(text, Parameters.maxIterations))
1093  {
1094  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal iterations." << std::endl;
1095  return 1;
1096  };
1097  if (Parameters.maxIterations<1)
1098  {
1099  std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal iterations values have to be at least 1." << std::endl;
1100  return 1;
1101  };
1102  };
1103  break;
1104  case OPT_MASK_INPUT:
1105  Parameters.maskInput = true;
1106  break;
1107  case OPT_MASK_SUFFIX:
1108  Parameters.maskSuffix = optarg;
1109  break;
1110  case OPT_MASK_SIGMA:
1111  {
1112  std::string text(optarg);
1113  if (!hugin_utils::stringToDouble(text, Parameters.maskSigma))
1114  {
1115  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid number for maximal sigma value." << std::endl;
1116  return 1;
1117  };
1118  if (Parameters.maskSigma<0.01)
1119  {
1120  std::cerr << hugin_utils::stripPath(argv[0]) << ": Maximal sigma value have to be positive." << std::endl;
1121  return 1;
1122  };
1123  };
1124  break;
1125  case OPT_MULTILAYER:
1126  Parameters.multiLayer = true;
1127  break;
1128  case OPT_BIGTIFF:
1129  Parameters.useBigTIFF = true;
1130  break;
1131  case ':':
1132  case '?':
1133  // missing argument or invalid switch
1134  return 1;
1135  break;
1136  default:
1137  // this should not happen
1138  abort();
1139  }
1140  };
1141 
1142  unsigned nFiles = argc - optind;
1143  if (nFiles < 1)
1144  {
1145  std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one image need to be specified" << std::endl;
1146  return 1;
1147  }
1148 
1149  // extract file names
1150  std::vector<std::string> files;
1151  size_t i = 0;
1152  bool isResponseFile = false;
1153  while (i < nFiles)
1154  {
1155  // @ indicates response file follows
1156  if (argv[optind + i][0] == '@')
1157  {
1158  ++i;
1159  isResponseFile = true;
1160  if (i == nFiles)
1161  {
1162  break;
1163  };
1164  };
1165  std::string currentFile(argv[optind + i]);
1166  if (isResponseFile)
1167  {
1168  // read response file
1169  std::ifstream textfile(currentFile);
1170  if (textfile.is_open())
1171  {
1172  std::string line;
1173  // read text file line for line
1174  while (std::getline(textfile, line))
1175  {
1176  const size_t pos = line.find_first_not_of(" \t");
1177  if (line.empty() || (pos != std::string::npos && line[pos] == '#'))
1178  {
1179  // ignore empty lines and lines beginning if "#"
1180  // these are regarded as comments
1181  continue;
1182  };
1183  // check if file exists and it is an image file
1184  if (hugin_utils::FileExists(line) && vigra::isImage(line.c_str()))
1185  {
1186  files.push_back(line);
1187  };
1188  };
1189  textfile.close();
1190  }
1191  else
1192  {
1193  std::cerr << "ERROR: Could not open file " << currentFile << std::endl;
1194  };
1195  isResponseFile = false;
1196  }
1197  else
1198  {
1199  // image file given, check file existence
1200  if (hugin_utils::FileExists(currentFile))
1201  {
1202  if (vigra::isImage(currentFile.c_str()))
1203  {
1204  // it is an image file
1205  files.push_back(currentFile);
1206  }
1207  else
1208  {
1209  std::cerr << "ERROR: File " << currentFile << " is not a image file recognized by vigra." << std::endl;
1210  };
1211  }
1212  else
1213  {
1214  std::cerr << "ERROR: File " << currentFile << " does not exists." << std::endl;
1215  };
1216  };
1217  ++i;
1218  };
1219 
1220  if (files.empty())
1221  {
1222  std::cerr << "ERROR: " << hugin_utils::stripPath(argv[0]) << " needs at least one image." << std::endl;
1223  return 1;
1224  };
1225 
1226  if (Parameters.outputFilename.empty())
1227  {
1228  Parameters.outputFilename = "final.tif";
1229  };
1230  // if no extension is given assume TIF format
1231  hugin_utils::EnforceExtension(Parameters.outputFilename, "tif");
1232  if (!hugin_utils::IsFileTypeSupported(Parameters.outputFilename))
1233  {
1234  std::cerr << "ERROR: Extension \"" << hugin_utils::getExtension(Parameters.outputFilename) << "\" is unknown." << std::endl;
1235  return 1;
1236  };
1237  const std::string extension = hugin_utils::tolower(hugin_utils::getExtension(Parameters.outputFilename));
1238  if (Parameters.multiLayer && extension != "tif" && extension != "tiff")
1239  {
1240  std::cerr << "ERROR: Multi layer output expects a tiff file as output." << std::endl
1241  << " Other image formates are not compatible with this option." << std::endl;
1242  return 1;
1243  };
1244  bool success = false;
1245  std::vector<InputImage*> images;
1246  for (size_t i = 0; i < files.size(); ++i)
1247  {
1248  images.push_back(new InputImage(files[i]));
1249  if (images.back()->getROI().area() == 0)
1250  {
1251  std::cerr << "ERROR: Image " << images.back()->getFilename() << " has no valid data." << std::endl;
1252  CleanUp(images);
1253  return 1;
1254  };
1255  };
1256  if (!images[0]->isColor() && !images[0]->isGrayscale())
1257  {
1258  std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1259  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numPixelSamples() << " channels per pixel." << std::endl;
1260  CleanUp(images);
1261  return 1;
1262  }
1263  if (images[0]->numExtraBands() > 1)
1264  {
1265  std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1266  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numExtraBands() << " extra channels." << std::endl;
1267  CleanUp(images);
1268  return 1;
1269  }
1270  const std::string pixeltype(images[0]->getPixelType());
1271  //check, that image information matches
1272  for (size_t i = 1; i < files.size(); ++i)
1273  {
1274  if (!images[i]->isColor() && !images[i]->isGrayscale())
1275  {
1276  std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1277  << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numPixelSamples() << " channels per pixel." << std::endl;
1278  CleanUp(images);
1279  return 1;
1280  };
1281  if (images[i]->numExtraBands() > 1)
1282  {
1283  std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1284  << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numExtraBands() << " extra channels." << std::endl;
1285  CleanUp(images);
1286  return 1;
1287  };
1288  if (images[0]->isColor() != images[i]->isColor())
1289  {
1290  std::cerr << "ERROR: You can't merge color and grayscale images." << std::endl;
1291  CleanUp(images);
1292  return 1;
1293  };
1294  if (images[0]->numPixelSamples() != images[i]->numPixelSamples())
1295  {
1296  std::cerr << "ERROR: You can't merge image with different number of channels." << std::endl
1297  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numBands() << " channels," << std::endl
1298  << " but image \"" << images[i]->getFilename() << "\" has " << images[i]->numBands() << " channels." << std::endl;
1299  CleanUp(images);
1300  return 1;
1301  };
1302  if (pixeltype!=images[i]->getPixelType())
1303  {
1304  std::cerr << "ERROR: You can't merge images with different pixel types." << std::endl
1305  << " Image \"" << images[0]->getFilename() << "\" has pixel type " << images[0]->getPixelType() << "," << std::endl
1306  << " but image \"" << images[i]->getFilename() << "\" has pixel type " << images[i]->getPixelType() << "." << std::endl;
1307  CleanUp(images);
1308  return 1;
1309  };
1310  };
1311 
1312  if (images[0]->isColor())
1313  {
1314  if (pixeltype == "UINT8")
1315  {
1316  success = main_stacker<vigra::RGBValue<vigra::UInt8>>(images);
1317  }
1318  else if (pixeltype == "UINT16")
1319  {
1320  success = main_stacker<vigra::RGBValue<vigra::UInt16>>(images);
1321  }
1322  else if (pixeltype == "UINT32")
1323  {
1324  success = main_stacker<vigra::RGBValue<vigra::UInt32>>(images);
1325  }
1326  else if (pixeltype == "FLOAT")
1327  {
1328  success = main_stacker<vigra::RGBValue<float>>(images);
1329  }
1330  else if (pixeltype == "DOUBLE")
1331  {
1332  success = main_stacker<vigra::RGBValue<double>>(images);
1333  }
1334  else
1335  {
1336  std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1337  };
1338  }
1339  else
1340  {
1341  //grayscale images
1342  if (pixeltype == "UINT8")
1343  {
1344  success = main_stacker<vigra::UInt8>(images);
1345  }
1346  else if (pixeltype == "UINT16")
1347  {
1348  success = main_stacker<vigra::UInt16>(images);
1349  }
1350  else if (pixeltype == "UINT32")
1351  {
1352  success = main_stacker<vigra::UInt32>(images);
1353  }
1354  else if (pixeltype == "FLOAT")
1355  {
1356  success = main_stacker<float>(images);
1357  }
1358  else if (pixeltype == "DOUBLE")
1359  {
1360  success = main_stacker<double>(images);
1361  }
1362  else
1363  {
1364  std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1365  };
1366  };
1367 
1368  CleanUp(images);
1369  if (success)
1370  {
1371  return 0;
1372  };
1373  return 1;
1374 }
int floori(double x)
Definition: hugin_math.h:65
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma) override
Definition: stacker.cpp:469
virtual bool IsValid()
Definition: stacker.cpp:591
virtual void getResult(ValueType &val)
Definition: stacker.cpp:539
const int numPixelSamples() const
Definition: stacker.cpp:236
const int numBands() const
Definition: stacker.cpp:234
std::vector< double > m_sortValues
Definition: stacker.cpp:596
unsigned m_height
Definition: stacker.cpp:356
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
const std::string getName() const
Definition: stacker.cpp:510
ValueType m_max
Definition: stacker.cpp:433
std::string outputFilename
Definition: stacker.cpp:184
const bool isGrayscale() const
Definition: stacker.cpp:233
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:565
void CleanUp(std::vector< InputImage * > &images)
Definition: stacker.cpp:885
PixelType second_argument_type
Definition: stacker.cpp:692
virtual void getResult(ValueType &val) override
Definition: stacker.cpp:500
bool StackImages(std::vector< InputImage * > &images, Functor &stacker)
loads images line by line and merge into final image, save the result
Definition: stacker.cpp:630
virtual void getResult(ValueType &val) override
Definition: stacker.cpp:456
void getValue(const int x, vigra::RGBValue< ValueType > &value, ValueType &mask)
Definition: stacker.cpp:271
const std::string getName() const
Definition: stacker.cpp:447
vigra::UInt8 third_argument_type
Definition: stacker.cpp:693
InputImage(const std::string filename)
Definition: stacker.cpp:205
Some functions to create tiff images with masks.
const vigra::ImageImportInfo::ICCProfile getICCProfile() const
Definition: stacker.cpp:239
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &gray, const vigra::UInt8 &mask, vigra::VigraTrueType) const
Definition: stacker.cpp:709
vigra::TinyVector< realPixelType, 2 > first_argument_type
Definition: stacker.cpp:691
static struct GeneralParameters Parameters
vigra::ImageImportInfo m_info
Definition: stacker.cpp:353
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:426
void EnforceExtension(std::string &filename, const std::string &defaultExtension)
check if filename contains extension, if not add default extension
Definition: utils.cpp:411
const vigra::ImageImportInfo & getImageImportInfo() const
Definition: stacker.cpp:244
ValueType m_min
Definition: stacker.cpp:415
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:427
void sort(vigra::VigraFalseType)
Definition: stacker.cpp:483
virtual void reset()
Definition: stacker.cpp:424
virtual bool IsValid()
Definition: stacker.cpp:433
VIGRA_UNIQUE_PTR< vigra::Decoder > m_decoder
Definition: stacker.cpp:357
void exportImageAlpha(ImageIterator image_upper_left, ImageIterator image_lower_right, ImageAccessor image_accessor, AlphaIterator alpha_upper_left, AlphaAccessor alpha_accessor, const ImageExportInfo &export_info)
Write the image and its alpha channel to a file.
static char * line
Definition: svm.cpp:2784
bool CheckInput(const std::vector< InputImage * > &images, vigra::Rect2D &outputROI, vigra::Size2D &canvasSize)
Definition: stacker.cpp:599
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
Definition: ROIImage.h:300
std::string toupper(const std::string &s)
Definition: stl_utils.h:59
const int numExtraBands() const
Definition: stacker.cpp:235
void reset()
Definition: stacker.cpp:406
bool IsValid()
Definition: stacker.cpp:415
virtual void getResult(ValueType &val)
Definition: stacker.cpp:444
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &color, const vigra::UInt8 &mask, vigra::VigraFalseType) const
Definition: stacker.cpp:695
static double sigma
int m_offsetX
Definition: stacker.cpp:355
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition: stacker.cpp:425
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)
Definition: openmp_vigra.h:288
unsigned m_offset
Definition: stacker.cpp:356
bool IsFileTypeSupported(const std::string &filename)
return true, if file type by extension is supported by vigra
Definition: utils.cpp:405
unsigned m_y
Definition: stacker.cpp:356
std::string getExtension(const std::string &basename2)
Get extension of a filename.
Definition: utils.cpp:99
vigra::FRGBImage ImageType
const float getYResolution() const
Definition: stacker.cpp:238
const std::string getFilename() const
Definition: stacker.cpp:240
void operator()(const ValueType &val)
Definition: stacker.cpp:409
unsigned m_x
Definition: stacker.cpp:356
virtual void reset()
Definition: stacker.cpp:531
vigra::UInt8 result_type
Definition: stacker.cpp:694
void sort(vigra::VigraTrueType)
Definition: stacker.cpp:478
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:533
vigra::NumericTraits< PixelType >::RealPromote realPixelType
Definition: stacker.cpp:690
void createAlphaTiffImage(ImageIterator upperleft, ImageIterator lowerright, ImageAccessor a, AlphaIterator alphaUpperleft, AlphaAccessor alphaA, vigra::TiffImage *tiff)
Definition: tiffUtils.h:522
const std::string getMaskFilename() const
Definition: stacker.cpp:243
std::string stripExtension(const std::string &basename2)
remove extension of a filename
Definition: utils.cpp:130
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition: stacker.cpp:532
virtual void reset()
Definition: stacker.cpp:442
void getMeanSigma(const std::vector< ValueType > &values, ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:376
const std::string getPixelType() const
Definition: stacker.cpp:231
bool stringToInt(const std::string &s, int &val)
convert string to integer value, returns true, if sucessful
Definition: utils.cpp:264
const vigra::Size2D getCanvasSize() const
Definition: stacker.cpp:242
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma) override
Definition: stacker.cpp:505
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition: utils.h:114
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
virtual bool IsValid()
Definition: stacker.cpp:446
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 readLine(const int y)
Definition: stacker.cpp:245
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
const vigra::Rect2D getROI() const
Definition: stacker.cpp:241
bool main_stacker(std::vector< InputImage * > &images)
Definition: stacker.cpp:894
int m_offsetY
Definition: stacker.cpp:355
unsigned m_width
Definition: stacker.cpp:356
bool m_noData
Definition: stacker.cpp:358
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
std::string maskSuffix
Definition: stacker.cpp:197
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:443
void sort()
Definition: stacker.cpp:489
const float getXResolution() const
Definition: stacker.cpp:237
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:408
void getMean(const std::vector< ValueType > &values, ValueType &val)
Definition: stacker.cpp:362
static T max(T x, T y)
Definition: svm.cpp:65
static void usage()
Definition: Main.cpp:32
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:445
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
void getValue(const int x, ValueType &value, ValueType &mask)
Definition: stacker.cpp:313
std::vector< ValueType > m_values
Definition: stacker.cpp:447
bool StackImagesAndMask(std::vector< InputImage * > &images, Functor &stacker)
Definition: stacker.cpp:730
vigra::Size2D m_canvassize
Definition: stacker.cpp:354
std::vector< ValueType > m_values
Definition: stacker.cpp:592
const std::string getName() const
Definition: stacker.cpp:592
const bool isColor() const
Definition: stacker.cpp:232
const std::string getName() const
Definition: stacker.cpp:475
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition: stacker.cpp:407
void getResult(ValueType &val)
Definition: stacker.cpp:414
static T min(T x, T y)
Definition: svm.cpp:62
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
virtual void getResult(ValueType &val)
Definition: stacker.cpp:432
std::string compression
Definition: stacker.cpp:185
std::string tolower(const std::string &s)
convert a string to lowercase
Definition: stl_utils.h:49
unsigned m_bands
Definition: stacker.cpp:356
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:534
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &pixel, const vigra::UInt8 &mask) const
Definition: stacker.cpp:721
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
std::string m_filename
Definition: stacker.cpp:349
bool m_hasAlpha
Definition: stacker.cpp:358
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
Definition: stacker.cpp:42
int main(int argc, char *argv[])
Definition: Main.cpp:167
std::string stackMode
Definition: stacker.cpp:187