Hugin trunk 0.1
Loading...
Searching...
No Matches
nona.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
26#include <hugin_config.h>
27#include <fstream>
28#include <sstream>
29
30#include <algorithm>
31#include <cctype>
32#include <string>
33
34#include <vigra/error.hxx>
35
36#include <getopt.h>
37
38#include <hugin_basic.h>
45
46#include <tiffio.h>
47
48static void usage(const char* name)
49{
50 std::cout << name << ": stitch a panorama image" << std::endl
51 << std::endl
52 << "nona version " << hugin_utils::GetHuginVersion() << std::endl
53 << std::endl
54 << " It uses the transform function from PanoTools, the stitching itself" << std::endl
55 << " is quite simple, no seam feathering is done." << std::endl
56 << " only the non-antialiasing interpolators of panotools are supported" << std::endl
57 << std::endl
58 << " The following output formats (n option of panotools p script line)" << std::endl
59 << " are supported:"<< std::endl
60 << std::endl
61 << " JPEG, TIFF, PNG : Single image formats with internal blender"<< std::endl
62 << " JPEG_m, TIFF_m, PNG_m : multiple image files"<< std::endl
63 << " TIFF_multilayer : Multilayer tiff files, readable by The Gimp 2.0" << std::endl
64 << std::endl
65 << "Usage: " << name << " [options] -o output project_file (image files)" << std::endl
66 << " Options: " << std::endl
67 << " -c create coordinate images (only TIFF_m output)" << std::endl
68 << " -v|--verbose verbose output" << std::endl
69 << " -d|--debug print detailed output for gpu processing" << std::endl
70 << " -g|--gpu perform image remapping on the GPU" << std::endl
71 << std::endl
72 << " The following options can be used to override settings in the project file:" << std::endl
73 << " -i num remap only image with number num" << std::endl
74 << " (can be specified multiple times)" << std::endl
75 << " -m str set output file format (TIFF, TIFF_m, TIFF_multilayer," << std::endl
76 << " EXR, EXR_m, JPEG, JPEG_m, PNG, PNG_m)" << std::endl
77 << " -r ldr/hdr set output mode." << std::endl
78 << " ldr keep original bit depth and response" << std::endl
79 << " hdr merge to hdr" << std::endl
80 << " -e exposure set exposure for ldr mode" << std::endl
81 << " -p TYPE pixel type of the output. Can be one of:" << std::endl
82 << " UINT8 8 bit unsigned integer" << std::endl
83 << " UINT16 16 bit unsigned integer" << std::endl
84 << " INT16 16 bit signed integer" << std::endl
85 << " UINT32 32 bit unsigned integer" << std::endl
86 << " INT32 32 bit signed integer" << std::endl
87 << " FLOAT 32 bit floating point" << std::endl
88 << " -z|--compression set compression type." << std::endl
89 << " Possible options for tiff output:" << std::endl
90 << " NONE no compression" << std::endl
91 << " PACKBITS packbits compression" << std::endl
92 << " LZW lzw compression" << std::endl
93 << " DEFLATE deflate compression" << std::endl
94 << " For jpeg output set quality number" << std::endl
95 << " --bigtiff Use BigTIFF format for TIFF images" << std::endl
96 << " --ignore-exposure don't correct exposure" << std::endl
97 << " (this does not work with -e switch together)" << std::endl
98 << " --output-range-compression=value set range compression" << std::endl
99 << " value should be a real in range 0..20" << std::endl
100 << " --save-intermediate-images saves also the intermediate" << std::endl
101 << " images (only when output is TIFF, PNG or JPEG)" << std::endl
102 << " --intermediate-suffix=SUFFIX suffix for intermediate images" << std::endl
103 << " --final-suffix=SUFFIX suffix for the final image" << std::endl
104 << " --create-exposure-layers create all exposure layers" << std::endl
105 << " (this will always use TIFF)" << std::endl
106 << " --clip-exposure[=lower cutoff:upper cutoff]" << std::endl
107 << " mask automatically all dark and bright pixels" << std::endl
108 << " optionally you can specify the limits for the" << std::endl
109 << " lower and upper cutoff (specify in range 0...1," << std::endl
110 << " relative the full range)" << std::endl
111 << " --seam=hard|blend select the blend mode for the seam" << std::endl
112 << std::endl;
113}
114
115int main(int argc, char* argv[])
116{
117
118 // parse arguments
119 const char* optstring = "z:cho:i:t:m:p:r:e:vgd";
120 int c;
121
122 bool doCoord = false;
124 std::string basename;
125 std::string outputFormat;
126 bool overrideOutputMode = false;
127 std::string compression;
129 bool overrideExposure = false;
130 double exposure=0;
131 double rangeCompression = -1;
133 int verbose = 0;
134 bool useGPU = false;
135 std::string outputPixelType;
136 bool createExposureLayers = false;
137
138 enum
139 {
140 IGNOREEXPOSURE=1000,
146 SEAMMODE,
149 };
150 static struct option longOptions[] =
151 {
152 { "ignore-exposure", no_argument, NULL, IGNOREEXPOSURE },
153 { "save-intermediate-images", no_argument, NULL, SAVEINTERMEDIATEIMAGES },
154 { "intermediate-suffix", required_argument, NULL, INTERMEDIATESUFFIX },
155 { "final-suffix", required_argument, NULL, FINALSUFFIX },
156 { "compression", required_argument, NULL, 'z' },
157 { "create-exposure-layers", no_argument, NULL, EXPOSURELAYERS },
158 { "clip-exposure", optional_argument, NULL, MASKCLIPEXPOSURE },
159 { "seam", required_argument, NULL, SEAMMODE},
160 { "gpu", no_argument, NULL, 'g'},
161 { "bigtiff", no_argument, NULL, USE_BIGTIFF },
162 { "output-range-compression", required_argument, NULL, RANGECOMPRESSION },
163 { "help", no_argument, NULL, 'h'},
164 { "debug", no_argument, NULL, 'd'},
165 { "verbose", no_argument, NULL, 'v'},
166 { "output", required_argument, NULL, 'o'},
167 0
168 };
169
170 while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
171 {
172 switch (c)
173 {
174 case 'o':
175 basename = optarg;
176 break;
177 case 'c':
178 doCoord = true;
179 break;
180 case 'i':
181 outputImages.insert(atoi(optarg));
182 break;
183 case 'm':
184 outputFormat = optarg;
185 break;
186 case 'p':
187 outputPixelType = optarg;
188 break;
189 case 'r':
190 if (std::string(optarg) == "ldr")
191 {
192 overrideOutputMode = true;
194 }
195 else if (std::string(optarg) == "hdr")
196 {
197 overrideOutputMode = true;
199 }
200 else
201 {
202 std::cerr << hugin_utils::stripPath(argv[0]) << ": \"" << optarg << "\" is not a valid parameter for output mode (-r)." << std::endl;
203 return 1;
204 }
205 break;
206 case 'e':
207 overrideExposure = true;
208 exposure = atof(optarg);
209 break;
210 case IGNOREEXPOSURE:
211 HuginBase::Nona::SetAdvancedOption(advOptions, "ignoreExposure", true);
212 break;
214 HuginBase::Nona::SetAdvancedOption(advOptions, "saveIntermediateImages", true);
215 break;
217 HuginBase::Nona::SetAdvancedOption(advOptions, "saveIntermediateImagesSuffix", std::string(optarg));
218 break;
219 case FINALSUFFIX:
220 HuginBase::Nona::SetAdvancedOption(advOptions, "saveFinalImageSuffix", std::string(optarg));
221 break;
222 case EXPOSURELAYERS:
224 break;
225 case MASKCLIPEXPOSURE:
226 HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposure", true);
227 if (optarg != NULL && *optarg != 0)
228 {
229 // optional argument given, check if valid
230 std::vector<std::string> tokens = hugin_utils::SplitString(std::string(optarg), ":");
231 if (tokens.size() == 2)
232 {
233 double lowerCutoff;
234 double upperCutoff;
236 {
238 {
239 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << tokens[0] << "\" is not a valid number for lower cutoff." << std::endl;
240 return 1;
241 };
243 {
244 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << tokens[1] << "\" is not a valid number for upper cutoff." << std::endl;
245 return 1;
246 };
248 {
249 std::cerr << hugin_utils::stripPath(argv[0]) << ": Lower cutoff \"" << tokens[0] << "\" is higher than upper cutoff" << std::endl
250 << " \"" << tokens[1] << "\". This is no valid input." << std::endl;
251 return 1;
252 };
253 HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposureLowerCutoff", static_cast<float>(lowerCutoff));
254 HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposureUpperCutoff", static_cast<float>(upperCutoff));
255 }
256 else
257 {
258 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << optarg << "\" is not valid number for --clip-exposure" << std::endl
259 << " Expected --clip-exposure=lower cutoff:upper cutoff" << std::endl
260 << " Both should be numbers between 0 and 1." << std::endl;
261 return 1;
262 }
263 }
264 else
265 {
266 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << optarg << "\" is not valid for --clip-exposure" << std::endl
267 << " Expected --clip-exposure=lower cutoff:upper cutoff" << std::endl;
268 return 1;
269 };
270 }
271 break;
272 case SEAMMODE:
273 {
274 std::string text(optarg);
276 if (text == "hard")
277 {
279 }
280 else
281 {
282 if (text == "blend")
283 {
285 }
286 else
287 {
288 std::cerr << hugin_utils::stripPath(argv[0]) << ": String \"" << text << "\" is not a recognized seam blend mode." << std::endl;
289 return 1;
290 };
291 };
292 };
293 break;
294 case 'h':
296 return 0;
297 case 't':
298 std::cout << "WARNING: Switch -t is deprecated. Set environment variable OMP_NUM_THREADS instead" << std::endl;
299 break;
300 case 'v':
301 ++verbose;
302 break;
303 case 'z':
304 compression = optarg;
305 compression=hugin_utils::toupper(compression);
306 break;
307 case 'g':
308#if defined __APPLE__ && defined __aarch64__
309 // disable GPU remapping on ARM Macs, GPU code is not working on these systems
310 useGPU = false;
311 std::cout << "WARNING: GPU remapping is not supported on ARM Macs. Switching back to CPU remapping." << std::endl;
312#else
313 useGPU = true;
314#endif
315 break;
316 case 'd':
318 break;
319 case USE_BIGTIFF:
321 break;
322 case RANGECOMPRESSION:
324 {
325 std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse output range compression (" << optarg << ")." << std::endl;
326 return 1;
327 };
329 {
330 std::cerr << hugin_utils::stripPath(argv[0]) << ": range compression must be a real between 0 and 20." << std::endl;
331 return 1;
332 };
333 break;
334 case ':':
335 case '?':
336 // missing argument or invalid switch
337 return 1;
338 break;
339 default:
340 // this should not happen
341 abort();
342 }
343 }
344
345 if (basename.empty())
346 {
347 std::cerr << hugin_utils::stripPath(argv[0]) << ": No output prefix given." << std::endl;
348 return 1;
349 };
350 if(argc - optind < 1)
351 {
352 std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
353 return 1;
354 }
355 unsigned nCmdLineImgs = argc -optind -1;
356
357 const char* scriptFile = argv[optind];
358
359 // suppress tiff warnings
361
364 {
365 exit(1);
366 };
367
368 if ( nCmdLineImgs > 0)
369 {
370 if (nCmdLineImgs != pano.getNrOfImages())
371 {
372 std::cerr << "Incorrect number of images specified on command line\nProject required " << pano.getNrOfImages() << " but " << nCmdLineImgs << " where given" << std::endl;
373 exit(1);
374 }
375 for (unsigned i=0; i < pano.getNrOfImages(); i++)
376 {
377 pano.setImageFilename(i, argv[optind+i+1]);
378 }
379
380 }
382
383 // save coordinate images, if requested
384 opts.saveCoordImgs = doCoord;
386 {
387 if (!outputFormat.empty())
388 {
389 std::cout << "Warning: Ignoring output format " << outputFormat << std::endl
390 << " Switch --create-exposure-layers will enforce TIFF_m output." << std::endl;
391 };
392 outputFormat = "TIFF";
393 if (!outputImages.empty())
394 {
395 std::cout << "Warning: Ignoring specified output images." << std::endl
396 << " Switch --create-exposure-layers will always work on all active images." << std::endl;
397 outputImages.clear();
398 };
399 };
400 if (outputFormat == "TIFF_m")
401 {
403 opts.outputImageType = "tif";
404 }
405 else if (outputFormat == "JPEG_m")
406 {
408 opts.tiff_saveROI = false;
409 opts.outputImageType = "jpg";
410 }
411 else if (outputFormat == "JPEG")
412 {
414 opts.tiff_saveROI = false;
415 opts.outputImageType = "jpg";
416 }
417 else if (outputFormat == "PNG_m")
418 {
420 opts.tiff_saveROI = false;
421 opts.outputImageType = "png";
422 }
423 else if (outputFormat == "PNG")
424 {
426 opts.tiff_saveROI = false;
427 opts.outputImageType = "png";
428 }
429 else if (outputFormat == "TIFF")
430 {
432 opts.outputImageType = "tif";
433 }
434 else if (outputFormat == "TIFF_multilayer")
435 {
437 opts.outputImageType = "tif";
438 }
439 else if (outputFormat == "EXR_m")
440 {
442 opts.outputImageType = "exr";
443 }
444 else if (outputFormat == "EXR")
445 {
447 opts.outputImageType = "exr";
448 }
449 else if (outputFormat != "")
450 {
451 std::cerr << "Error: unknown output format: " << outputFormat << endl;
452 return 1;
453 }
454
455 if (!compression.empty())
456 {
457 if (opts.outputImageType == "tif")
458 {
459 opts.tiffCompression = compression;
460 }
461 else
462 {
463 if (opts.outputImageType == "jpg")
464 {
465 int q = atoi(compression.c_str());
466 if (q > 0 && q <= 100)
467 {
468 opts.quality = q;
469 }
470 else
471 {
472 std::cerr << "WARNING: \"" << compression << "\" is not valid compression value for jpeg images." << std::endl
473 << " Using value " << opts.quality << " found in pto file." << std::endl;
474 };
475 };
476 };
477 };
478
479 if (!outputPixelType.empty())
480 {
481 opts.outputPixelType = outputPixelType;
482 }
483
485 {
486 opts.outputMode = outputMode;
487 }
488
490 {
491 opts.outputExposureValue = exposure;
492 if (HuginBase::Nona::GetAdvancedOption(advOptions, "ignoreExposure", false))
493 {
494 HuginBase::Nona::SetAdvancedOption(advOptions, "ignoreExposure", false);
495 std::cout << "WARNING: Switches --ignore-exposure and -e can't to used together." << std::endl
496 << " Ignore switch --ignore-exposure." << std::endl;
497 }
498 }
499 if (rangeCompression >= 0.0)
500 {
501 if (HuginBase::Nona::GetAdvancedOption(advOptions, "ignoreExposure", false))
502 {
503 std::cout << "WARNING: Switch --ignore-exposure disables range compression." << std::endl
504 << " --output-range-compression is therefore ignored." << std::endl;
505 }
506 else
507 {
509 };
510 };
511
512 if (outputImages.empty())
513 {
515 }
516 else
517 {
519 for (HuginBase::UIntSet::const_iterator it = outputImages.begin(); it != outputImages.end(); ++it)
520 {
521 if(!set_contains(activeImages,*it))
522 {
523 std::cerr << "The project file does not contains an image with number " << *it << std::endl;
524 return 1;
525 };
526 };
527 };
528 if(outputImages.empty())
529 {
530 std::cout << "Project does not contain active images." << std::endl
531 << "Nothing to do for nona." << std::endl;
532 return 0;
533 };
534 if(useGPU)
535 {
536 switch(opts.getProjection())
537 {
538 // the following projections are not supported by nona-gpu
544 useGPU=false;
545 std::cout << "Nona-GPU does not support this projection. Switch to CPU calculation."<<std::endl;
546 break;
547 default:
548 //fallthrough, to silence compiler warning
549 break;
550 };
551 };
552
553 DEBUG_DEBUG("output basename: " << basename);
554
555 try
556 {
558 if(verbose > 0)
559 {
560 pdisp = new AppBase::StreamProgressDisplay(std::cout);
561 }
562 else
563 {
565 }
566
567 if (useGPU)
568 {
570 if (!useGPU)
571 {
572 std::cout << "Could not initialize GPU. Switching back to CPU calculation." << std::endl;
573 };
574 }
575 opts.remapUsingGPU = useGPU;
576 pano.setOptions(opts);
577
579 {
580 HuginBase::UIntSetVector exposureLayers = getExposureLayers(pano, outputImages, opts);
581 if (exposureLayers.empty())
582 {
583 std::cerr << "ERROR: Could not determine exposure layers. Cancel execution." << std::endl;
584 }
585 else
586 {
587 // we need to pass the basename to the stitcher
588 // because NonaFileOutputStitcher get already filename with numbers added
589 HuginBase::Nona::SetAdvancedOption(advOptions, "basename", basename);
590 for (size_t i = 0; i < exposureLayers.size(); ++i)
591 {
593 // set output exposure to exposure value of first image of layers
594 // normally this this invoked with --ignore-exposure, so this has no effect
595 modOptions.outputExposureValue = pano.getImage(*(exposureLayers[i].begin())).getExposureValue();
596 // build filename
597 std::ostringstream filename;
598 filename << basename << std::setfill('0') << std::setw(4) << i;
600 }
601 }
602 }
603 else
604 {
605 // stitch panorama
607 };
608 // add a final newline, after the last progress message
609 if (verbose > 0)
610 {
611 std::cout << std::endl;
612 }
613
614 if (useGPU)
615 {
617 }
618
619 if(pdisp != NULL)
620 {
621 delete pdisp;
622 pdisp=NULL;
623 }
624 }
625 catch (std::exception& e)
626 {
627 std::cerr << "caught exception: " << e.what() << std::endl;
628 return 1;
629 }
630
631 return 0;
632}
Contains functions to transform whole images.
declaration of functions to handle stacks and layers
static void usage()
Definition Main.cpp:32
Helper class for storing different options.
Dummy progress display, without output.
a progress display to print progress reports to a stream
This class will use the stitchPanorama function of nona.
virtual void run()
runs the algorithm.
Panorama image options.
PanoramaOptions::ProjectionFormat getProjection() const
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
Definition Panorama.cpp:373
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
UIntSet getActiveImages() const
get active images
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
platform/compiler specific stuff.
#define DEBUG_DEBUG(msg)
Definition utils.h:68
std::map< std::string, std::string > AdvancedOptions
void SetAdvancedOption(AdvancedOptions &opts, const std::string &name, const bool value)
store the option with name in AdvancedOptions
bool GetAdvancedOption(const AdvancedOptions &opts, const std::string &name, const bool defaultValue)
check if given option is saved and return its boolean value, otherwise return defaultValue
std::vector< UIntSet > UIntSetVector
UIntSet getImagesinROI(const PanoramaData &pano, const UIntSet activeImages)
returns set of images which are visible in output ROI
std::set< unsigned int > UIntSet
std::string toupper(const std::string &s)
Definition stl_utils.h:59
std::vector< std::string > SplitString(const std::string &s, const std::string &sep)
split string s at given sep, returns vector of strings
Definition utils.cpp:294
bool initGPU(int *argcp, char **argv)
Try to initalise GLUT and GLEW, and create an OpenGL context for GPU stitching.
Definition utils.cpp:845
bool wrapupGPU()
cleanup GPU settings
Definition utils.cpp:901
std::string tolower(const std::string &s)
convert a string to lowercase
Definition stl_utils.h:49
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition utils.h:114
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 getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
void SetGPUDebugMessages(const bool doPrint)
int main(int argc, char *argv[])
Definition nona.cpp:115
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
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