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  SigmaClip();
542  getMean(m_values, val);
543  };
544  virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
545  {
546  SigmaClip();
547  getMeanSigma(m_values, val, sigma);
548  };
549  virtual bool IsValid() { return !m_values.empty(); };
550  const std::string getName() const { return "sigma clipped mean"; };
551 
552 private:
553  void SigmaClip()
554  {
555  size_t iteration = 0;
556  while (iteration < Parameters.maxIterations)
557  {
558  double mean, sigma;
559  getMeanSigma(m_sortValues, 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 
582 bool 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 
612 template <class PixelType, class Functor>
613 bool 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());
629  SetCompression(exportImageInfo, Parameters.compression);
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
646  Functor privateStacker(stacker);
647  privateStacker.reset();
648  for (size_t i = 0; i < images.size(); ++i)
649  {
650  PixelType value;
651  ChannelType maskValue;
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 
669 template <class PixelType>
671 {
672 public:
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 
709 template <class PixelType, class Functor>
710 bool 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());
726  SetCompression(exportImageInfo, Parameters.compression);
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
746  Functor privateStacker(stacker);
747  privateStacker.reset();
748  for (size_t i = 0; i < images.size(); ++i)
749  {
750  PixelType value;
751  ChannelType maskValue;
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");
773  vigra_ext::createTiffDirectory(tiffImage, stacker.getName(), stacker.getName(),
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);
777  TIFFFlush(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());
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);
827  TIFFFlush(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  {
860  TIFFClose(tiffImage);
861  }
862  return true;
863 };
864 
865 void CleanUp(std::vector<InputImage*>& images)
866 {
867  for (auto& img : images)
868  {
869  delete img;
870  };
871 };
872 
873 template <class PixelType>
874 bool 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;
879  MinStacker<PixelType> stacker;
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;
887  MaxStacker<PixelType> stacker;
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  {
898  return StackImagesAndMask<PixelType>(images, stacker);
899  }
900  else
901  {
902  return StackImages<PixelType>(images, stacker);
903  };
904  }
905  else
906  {
907  if (Parameters.stackMode == "median")
908  {
909  MedianStacker<PixelType> stacker;
910  std::cout << "Merging stack with median operator." << std::endl;
911  if (Parameters.maskInput)
912  {
913  return StackImagesAndMask<PixelType>(images, stacker);
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  {
928  return StackImagesAndMask<PixelType>(images, stacker);
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  {
943  return StackImagesAndMask<PixelType>(images, stacker);
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 
971 int main(int argc, char* argv[])
972 {
973  // parse arguments
974  const char* optstring = "o:h";
975 
976  enum
977  {
978  OPT_COMPRESSION = 1000,
979  OPT_STACKMODE,
980  OPT_WINSOR_TRIM,
981  OPT_SIGMA_MAX,
982  OPT_MAX_ITER,
983  OPT_MASK_INPUT,
984  OPT_MASK_SUFFIX,
985  OPT_MASK_SIGMA,
986  OPT_MULTILAYER,
987  OPT_BIGTIFF
988  };
989  static struct option longOptions[] =
990  {
991  { "output", required_argument, NULL, 'o' },
992  { "compression", required_argument, NULL, OPT_COMPRESSION },
993  { "mode", required_argument, NULL, OPT_STACKMODE },
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':
1015  usage(hugin_utils::stripPath(argv[0]).c_str());
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);
1032  if (!hugin_utils::stringToDouble(text, Parameters.winsorTrim))
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  {
1041  if (!hugin_utils::stringToDouble(text, Parameters.winsorTrim))
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);
1057  if (!hugin_utils::stringToDouble(text, Parameters.sigma))
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);
1093  if (!hugin_utils::stringToDouble(text, Parameters.maskSigma))
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
1180  if (hugin_utils::FileExists(currentFile))
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  {
1296  success = main_stacker<vigra::RGBValue<vigra::UInt8>>(images);
1297  }
1298  else if (pixeltype == "UINT16")
1299  {
1300  success = main_stacker<vigra::RGBValue<vigra::UInt16>>(images);
1301  }
1302  else if (pixeltype == "UINT32")
1303  {
1304  success = main_stacker<vigra::RGBValue<vigra::UInt32>>(images);
1305  }
1306  else if (pixeltype == "FLOAT")
1307  {
1308  success = main_stacker<vigra::RGBValue<float>>(images);
1309  }
1310  else if (pixeltype == "DOUBLE")
1311  {
1312  success = main_stacker<vigra::RGBValue<double>>(images);
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  {
1324  success = main_stacker<vigra::UInt8>(images);
1325  }
1326  else if (pixeltype == "UINT16")
1327  {
1328  success = main_stacker<vigra::UInt16>(images);
1329  }
1330  else if (pixeltype == "UINT32")
1331  {
1332  success = main_stacker<vigra::UInt32>(images);
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 }
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:549
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:579
unsigned m_height
Definition: stacker.cpp:356
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &color, const vigra::UInt8 &mask, vigra::VigraFalseType) const
Definition: stacker.cpp:677
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:544
void CleanUp(std::vector< InputImage * > &images)
Definition: stacker.cpp:865
PixelType second_argument_type
Definition: stacker.cpp:674
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:613
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:675
InputImage(const std::string filename)
Definition: stacker.cpp:205
vigra::TinyVector< PixelType, 2 > first_argument_type
Definition: stacker.cpp:673
Some functions to create tiff images with masks.
const vigra::ImageImportInfo::ICCProfile getICCProfile() const
Definition: stacker.cpp:239
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:582
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
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
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &pixel, const vigra::UInt8 &mask) const
Definition: stacker.cpp:701
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:676
void sort(vigra::VigraTrueType)
Definition: stacker.cpp:478
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:533
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:874
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:710
vigra::Size2D m_canvassize
Definition: stacker.cpp:354
std::vector< ValueType > m_values
Definition: stacker.cpp:577
const std::string getName() const
Definition: stacker.cpp:550
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
void SigmaClip()
Definition: stacker.cpp:553
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
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &gray, const vigra::UInt8 &mask, vigra::VigraTrueType) const
Definition: stacker.cpp:689
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
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