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

Generated on 29 Aug 2015 for Hugintrunk by  doxygen 1.4.7