00001
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
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
00068
00069 typedef vigra::FRGBImage ImageType;
00070
00071
00072 typedef boost::shared_ptr<ImageType> ImagePtr;
00073
00074
00075 static int g_verbose = 0;
00076
00077 const uint16_t OTHER_GRAY = 1;
00078
00079
00080
00081 bool mergeWeightedAverage(vector<string> inputFiles, FRGBImage & output)
00082 {
00083
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
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
00121
00122 ReduceToHDRFunctor<ImageType::value_type> waverage;
00123
00124
00125 for (int y=0; y < height; y++) {
00126 for (int x=0; x < width; x++) {
00127 waverage.reset();
00128
00129 for (unsigned imgNr=0; imgNr < images.size(); imgNr++) {
00130
00131 waverage( (*images[imgNr])(x,y), (*weightImages[imgNr])(x,y) );
00132 }
00133
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
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
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
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 }
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
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
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
00296 ImageType output;
00297 try {
00298 if (mode == "avg_slow") {
00299
00300
00301 if (g_verbose > 0) {
00302 cout << "Running simple weighted avg algorithm" << std::endl;
00303 }
00304
00305 mergeWeightedAverage(inputFiles, output);
00306
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
00316
00317 ReduceToHDRFunctor<ImageType::value_type> waverage;
00318
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