nona.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00026 #include <hugin_config.h>
00027 #include <fstream>
00028 #include <sstream>
00029 
00030 #include <algorithm>
00031 #include <cctype>
00032 #include <string>
00033 
00034 #include <vigra/error.hxx>
00035 
00036 #include <getopt.h>
00037 
00038 #include <hugin_basic.h>
00039 #include "hugin_base/algorithms/basic/LayerStacks.h"
00040 #include <hugin_utils/platform.h>
00041 #include <algorithms/nona/NonaFileStitcher.h>
00042 #include <vigra_ext/ImageTransformsGPU.h>
00043 #include "hugin_utils/stl_utils.h"
00044 #include "nona/StitcherOptions.h"
00045 
00046 #include <tiffio.h>
00047 
00048 static void usage(const char* name)
00049 {
00050     std::cout << name << ": stitch a panorama image" << std::endl
00051          << std::endl
00052          << "nona version " << hugin_utils::GetHuginVersion() << std::endl
00053          << std::endl
00054          << " It uses the transform function from PanoTools, the stitching itself" << std::endl
00055          << " is quite simple, no seam feathering is done." << std::endl
00056          << " only the non-antialiasing interpolators of panotools are supported" << std::endl
00057          << std::endl
00058          << " The following output formats (n option of panotools p script line)" << std::endl
00059          << " are supported:"<< std::endl
00060          << std::endl
00061          << "  JPEG, TIFF, PNG  : Single image formats with internal blender"<< std::endl
00062          << "  JPEG_m, TIFF_m, PNG_m : multiple image files"<< std::endl
00063          << "  TIFF_multilayer : Multilayer tiff files, readable by The Gimp 2.0" << std::endl
00064          << std::endl
00065          << "Usage: " << name  << " [options] -o output project_file (image files)" << std::endl
00066          << "  Options: " << std::endl
00067          << "      -c         create coordinate images (only TIFF_m output)" << std::endl
00068          << "      -v         verbose output" << std::endl
00069          << "      -d         print detailed output for gpu processing" << std::endl
00070          << "      -g|--gpu   perform image remapping on the GPU" << std::endl
00071          << std::endl
00072          << "  The following options can be used to override settings in the project file:" << std::endl
00073          << "      -i num     remap only image with number num" << std::endl
00074          << "                   (can be specified multiple times)" << std::endl
00075          << "      -m str     set output file format (TIFF, TIFF_m, TIFF_multilayer," << std::endl
00076          << "                    EXR, EXR_m, JPEG, JPEG_m, PNG, PNG_m)" << std::endl
00077          << "      -r ldr/hdr set output mode." << std::endl
00078          << "                   ldr  keep original bit depth and response" << std::endl
00079          << "                   hdr  merge to hdr" << std::endl
00080          << "      -e exposure set exposure for ldr mode" << std::endl
00081          << "      -p TYPE    pixel type of the output. Can be one of:" << std::endl
00082          << "                  UINT8   8 bit unsigned integer" << std::endl
00083          << "                  UINT16  16 bit unsigned integer" << std::endl
00084          << "                  INT16   16 bit signed integer" << std::endl
00085          << "                  UINT32  32 bit unsigned integer" << std::endl
00086          << "                  INT32   32 bit signed integer" << std::endl
00087          << "                  FLOAT   32 bit floating point" << std::endl
00088          << "      -z|--compression set compression type." << std::endl
00089          << "                  Possible options for tiff output:" << std::endl
00090          << "                   NONE      no compression" << std::endl
00091          << "                   PACKBITS  packbits compression" << std::endl
00092          << "                   LZW       lzw compression" << std::endl
00093          << "                   DEFLATE   deflate compression" << std::endl
00094          << "                  For jpeg output set quality number" << std::endl
00095          << "      --bigtiff   Use BigTIFF format for TIFF images" << std::endl
00096          << "      --ignore-exposure  don't correct exposure" << std::endl
00097          << "                   (this does not work with -e switch together)" << std::endl
00098          << "      --save-intermediate-images  saves also the intermediate" << std::endl
00099          << "                   images (only when output is TIFF, PNG or JPEG)" << std::endl
00100          << "      --intermediate-suffix=SUFFIX  suffix for intermediate images" << std::endl
00101          << "      --create-exposure-layers  create all exposure layers" << std::endl
00102          << "                   (this will always use TIFF)" << std::endl
00103          << "      --clip-exposure[=lower cutoff:upper cutoff]" << std::endl
00104          << "                   mask automatically all dark and bright pixels" << std::endl
00105          << "                   optionally you can specify the limits for the" << std::endl
00106          << "                   lower and upper cutoff (specify in range 0...1," << std::endl
00107          << "                   relative the full range)" << std::endl
00108          << "      --seam=hard|blend   select the blend mode for the seam" << std::endl
00109          << std::endl;
00110 }
00111 
00112 int main(int argc, char* argv[])
00113 {
00114 
00115     // parse arguments
00116     const char* optstring = "z:cho:i:t:m:p:r:e:vgd";
00117     int c;
00118 
00119     bool doCoord = false;
00120     HuginBase::UIntSet outputImages;
00121     std::string basename;
00122     std::string outputFormat;
00123     bool overrideOutputMode = false;
00124     std::string compression;
00125     HuginBase::PanoramaOptions::OutputMode outputMode = HuginBase::PanoramaOptions::OUTPUT_LDR;
00126     bool overrideExposure = false;
00127     double exposure=0;
00128     HuginBase::Nona::AdvancedOptions advOptions;
00129     int verbose = 0;
00130     bool useGPU = false;
00131     std::string outputPixelType;
00132     bool createExposureLayers = false;
00133 
00134     enum
00135     {
00136         IGNOREEXPOSURE=1000,
00137         SAVEINTERMEDIATEIMAGES,
00138         INTERMEDIATESUFFIX,
00139         EXPOSURELAYERS,
00140         MASKCLIPEXPOSURE,
00141         SEAMMODE,
00142         USE_BIGTIFF
00143     };
00144     static struct option longOptions[] =
00145     {
00146         { "ignore-exposure", no_argument, NULL, IGNOREEXPOSURE },
00147         { "save-intermediate-images", no_argument, NULL, SAVEINTERMEDIATEIMAGES },
00148         { "intermediate-suffix", required_argument, NULL, INTERMEDIATESUFFIX },
00149         { "compression", required_argument, NULL, 'z' },
00150         { "create-exposure-layers", no_argument, NULL, EXPOSURELAYERS },
00151         { "clip-exposure", optional_argument, NULL, MASKCLIPEXPOSURE },
00152         { "seam", required_argument, NULL, SEAMMODE},
00153         { "gpu", no_argument, NULL, 'g'},
00154         { "bigtiff", no_argument, NULL, USE_BIGTIFF },
00155         { "help", no_argument, NULL, 'h'},
00156         0
00157     };
00158     
00159     while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
00160     {
00161         switch (c)
00162         {
00163             case 'o':
00164                 basename = optarg;
00165                 break;
00166             case 'c':
00167                 doCoord = true;
00168                 break;
00169             case 'i':
00170                 outputImages.insert(atoi(optarg));
00171                 break;
00172             case 'm':
00173                 outputFormat = optarg;
00174                 break;
00175             case 'p':
00176                 outputPixelType = optarg;
00177                 break;
00178             case 'r':
00179                 if (std::string(optarg) == "ldr")
00180                 {
00181                     overrideOutputMode = true;
00182                     outputMode = HuginBase::PanoramaOptions::OUTPUT_LDR;
00183                 }
00184                 else if (std::string(optarg) == "hdr")
00185                 {
00186                     overrideOutputMode = true;
00187                     outputMode = HuginBase::PanoramaOptions::OUTPUT_HDR;
00188                 }
00189                 else
00190                 {
00191                     std::cerr << hugin_utils::stripPath(argv[0]) << ": \"" << optarg << "\" is not a valid parameter for output mode (-r)." << std::endl;
00192                     return 1;
00193                 }
00194                 break;
00195             case 'e':
00196                 overrideExposure = true;
00197                 exposure = atof(optarg);
00198                 break;
00199             case IGNOREEXPOSURE:
00200                 HuginBase::Nona::SetAdvancedOption(advOptions, "ignoreExposure", true);
00201                 break;
00202             case SAVEINTERMEDIATEIMAGES:
00203                 HuginBase::Nona::SetAdvancedOption(advOptions, "saveIntermediateImages", true);
00204                 break;
00205             case INTERMEDIATESUFFIX:
00206                 HuginBase::Nona::SetAdvancedOption(advOptions, "saveIntermediateImagesSuffix", std::string(optarg));
00207                 break;
00208             case EXPOSURELAYERS:
00209                 createExposureLayers = true;
00210                 break;
00211             case MASKCLIPEXPOSURE:
00212                 HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposure", true);
00213                 if (optarg != NULL && *optarg != 0)
00214                 {
00215                     // optional argument given, check if valid
00216                     std::vector<std::string> tokens = hugin_utils::SplitString(std::string(optarg), ":");
00217                     if (tokens.size() == 2)
00218                     {
00219                         double lowerCutoff;
00220                         double upperCutoff;
00221                         if (hugin_utils::stringToDouble(tokens[0], lowerCutoff) && hugin_utils::stringToDouble(tokens[1], upperCutoff))
00222                         {
00223                             if (lowerCutoff < 0 || lowerCutoff>1)
00224                             {
00225                                 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << tokens[0] << "\" is not a valid number for lower cutoff." << std::endl;
00226                                 return 1;
00227                             };
00228                             if (upperCutoff < 0 || upperCutoff>1)
00229                             {
00230                                 std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << tokens[1] << "\" is not a valid number for upper cutoff." << std::endl;
00231                                 return 1;
00232                             };
00233                             if (lowerCutoff >= upperCutoff)
00234                             {
00235                                 std::cerr << hugin_utils::stripPath(argv[0]) << ": Lower cutoff \"" << tokens[0] << "\" is higher than upper cutoff" << std::endl
00236                                     << "     \"" << tokens[1] << "\". This is no valid input." << std::endl;
00237                                 return 1;
00238                             };
00239                             HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposureLowerCutoff", static_cast<float>(lowerCutoff));
00240                             HuginBase::Nona::SetAdvancedOption(advOptions, "maskClipExposureUpperCutoff", static_cast<float>(upperCutoff));
00241                         }
00242                         else
00243                         {
00244                             std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << optarg << "\" is not valid number for --clip-exposure" << std::endl
00245                                 << "      Expected --clip-exposure=lower cutoff:upper cutoff" << std::endl
00246                                 << "      Both should be numbers between 0 and 1." << std::endl;
00247                             return 1;
00248                         }
00249                     }
00250                     else
00251                     {
00252                         std::cerr << hugin_utils::stripPath(argv[0]) << ": Argument \"" << optarg << "\" is not valid for --clip-exposure" << std::endl
00253                             << "      Expected --clip-exposure=lower cutoff:upper cutoff" << std::endl;
00254                         return 1;
00255                     };
00256                 }
00257                 break;
00258             case SEAMMODE:
00259                 {
00260                     std::string text(optarg);
00261                     text = hugin_utils::tolower(text);
00262                     if (text == "hard")
00263                     {
00264                         HuginBase::Nona::SetAdvancedOption(advOptions, "hardSeam", true);
00265                     }
00266                     else
00267                     {
00268                         if (text == "blend")
00269                         {
00270                             HuginBase::Nona::SetAdvancedOption(advOptions, "hardSeam", false);
00271                         }
00272                         else
00273                         {
00274                             std::cerr << hugin_utils::stripPath(argv[0]) << ": String \"" << text << "\" is not a recognized seam blend mode." << std::endl;
00275                             return 1;
00276                         };
00277                     };
00278                 };
00279                 break;
00280             case 'h':
00281                 usage(hugin_utils::stripPath(argv[0]).c_str());
00282                 return 0;
00283             case 't':
00284                 std::cout << "WARNING: Switch -t is deprecated. Set environment variable OMP_NUM_THREADS instead" << std::endl;
00285                 break;
00286             case 'v':
00287                 ++verbose;
00288                 break;
00289             case 'z':
00290                 compression = optarg;
00291                 compression=hugin_utils::toupper(compression);
00292                 break;
00293             case 'g':
00294                 useGPU = true;
00295                 break;
00296             case 'd':
00297                 vigra_ext::SetGPUDebugMessages(true);
00298                 break;
00299             case USE_BIGTIFF:
00300                 HuginBase::Nona::SetAdvancedOption(advOptions, "useBigTIFF", true);
00301                 break;
00302             case ':':
00303             case '?':
00304                 // missing argument or invalid switch
00305                 return 1;
00306                 break;
00307             default:
00308                 // this should not happen
00309                 abort();
00310         }
00311     }
00312 
00313     if (basename.empty())
00314     {
00315         std::cerr << hugin_utils::stripPath(argv[0]) << ": No output prefix given." << std::endl;
00316         return 1;
00317     };
00318     if(argc - optind < 1)
00319     {
00320         std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
00321         return 1;
00322     }
00323     unsigned nCmdLineImgs = argc -optind -1;
00324 
00325     const char* scriptFile = argv[optind];
00326 
00327     // suppress tiff warnings
00328     TIFFSetWarningHandler(0);
00329 
00330     HuginBase::Panorama pano;
00331     std::ifstream prjfile(scriptFile);
00332     if (prjfile.bad())
00333     {
00334         std::cerr << "could not open script : " << scriptFile << std::endl;
00335         exit(1);
00336     }
00337     pano.setFilePrefix(hugin_utils::getPathPrefix(scriptFile));
00338     AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00339     if (err != AppBase::DocumentData::SUCCESSFUL)
00340     {
00341         std::cerr << "error while parsing panos tool script: " << scriptFile << std::endl;
00342         exit(1);
00343     }
00344 
00345     if ( nCmdLineImgs > 0)
00346     {
00347         if (nCmdLineImgs != pano.getNrOfImages())
00348         {
00349             std::cerr << "Incorrect number of images specified on command line\nProject required " << pano.getNrOfImages() << " but " << nCmdLineImgs << " where given" << std::endl;
00350             exit(1);
00351         }
00352         for (unsigned i=0; i < pano.getNrOfImages(); i++)
00353         {
00354             pano.setImageFilename(i, argv[optind+i+1]);
00355         }
00356 
00357     }
00358     HuginBase::PanoramaOptions  opts = pano.getOptions();
00359 
00360     // save coordinate images, if requested
00361     opts.saveCoordImgs = doCoord;
00362     if (createExposureLayers)
00363     {
00364         if (!outputFormat.empty())
00365         {
00366             std::cout << "Warning: Ignoring output format " << outputFormat << std::endl
00367                 << "         Switch --create-exposure-layers will enforce TIFF_m output." << std::endl;
00368         };
00369         outputFormat = "TIFF";
00370         if (!outputImages.empty())
00371         {
00372             std::cout << "Warning: Ignoring specified output images." << std::endl
00373                 << "         Switch --create-exposure-layers will always work on all active images." << std::endl;
00374             outputImages.clear();
00375         };
00376     };
00377     if (outputFormat == "TIFF_m")
00378     {
00379         opts.outputFormat = HuginBase::PanoramaOptions::TIFF_m;
00380         opts.outputImageType = "tif";
00381     }
00382     else if (outputFormat == "JPEG_m")
00383     {
00384         opts.outputFormat = HuginBase::PanoramaOptions::JPEG_m;
00385         opts.tiff_saveROI = false;
00386         opts.outputImageType = "jpg";
00387     }
00388     else if (outputFormat == "JPEG")
00389     {
00390         opts.outputFormat = HuginBase::PanoramaOptions::JPEG;
00391         opts.tiff_saveROI = false;
00392         opts.outputImageType = "jpg";
00393     }
00394     else if (outputFormat == "PNG_m")
00395     {
00396         opts.outputFormat = HuginBase::PanoramaOptions::PNG_m;
00397         opts.tiff_saveROI = false;
00398         opts.outputImageType = "png";
00399     }
00400     else if (outputFormat == "PNG")
00401     {
00402         opts.outputFormat = HuginBase::PanoramaOptions::PNG;
00403         opts.tiff_saveROI = false;
00404         opts.outputImageType = "png";
00405     }
00406     else if (outputFormat == "TIFF")
00407     {
00408         opts.outputFormat = HuginBase::PanoramaOptions::TIFF;
00409         opts.outputImageType = "tif";
00410     }
00411     else if (outputFormat == "TIFF_multilayer")
00412     {
00413         opts.outputFormat = HuginBase::PanoramaOptions::TIFF_multilayer;
00414         opts.outputImageType = "tif";
00415     }
00416     else if (outputFormat == "EXR_m")
00417     {
00418         opts.outputFormat = HuginBase::PanoramaOptions::EXR_m;
00419         opts.outputImageType = "exr";
00420     }
00421     else if (outputFormat == "EXR")
00422     {
00423         opts.outputFormat = HuginBase::PanoramaOptions::EXR;
00424         opts.outputImageType = "exr";
00425     }
00426     else if (outputFormat != "")
00427     {
00428         std::cerr << "Error: unknown output format: " << outputFormat << endl;
00429         return 1;
00430     }
00431 
00432     if (!compression.empty())
00433     {
00434         if (opts.outputImageType == "tif")
00435         {
00436             opts.tiffCompression = compression;
00437         }
00438         else
00439         {
00440             if (opts.outputImageType == "jpg")
00441             {
00442                 int q = atoi(compression.c_str());
00443                 if (q > 0 && q <= 100)
00444                 {
00445                     opts.quality = q;
00446                 }
00447                 else
00448                 {
00449                     std::cerr << "WARNING: \"" << compression << "\" is not valid compression value for jpeg images." << std::endl
00450                         << "         Using value " << opts.quality << " found in pto file." << std::endl;
00451                 };
00452             };
00453         };
00454     };
00455 
00456     if (outputPixelType.size() > 0)
00457     {
00458         opts.outputPixelType = outputPixelType;
00459     }
00460 
00461     if (overrideOutputMode)
00462     {
00463         opts.outputMode = outputMode;
00464     }
00465 
00466     if (overrideExposure)
00467     {
00468         opts.outputExposureValue = exposure;
00469         if (HuginBase::Nona::GetAdvancedOption(advOptions, "ignoreExosure", false))
00470         {
00471             HuginBase::Nona::SetAdvancedOption(advOptions, "ignoreExposure", false);
00472             std::cout << "WARNING: Switches --ignore-exposure and -e can't to used together." << std::endl
00473                 << "         Ignore switch --ignore-exposure." << std::endl;
00474         }
00475     }
00476 
00477     if (outputImages.empty())
00478     {
00479         outputImages = HuginBase::getImagesinROI(pano, pano.getActiveImages());
00480     }
00481     else
00482     {
00483         HuginBase::UIntSet activeImages = HuginBase::getImagesinROI(pano, pano.getActiveImages());
00484         for (HuginBase::UIntSet::const_iterator it = outputImages.begin(); it != outputImages.end(); ++it)
00485         {
00486             if(!set_contains(activeImages,*it))
00487             {
00488                 std::cerr << "The project file does not contains an image with number " << *it << std::endl;
00489                 return 1;
00490             };
00491         };
00492     };
00493     if(outputImages.empty())
00494     {
00495         std::cout << "Project does not contain active images." << std::endl
00496                   << "Nothing to do for nona." << std::endl;
00497         return 0;
00498     };
00499     if(useGPU)
00500     {
00501         switch(opts.getProjection())
00502         {
00503             // the following projections are not supported by nona-gpu
00504             case HuginBase::PanoramaOptions::BIPLANE:
00505             case HuginBase::PanoramaOptions::TRIPLANE:
00506             case HuginBase::PanoramaOptions::PANINI:
00507             case HuginBase::PanoramaOptions::EQUI_PANINI:
00508             case HuginBase::PanoramaOptions::GENERAL_PANINI:
00509                 useGPU=false;
00510                 std::cout << "Nona-GPU does not support this projection. Switch to CPU calculation."<<std::endl;
00511                 break;
00512             default:
00513                 //fallthrough, to silence compiler warning
00514                 break;
00515         };
00516     };
00517 
00518     DEBUG_DEBUG("output basename: " << basename);
00519 
00520     try
00521     {
00522         AppBase::ProgressDisplay* pdisp = NULL;
00523         if(verbose > 0)
00524         {
00525             pdisp = new AppBase::StreamProgressDisplay(std::cout);
00526         }
00527         else
00528         {
00529             pdisp = new AppBase::DummyProgressDisplay;
00530         }
00531 
00532         if (useGPU)
00533         {
00534             useGPU = hugin_utils::initGPU(&argc, argv);
00535         }
00536         opts.remapUsingGPU = useGPU;
00537         pano.setOptions(opts);
00538 
00539         if (createExposureLayers)
00540         {
00541             HuginBase::UIntSetVector exposureLayers = getExposureLayers(pano, outputImages, opts);
00542             if (exposureLayers.empty())
00543             {
00544                 std::cerr << "ERROR: Could not determine exposure layers. Cancel execution." << std::endl;
00545             }
00546             else
00547             {
00548                 // we need to pass the basename to the stitcher
00549                 // because NonaFileOutputStitcher get already filename with numbers added
00550                 HuginBase::Nona::SetAdvancedOption(advOptions, "basename", basename);
00551                 for (size_t i = 0; i < exposureLayers.size(); ++i)
00552                 {
00553                     HuginBase::PanoramaOptions modOptions(opts);
00554                     // set output exposure to exposure value of first image of layers
00555                     // normaly this this invoked with --ignore-exposure, so this has no effect
00556                     modOptions.outputExposureValue = pano.getImage(*(exposureLayers[i].begin())).getExposureValue();
00557                     // build filename
00558                     std::ostringstream filename;
00559                     filename << basename << std::setfill('0') << std::setw(4) << i;
00560                     HuginBase::NonaFileOutputStitcher(pano, pdisp, modOptions, exposureLayers[i], filename.str(), advOptions).run();
00561                 }
00562             }
00563         }
00564         else
00565         {
00566             // stitch panorama
00567             HuginBase::NonaFileOutputStitcher(pano, pdisp, opts, outputImages, basename, advOptions).run();
00568         };
00569         // add a final newline, after the last progress message
00570         if (verbose > 0)
00571         {
00572             std::cout << std::endl;
00573         }
00574 
00575         if (useGPU)
00576         {
00577             hugin_utils::wrapupGPU();
00578         }
00579 
00580         if(pdisp != NULL)
00581         {
00582             delete pdisp;
00583             pdisp=NULL;
00584         }
00585     }
00586     catch (std::exception& e)
00587     {
00588         std::cerr << "caught exception: " << e.what() << std::endl;
00589         return 1;
00590     }
00591 
00592     return 0;
00593 }

Generated on 17 Nov 2017 for Hugintrunk by  doxygen 1.4.7