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 useGPU = true;
309 break;
310 case 'd':
312 break;
313 case USE_BIGTIFF:
315 break;
316 case RANGECOMPRESSION:
318 {
319 std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse output range compression (" << optarg << ")." << std::endl;
320 return 1;
321 };
323 {
324 std::cerr << hugin_utils::stripPath(argv[0]) << ": range compression must be a real between 0 and 20." << std::endl;
325 return 1;
326 };
327 break;
328 case ':':
329 case '?':
330 // missing argument or invalid switch
331 return 1;
332 break;
333 default:
334 // this should not happen
335 abort();
336 }
337 }
338
339 if (basename.empty())
340 {
341 std::cerr << hugin_utils::stripPath(argv[0]) << ": No output prefix given." << std::endl;
342 return 1;
343 };
344 if(argc - optind < 1)
345 {
346 std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
347 return 1;
348 }
349 unsigned nCmdLineImgs = argc -optind -1;
350
351 const char* scriptFile = argv[optind];
352
353 // suppress tiff warnings
355
358 {
359 exit(1);
360 };
361
362 if ( nCmdLineImgs > 0)
363 {
364 if (nCmdLineImgs != pano.getNrOfImages())
365 {
366 std::cerr << "Incorrect number of images specified on command line\nProject required " << pano.getNrOfImages() << " but " << nCmdLineImgs << " where given" << std::endl;
367 exit(1);
368 }
369 for (unsigned i=0; i < pano.getNrOfImages(); i++)
370 {
371 pano.setImageFilename(i, argv[optind+i+1]);
372 }
373
374 }
376
377 // save coordinate images, if requested
378 opts.saveCoordImgs = doCoord;
380 {
381 if (!outputFormat.empty())
382 {
383 std::cout << "Warning: Ignoring output format " << outputFormat << std::endl
384 << " Switch --create-exposure-layers will enforce TIFF_m output." << std::endl;
385 };
386 outputFormat = "TIFF";
387 if (!outputImages.empty())
388 {
389 std::cout << "Warning: Ignoring specified output images." << std::endl
390 << " Switch --create-exposure-layers will always work on all active images." << std::endl;
391 outputImages.clear();
392 };
393 };
394 if (outputFormat == "TIFF_m")
395 {
397 opts.outputImageType = "tif";
398 }
399 else if (outputFormat == "JPEG_m")
400 {
402 opts.tiff_saveROI = false;
403 opts.outputImageType = "jpg";
404 }
405 else if (outputFormat == "JPEG")
406 {
408 opts.tiff_saveROI = false;
409 opts.outputImageType = "jpg";
410 }
411 else if (outputFormat == "PNG_m")
412 {
414 opts.tiff_saveROI = false;
415 opts.outputImageType = "png";
416 }
417 else if (outputFormat == "PNG")
418 {
420 opts.tiff_saveROI = false;
421 opts.outputImageType = "png";
422 }
423 else if (outputFormat == "TIFF")
424 {
426 opts.outputImageType = "tif";
427 }
428 else if (outputFormat == "TIFF_multilayer")
429 {
431 opts.outputImageType = "tif";
432 }
433 else if (outputFormat == "EXR_m")
434 {
436 opts.outputImageType = "exr";
437 }
438 else if (outputFormat == "EXR")
439 {
441 opts.outputImageType = "exr";
442 }
443 else if (outputFormat != "")
444 {
445 std::cerr << "Error: unknown output format: " << outputFormat << endl;
446 return 1;
447 }
448
449 if (!compression.empty())
450 {
451 if (opts.outputImageType == "tif")
452 {
453 opts.tiffCompression = compression;
454 }
455 else
456 {
457 if (opts.outputImageType == "jpg")
458 {
459 int q = atoi(compression.c_str());
460 if (q > 0 && q <= 100)
461 {
462 opts.quality = q;
463 }
464 else
465 {
466 std::cerr << "WARNING: \"" << compression << "\" is not valid compression value for jpeg images." << std::endl
467 << " Using value " << opts.quality << " found in pto file." << std::endl;
468 };
469 };
470 };
471 };
472
473 if (!outputPixelType.empty())
474 {
475 opts.outputPixelType = outputPixelType;
476 }
477
479 {
480 opts.outputMode = outputMode;
481 }
482
484 {
485 opts.outputExposureValue = exposure;
486 if (HuginBase::Nona::GetAdvancedOption(advOptions, "ignoreExposure", false))
487 {
488 HuginBase::Nona::SetAdvancedOption(advOptions, "ignoreExposure", false);
489 std::cout << "WARNING: Switches --ignore-exposure and -e can't to used together." << std::endl
490 << " Ignore switch --ignore-exposure." << std::endl;
491 }
492 }
493 if (rangeCompression >= 0.0)
494 {
495 if (HuginBase::Nona::GetAdvancedOption(advOptions, "ignoreExposure", false))
496 {
497 std::cout << "WARNING: Switch --ignore-exposure disables range compression." << std::endl
498 << " --output-range-compression is therefore ignored." << std::endl;
499 }
500 else
501 {
503 };
504 };
505
506 if (outputImages.empty())
507 {
509 }
510 else
511 {
513 for (HuginBase::UIntSet::const_iterator it = outputImages.begin(); it != outputImages.end(); ++it)
514 {
515 if(!set_contains(activeImages,*it))
516 {
517 std::cerr << "The project file does not contains an image with number " << *it << std::endl;
518 return 1;
519 };
520 };
521 };
522 if(outputImages.empty())
523 {
524 std::cout << "Project does not contain active images." << std::endl
525 << "Nothing to do for nona." << std::endl;
526 return 0;
527 };
528 if(useGPU)
529 {
530 switch(opts.getProjection())
531 {
532 // the following projections are not supported by nona-gpu
538 useGPU=false;
539 std::cout << "Nona-GPU does not support this projection. Switch to CPU calculation."<<std::endl;
540 break;
541 default:
542 //fallthrough, to silence compiler warning
543 break;
544 };
546 {
547 useGPU = false;
548 std::cout << "Nona-GPU does not support anti-alias interpolator. Switch to CPU calculation." << std::endl;
549 };
550 };
551
552 DEBUG_DEBUG("output basename: " << basename);
553
554 try
555 {
557 if(verbose > 0)
558 {
559 pdisp = new AppBase::StreamProgressDisplay(std::cout);
560 }
561 else
562 {
564 }
565
566 if (useGPU)
567 {
569 if (!useGPU)
570 {
571 std::cout << "Could not initialize GPU. Switching back to CPU calculation." << std::endl;
572 };
573 }
574 opts.remapUsingGPU = useGPU;
575 pano.setOptions(opts);
576
578 {
579 HuginBase::UIntSetVector exposureLayers = getExposureLayers(pano, outputImages, opts);
580 if (exposureLayers.empty())
581 {
582 std::cerr << "ERROR: Could not determine exposure layers. Cancel execution." << std::endl;
583 }
584 else
585 {
586 // we need to pass the basename to the stitcher
587 // because NonaFileOutputStitcher get already filename with numbers added
588 HuginBase::Nona::SetAdvancedOption(advOptions, "basename", basename);
589 for (size_t i = 0; i < exposureLayers.size(); ++i)
590 {
592 // set output exposure to exposure value of first image of layers
593 // normally this this invoked with --ignore-exposure, so this has no effect
594 modOptions.outputExposureValue = pano.getImage(*(exposureLayers[i].begin())).getExposureValue();
595 // build filename
596 std::ostringstream filename;
597 filename << basename << std::setfill('0') << std::setw(4) << i;
599 }
600 }
601 }
602 else
603 {
604 // stitch panorama
606 };
607 // add a final newline, after the last progress message
608 if (verbose > 0)
609 {
610 std::cout << std::endl;
611 }
612
613 if (useGPU)
614 {
616 }
617
618 if(pdisp != NULL)
619 {
620 delete pdisp;
621 pdisp=NULL;
622 }
623 }
624 catch (std::exception& e)
625 {
626 std::cerr << "caught exception: " << e.what() << std::endl;
627 return 1;
628 }
629
630 return 0;
631}
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.
vigra_ext::Interpolator interpolator
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