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

Generated on 25 May 2016 for Hugintrunk by  doxygen 1.4.7