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

Generated on 23 Aug 2016 for Hugintrunk by  doxygen 1.4.7