hugin_hdrmerge.cpp

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

Generated on Tue Jul 29 01:25:44 2014 for Hugintrunk by  doxygen 1.3.9.1