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 <getopt.h>
30 #include <hugin_utils/utils.h>
31 #include <hugin_utils/stl_utils.h>
32 #include <vigra/imageinfo.hxx>
33 #include <vigra/codec.hxx>
34 #include <vigra/stdimage.hxx>
35 #include <vigra_ext/impexalpha.hxx>
36 #include <vigra/tiff.hxx>
37 #include <vigra_ext/tiffUtils.h>
38 #include <vigra_ext/openmp_vigra.h>
39 
41 void SetCompression(vigra::ImageExportInfo& output, const std::string& compression)
42 {
43  const std::string ext(hugin_utils::toupper(hugin_utils::getExtension(output.getFileName())));
44  if (!compression.empty())
45  {
46  if (ext == "JPEG" || ext == "JPG")
47  {
48  output.setCompression(std::string("JPEG QUALITY=" + compression).c_str());
49  }
50  else
51  {
52  output.setCompression(compression.c_str());
53  };
54  };
55 };
56 
58 template <class ImageType, class MaskType>
59 bool SaveImage(ImageType& image, MaskType& mask, vigra::ImageExportInfo& exportImageInfo, std::string filetype, std::string pixelType)
60 {
61  typedef typename vigra::NumericTraits<typename ImageType::value_type>::isScalar scalar;
62  const int numberChannels = scalar().asBool ? 1 : 3;
63  exportImageInfo.setPixelType(pixelType.c_str());
64  if (vigra::isBandNumberSupported(filetype, numberChannels +1))
65  {
66  try
67  {
68  vigra::exportImageAlpha(vigra::srcImageRange(image), vigra::srcImage(mask), exportImageInfo);
69  }
70  catch (std::exception& e)
71  {
72  std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
73  << "Cause: " << e.what() << std::endl;
74  return false;
75  }
76  return true;
77  }
78  else
79  {
80  if (vigra::isBandNumberSupported(filetype, numberChannels))
81  {
82  std::cout << "Warning: Filetype " << filetype << " does not support alpha channels." << std::endl
83  << "Saving image without alpha channel." << std::endl;
84  try
85  {
86  vigra::exportImage(vigra::srcImageRange(image), exportImageInfo);
87  }
88  catch (std::exception& e)
89  {
90  std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
91  << "Cause: " << e.what() << std::endl;
92  return false;
93  };
94  return true;
95  }
96  else
97  {
98  std::cerr << "ERROR: Output filetype " << filetype << " does not support " << numberChannels << " channels." << std::endl
99  << "Can't save image." << std::endl;
100  };
101  };
102  return false;
103 };
104 
106 template <class ImageType, class MaskType>
107 bool SaveFinalImage(ImageType& image, MaskType& mask, const std::string& inputPixelType, vigra::ImageExportInfo& output)
108 {
109  VIGRA_UNIQUE_PTR<vigra::Encoder> encoder(vigra::encoder(output));
110  if (vigra::isPixelTypeSupported(encoder->getFileType(), inputPixelType))
111  {
112  return SaveImage(image, mask, output, encoder->getFileType(), inputPixelType);
113  }
114  else
115  {
116  if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT8"))
117  {
118  // transform to UINT8
119  output.setForcedRangeMapping(0, vigra::NumericTraits<typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::max(), 0, 255);
120  return SaveImage(image, mask, output, encoder->getFileType(), "UINT8");
121  }
122  else
123  {
124  std::cerr << "ERROR: Output file type " << encoder->getFileType() << " does not support" << std::endl
125  << "requested pixeltype " << inputPixelType << "." << std::endl
126  << "Save output in other file format." << std::endl;
127  };
128  };
129  return false;
130 };
131 
133 static void usage(const char* name)
134 {
135  std::cout << name << ": stack images" << std::endl
136  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
137  << std::endl
138  << "Usage: " << name << " [options] images" << std::endl
139  << std::endl
140  << " --output=FILE Set the filename for the output file." << std::endl
141  << " --compression=value Compression of the output files" << std::endl
142  << " For jpeg output: 0-100" << std::endl
143  << " For tiff output: PACKBITS, DEFLATE, LZW" << std::endl
144  << " --mode=STRING Select the mode for stacking" << std::endl
145  << " Possible names are described below." << std::endl
146  << std::endl
147  << " min|minimum|darkest Select the darkest pixel" << std::endl
148  << " max|maximum|brightest Select the brightest pixel" << std::endl
149  << " avg|average|mean Calculate the mean for each position" << std::endl
150  << " median Calculate the median for each position" << std::endl
151  << " winsor Calculate the Winsor trimmed mean" << std::endl
152  << " for each position. The parameter can be" << std::endl
153  << " set with --winsor-trim=NUMBER (default: 0.2)" << std::endl
154  << " sigma Calculate the sigma clipped mean for" << std::endl
155  << " each position. Fine-tune with" << std::endl
156  << " --max-sigma=NUMBER (default: 2) and" << std::endl
157  << " --max-iterations=NUMBER (default: 5)" << std::endl
158  << std::endl
159  << " --mask-input Mask input images" << std::endl
160  << " Only pixel which differ more than" << std::endl
161  << " mask-sigma * standard deviation" << std::endl
162  << " are visible in output" << std::endl
163  << " available for modes median, winsor or sigma" << std::endl
164  << " --mask-suffix=STRING Suffix for the masked input images" << std::endl
165  << " (default: _mask)" << std::endl
166  << " --mask-sigma=NUMBER Sigma parameter for input images masking" << std::endl
167  << " (default: 2)" << std::endl
168  << " --multi-layer-output Output layered TIFF instead of single images" << std::endl
169  << " (has only effect with --mask-input)" << std::endl
170  << " --bigtiff Write output in BigTIFF format" << std::endl
171  << " (only with TIFF output)" << std::endl
172  << " -h, --help Shows this help" << std::endl
173  << std::endl;
174 };
175 
176 static struct GeneralParameters
177 {
178 public:
179  // for output file
180  std::string outputFilename;
181  std::string compression;
182  // stacking mode
183  std::string stackMode;
184  // for sigma clipping
185  double sigma = 2;
186  int maxIterations = 5;
187  // for Winsor trimmed mean
188  double winsorTrim = 0.2;
189  // for masking input images
190  double maskSigma = 2;
191  // should masked input images saved
192  bool maskInput = false;
193  std::string maskSuffix = "_mask";
194  bool multiLayer = false;
195  bool useBigTIFF = false;
196 } Parameters;
197 
199 {
200 public:
201  explicit InputImage(const std::string filename) : m_info(filename.c_str())
202  {
203  m_filename = filename;
204  m_canvassize=m_info.getCanvasSize();
205  if (m_canvassize.area() == 0)
206  {
207  // not all images contains the canvas size/full image size
208  // in this case take also the position into account to get full image size
209  m_canvassize =vigra::Size2D(m_info.width(), m_info.height());
210  };
211  m_offsetX = m_info.getPosition().x;
212  m_offsetY = m_info.getPosition().y;
213  m_decoder=vigra::decoder(m_info);
214  m_hasAlpha = m_info.numExtraBands() == 1;
215  m_width=m_decoder->getWidth();
216  m_height=m_decoder->getHeight();
217  m_bands=m_decoder->getNumBands();
218  m_offset=m_decoder->getOffset();
219  m_x = 0;
220  m_y = 0;
221  };
223  {
224  m_decoder->abort();
225  };
226  const std::string getPixelType() const { return m_info.getPixelType(); };
227  const bool isColor() const { return m_info.isColor(); };
228  const bool isGrayscale() const { return m_info.isGrayscale(); };
229  const int numBands() const { return m_bands; };
230  const int numExtraBands() const { return m_info.numExtraBands(); }
231  const int numPixelSamples() const { return m_bands - m_info.numExtraBands(); };
232  const float getXResolution() const { return m_info.getXResolution(); };
233  const float getYResolution() const { return m_info.getYResolution(); };
234  const vigra::ImageImportInfo::ICCProfile getICCProfile() const { return m_info.getICCProfile(); };
235  const std::string getFilename() const { return m_filename; };
236  const vigra::Rect2D getROI() const { return vigra::Rect2D(vigra::Point2D(m_offsetX,m_offsetY), vigra::Size2D(m_width, m_height)); };
237  const vigra::Size2D getCanvasSize() const { return m_canvassize; };
238  const std::string getMaskFilename() const { return hugin_utils::stripExtension(m_filename) + Parameters.maskSuffix + ".tif"; };
239  const vigra::ImageImportInfo& getImageImportInfo() const { return m_info; };
240  void readLine(const int y)
241  {
242  if (y < m_offsetY + static_cast<int>(m_height))
243  {
244  if (y < m_offsetY)
245  {
246  m_noData = true;
247  }
248  else
249  {
250  m_noData = false;
251  m_decoder->nextScanline();
252  ++m_y;
253  while (y+1 < static_cast<int>(m_y) + m_offsetY)
254  {
255  m_decoder->nextScanline();
256  ++m_y;
257  }
258  }
259  }
260  else
261  {
262  m_noData = true;
263  };
264  };
265  template<class ValueType>
266  void getValue(const int x, vigra::RGBValue<ValueType>& value, ValueType& mask)
267  {
268  if (m_noData)
269  {
270  mask = vigra::NumericTraits<ValueType>::zero();
271  }
272  else
273  {
274  if (x < m_offsetX)
275  {
276  mask = vigra::NumericTraits<ValueType>::zero();
277  }
278  else
279  {
280  if (x < m_offsetX + static_cast<int>(m_width))
281  {
282  const ValueType* band0= static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(0));
283  const ValueType* band1 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(1));
284  const ValueType* band2 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(2));
285  band0 += m_offset*(x - m_offsetX);
286  band1 += m_offset*(x - m_offsetX);
287  band2 += m_offset*(x - m_offsetX);
288  if (m_bands == 4)
289  {
290  const ValueType* band3 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(3));
291  band3 += m_offset*(x - m_offsetX);
292  mask = *band3;
293  }
294  else
295  {
297  };
298  value = vigra::RGBValue<ValueType>(*band0, *band1, *band2);
299  }
300  else
301  {
302  mask = vigra::NumericTraits<ValueType>::zero();
303  };
304  };
305  };
306  };
307  template<class ValueType>
308  void getValue(const int x, ValueType& value, ValueType& mask)
309  {
310  if (m_noData)
311  {
312  mask = vigra::NumericTraits<ValueType>::zero();
313  }
314  else
315  {
316  if (x < m_offsetX)
317  {
318  mask = vigra::NumericTraits<ValueType>::zero();
319  }
320  else
321  {
322  if (x < m_offsetX + static_cast<int>(m_width))
323  {
324  const ValueType* band0 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(0));
325  band0 += m_offset*(x - m_offsetX);
326  if (m_bands == 2)
327  {
328  const ValueType* band1 = static_cast<const ValueType*>(m_decoder->currentScanlineOfBand(1));
329  band1 += m_offset*(x - m_offsetX);
330  mask = *band1;
331  }
332  else
333  {
335  };
336  value = *band0;
337  }
338  else
339  {
340  mask = vigra::NumericTraits<ValueType>::zero();
341  };
342  };
343  };
344  };
345 
346 private:
347  std::string m_filename;
348  vigra::ImageImportInfo m_info;
349  vigra::Size2D m_canvassize;
352  VIGRA_UNIQUE_PTR<vigra::Decoder> m_decoder;
354 };
355 
356 template<class ValueType>
357 void getMean(const std::vector<ValueType>& values, ValueType& val)
358 {
359  size_t n = 0;
360  typedef vigra::NumericTraits<ValueType> RealTraits;
361  typename RealTraits::RealPromote mean = RealTraits::zero();
362  for (auto& x : values)
363  {
364  ++n;
365  mean += (x - mean) / n;
366  };
367  val = RealTraits::fromRealPromote(mean);
368 };
369 
370 template<class ValueType>
371 void getMeanSigma(const std::vector<ValueType>& values, ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
372 {
373  typedef vigra::NumericTraits<ValueType> RealTraits;
374  typedef typename RealTraits::RealPromote RealType;
375  size_t n = 0;
376  RealType mean = RealTraits::zero();
377  RealType m2 = RealTraits::zero();
378  for (auto& x : values)
379  {
380  ++n;
381  const RealType delta = x - mean;
382  mean += delta / n;
383  const RealType delta2 = x - mean;
384  m2 += delta*delta2;
385  };
386  val = RealTraits::fromRealPromote(mean);
387  if (n >= 2)
388  {
389  sigma = sqrt(m2 / n);
390  }
391  else
392  {
393  sigma = vigra::NumericTraits<RealType>::zero();
394  };
395 };
396 
397 template<class ValueType>
399 {
400 public:
402  void operator()(const ValueType& val, vigra::VigraTrueType) { if (val < m_min) m_min = val; };
403  void operator()(const ValueType& val, vigra::VigraFalseType) { if (val.luminance() < m_min.luminance()) m_min = val; }
404  void operator()(const ValueType& val)
405  {
406  typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
407  operator()(val, is_scalar());
408  };
409  void getResult(ValueType& val) { val = m_min; };
411 private:
412  ValueType m_min;
413 };
414 
415 template<class ValueType>
417 {
418 public:
420  void operator()(const ValueType& val, vigra::VigraTrueType) { if (val > m_max) m_max = val; };
421  void operator()(const ValueType& val, vigra::VigraFalseType) { if (val.luminance() > m_max.luminance()) m_max = val;}
422  virtual void operator()(const ValueType& val)
423  {
424  typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
425  operator()(val, is_scalar());
426  };
427  virtual void getResult(ValueType& val) { val = m_max; };
428  virtual bool IsValid() { return m_max != vigra::NumericTraits<ValueType>::min(); };
429 private:
430  ValueType m_max;
431 };
432 
433 template<class ValueType>
435 {
436 public:
437  virtual void reset() { m_sum = vigra::NumericTraits<ValueType>::zero(); m_count = 0; };
438  virtual void operator()(const ValueType& val) { m_sum += val; ++m_count;};
439  virtual void getResult(ValueType& val) { val = m_sum / m_count; };
440  virtual bool IsValid() { return m_count > 0; };
441 
442 private:
443  typename vigra::NumericTraits<ValueType>::RealPromote m_sum;
444  size_t m_count;
445 };
446 
447 template<class ValueType>
449 {
450 public:
451  void reset() { m_values.clear(); };
452  void operator()(const ValueType& val) { m_values.push_back(val); };
453  void getResult(ValueType& val)
454  {
455  sort();
456  if (m_values.size() % 2 == 1)
457  {
458  val = m_values[(m_values.size() - 1) / 2];
459  }
460  else
461  {
462  const int index = m_values.size() / 2;
463  val = 0.5 * (m_values[index - 1] + m_values[index]);
464  };
465  };
466  bool IsValid() { return !m_values.empty();};
467  void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
468  {
469  sort();
470  if (m_values.size() % 2 == 1)
471  {
472  val = m_values[(m_values.size() - 1) / 2];
473  }
474  else
475  {
476  const int index = m_values.size() / 2;
477  val = 0.5 * (m_values[index - 1] + m_values[index]);
478  };
479  ValueType mean;
480  getMeanSigma(m_values, mean, sigma);
481  };
482  const std::string getName() const { return "median"; };
483 protected:
484  // sort gray scale
485  void sort(vigra::VigraTrueType)
486  {
487  std::sort(m_values.begin(), m_values.end());
488  };
489  // sort color values
490  void sort(vigra::VigraFalseType)
491  {
492  std::sort(m_values.begin(), m_values.end(),
493  [](const ValueType & a, const ValueType & b) {return a.luminance() < b.luminance(); });
494  };
495  // generic sort
496  void sort()
497  {
498  typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
499  sort(is_scalar());
500  };
501 
502  std::vector<ValueType> m_values;
503 };
504 
505 template<class ValueType>
506 class WinsorMeanStacker : public MedianStacker<ValueType>
507 {
508 public:
509  virtual void getResult(ValueType& val)
510  {
511  this->sort();
512  const int indexTrim = hugin_utils::floori(Parameters.winsorTrim * this->m_values.size());
513  for (size_t i = 0; i < indexTrim; ++i)
514  {
515  this->m_values[i] = this->m_values[indexTrim];
516  }
517  for (size_t i = this->m_values.size() - indexTrim; i < this->m_values.size(); ++i)
518  {
519  this->m_values[i] = this->m_values[this->m_values.size() - indexTrim - 1];
520  };
521  getMean(this->m_values, val);
522  };
523  virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
524  {
525  this->sort();
526  const int indexTrim = hugin_utils::floori(Parameters.winsorTrim * this->m_values.size());
527  for (size_t i = 0; i < indexTrim; ++i)
528  {
529  this->m_values[i] = this->m_values[indexTrim];
530  }
531  for (size_t i = this->m_values.size() - indexTrim; i < this->m_values.size(); ++i)
532  {
533  this->m_values[i] = this->m_values[this->m_values.size() - indexTrim - 1];
534  };
535  getMeanSigma(this->m_values, val, sigma);
536  };
537  const std::string getName() const { return "Winsor clipped mean"; };
538 };
539 
540 template<class ValueType>
542 {
543 public:
544  virtual void reset() { m_values.clear(); m_sortValues.clear(); };
545  void operator()(const ValueType& val, vigra::VigraTrueType) { m_values.push_back(val); m_sortValues.push_back(val); };
546  void operator()(const ValueType& val, vigra::VigraFalseType) { m_values.push_back(val); m_sortValues.push_back(val.luminance()); };
547  virtual void operator()(const ValueType& val)
548  {
549  typedef typename vigra::NumericTraits<ValueType>::isScalar is_scalar;
550  operator()(val, is_scalar());
551  };
552  virtual void getResult(ValueType& val)
553  {
554  size_t iteration = 0;
555  while (iteration < Parameters.maxIterations)
556  {
557  double mean, sigma;
558  getMeanSigma(m_sortValues, mean, sigma);
559  const size_t oldSize = m_sortValues.size();
560  for (int i = m_sortValues.size() - 1; i >= 0 && m_sortValues.size() > 1; --i)
561  {
562  // check if values are in range
563  if (abs(m_sortValues[i] - mean) > Parameters.sigma * sigma)
564  {
565  m_sortValues.erase(m_sortValues.begin() + i);
566  m_values.erase(m_values.begin() + i);
567  };
568  };
569  if (m_sortValues.size() == oldSize)
570  {
571  // no values outside range, return mean value
572  getMean(m_values, val);
573  return;
574  };
575  ++iteration;
576  };
577  getMean(m_values, val);
578  };
579  virtual void getResultAndSigma(ValueType& val, typename vigra::NumericTraits<ValueType>::RealPromote& sigma)
580  {
581  size_t iteration = 0;
582  while (iteration < Parameters.maxIterations)
583  {
584  double grayMean, graySigma;
585  getMeanSigma(m_sortValues, grayMean, graySigma);
586  const size_t oldSize = m_sortValues.size();
587  for (int i = m_sortValues.size() - 1; i >= 0 && m_sortValues.size() > 1; --i)
588  {
589  // check if values are in range
590  if (abs(m_sortValues[i] - grayMean) > Parameters.sigma * graySigma)
591  {
592  m_sortValues.erase(m_sortValues.begin() + i);
593  m_values.erase(m_values.begin() + i);
594  };
595  };
596  if (m_sortValues.size() == oldSize)
597  {
598  // no values outside range, return mean value
599  getMeanSigma(m_values, val, sigma);
600  return;
601  };
602  ++iteration;
603  };
604  getMeanSigma(m_values, val, sigma);
605  };
606  virtual bool IsValid() { return !m_values.empty(); };
607  const std::string getName() const { return "sigma clipped mean"; };
608 
609 private:
610  std::vector<ValueType> m_values;
611  std::vector<double> m_sortValues;
612 };
613 
614 bool CheckInput(const std::vector<InputImage*>& images, vigra::Rect2D& outputROI, vigra::Size2D& canvasSize)
615 {
616  if (images.empty())
617  {
618  return false;
619  };
620  // get ROI and canvas size which contains all images
621  outputROI = images[0]->getROI();
622  canvasSize = images[0]->getCanvasSize();
623  for (size_t i = 1; i < images.size(); ++i)
624  {
625  outputROI |= images[i]->getROI();
626  if (images[i]->getCanvasSize().width() > canvasSize.width())
627  {
628  canvasSize.setWidth(images[i]->getCanvasSize().width());
629  };
630  if (images[i]->getCanvasSize().height() > canvasSize.height())
631  {
632  canvasSize.setHeight(images[i]->getCanvasSize().height());
633  };
634  };
635  if (outputROI.area() == 0)
636  {
637  std::cerr << "ERROR: You can't stack non-overlapping images." << std::endl;
638  return false;
639  };
640  return true;
641 }
642 
644 template <class PixelType, class Functor>
645 bool StackImages(std::vector<InputImage*>& images, Functor& stacker)
646 {
647  typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
648  vigra::Rect2D outputROI;
649  vigra::Size2D canvasSize;
650  if (!CheckInput(images, outputROI, canvasSize))
651  {
652  return false;
653  }
654  // prepare output
655  vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
656  exportImageInfo.setXResolution(images[0]->getXResolution());
657  exportImageInfo.setYResolution(images[0]->getYResolution());
658  exportImageInfo.setPosition(outputROI.upperLeft());
659  exportImageInfo.setCanvasSize(canvasSize);
660  exportImageInfo.setICCProfile(images[0]->getICCProfile());
661  SetCompression(exportImageInfo, Parameters.compression);
662  vigra::BasicImage<PixelType> output(outputROI.size());
663  vigra::BImage mask(output.size(),vigra::UInt8(0));
664  // loop over all lines
665  for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
666  {
667  // load next line
668 #pragma omp parallel for
669  for (int i = 0; i < images.size(); ++i)
670  {
671  images[i]->readLine(y);
672  };
673  // process current line
674 #pragma omp parallel for schedule(static, 100)
675  for (int x = outputROI.left(); x < outputROI.right(); ++x)
676  {
677  // we need a private copy for each thread
678  Functor privateStacker(stacker);
679  privateStacker.reset();
680  for (size_t i = 0; i < images.size(); ++i)
681  {
682  PixelType value;
683  ChannelType maskValue;
684  images[i]->getValue(x, value, maskValue);
685  if (maskValue > 0)
686  {
687  privateStacker(value);
688  }
689  };
690  if (privateStacker.IsValid())
691  {
692  privateStacker.getResult(output(x - outputROI.left(), y - outputROI.top()));
693  mask(x-outputROI.left(), y-outputROI.top()) = 255;
694  };
695  };
696  };
697  std::cout << "Write result to " << Parameters.outputFilename << std::endl;
698  return SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo);
699 };
700 
701 template <class PixelType>
703 {
704 public:
705  typedef typename vigra::NumericTraits<PixelType>::RealPromote realPixelType;
706  typedef vigra::TinyVector<realPixelType, 2> first_argument_type;
707  typedef PixelType second_argument_type;
708  typedef vigra::UInt8 third_argument_type;
709  typedef vigra::UInt8 result_type;
710  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& color, const vigra::UInt8& mask, vigra::VigraFalseType) const
711  {
712  if (mask > 0 && (color.red() < limits[0].red() || color.red()>limits[1].red() ||
713  color.green() < limits[0].green() || color.green() > limits[1].green() ||
714  color.blue() < limits[0].blue() || color.blue() > limits[1].blue()))
715  {
716  return 255;
717  }
718  else
719  {
720  return 0;
721  };
722  };
723 
724  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& gray, const vigra::UInt8& mask, vigra::VigraTrueType) const
725  {
726  if (mask > 0 && (gray < limits[0] || gray>limits[1]))
727  {
728  return 255;
729  }
730  else
731  {
732  return 0;
733  };
734  };
735 
736  vigra::UInt8 operator()(const vigra::TinyVector<realPixelType, 2>& limits, const PixelType& pixel, const vigra::UInt8& mask) const
737  {
738  typedef typename vigra::NumericTraits<PixelType>::isScalar is_scalar;
739  return (*this)(limits, pixel, mask, is_scalar());
740  }
741 
742 };
743 
744 template <class PixelType, class Functor>
745 bool StackImagesAndMask(std::vector<InputImage*>& images, Functor& stacker)
746 {
747  typedef typename vigra::NumericTraits<PixelType>::ValueType ChannelType;
748  vigra::Rect2D outputROI;
749  vigra::Size2D canvasSize;
750  if (!CheckInput(images, outputROI, canvasSize))
751  {
752  return false;
753  }
754  // prepare output
755  vigra::ImageExportInfo exportImageInfo(Parameters.outputFilename.c_str(), Parameters.useBigTIFF ? "w8" : "w");
756  exportImageInfo.setXResolution(images[0]->getXResolution());
757  exportImageInfo.setYResolution(images[0]->getYResolution());
758  exportImageInfo.setPosition(outputROI.upperLeft());
759  exportImageInfo.setCanvasSize(canvasSize);
760  exportImageInfo.setICCProfile(images[0]->getICCProfile());
761  SetCompression(exportImageInfo, Parameters.compression);
762  // for multi-layer output
763  vigra::TiffImage* tiffImage;
764  vigra::BasicImage<PixelType> output(outputROI.size());
765  vigra::BImage mask(output.size(), vigra::UInt8(0));
766  vigra::BasicImage<vigra::TinyVector<typename vigra::NumericTraits<PixelType>::RealPromote, 2>> limits(output.size());
767  // loop over all lines
768  for (size_t y = outputROI.top(); y < outputROI.bottom(); ++y)
769  {
770  // load next line
771 #pragma omp parallel for
772  for (int i = 0; i < images.size(); ++i)
773  {
774  images[i]->readLine(y);
775  };
776  // process current line
777 #pragma omp parallel for schedule(static, 100)
778  for (int x = outputROI.left(); x < outputROI.right(); ++x)
779  {
780  // we need a private copy for each thread
781  Functor privateStacker(stacker);
782  privateStacker.reset();
783  for (size_t i = 0; i < images.size(); ++i)
784  {
785  PixelType value;
786  ChannelType maskValue;
787  images[i]->getValue(x, value, maskValue);
788  if (maskValue > 0)
789  {
790  privateStacker(value);
791  }
792  };
793  if (privateStacker.IsValid())
794  {
795  PixelType mean;
796  typename vigra::NumericTraits<PixelType>::RealPromote sigma;
797  privateStacker.getResultAndSigma(mean, sigma);
798  output(x - outputROI.left(), y - outputROI.top()) = mean;
799  mask(x - outputROI.left(), y - outputROI.top()) = 255;
800  limits(x - outputROI.left(), y - outputROI.top()) = vigra::TinyVector<PixelType, 2>(mean - Parameters.maskSigma*sigma, mean + Parameters.maskSigma*sigma);
801  };
802  };
803  };
804  std::cout << "Write result to " << Parameters.outputFilename << std::endl;
805  if (Parameters.multiLayer)
806  {
807  tiffImage = TIFFOpen(Parameters.outputFilename.c_str(), "w");
808  vigra_ext::createTiffDirectory(tiffImage, stacker.getName(), stacker.getName(),
809  Parameters.compression.empty() ? "LZW" : Parameters.compression, 0, images.size() + 1,
810  outputROI.upperLeft(), canvasSize, images[0]->getICCProfile());
811  vigra_ext::createAlphaTiffImage(vigra::srcImageRange(output), vigra::maskImage(mask), tiffImage);
812  TIFFFlush(tiffImage);
813  }
814  else
815  {
816  if (!SaveFinalImage(output, mask, images[0]->getPixelType(), exportImageInfo))
817  {
818  return false;
819  };
820  };
821  // we don't need median image any more
822  output.resize(0, 0);
823  std::cout << "Masking input images with sigma=" << Parameters.maskSigma;
824  if (Parameters.multiLayer)
825  {
826  std::cout << std::endl;
827  }
828  else
829  {
830  std::cout << " and suffix " << Parameters.maskSuffix << ".tif" << std::endl;
831  };
832  for (size_t i = 0; i < images.size(); ++i)
833  {
834  std::cout << "Masking " << images[i]->getFilename();
835  if (Parameters.multiLayer)
836  {
837  std::cout << std::endl;
838  }
839  else
840  {
841  std::cout << " -> " << images[i]->getMaskFilename() << std::endl;
842  };
843  vigra::BasicImage<PixelType> image(images[i]->getROI().size());
844  vigra::BImage mask(image.size(), 255);
845  if (images[i]->numExtraBands() == 1)
846  {
847  vigra::importImageAlpha(images[i]->getImageImportInfo(), vigra::destImage(image), vigra::destImage(mask));
848  }
849  else
850  {
851  vigra::importImage(images[i]->getImageImportInfo(), vigra::destImage(image));
852  };
853  vigra::Rect2D roi = images[i]->getROI();
854  roi.moveBy(-outputROI.upperLeft());
856  if (hugin_utils::FileExists(images[i]->getMaskFilename()))
857  {
858  std::cout << "Masked file \"" << images[i]->getMaskFilename() << "\" already exists." << std::endl
859  << "Processing aborted." << std::endl;
860  return false;
861  }
862  if (Parameters.multiLayer)
863  {
864  vigra_ext::createTiffDirectory(tiffImage, images[i]->getFilename(), images[i]->getFilename(),
865  Parameters.compression.empty() ? "LZW" : Parameters.compression, i+1, images.size() + 1,
866  images[i]->getROI().upperLeft(), images[i]->getCanvasSize(), images[i]->getICCProfile());
867  vigra_ext::createAlphaTiffImage(vigra::srcImageRange(image), vigra::maskImage(mask), tiffImage);
868  TIFFFlush(tiffImage);
869  }
870  else
871  {
872  vigra::ImageExportInfo exportMaskImage(images[i]->getMaskFilename().c_str(), Parameters.useBigTIFF ? "w8" : "w");
873  exportMaskImage.setXResolution(images[i]->getXResolution());
874  exportMaskImage.setYResolution(images[i]->getYResolution());
875  exportMaskImage.setPosition(images[i]->getROI().upperLeft());
876  exportMaskImage.setCanvasSize(images[i]->getCanvasSize());
877  exportMaskImage.setICCProfile(images[i]->getICCProfile());
878  exportMaskImage.setPixelType(images[i]->getPixelType().c_str());
879  exportMaskImage.setCompression("LZW");
880  try
881  {
882  vigra::exportImageAlpha(vigra::srcImageRange(image), vigra::srcImage(mask), exportMaskImage);
883  }
884  catch (std::exception& e)
885  {
886  std::cerr << "Could not save masked images \"" << exportMaskImage.getFileName() << "\"." << std::endl
887  << "Error code: " << e.what() << std::endl
888  << "Processing aborted." << std::endl;
889  return false;
890  }
891  };
892  };
893  if (Parameters.multiLayer)
894  {
895  TIFFClose(tiffImage);
896  }
897  return true;
898 };
899 
900 void CleanUp(std::vector<InputImage*>& images)
901 {
902  for (auto& img : images)
903  {
904  delete img;
905  };
906 };
907 
908 template <class PixelType>
909 bool main_stacker(std::vector<InputImage*>& images)
910 {
911  if (Parameters.stackMode == "min" || Parameters.stackMode == "minimum" || Parameters.stackMode == "darkest")
912  {
913  std::cout << "Merging stack with minimum operator." << std::endl;
914  MinStacker<PixelType> stacker;
915  return StackImages<PixelType>(images, stacker);
916  }
917  else
918  {
919  if (Parameters.stackMode == "max" || Parameters.stackMode == "maximum" || Parameters.stackMode == "brightest")
920  {
921  std::cout << "Merging stack with maximum operator." << std::endl;
922  MaxStacker<PixelType> stacker;
923  return StackImages<PixelType>(images, stacker);
924  }
925  else
926  {
927  if (Parameters.stackMode == "avg" || Parameters.stackMode == "average" || Parameters.stackMode == "mean")
928  {
929  std::cout << "Merging stack with average operator." << std::endl;
931  return StackImages<PixelType>(images, stacker);
932  }
933  else
934  {
935  if (Parameters.stackMode == "median")
936  {
937  MedianStacker<PixelType> stacker;
938  std::cout << "Merging stack with median operator." << std::endl;
939  if (Parameters.maskInput)
940  {
941  return StackImagesAndMask<PixelType>(images, stacker);
942  }
943  else
944  {
945  return StackImages<PixelType>(images, stacker);
946  };
947  }
948  else
949  {
950  if (Parameters.stackMode == "winsor")
951  {
953  std::cout << "Merging stack with Winsor clipping operator (trim=" << Parameters.winsorTrim << ")." << std::endl;
954  if (Parameters.maskInput)
955  {
956  return StackImagesAndMask<PixelType>(images, stacker);
957  }
958  else
959  {
960  return StackImages<PixelType>(images, stacker);
961  };
962  }
963  else
964  {
965  if (Parameters.stackMode == "sigma")
966  {
968  std::cout << "Merging stack with sigma clipping operator (max sigma=" << Parameters.sigma << ", max " << Parameters.maxIterations << " iterations)." << std::endl;
969  if (Parameters.maskInput)
970  {
971  return StackImagesAndMask<PixelType>(images, stacker);
972  }
973  else
974  {
975  return StackImages<PixelType>(images, stacker);
976  };
977  }
978  else
979  {
980  std::cerr << "ERROR: " << "\"" << Parameters.stackMode << "\" is not a valid stack mode." << std::endl
981  << " Allowed values are min|max|average|median|winsor|sigma" << std::endl;
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  for (size_t i = 0; i < nFiles; i++)
1152  {
1153  std::string currentFile(argv[optind + i]);
1154  // check file existence
1155  if (hugin_utils::FileExists(currentFile) && vigra::isImage(currentFile.c_str()))
1156  {
1157  files.push_back(currentFile);
1158  };
1159  }
1160 
1161  if (files.empty())
1162  {
1163  std::cerr << "ERROR: " << hugin_utils::stripPath(argv[0]) << " needs at least one image." << std::endl;
1164  return 1;
1165  };
1166 
1167  if (Parameters.outputFilename.empty())
1168  {
1169  Parameters.outputFilename = "final.tif";
1170  };
1171  // if no extension is given assume TIF format
1172  hugin_utils::EnforceExtension(Parameters.outputFilename, "tif");
1173  if (!hugin_utils::IsFileTypeSupported(Parameters.outputFilename))
1174  {
1175  std::cerr << "ERROR: Extension \"" << hugin_utils::getExtension(Parameters.outputFilename) << "\" is unknown." << std::endl;
1176  return 1;
1177  };
1178  const std::string extension = hugin_utils::tolower(hugin_utils::getExtension(Parameters.outputFilename));
1179  if (Parameters.multiLayer && extension != "tif" && extension != "tiff")
1180  {
1181  std::cerr << "ERROR: Multi layer output expects a tiff file as output." << std::endl
1182  << " Other image formates are not compatible with this option." << std::endl;
1183  return 1;
1184  };
1185  bool success = false;
1186  std::vector<InputImage*> images;
1187  for (size_t i = 0; i < files.size(); ++i)
1188  {
1189  images.push_back(new InputImage(files[i]));
1190  };
1191  if (!images[0]->isColor() && !images[0]->isGrayscale())
1192  {
1193  std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1194  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numPixelSamples() << " channels per pixel." << std::endl;
1195  CleanUp(images);
1196  return 1;
1197  }
1198  if (images[0]->numExtraBands() > 1)
1199  {
1200  std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1201  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numExtraBands() << " extra channels." << std::endl;
1202  CleanUp(images);
1203  return 1;
1204  }
1205  const std::string pixeltype(images[0]->getPixelType());
1206  //check, that image information matches
1207  for (size_t i = 1; i < files.size(); ++i)
1208  {
1209  if (!images[i]->isColor() && !images[i]->isGrayscale())
1210  {
1211  std::cerr << "ERROR: Only RGB and grayscale images are supported." << std::endl
1212  << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numPixelSamples() << " channels per pixel." << std::endl;
1213  CleanUp(images);
1214  return 1;
1215  };
1216  if (images[i]->numExtraBands() > 1)
1217  {
1218  std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl
1219  << " Image \"" << images[i]->getFilename() << "\" has " << images[i]->numExtraBands() << " extra channels." << std::endl;
1220  CleanUp(images);
1221  return 1;
1222  };
1223  if (images[0]->isColor() != images[i]->isColor())
1224  {
1225  std::cerr << "ERROR: You can't merge color and grayscale images." << std::endl;
1226  CleanUp(images);
1227  return 1;
1228  };
1229  if (images[0]->numPixelSamples() != images[i]->numPixelSamples())
1230  {
1231  std::cerr << "ERROR: You can't merge image with different number of channels." << std::endl
1232  << " Image \"" << images[0]->getFilename() << "\" has " << images[0]->numBands() << " channels," << std::endl
1233  << " but image \"" << images[i]->getFilename() << "\" has " << images[i]->numBands() << " channels." << std::endl;
1234  CleanUp(images);
1235  return 1;
1236  };
1237  if (pixeltype!=images[i]->getPixelType())
1238  {
1239  std::cerr << "ERROR: You can't merge images with different pixel types." << std::endl
1240  << " Image \"" << images[0]->getFilename() << "\" has pixel type " << images[0]->getPixelType() << "," << std::endl
1241  << " but image \"" << images[i]->getFilename() << "\" has pixel type " << images[i]->getPixelType() << "." << std::endl;
1242  CleanUp(images);
1243  return 1;
1244  };
1245  };
1246 
1247  if (images[0]->isColor())
1248  {
1249  if (pixeltype == "UINT8")
1250  {
1251  success = main_stacker<vigra::RGBValue<vigra::UInt8>>(images);
1252  }
1253  else if (pixeltype == "UINT16")
1254  {
1255  success = main_stacker<vigra::RGBValue<vigra::UInt16>>(images);
1256  }
1257  else if (pixeltype == "UINT32")
1258  {
1259  success = main_stacker<vigra::RGBValue<vigra::UInt32>>(images);
1260  }
1261  else if (pixeltype == "FLOAT")
1262  {
1263  success = main_stacker<vigra::RGBValue<float>>(images);
1264  }
1265  else if (pixeltype == "DOUBLE")
1266  {
1267  success = main_stacker<vigra::RGBValue<double>>(images);
1268  }
1269  else
1270  {
1271  std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1272  };
1273  }
1274  else
1275  {
1276  //grayscale images
1277  if (pixeltype == "UINT8")
1278  {
1279  success = main_stacker<vigra::UInt8>(images);
1280  }
1281  else if (pixeltype == "UINT16")
1282  {
1283  success = main_stacker<vigra::UInt16>(images);
1284  }
1285  else if (pixeltype == "UINT32")
1286  {
1287  success = main_stacker<vigra::UInt32>(images);
1288  }
1289  else if (pixeltype == "FLOAT")
1290  {
1291  success = main_stacker<float>(images);
1292  }
1293  else if (pixeltype == "DOUBLE")
1294  {
1295  success = main_stacker<double>(images);
1296  }
1297  else
1298  {
1299  std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
1300  };
1301  };
1302 
1303  CleanUp(images);
1304  if (success)
1305  {
1306  return 0;
1307  };
1308  return 1;
1309 }
int floori(double x)
Definition: hugin_math.h:65
virtual bool IsValid()
Definition: stacker.cpp:606
virtual void getResult(ValueType &val)
Definition: stacker.cpp:552
const int numPixelSamples() const
Definition: stacker.cpp:231
const int numBands() const
Definition: stacker.cpp:229
std::vector< double > m_sortValues
Definition: stacker.cpp:611
unsigned m_height
Definition: stacker.cpp:351
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
const std::string getName() const
Definition: stacker.cpp:537
ValueType m_max
Definition: stacker.cpp:428
std::string outputFilename
Definition: stacker.cpp:180
const bool isGrayscale() const
Definition: stacker.cpp:228
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:579
void CleanUp(std::vector< InputImage * > &images)
Definition: stacker.cpp:900
PixelType second_argument_type
Definition: stacker.cpp:707
bool StackImages(std::vector< InputImage * > &images, Functor &stacker)
loads images line by line and merge into final image, save the result
Definition: stacker.cpp:645
void getValue(const int x, vigra::RGBValue< ValueType > &value, ValueType &mask)
Definition: stacker.cpp:266
vigra::UInt8 third_argument_type
Definition: stacker.cpp:708
InputImage(const std::string filename)
Definition: stacker.cpp:201
Some functions to create tiff images with masks.
const vigra::ImageImportInfo::ICCProfile getICCProfile() const
Definition: stacker.cpp:234
std::vector< ValueType > m_values
Definition: stacker.cpp:500
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &gray, const vigra::UInt8 &mask, vigra::VigraTrueType) const
Definition: stacker.cpp:724
vigra::TinyVector< realPixelType, 2 > first_argument_type
Definition: stacker.cpp:706
static struct GeneralParameters Parameters
vigra::ImageImportInfo m_info
Definition: stacker.cpp:348
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:421
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:239
ValueType m_min
Definition: stacker.cpp:410
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:422
void sort(vigra::VigraFalseType)
Definition: stacker.cpp:490
virtual void reset()
Definition: stacker.cpp:419
virtual bool IsValid()
Definition: stacker.cpp:428
VIGRA_UNIQUE_PTR< vigra::Decoder > m_decoder
Definition: stacker.cpp:352
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:523
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.
bool CheckInput(const std::vector< InputImage * > &images, vigra::Rect2D &outputROI, vigra::Size2D &canvasSize)
Definition: stacker.cpp:614
size_t m_count
Definition: stacker.cpp:444
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:230
void reset()
Definition: stacker.cpp:401
bool IsValid()
Definition: stacker.cpp:410
virtual void getResult(ValueType &val)
Definition: stacker.cpp:439
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &color, const vigra::UInt8 &mask, vigra::VigraFalseType) const
Definition: stacker.cpp:710
static double sigma
void operator()(const ValueType &val)
Definition: stacker.cpp:452
int m_offsetX
Definition: stacker.cpp:350
vigra::NumericTraits< ValueType >::RealPromote m_sum
Definition: stacker.cpp:440
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition: stacker.cpp:420
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
void reset()
Definition: stacker.cpp:451
void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:467
unsigned m_offset
Definition: stacker.cpp:351
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:351
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:233
const std::string getFilename() const
Definition: stacker.cpp:235
void operator()(const ValueType &val)
Definition: stacker.cpp:404
bool IsValid()
Definition: stacker.cpp:466
unsigned m_x
Definition: stacker.cpp:351
virtual void reset()
Definition: stacker.cpp:544
vigra::UInt8 result_type
Definition: stacker.cpp:709
void sort(vigra::VigraTrueType)
Definition: stacker.cpp:485
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:546
vigra::NumericTraits< PixelType >::RealPromote realPixelType
Definition: stacker.cpp:705
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:238
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:545
virtual void reset()
Definition: stacker.cpp:437
void getMeanSigma(const std::vector< ValueType > &values, ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
Definition: stacker.cpp:371
const std::string getPixelType() const
Definition: stacker.cpp:226
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:237
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
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:440
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 getResult(ValueType &val)
Definition: stacker.cpp:453
void readLine(const int y)
Definition: stacker.cpp:240
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:236
bool main_stacker(std::vector< InputImage * > &images)
Definition: stacker.cpp:909
int m_offsetY
Definition: stacker.cpp:350
unsigned m_width
Definition: stacker.cpp:351
bool m_noData
Definition: stacker.cpp:353
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:59
std::string maskSuffix
Definition: stacker.cpp:193
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:438
void sort()
Definition: stacker.cpp:496
const float getXResolution() const
Definition: stacker.cpp:232
void operator()(const ValueType &val, vigra::VigraFalseType)
Definition: stacker.cpp:403
void getMean(const std::vector< ValueType > &values, ValueType &val)
Definition: stacker.cpp:357
static T max(T x, T y)
Definition: svm.cpp:65
static void usage()
Definition: Main.cpp:32
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:308
bool StackImagesAndMask(std::vector< InputImage * > &images, Functor &stacker)
Definition: stacker.cpp:745
vigra::Size2D m_canvassize
Definition: stacker.cpp:349
std::vector< ValueType > m_values
Definition: stacker.cpp:607
const std::string getName() const
Definition: stacker.cpp:607
const bool isColor() const
Definition: stacker.cpp:227
const std::string getName() const
Definition: stacker.cpp:482
void operator()(const ValueType &val, vigra::VigraTrueType)
Definition: stacker.cpp:402
void getResult(ValueType &val)
Definition: stacker.cpp:409
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:107
virtual void getResult(ValueType &val)
Definition: stacker.cpp:509
virtual void getResult(ValueType &val)
Definition: stacker.cpp:427
std::string compression
Definition: stacker.cpp:181
std::string tolower(const std::string &s)
convert a string to lowercase
Definition: stl_utils.h:49
unsigned m_bands
Definition: stacker.cpp:351
virtual void operator()(const ValueType &val)
Definition: stacker.cpp:547
vigra::UInt8 operator()(const vigra::TinyVector< realPixelType, 2 > &limits, const PixelType &pixel, const vigra::UInt8 &mask) const
Definition: stacker.cpp:736
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:344
bool m_hasAlpha
Definition: stacker.cpp:353
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
Definition: stacker.cpp:41
int main(int argc, char *argv[])
Definition: Main.cpp:167
std::string stackMode
Definition: stacker.cpp:183