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

Generated on 2 Dec 2016 for Hugintrunk by  doxygen 1.4.7