37 template <
class ImageType,
class MaskType>
38 bool SaveImage(
ImageType& image, MaskType& mask, vigra::ImageExportInfo& exportImageInfo, std::string filetype, std::string pixelType,
const vigra::Rect2D& roi,
const int inputNumberBands)
40 exportImageInfo.setPixelType(pixelType.c_str());
41 if (vigra::isBandNumberSupported(filetype, inputNumberBands))
47 catch (std::exception& e)
49 std::cerr <<
"ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
50 <<
"Cause: " << e.what() << std::endl;
57 if (vigra::isBandNumberSupported(filetype, inputNumberBands - 1))
59 std::cout <<
"Warning: Filetype " << filetype <<
" does not support alpha channels." << std::endl
60 <<
"Saving image without alpha channel." << std::endl;
65 catch (std::exception& e)
67 std::cerr <<
"ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
68 <<
"Cause: " << e.what() << std::endl;
75 std::cerr <<
"ERROR: Output filetype " << filetype <<
" does not support " << inputNumberBands <<
" channels." << std::endl
76 <<
"Can't save image." << std::endl;
83 template <
class ImageType,
class MaskType>
84 bool SaveFinalImage(
ImageType& image, MaskType& mask,
const std::string& inputPixelType,
const int inputNumBands, vigra::ImageExportInfo& output,
const vigra::Rect2D& roi)
86 VIGRA_UNIQUE_PTR<vigra::Encoder> encoder(vigra::encoder(output));
87 if (vigra::isPixelTypeSupported(encoder->getFileType(), inputPixelType))
89 return SaveImage(image, mask, output, encoder->getFileType(), inputPixelType, roi, inputNumBands);
93 if (vigra::isPixelTypeSupported(encoder->getFileType(),
"UINT16"))
96 output.setForcedRangeMapping(0, vigra::NumericTraits<
typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::
max(), 0, 65535);
97 return SaveImage(image, mask, output, encoder->getFileType(),
"UINT16", roi, inputNumBands);
101 if (vigra::isPixelTypeSupported(encoder->getFileType(),
"UINT8"))
104 output.setForcedRangeMapping(0, vigra::NumericTraits<
typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::
max(), 0, 255);
105 return SaveImage(image, mask, output, encoder->getFileType(),
"UINT8", roi, inputNumBands);
109 std::cerr <<
"ERROR: Output file type " << encoder->getFileType() <<
" does not support" << std::endl
110 <<
"requested pixeltype " << inputPixelType <<
"." << std::endl
111 <<
"Save output in other file format." << std::endl;
119 void SetCompression(vigra::ImageExportInfo& output,
const std::string& compression)
122 if (!compression.empty())
124 if (ext ==
"JPEG" || ext ==
"JPG")
126 output.setCompression(std::string(
"JPEG QUALITY=" + compression).c_str());
130 output.setCompression(compression.c_str());
136 template <
class ImageType>
137 bool LoadAndMergeImages(std::vector<vigra::ImageImportInfo> imageInfos,
const std::string& filename,
const std::string& compression,
const bool wrap,
const bool hardSeam,
const bool useBigTiff)
139 if (imageInfos.empty())
143 vigra::Size2D imageSize(imageInfos[0].getCanvasSize());
144 if (imageSize.area() == 0)
148 imageSize = vigra::Size2D(imageInfos[0].width() + imageInfos[0].getPosition().x,
149 imageInfos[0].height() + imageInfos[0].getPosition().y);
152 vigra::BImage mask(imageSize);
154 std::pair<typename ImageType::Iterator, typename ImageType::Accessor>(image.upperLeft() + imageInfos[0].getPosition(), image.accessor()),
155 std::pair<typename vigra::BImage::Iterator, typename vigra::BImage::Accessor>(mask.upperLeft() + imageInfos[0].getPosition(), mask.accessor()));
156 std::cout <<
"Loaded " << imageInfos[0].getFileName() << std::endl;
157 vigra::Rect2D roi(vigra::Point2D(imageInfos[0].getPosition()), imageInfos[0].size());
159 for (
size_t i = 1; i < imageInfos.size(); ++i)
162 vigra::BImage mask2(image2.size());
164 std::cout <<
"Loaded " << imageInfos[i].getFileName() << std::endl;
165 roi |= vigra::Rect2D(vigra::Point2D(imageInfos[i].getPosition()), imageInfos[i].size());
171 vigra::ImageExportInfo exportImageInfo(filename.c_str(), useBigTiff ?
"w8" :
"w");
172 exportImageInfo.setXResolution(imageInfos[0].getXResolution());
173 exportImageInfo.setYResolution(imageInfos[0].getYResolution());
174 exportImageInfo.setPosition(roi.upperLeft());
175 exportImageInfo.setCanvasSize(mask.size());
176 exportImageInfo.setICCProfile(imageInfos[0].getICCProfile());
178 return SaveFinalImage(image, mask, imageInfos[0].getPixelType(), imageInfos[0].numBands(), exportImageInfo, roi);
185 std::cout << name <<
": blend images using watershed algorithm" << std::endl
188 <<
"Usage: " << name <<
" [options] images" << std::endl
190 <<
" --output=FILE Set the filename for the output file." << std::endl
191 <<
" --compression=value Compression of the output files" << std::endl
192 <<
" For jpeg output: 0-100" << std::endl
193 <<
" For tiff output: PACKBITS, DEFLATE, LZW" << std::endl
194 <<
" -w, --wrap Wraparound 360 deg border." << std::endl
195 <<
" --seam=hard|blend Select the blend mode for the seam" << std::endl
196 <<
" --bigtiff Write output in BigTIFF format" << std::endl
197 <<
" (only with TIFF output)" << std::endl
198 <<
" -h, --help Shows this help" << std::endl
205 template<
class ImageType,
class MaskType>
206 bool ResaveImageFill(
const vigra::ImageImportInfo& importInfo, vigra::ImageExportInfo& exportInfo,
bool fillEdges,
bool doWrap)
209 MaskType mask(image.size());
210 int numBands = importInfo.numBands();
211 if (importInfo.numExtraBands() == 0)
222 if (importInfo.numExtraBands() == 1)
228 std::cerr <<
"ERROR: Images with several alpha channels are not supported." << std::endl;
232 std::cout <<
"Loaded " << importInfo.getFileName() << std::endl;
239 MaskType dummyMask(image.size());
242 vigra::BImage label(image.size());
245 vigra::FindMinMax<vigra::UInt8> labelMinmax;
247 if (labelMinmax.max > 1)
249 std::cout <<
"Filling edges" << std::endl;
256 const vigra::Rect2D roi(vigra::Point2D(0, 0), image.size());
257 return SaveFinalImage(image, mask, importInfo.getPixelType(), numBands, exportInfo, roi);
260 int main(
int argc,
char* argv[])
263 const char* optstring =
"o:hw";
267 OPT_COMPRESSION = 1000,
271 static struct option longOptions[] =
273 {
"output", required_argument, NULL,
'o' },
274 {
"compression", required_argument, NULL, OPT_COMPRESSION},
275 {
"seam", required_argument, NULL, OPT_SEAMMODE},
276 {
"wrap", no_argument, NULL,
'w' },
277 {
"bigtiff", no_argument, NULL, OPT_BIGTIFF},
278 {
"help", no_argument, NULL,
'h' },
284 std::string compression;
285 bool wraparound =
false;
286 bool hardSeam =
true;
287 bool useBigTIFF =
false;
288 while ((c = getopt_long(argc, argv, optstring, longOptions,
nullptr)) != -1)
299 case OPT_COMPRESSION:
304 std::string text(optarg);
318 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": String \"" << text <<
"\" is not a recognized seam blend mode." << std::endl;
341 unsigned nFiles = argc - optind;
349 std::vector<std::string> files;
350 for (
size_t i = 0; i < nFiles; i++)
352 std::string currentFile(argv[optind + i]);
356 files.push_back(currentFile);
368 output =
"final.tif";
378 bool success =
false;
379 if (files.size() == 1)
382 vigra::ImageImportInfo imageInfo(files[0].c_str());
383 vigra::ImageExportInfo exportInfo(output.c_str(), useBigTIFF ?
"w8" :
"w");
384 exportInfo.setXResolution(imageInfo.getXResolution());
385 exportInfo.setYResolution(imageInfo.getYResolution());
386 exportInfo.setCanvasSize(imageInfo.getCanvasSize());
387 exportInfo.setPosition(imageInfo.getPosition());
388 exportInfo.setICCProfile(imageInfo.getICCProfile());
390 const std::string pixeltype = imageInfo.getPixelType();
391 if (imageInfo.isColor())
393 if (pixeltype ==
"UINT8")
395 success = ResaveImageFill<vigra::BRGBImage, vigra::BImage>(imageInfo, exportInfo, !hardSeam, wraparound);
397 else if (pixeltype ==
"INT16")
399 success = ResaveImageFill<vigra::Int16RGBImage, vigra::Int16Image>(imageInfo, exportInfo, !hardSeam, wraparound);
401 else if (pixeltype ==
"UINT16")
403 success = ResaveImageFill<vigra::UInt16RGBImage, vigra::UInt16Image>(imageInfo, exportInfo, !hardSeam, wraparound);
405 else if (pixeltype ==
"INT32")
407 success = ResaveImageFill<vigra::Int32RGBImage, vigra::UInt32Image>(imageInfo, exportInfo, !hardSeam, wraparound);
409 else if (pixeltype ==
"UINT32")
411 success = ResaveImageFill<vigra::UInt32RGBImage, vigra::UInt32Image>(imageInfo, exportInfo, !hardSeam, wraparound);
413 else if (pixeltype ==
"FLOAT")
415 success = ResaveImageFill<vigra::FRGBImage, vigra::FImage>(imageInfo, exportInfo, !hardSeam, wraparound);
419 std::cerr <<
" ERROR: unsupported pixel type: " << pixeltype << std::endl;
425 if (pixeltype ==
"UINT8")
427 success = ResaveImageFill<vigra::BImage, vigra::BImage>(imageInfo, exportInfo, !hardSeam, wraparound);
429 else if (pixeltype ==
"INT16")
431 success = ResaveImageFill<vigra::Int16Image, vigra::Int16Image>(imageInfo, exportInfo, !hardSeam, wraparound);
433 else if (pixeltype ==
"UINT16")
435 success = ResaveImageFill<vigra::UInt16Image, vigra::UInt16Image>(imageInfo, exportInfo, !hardSeam, wraparound);
437 else if (pixeltype ==
"INT32")
439 success = ResaveImageFill<vigra::Int32Image, vigra::Int32Image>(imageInfo, exportInfo, !hardSeam, wraparound);
441 else if (pixeltype ==
"UINT32")
443 success = ResaveImageFill<vigra::UInt32Image, vigra::UInt32Image>(imageInfo, exportInfo, !hardSeam, wraparound);
445 else if (pixeltype ==
"FLOAT")
447 success = ResaveImageFill<vigra::FImage, vigra::FImage>(imageInfo, exportInfo, !hardSeam, wraparound);
451 std::cerr <<
" ERROR: unsupported pixel type: " << pixeltype << std::endl;
457 std::vector<vigra::ImageImportInfo> imageInfos;
458 for (
size_t i = 0; i < files.size(); ++i)
460 vigra::ImageImportInfo imageInfo(files[i].c_str());
461 imageInfos.push_back(imageInfo);
463 const std::string pixeltype(imageInfos[0].getPixelType());
464 if (imageInfos[0].numExtraBands() != 1)
466 std::cerr <<
"ERROR: Image does not contain alpha channel." << std::endl;
470 for (
size_t i = 1; i < files.size(); ++i)
472 if (imageInfos[0].isColor() != imageInfos[i].isColor())
474 std::cerr <<
"ERROR: You can't merge color and grayscale images." << std::endl;
477 if (imageInfos[0].numBands() != imageInfos[i].numBands())
479 std::cerr <<
"ERROR: You can't merge image with different number of channels." << std::endl
480 <<
" Image \"" << imageInfos[0].getFileName() <<
"\" has " << imageInfos[0].numBands() <<
" channels," << std::endl
481 <<
" but image \"" << imageInfos[i].getFileName() <<
"\" has " << imageInfos[i].numBands() <<
" channels." << std::endl;
484 if (strcmp(pixeltype.c_str(), imageInfos[i].getPixelType()) != 0)
486 std::cerr <<
"ERROR: You can't merge images with different pixel types." << std::endl
487 <<
" Image \"" << imageInfos[0].getFileName() <<
"\" has pixel type " << imageInfos[0].getPixelType() <<
"," << std::endl
488 <<
" but image \"" << imageInfos[i].getFileName() <<
"\" has pixel type " << imageInfos[i].getPixelType() <<
"." << std::endl;
493 if (imageInfos[0].isColor())
495 if (pixeltype ==
"UINT8")
497 success = LoadAndMergeImages<vigra::BRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
499 else if (pixeltype ==
"INT16")
501 success = LoadAndMergeImages<vigra::Int16RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
503 else if (pixeltype ==
"UINT16")
505 success = LoadAndMergeImages<vigra::UInt16RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
507 else if (pixeltype ==
"INT32")
509 success = LoadAndMergeImages<vigra::Int32RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
511 else if (pixeltype ==
"UINT32")
513 success = LoadAndMergeImages<vigra::UInt32RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
515 else if (pixeltype ==
"FLOAT")
517 success = LoadAndMergeImages<vigra::FRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
519 else if (pixeltype ==
"DOUBLE")
521 success = LoadAndMergeImages<vigra::DRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
525 std::cerr <<
" ERROR: unsupported pixel type: " << pixeltype << std::endl;
531 if (pixeltype ==
"UINT8")
533 success = LoadAndMergeImages<vigra::BImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
535 else if (pixeltype ==
"INT16")
537 success = LoadAndMergeImages<vigra::Int16Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
539 else if (pixeltype ==
"UINT16")
541 success = LoadAndMergeImages<vigra::UInt16Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
543 else if (pixeltype ==
"INT32")
545 success = LoadAndMergeImages<vigra::Int32Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
547 else if (pixeltype ==
"UINT32")
549 success = LoadAndMergeImages<vigra::UInt32Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
551 else if (pixeltype ==
"FLOAT")
553 success = LoadAndMergeImages<vigra::FImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
555 else if (pixeltype ==
"DOUBLE")
557 success = LoadAndMergeImages<vigra::DImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
561 std::cerr <<
" ERROR: unsupported pixel type: " << pixeltype << std::endl;
568 std::cout <<
"Written result to " << output << std::endl;
bool FileExists(const std::string &filename)
checks if file exists
void EnforceExtension(std::string &filename, const std::string &defaultExtension)
check if filename contains extension, if not add default extension
void MergeImages(ImageType &image1, MaskType &mask1, const ImageType &image2, const MaskType &mask2, const vigra::Diff2D offset, const bool wrap, const bool hardSeam)
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.
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
std::string toupper(const std::string &s)
functions to manage ROI's
bool IsFileTypeSupported(const std::string &filename)
return true, if file type by extension is supported by vigra
bool LoadAndMergeImages(std::vector< vigra::ImageImportInfo > imageInfos, const std::string &filename, const std::string &compression, const bool wrap, const bool hardSeam, const bool useBigTiff)
loads image one by one and merge with all previouly loaded images, saves the final results ...
std::string getExtension(const std::string &basename2)
Get extension of a filename.
vigra::FRGBImage ImageType
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
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
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...
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...
std::string GetHuginVersion()
return a string with version numbers
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
void transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
bool ResaveImageFill(const vigra::ImageImportInfo &importInfo, vigra::ImageExportInfo &exportInfo, bool fillEdges, bool doWrap)
resave a single image LoadAndMergeImage would require the full canvas size for loading, so using this specialized version which is using the cropped intermediates images
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...
std::string tolower(const std::string &s)
convert a string to lowercase
void PoissonBlend(ImageType &image1, const ImageType &image2, const MaskType &mask2, const vigra::BImage &labels, const vigra::Point2D &offsetPoint, const bool doWrap)
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
int main(int argc, char *argv[])