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

Generated on 27 Apr 2017 for Hugintrunk by  doxygen 1.4.7