hugin_hdrmerge.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include <hugin_config.h>
00028 
00029 #include <fstream>
00030 #include <sstream>
00031 #include <cmath>
00032 #include <algorithm>
00033 
00034 #include <memory>
00035 
00036 #include <vigra/error.hxx>
00037 #include <vigra/functorexpression.hxx>
00038 
00039 #include <hugin_utils/utils.h>
00040 
00041 #include <vigra_ext/impexalpha.hxx>
00042 #include <vigra_ext/HDRUtils.h>
00043 #include <vigra_ext/ReduceOpenEXR.h>
00044 
00045 #include <getopt.h>
00046 
00047 #include "../deghosting/deghosting.h"
00048 #include "../deghosting/khan.h"
00049 
00050 // define the types of images we will use.
00051 // use float for RGB
00052 typedef vigra::FRGBImage ImageType;
00053 
00054 // smart pointers to the images.
00055 typedef std::shared_ptr<ImageType> ImagePtr;
00056 
00057 static int g_verbose = 0;
00058 
00059 const uint16_t OTHER_GRAY = 1;
00060 
00061 // load all images and apply a weighted average merge, with
00062 // special cases for completely over or underexposed pixels.
00063 bool mergeWeightedAverage(std::vector<std::string> inputFiles, vigra::FRGBImage& output)
00064 {
00065     // load all images into memory
00066     std::vector<ImagePtr> images;
00067     std::vector<deghosting::BImagePtr> weightImages;
00068 
00069     for (size_t i=0; i < inputFiles.size(); i++)
00070     {
00071         ImagePtr img = ImagePtr(new ImageType());
00072         deghosting::BImagePtr weight = deghosting::BImagePtr(new vigra::BImage());
00073 
00074         if (g_verbose > 0)
00075         {
00076             std::cout << "Loading image: " << inputFiles[i] << std::endl;
00077         }
00078         vigra::ImageImportInfo info(inputFiles[i].c_str());
00079         img->resize(info.size());
00080         weight->resize(info.size());
00081         if (info.numBands() == 4)
00082         {
00083             importImageAlpha(info, destImage(*img), destImage(*weight));
00084         }
00085         else
00086         {
00087             importImage(info, destImage(*img));
00088         }
00089         images.push_back(img);
00090         weightImages.push_back(weight);
00091     }
00092 
00093     // ensure all images have the same size (cropped images not supported yet)
00094     int width=images[0]->width();
00095     int height=images[0]->height();
00096     for (unsigned i=1; i < images.size(); i++)
00097     {
00098         if (images[i]->width() != width || images[i]->height() != height)
00099         {
00100             std::cerr << "Error: Input images need to be of the same size" << std::endl;
00101             return false;
00102         }
00103     }
00104     output.resize(width,height);
00105     if (g_verbose > 0)
00106     {
00107         std::cout << "Calculating weighted average " << std::endl;
00108     }
00109     // apply weighted average functor with
00110     // heuristic to deal with pixels that are overexposed in all images
00111     vigra_ext::ReduceToHDRFunctor<ImageType::value_type> waverage;
00112 
00113     // loop over all pixels in the image (very low level access)
00114     for (int y=0; y < height; y++)
00115     {
00116         for (int x=0; x < width; x++)
00117         {
00118             waverage.reset();
00119             // loop over all exposures
00120             for (unsigned imgNr=0; imgNr < images.size(); imgNr++)
00121             {
00122                 // add pixel to weighted average
00123                 waverage( (*images[imgNr])(x,y), (*weightImages[imgNr])(x,y) );
00124             }
00125             // get result
00126             output(x,y) = waverage();
00127         }
00128     }
00129     return true;
00130 }
00131 
00133 bool weightedAverageOfImageFiles(const std::vector<std::string>& inputFiles,
00134                                  const std::vector<deghosting::FImagePtr>& weights,
00135                                  vigra::FRGBImage& output)
00136 {
00137     if(g_verbose > 0)
00138     {
00139         std::cout << "Merging input images" << std::endl;
00140     }
00141     int width = (weights[0])->width();
00142     int height = (weights[0])->height();
00143 
00144     assert(inputFiles.size() == weights.size());
00145 
00146     vigra::BasicImage<vigra::NumericTraits<vigra::FRGBImage::PixelType>::Promote> weightedImg(width, height);
00147     vigra::BasicImage<vigra::NumericTraits<vigra::FImage::PixelType>::Promote> weightAdded(width, height);
00148     for(unsigned i = 0; i < inputFiles.size(); i++)
00149     {
00150         vigra::ImageImportInfo inputInfo(inputFiles[i].c_str());
00151         vigra::BasicImage<vigra::NumericTraits<vigra::FRGBImage::PixelType>::Promote> tmpImg(inputInfo.size());
00152 
00153         //load image
00154         if (inputInfo.numBands() == 4)
00155         {
00156             vigra::BImage tmpMask(inputInfo.size());
00157             vigra::importImageAlpha(inputInfo, vigra::destImage(tmpImg), vigra::destImage(tmpMask));
00158         }
00159         else
00160         {
00161             vigra::importImage(inputInfo, vigra::destImage(tmpImg));
00162         }
00163 
00164         //combine with weight
00165         vigra::combineTwoImages(vigra::srcImageRange(tmpImg), vigra::srcImage(*(weights[i])), vigra::destImage(tmpImg), vigra::functor::Arg1() * vigra::functor::Arg2());
00166 
00167         vigra::combineTwoImages(srcImageRange(tmpImg), srcImage(weightedImg), destImage(weightedImg), vigra::functor::Arg1() + vigra::functor::Arg2());
00168 
00169         vigra::combineTwoImages(srcImageRange(weightAdded), srcImage(*(weights[i])), destImage(weightAdded), vigra::functor::Arg1() + vigra::functor::Arg2());
00170     }
00171     output.resize(width, height);
00172     vigra::combineTwoImages(srcImageRange(weightedImg), srcImage(weightAdded), destImage(output), vigra::functor::Arg1() / vigra::functor::Arg2());
00173 
00174     return true;
00175 }
00176 
00177 static void usage(const char* name)
00178 {
00179     std::cerr << name << ": merge overlapping images" << std::endl
00180          << std::endl
00181          << "hugin_hdrmerge version " << hugin_utils::GetHuginVersion() << std::endl
00182          << std::endl
00183          << "Usage: " << name  << " [options] -o output.exr <input-files>" << std::endl
00184          << "Valid options are:" << std::endl
00185          << "  -o|--output prefix output file" << std::endl
00186          << "  -m mode   merge mode, can be one of: avg (default), avg_slow, khan, if avg, no" << std::endl
00187          << "            -i and -s options apply" << std::endl
00188          << "  -i iter   number of iterations to execute (default is 4). Khan only" << std::endl
00189          << "  -s sigma  standard deviation of Gaussian weighting" << std::endl
00190          << "            function (sigma > 0); default: 30. Khan only" << std::endl
00191          << "  -a set    advanced settings. Possible options are:" << std::endl
00192          << "              f   use gray images for computation. It's about two times faster" << std::endl
00193          << "                  but it usually returns worse results." << std::endl
00194          << "              g   use gamma 2.2 correction instead of logarithm" << std::endl
00195          << "              m   do not scale image, NOTE: slows down process" << std::endl
00196          << "  -c        Only consider pixels that are defined in all images (avg mode only)" << std::endl
00197          << "  -v|--verbose   Verbose, print progress messages, repeat for" << std::endl
00198          << "                 even more verbose output" << std::endl
00199          << "  -h|help   Display help (this text)" << std::endl
00200          << std::endl;
00201 }
00202 
00203 
00204 int main(int argc, char* argv[])
00205 {
00206 
00207     // parse arguments
00208     const char* optstring = "chvo:m:i:s:a:el";
00209     static struct option longOptions[] =
00210     {
00211         { "output", required_argument, NULL, 'o' },
00212         { "verbose", no_argument, NULL, 'v' },
00213         { "help", no_argument, NULL, 'h' },
00214         0
00215     };
00216     int c;
00217 
00218     g_verbose = 0;
00219     std::string outputFile = "merged.exr";
00220     std::string mode = "avg";
00221     bool onlyCompleteOverlap = false;
00222     int iterations = 4;
00223     double sigma = 30;
00224     uint16_t flags = 0;
00225     uint16_t otherFlags = 0;
00226 
00227     while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
00228     {
00229         switch (c)
00230         {
00231             case 'm':
00232                 mode = optarg;
00233                 break;
00234             case 'i':
00235                 iterations = atoi(optarg);
00236                 break;
00237             case 's':
00238                 sigma = atof(optarg);
00239                 break;
00240             case 'a':
00241                 for(char* c = optarg; *c; c++)
00242                 {
00243                     switch(*c)
00244                     {
00245                         case 'f':
00246                             otherFlags += OTHER_GRAY;
00247                             break;
00248                         case 'g':
00249                             flags += deghosting::ADV_GAMMA;
00250                             break;
00251                         case 'm':
00252                             flags -= deghosting::ADV_MULTIRES;
00253                             break;
00254                         default:
00255                             std::cerr << hugin_utils::stripPath(argv[0]) << ": unknown option" << std::endl;
00256                             exit(1);
00257                     }
00258                 }
00259             case 'c':
00260                 onlyCompleteOverlap = true;
00261                 break;
00262             case 'o':
00263                 outputFile = optarg;
00264                 break;
00265             case 'v':
00266                 g_verbose++;
00267                 break;
00268             case 'h':
00269                 usage(hugin_utils::stripPath(argv[0]).c_str());
00270                 return 0;
00271             case ':':
00272             case '?':
00273                 // missing argument or invalid switch
00274                 return 1;
00275                 break;
00276             default:
00277                 // this should not happen
00278                 abort();
00279         }
00280     }//end while
00281 
00282     unsigned nFiles = argc - optind;
00283     if (nFiles == 0)
00284     {
00285         std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one input image needed" << std::endl;
00286         return 1;
00287     }
00288     else if (nFiles == 1)
00289     {
00290         std::cout << std::endl << "Only one input image given. Copying input image to output image." << std::endl;
00291         // simply copy image file
00292         std::ifstream infile(argv[optind], std::ios_base::binary);
00293         std::ofstream outfile(outputFile.c_str(), std::ios_base::binary);
00294         outfile << infile.rdbuf();
00295         return 0;
00296     }
00297 
00298     // load all images
00299     std::vector<std::string> inputFiles;
00300     for (size_t i=optind; i < (size_t)argc; i++)
00301     {
00302         inputFiles.push_back(argv[i]);
00303     }
00304 
00305     // output image
00306     ImageType output;
00307     try
00308     {
00309         if (mode == "avg_slow")
00310         {
00311             // use a weighted average, with special consideration of pixels
00312             // that are completely over or underexposed in all exposures.
00313             if (g_verbose > 0)
00314             {
00315                 std::cout << "Running simple weighted avg algorithm" << std::endl;
00316             }
00317 
00318             mergeWeightedAverage(inputFiles, output);
00319             // save output file
00320             if (g_verbose > 0)
00321             {
00322                 std::cout << "Writing " << outputFile << std::endl;
00323             }
00324             vigra::ImageExportInfo exinfo(outputFile.c_str());
00325             exinfo.setPixelType("FLOAT");
00326             vigra::BImage alpha(output.width(), output.height(), 255);
00327             vigra::exportImageAlpha(srcImageRange(output), srcImage(alpha), exinfo);
00328         }
00329         else if (mode == "avg")
00330         {
00331             // apply weighted average functor with
00332             // heuristic to deal with pixels that are overexposed in all images
00333             vigra_ext::ReduceToHDRFunctor<ImageType::value_type> waverage;
00334             // calc weighted average without loading the whole images into memory
00335             reduceFilesToHDR(inputFiles, outputFile, onlyCompleteOverlap, waverage);
00336         }
00337         else if (mode == "khan")
00338         {
00339             deghosting::Deghosting* deghoster = NULL;
00340             std::vector<deghosting::FImagePtr> weights;
00341 
00342             if (otherFlags & OTHER_GRAY)
00343             {
00344                 deghosting::Khan<float> khanDeghoster(inputFiles, flags, 0, iterations, sigma, g_verbose);
00345                 deghoster = &khanDeghoster;
00346                 weights = deghoster->createWeightMasks();
00347             }
00348             else
00349             {
00350                 deghosting::Khan<vigra::RGBValue<float> > khanDeghoster(inputFiles, flags, 0, iterations, sigma, g_verbose);
00351                 deghoster = &khanDeghoster;
00352                 weights = deghoster->createWeightMasks();
00353             }
00354 
00355             weightedAverageOfImageFiles(inputFiles, weights, output);
00356             if (g_verbose > 0)
00357             {
00358                 std::cout << "Writing " << outputFile << std::endl;
00359             }
00360             vigra::ImageExportInfo exinfo(outputFile.c_str());
00361             exinfo.setPixelType("FLOAT");
00362             vigra::BImage alpha(output.width(), output.height(), 255);
00363             vigra::exportImageAlpha(vigra::srcImageRange(output), vigra::srcImage(alpha), exinfo);
00364         }
00365         else
00366         {
00367             std::cerr << "Unknown merge mode, see help for a list of possible modes" << std::endl;
00368             return 1;
00369         }
00370     }
00371     catch (std::exception& e)
00372     {
00373         std::cerr << "caught exception: " << e.what() << std::endl;
00374         abort();
00375     }
00376 
00377     return 0;
00378 }
00379 
00380 

Generated on 10 Dec 2016 for Hugintrunk by  doxygen 1.4.7