Hugin trunk 0.1
Loading...
Searching...
No Matches
verdandi.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>
32#include <vigra_ext/utils.h>
33#include <hugin_utils/utils.h>
35
37template <class ImageType, class MaskType>
38bool SaveImage(ImageType& image, MaskType& mask, vigra::ImageExportInfo& exportImageInfo, std::string filetype, std::string pixelType, const vigra::Rect2D& roi, const int inputNumberBands)
39{
40 exportImageInfo.setPixelType(pixelType.c_str());
41 if (vigra::isBandNumberSupported(filetype, inputNumberBands))
42 {
43 try
44 {
45 vigra::exportImageAlpha(vigra::srcImageRange(image, roi), vigra::srcImage(mask, roi.upperLeft()), exportImageInfo);
46 }
47 catch (std::exception& e)
48 {
49 std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
50 << "Cause: " << e.what() << std::endl;
51 return false;
52 };
53 return true;
54 }
55 else
56 {
57 if (vigra::isBandNumberSupported(filetype, inputNumberBands - 1))
58 {
59 std::cout << "Warning: Filetype " << filetype << " does not support alpha channels." << std::endl
60 << "Saving image without alpha channel." << std::endl;
61 try
62 {
63 vigra::exportImage(vigra::srcImageRange(image, roi), exportImageInfo);
64 }
65 catch (std::exception& e)
66 {
67 std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
68 << "Cause: " << e.what() << std::endl;
69 return false;
70 };
71 return true;
72 }
73 else
74 {
75 std::cerr << "ERROR: Output filetype " << filetype << " does not support " << inputNumberBands << " channels." << std::endl
76 << "Can't save image." << std::endl;
77 };
78 };
79 return false;
80};
81
83template <class ImageType, class MaskType>
84bool SaveFinalImage(ImageType& image, MaskType& mask, const std::string& inputPixelType, const int inputNumBands, vigra::ImageExportInfo& output, const vigra::Rect2D& roi)
85{
87 if (vigra::isPixelTypeSupported(encoder->getFileType(), inputPixelType))
88 {
89 return SaveImage(image, mask, output, encoder->getFileType(), inputPixelType, roi, inputNumBands);
90 }
91 else
92 {
93 if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT16"))
94 {
95 // transform to 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);
98 }
99 else
100 {
101 if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT8"))
102 {
103 // transform to 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);
106 }
107 else
108 {
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;
112 };
113 };
114 };
115 return false;
116};
117
119void SetCompression(vigra::ImageExportInfo& output, const std::string& compression)
120{
121 const std::string ext(hugin_utils::toupper(hugin_utils::getExtension(output.getFileName())));
122 if (!compression.empty())
123 {
124 if (ext == "JPEG" || ext == "JPG")
125 {
126 output.setCompression(std::string("JPEG QUALITY=" + compression).c_str());
127 }
128 else
129 {
130 output.setCompression(compression.c_str());
131 };
132 };
133};
134
136template <class ImageType>
137bool LoadAndMergeImages(std::vector<vigra::ImageImportInfo> imageInfos, const std::string& filename, const std::string& compression, const bool wrap, const bool hardSeam, const bool useBigTiff)
138{
139 if (imageInfos.empty())
140 {
141 return false;
142 };
143 vigra::Size2D imageSize(imageInfos[0].getCanvasSize());
144 if (imageSize.area() == 0)
145 {
146 // not all images contains the canvas size/full image size
147 // in this case take also the position into account to get full image size
148 imageSize = vigra::Size2D(imageInfos[0].width() + imageInfos[0].getPosition().x,
149 imageInfos[0].height() + imageInfos[0].getPosition().y);
150 };
151 ImageType image(imageSize);
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());
158
159 for (size_t i = 1; i < imageInfos.size(); ++i)
160 {
161 ImageType image2(imageInfos[i].size());
162 vigra::BImage mask2(image2.size());
163 vigra::importImageAlpha(imageInfos[i], vigra::destImage(image2), vigra::destImage(mask2));
164 std::cout << "Loaded " << imageInfos[i].getFileName() << std::endl;
165 roi |= vigra::Rect2D(vigra::Point2D(imageInfos[i].getPosition()), imageInfos[i].size());
166
168 };
169 // save output
170 {
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());
177 SetCompression(exportImageInfo, compression);
178 return SaveFinalImage(image, mask, imageInfos[0].getPixelType(), imageInfos[0].numBands(), exportImageInfo, roi);
179 };
180};
181
183static void usage(const char* name)
184{
185 std::cout << name << ": blend images using watershed algorithm" << std::endl
186 << name << " version " << hugin_utils::GetHuginVersion() << std::endl
187 << std::endl
188 << "Usage: " << name << " [options] images" << std::endl
189 << 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
199 << std::endl;
200};
201
205template<class ImageType, class MaskType>
206bool ResaveImageFill(const vigra::ImageImportInfo& importInfo, vigra::ImageExportInfo& exportInfo, bool fillEdges, bool doWrap)
207{
208 ImageType image(importInfo.size());
209 MaskType mask(image.size());
210 int numBands = importInfo.numBands();
211 if (importInfo.numExtraBands() == 0)
212 {
213 vigra::importImage(importInfo, vigra::destImage(image));
214 // init mask
215 vigra::initImage(vigra::destImageRange(mask), vigra_ext::LUTTraits<typename MaskType::value_type>::max());
216 // no alpha channel, so we can't detect empty edges
217 fillEdges = false;
218 ++numBands;
219 }
220 else
221 {
222 if (importInfo.numExtraBands() == 1)
223 {
224 vigra::importImageAlpha(importInfo, vigra::destImage(image), vigra::destImage(mask));
225 }
226 else
227 {
228 std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl;
229 return false;
230 };
231 };
232 std::cout << "Loaded " << importInfo.getFileName() << std::endl;
233 if (fillEdges)
234 {
235 const typename MaskType::PixelType maskZero = vigra_ext::LUTTraits<typename MaskType::PixelType>::min();
236 const typename MaskType::PixelType maskMax = vigra_ext::LUTTraits<typename MaskType::PixelType>::max();
238 // invert mask
239 MaskType dummyMask(image.size());
240 vigra::omp::transformImage(vigra::srcImageRange(mask), vigra::destImage(dummyMask), vigra::functor::ifThenElse(vigra::functor::Arg1() > vigra::functor::Param(maskMax / 2), vigra::functor::Param(maskZero), vigra::functor::Param(maskMax)));
241 // create label
242 vigra::BImage label(image.size());
243 vigra::omp::transformImage(vigra::srcImageRange(mask), vigra::destImage(label), vigra::functor::ifThenElse(vigra::functor::Arg1() > vigra::functor::Param(maskMax / 2), vigra::functor::Param(1), vigra::functor::Param(5)));
244 // check that we have uncovered labels
245 vigra::FindMinMax<vigra::UInt8> labelMinmax;
246 vigra::inspectImage(vigra::srcImageRange(label), labelMinmax);
247 if (labelMinmax.max > 1)
248 {
249 std::cout << "Filling edges" << std::endl;
250 // fill edges
251 vigra_ext::PoissonBlend(image, dummyImage, dummyMask, label, vigra::Point2D(0, 0), doWrap);
252 // now we need to update the mask
253 vigra::initImage(vigra::destImageRange(mask), maskMax);
254 };
255 };
256 const vigra::Rect2D roi(vigra::Point2D(0, 0), image.size());
257 return SaveFinalImage(image, mask, importInfo.getPixelType(), numBands, exportInfo, roi);
258};
259
260int main(int argc, char* argv[])
261{
262 // parse arguments
263 const char* optstring = "o:hw";
264
265 enum
266 {
267 OPT_COMPRESSION = 1000,
270 };
271 static struct option longOptions[] =
272 {
273 { "output", required_argument, NULL, 'o' },
274 { "compression", required_argument, NULL, OPT_COMPRESSION},
276 { "wrap", no_argument, NULL, 'w' },
277 { "bigtiff", no_argument, NULL, OPT_BIGTIFF},
278 { "help", no_argument, NULL, 'h' },
279 0
280 };
281
282 int c;
283 std::string output;
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)
289 {
290 switch (c)
291 {
292 case 'o':
293 output = optarg;
294 break;
295 case 'h':
297 return 0;
298 break;
299 case OPT_COMPRESSION:
300 compression = hugin_utils::toupper(optarg);
301 break;
302 case OPT_SEAMMODE:
303 {
304 std::string text(optarg);
306 if (text == "hard")
307 {
308 hardSeam = true;
309 }
310 else
311 {
312 if (text == "blend")
313 {
314 hardSeam = false;
315 }
316 else
317 {
318 std::cerr << hugin_utils::stripPath(argv[0]) << ": String \"" << text << "\" is not a recognized seam blend mode." << std::endl;
319 return 1;
320 };
321 };
322 };
323 break;
324 case 'w':
325 wraparound = true;
326 break;
327 case OPT_BIGTIFF:
328 useBigTIFF = true;
329 break;
330 case ':':
331 case '?':
332 // missing argument or invalid switch
333 return 1;
334 break;
335 default:
336 // this should not happen
337 abort();
338 }
339 };
340
341 unsigned nFiles = argc - optind;
342 if (nFiles < 1)
343 {
344 std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one image need to be specified" << std::endl;
345 return 1;
346 }
347
348 // extract file names
349 std::vector<std::string> files;
350 for (size_t i = 0; i < nFiles; i++)
351 {
352 std::string currentFile(argv[optind + i]);
353 // check file existence
355 {
356 files.push_back(currentFile);
357 };
358 }
359
360 if (files.empty())
361 {
362 std::cerr << "ERROR: " << hugin_utils::stripPath(argv[0]) << " needs at least one image." << std::endl;
363 return 1;
364 };
365
366 if (output.empty())
367 {
368 output = "final.tif";
369 };
372 {
373 std::cerr << "ERROR: Extension \"" << hugin_utils::getExtension(output) << "\" is unknown." << std::endl;
374 return 1;
375 };
376
377
378 bool success = false;
379 if (files.size() == 1)
380 {
381 //special case, only one image given
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());
389 SetCompression(exportInfo, compression);
390 const std::string pixeltype = imageInfo.getPixelType();
391 if (imageInfo.isColor())
392 {
393 if (pixeltype == "UINT8")
394 {
396 }
397 else if (pixeltype == "INT16")
398 {
400 }
401 else if (pixeltype == "UINT16")
402 {
404 }
405 else if (pixeltype == "INT32")
406 {
408 }
409 else if (pixeltype == "UINT32")
410 {
412 }
413 else if (pixeltype == "FLOAT")
414 {
416 }
417 else
418 {
419 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
420 };
421 }
422 else
423 {
424 //grayscale images
425 if (pixeltype == "UINT8")
426 {
428 }
429 else if (pixeltype == "INT16")
430 {
432 }
433 else if (pixeltype == "UINT16")
434 {
436 }
437 else if (pixeltype == "INT32")
438 {
440 }
441 else if (pixeltype == "UINT32")
442 {
444 }
445 else if (pixeltype == "FLOAT")
446 {
448 }
449 else
450 {
451 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
452 };
453 };
454 }
455 else
456 {
457 std::vector<vigra::ImageImportInfo> imageInfos;
458 for (size_t i = 0; i < files.size(); ++i)
459 {
460 vigra::ImageImportInfo imageInfo(files[i].c_str());
461 imageInfos.push_back(imageInfo);
462 };
463 const std::string pixeltype(imageInfos[0].getPixelType());
464 if (imageInfos[0].numExtraBands() != 1)
465 {
466 std::cerr << "ERROR: Image does not contain alpha channel." << std::endl;
467 return 1;
468 }
469 //check, that image information matches
470 for (size_t i = 1; i < files.size(); ++i)
471 {
472 if (imageInfos[0].isColor() != imageInfos[i].isColor())
473 {
474 std::cerr << "ERROR: You can't merge color and grayscale images." << std::endl;
475 return 1;
476 };
477 if (imageInfos[0].numBands() != imageInfos[i].numBands())
478 {
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;
482 return 1;
483 };
484 if (strcmp(pixeltype.c_str(), imageInfos[i].getPixelType()) != 0)
485 {
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;
489 return 1;
490 };
491 };
492
493 if (imageInfos[0].isColor())
494 {
495 if (pixeltype == "UINT8")
496 {
498 }
499 else if (pixeltype == "INT16")
500 {
502 }
503 else if (pixeltype == "UINT16")
504 {
506 }
507 else if (pixeltype == "INT32")
508 {
510 }
511 else if (pixeltype == "UINT32")
512 {
514 }
515 else if (pixeltype == "FLOAT")
516 {
518 }
519 else if (pixeltype == "DOUBLE")
520 {
522 }
523 else
524 {
525 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
526 };
527 }
528 else
529 {
530 //grayscale images
531 if (pixeltype == "UINT8")
532 {
534 }
535 else if (pixeltype == "INT16")
536 {
538 }
539 else if (pixeltype == "UINT16")
540 {
542 }
543 else if (pixeltype == "INT32")
544 {
546 }
547 else if (pixeltype == "UINT32")
548 {
550 }
551 else if (pixeltype == "FLOAT")
552 {
554 }
555 else if (pixeltype == "DOUBLE")
556 {
558 }
559 else
560 {
561 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
562 };
563 };
564 };
565
566 if (success)
567 {
568 std::cout << "Written result to " << output << std::endl;
569 return 0;
570 };
571 return 1;
572}
static void usage()
Definition Main.cpp:32
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)
Definition stl_utils.h:59
std::string getExtension(const std::string &basename2)
Get extension of a filename.
Definition utils.cpp:99
void EnforceExtension(std::string &filename, const std::string &defaultExtension)
check if filename contains extension, if not add default extension
Definition utils.cpp:411
std::string tolower(const std::string &s)
convert a string to lowercase
Definition stl_utils.h:49
bool IsFileTypeSupported(const std::string &filename)
return true, if file type by extension is supported by vigra
Definition utils.cpp:405
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
bool FileExists(const std::string &filename)
checks if file exists
Definition utils.cpp:362
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
void transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void PoissonBlend(ImageType &image1, const ImageType &image2, const MaskType &mask2, const vigra::BImage &labels, const vigra::Point2D &offsetPoint, const bool doWrap)
void MergeImages(ImageType &image1, MaskType &mask1, const ImageType &image2, const MaskType &mask2, const vigra::Diff2D offset, const bool wrap, const bool hardSeam)
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.
Definition threshold.h:41
int main(int argc, char *argv[])
Definition verdandi.cpp:260
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
Definition verdandi.cpp:137
bool SaveImage(ImageType &image, MaskType &mask, vigra::ImageExportInfo &exportImageInfo, std::string filetype, std::string pixelType, const vigra::Rect2D &roi, const int inputNumberBands)
save image, when possible with alpha channel, take care of formats which does not support alpha chann...
Definition verdandi.cpp:38
void SetCompression(vigra::ImageExportInfo &output, const std::string &compression)
set compression for jpeg or tiff
Definition verdandi.cpp:119
bool SaveFinalImage(ImageType &image, MaskType &mask, const std::string &inputPixelType, const int inputNumBands, vigra::ImageExportInfo &output, const vigra::Rect2D &roi)
save final image, take care of some supported pixel types and convert when necessary to smaller pixel...
Definition verdandi.cpp:84
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,...
Definition verdandi.cpp:206
functions to manage ROI's