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

Generated on 8 Feb 2016 for Hugintrunk by  doxygen 1.4.7