33#include <vigra/imageinfo.hxx>
34#include <vigra/codec.hxx>
35#include <vigra/stdimage.hxx>
37#include <vigra/tiff.hxx>
45 if (!compression.empty())
47 if (
ext ==
"JPEG" ||
ext ==
"JPG")
49 output.setCompression(std::string(
"JPEG QUALITY=" + compression).
c_str());
53 output.setCompression(compression.c_str());
59template <
class ImageType,
class MaskType>
62 typedef typename vigra::NumericTraits<typename ImageType::value_type>::isScalar
scalar;
71 catch (std::exception& e)
73 std::cerr <<
"ERROR: Could not save " <<
exportImageInfo.getFileName() << std::endl
74 <<
"Cause: " << e.what() << std::endl;
83 std::cout <<
"Warning: Filetype " <<
filetype <<
" does not support alpha channels." << std::endl
84 <<
"Saving image without alpha channel." << std::endl;
89 catch (std::exception& e)
91 std::cerr <<
"ERROR: Could not save " <<
exportImageInfo.getFileName() << std::endl
92 <<
"Cause: " << e.what() << std::endl;
99 std::cerr <<
"ERROR: Output filetype " <<
filetype <<
" does not support " <<
numberChannels <<
" channels." << std::endl
100 <<
"Can't save image." << std::endl;
107template <
class ImageType,
class MaskType>
117 if (vigra::isPixelTypeSupported(
encoder->getFileType(),
"UINT8"))
120 output.setForcedRangeMapping(0, vigra::NumericTraits<
typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::max(), 0, 255);
125 std::cerr <<
"ERROR: Output file type " <<
encoder->getFileType() <<
" does not support" << std::endl
127 <<
"Save output in other file format." << std::endl;
136 std::cout << name <<
": stack images" << std::endl
139 <<
"Usage: " << name <<
" [options] images" << 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
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
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
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
270 template<
class ValueType>
271 void getValue(
const int x, vigra::RGBValue<ValueType>& value, ValueType& mask)
275 mask = vigra::NumericTraits<ValueType>::zero();
281 mask = vigra::NumericTraits<ValueType>::zero();
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));
295 const ValueType*
band3 =
static_cast<const ValueType*
>(
m_decoder->currentScanlineOfBand(3));
301 mask = vigra::NumericTraits<ValueType>::max();
307 mask = vigra::NumericTraits<ValueType>::zero();
312 template<
class ValueType>
313 void getValue(
const int x, ValueType& value, ValueType& mask)
317 mask = vigra::NumericTraits<ValueType>::zero();
323 mask = vigra::NumericTraits<ValueType>::zero();
329 const ValueType*
band0 =
static_cast<const ValueType*
>(
m_decoder->currentScanlineOfBand(0));
333 const ValueType*
band1 =
static_cast<const ValueType*
>(
m_decoder->currentScanlineOfBand(1));
339 mask = vigra::NumericTraits<ValueType>::max();
345 mask = vigra::NumericTraits<ValueType>::zero();
361template<
class ValueType>
362void getMean(
const std::vector<ValueType>& values, ValueType& val)
365 typedef vigra::NumericTraits<ValueType>
RealTraits;
366 typename RealTraits::RealPromote
mean = RealTraits::zero();
367 for (
auto& x : values)
372 val = RealTraits::fromRealPromote(
mean);
375template<
class ValueType>
376void getMeanSigma(
const std::vector<ValueType>& values, ValueType& val,
typename vigra::NumericTraits<ValueType>::RealPromote&
sigma)
378 typedef vigra::NumericTraits<ValueType>
RealTraits;
379 typedef typename RealTraits::RealPromote
RealType;
383 for (
auto& x : values)
391 val = RealTraits::fromRealPromote(
mean);
398 sigma = vigra::NumericTraits<RealType>::zero();
402template<
class ValueType>
406 void reset() {
m_min = vigra::NumericTraits<ValueType>::max(); };
408 void operator()(
const ValueType& val, vigra::VigraFalseType) {
if (val.luminance() <
m_min.luminance())
m_min = val; }
411 typedef typename vigra::NumericTraits<ValueType>::isScalar
is_scalar;
415 bool IsValid() {
return m_min != vigra::NumericTraits<ValueType>::max(); };
420template<
class ValueType>
424 virtual void reset() {
m_max = vigra::NumericTraits<ValueType>::min(); };
426 void operator()(
const ValueType& val, vigra::VigraFalseType) {
if (val.luminance() >
m_max.luminance())
m_max = val;}
429 typedef typename vigra::NumericTraits<ValueType>::isScalar
is_scalar;
433 virtual bool IsValid() {
return m_max != vigra::NumericTraits<ValueType>::min(); };
438template<
class ValueType>
447 const std::string
getName()
const {
return "mean"; };
452template<
class ValueType>
465 const int index = this->
m_values.size() / 2;
475 const std::string
getName()
const {
return "median"; };
478 void sort(vigra::VigraTrueType)
480 std::sort(this->
m_values.begin(),
this->m_values.end());
483 void sort(vigra::VigraFalseType)
486 [](
const ValueType & a,
const ValueType & b) {return a.luminance() < b.luminance(); });
491 typedef typename vigra::NumericTraits<ValueType>::isScalar
is_scalar;
496template<
class ValueType>
510 const std::string
getName()
const {
return "Winsor clipped mean"; };
527template<
class ValueType>
536 typedef typename vigra::NumericTraits<ValueType>::isScalar
is_scalar;
550 const std::string
getName()
const {
return "sigma clipped mean"; };
591 for (
size_t i = 1;
i < images.size(); ++
i)
594 if (images[
i]->getCanvasSize().width() >
canvasSize.width())
596 canvasSize.setWidth(images[
i]->getCanvasSize().width());
598 if (images[
i]->getCanvasSize().height() >
canvasSize.height())
600 canvasSize.setHeight(images[
i]->getCanvasSize().height());
605 std::cerr <<
"ERROR: You can't stack non-overlapping images." << std::endl;
612template <
class PixelType,
class Functor>
615 typedef typename vigra::NumericTraits<PixelType>::ValueType
ChannelType;
631 vigra::BImage mask(
output.size(),vigra::UInt8(0));
636#pragma omp parallel for
637 for (
int i = 0;
i < images.size(); ++
i)
639 images[
i]->readLine(y);
642#pragma omp parallel for schedule(static, 100)
648 for (
size_t i = 0;
i < images.size(); ++
i)
665 std::cout <<
"Write result to " <<
Parameters.outputFilename << std::endl;
669template <
class PixelType>
677 vigra::UInt8
operator()(
const vigra::TinyVector<PixelType, 2>&
limits,
const PixelType&
color,
const vigra::UInt8& mask, vigra::VigraFalseType)
const
689 vigra::UInt8
operator()(
const vigra::TinyVector<PixelType, 2>&
limits,
const PixelType&
gray,
const vigra::UInt8& mask, vigra::VigraTrueType)
const
701 vigra::UInt8
operator()(
const vigra::TinyVector<PixelType, 2>&
limits,
const PixelType&
pixel,
const vigra::UInt8& mask)
const
703 typedef typename vigra::NumericTraits<PixelType>::isScalar
is_scalar;
709template <
class PixelType,
class Functor>
712 typedef typename vigra::NumericTraits<PixelType>::ValueType
ChannelType;
730 vigra::BImage mask(
output.size(), vigra::UInt8(0));
731 vigra::BasicImage<vigra::TinyVector<PixelType, 2>>
limits(
output.size());
736#pragma omp parallel for
737 for (
int i = 0;
i < images.size(); ++
i)
739 images[
i]->readLine(y);
742#pragma omp parallel for schedule(static, 100)
748 for (
size_t i = 0;
i < images.size(); ++
i)
761 typename vigra::NumericTraits<PixelType>::RealPromote
sigma;
769 std::cout <<
"Write result to " <<
Parameters.outputFilename << std::endl;
788 std::cout <<
"Masking input images with sigma=" <<
Parameters.maskSigma;
791 std::cout << std::endl;
795 std::cout <<
" and suffix " <<
Parameters.maskSuffix <<
".tif" << std::endl;
797 for (
size_t i = 0;
i < images.size(); ++
i)
799 std::cout <<
"Masking " << images[
i]->getFilename();
802 std::cout << std::endl;
806 std::cout <<
" -> " << images[
i]->getMaskFilename() << std::endl;
808 vigra::BasicImage<PixelType> image(images[
i]->getROI().size());
809 vigra::BImage mask(image.size(), 255);
810 if (images[
i]->numExtraBands() == 1)
816 vigra::importImage(images[
i]->getImageImportInfo(), vigra::destImage(image));
818 vigra::Rect2D
roi = images[
i]->getROI();
825 images[
i]->getROI().upperLeft(), images[
i]->getCanvasSize(), images[
i]->getICCProfile());
833 std::cout <<
"Masked file \"" << images[
i]->getMaskFilename() <<
"\" already exists." << std::endl
834 <<
"Processing aborted." << std::endl;
849 catch (std::exception& e)
851 std::cerr <<
"Could not save masked images \"" <<
exportMaskImage.getFileName() <<
"\"." << std::endl
852 <<
"Error code: " << e.what() << std::endl
853 <<
"Processing aborted." << std::endl;
867 for (
auto& img : images)
873template <
class PixelType>
878 std::cout <<
"Merging stack with minimum operator." << std::endl;
886 std::cout <<
"Merging stack with maximum operator." << std::endl;
894 std::cout <<
"Merging stack with mean operator." << std::endl;
910 std::cout <<
"Merging stack with median operator." << std::endl;
925 std::cout <<
"Merging stack with Winsor clipping operator (trim=" <<
Parameters.winsorTrim <<
")." << std::endl;
940 std::cout <<
"Merging stack with sigma clipping operator (max sigma=" <<
Parameters.sigma <<
", max " <<
Parameters.maxIterations <<
" iterations)." << std::endl;
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;
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;
1029 if (
pos != std::string::npos)
1130 std::vector<std::string> files;
1154 while (std::getline(
textfile, line))
1156 const size_t pos = line.find_first_not_of(
" \t");
1157 if (line.empty() || (
pos != std::string::npos && line[
pos] ==
'#'))
1166 files.push_back(line);
1173 std::cerr <<
"ERROR: Could not open file " <<
currentFile << std::endl;
1189 std::cerr <<
"ERROR: File " <<
currentFile <<
" is not a image file recognized by vigra." << std::endl;
1194 std::cerr <<
"ERROR: File " <<
currentFile <<
" does not exists." << std::endl;
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;
1225 std::vector<InputImage*> images;
1226 for (
size_t i = 0;
i < files.size(); ++
i)
1229 if (images.back()->getROI().area() == 0)
1231 std::cerr <<
"ERROR: Image " << images.back()->getFilename() <<
" has no valid data." << std::endl;
1236 if (!images[0]->isColor() && !images[0]->isGrayscale())
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;
1243 if (images[0]->numExtraBands() > 1)
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;
1250 const std::string
pixeltype(images[0]->getPixelType());
1252 for (
size_t i = 1;
i < files.size(); ++
i)
1254 if (!images[
i]->isColor() && !images[
i]->isGrayscale())
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;
1261 if (images[
i]->numExtraBands() > 1)
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;
1268 if (images[0]->isColor() != images[
i]->isColor())
1270 std::cerr <<
"ERROR: You can't merge color and grayscale images." << std::endl;
1274 if (images[0]->numPixelSamples() != images[
i]->numPixelSamples())
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;
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;
1292 if (images[0]->isColor())
1316 std::cerr <<
" ERROR: unsupported pixel type: " <<
pixeltype << std::endl;
1344 std::cerr <<
" ERROR: unsupported pixel type: " <<
pixeltype << std::endl;
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
std::vector< ValueType > m_values
virtual void operator()(const ValueType &val)
virtual void getResult(ValueType &val)
const std::string getName() const
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &pixel, const vigra::UInt8 &mask) const
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &gray, const vigra::UInt8 &mask, vigra::VigraTrueType) const
PixelType second_argument_type
vigra::UInt8 operator()(const vigra::TinyVector< PixelType, 2 > &limits, const PixelType &color, const vigra::UInt8 &mask, vigra::VigraFalseType) const
vigra::TinyVector< PixelType, 2 > first_argument_type
vigra::UInt8 third_argument_type
void operator()(const ValueType &val, vigra::VigraFalseType)
virtual void getResult(ValueType &val)
virtual void operator()(const ValueType &val)
void operator()(const ValueType &val, vigra::VigraTrueType)
void operator()(const ValueType &val, vigra::VigraFalseType)
void operator()(const ValueType &val)
void getResult(ValueType &val)
void operator()(const ValueType &val, vigra::VigraTrueType)
std::vector< ValueType > m_values
void operator()(const ValueType &val, vigra::VigraTrueType)
virtual void operator()(const ValueType &val)
const std::string getName() const
void operator()(const ValueType &val, vigra::VigraFalseType)
virtual void getResult(ValueType &val)
std::vector< double > m_sortValues
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
virtual void getResult(ValueType &val) override
virtual void getResultAndSigma(ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma) override
const std::string getName() const
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
class AlphaIterator class AlphaAccessor inline void exportImageAlpha(ImageIterator image_upper_left, ImageIterator image_lower_right, ImageAccessor image_accessor, AlphaIterator alpha_upper_left, AlphaAccessor alpha_accessor, const ImageExportInfo &export_info)
vigra::FRGBImage ImageType
std::string toupper(const std::string &s)
std::string stripExtension(const std::string &basename2)
remove extension of a filename
std::string getExtension(const std::string &basename2)
Get extension of a filename.
void EnforceExtension(std::string &filename, const std::string &defaultExtension)
check if filename contains extension, if not add default extension
std::string tolower(const std::string &s)
convert a string to lowercase
bool IsFileTypeSupported(const std::string &filename)
return true, if file type by extension is supported by vigra
std::string GetHuginVersion()
return a string with version numbers
bool FileExists(const std::string &filename)
checks if file exists
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
bool stringToInt(const std::string &s, int &val)
convert string to integer value, returns true, if sucessful
void combineThreeImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, SrcImageIterator3 src3_upperleft, SrcAccessor3 src3_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void createTiffDirectory(vigra::TiffImage *tiff, const std::string &pagename, const std::string &documentname, const std::string comp, uint16_t page, uint16_t nImg, vigra::Diff2D offset, vigra::Size2D fullSize, const vigra::ImageExportInfo::ICCProfile &icc)
write a new Tiff directory, for a new layer
void createAlphaTiffImage(ImageIterator upperleft, ImageIterator lowerright, ImageAccessor a, AlphaIterator alphaUpperleft, AlphaAccessor alphaA, vigra::TiffImage *tiff)
int main(int argc, char *argv[])
bool CheckInput(const std::vector< InputImage * > &images, vigra::Rect2D &outputROI, vigra::Size2D &canvasSize)
bool StackImagesAndMask(std::vector< InputImage * > &images, Functor &stacker)
bool main_stacker(std::vector< InputImage * > &images)
void getMeanSigma(const std::vector< ValueType > &values, ValueType &val, typename vigra::NumericTraits< ValueType >::RealPromote &sigma)
void getMean(const std::vector< ValueType > &values, ValueType &val)
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
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...
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...
static struct GeneralParameters Parameters
void CleanUp(std::vector< InputImage * > &images)
bool StackImages(std::vector< InputImage * > &images, Functor &stacker)
loads images line by line and merge into final image, save the result
std::string outputFilename
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Some functions to create tiff images with masks.