Hugin trunk 0.1
Loading...
Searching...
No Matches
hugin_hdrmerge.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
27#include <hugin_config.h>
28
29#include <fstream>
30#include <sstream>
31#include <cmath>
32#include <algorithm>
33
34#include <memory>
35
36#include <vigra/error.hxx>
37#include <vigra/functorexpression.hxx>
38#include <vigra/transformimage.hxx>
39
40#include <hugin_utils/utils.h>
41
43#include <vigra_ext/HDRUtils.h>
45
46#include <getopt.h>
47
48#include "../deghosting/deghosting.h"
49#include "../deghosting/khan.h"
50
51// define the types of images we will use.
52// use float for RGB
53typedef vigra::FRGBImage ImageType;
54
55// smart pointers to the images.
56typedef std::shared_ptr<ImageType> ImagePtr;
57
58static int g_verbose = 0;
59
61
62// load all images and apply a weighted average merge, with
63// special cases for completely over or underexposed pixels.
64bool mergeWeightedAverage(std::vector<std::string> inputFiles, vigra::FRGBImage& output, vigra::BImage& alpha, vigra::Rect2D& outputROI)
65{
66 // load all images into memory
67 std::vector<ImagePtr> images;
68 std::vector<deghosting::BImagePtr> weightImages;
69 std::vector<vigra::ImageImportInfo> imageInfo;
70
71 // read information of all images and check ROI
72 for (size_t i = 0; i < inputFiles.size(); i++)
73 {
74 vigra::ImageImportInfo info(inputFiles[i].c_str());
75 imageInfo.push_back(info);
76 if (i == 0)
77 {
78 outputROI = vigra::Rect2D(vigra::Point2D(info.getPosition()), info.size());
79 }
80 else
81 {
82 outputROI |= vigra::Rect2D(vigra::Point2D(info.getPosition()), info.size());
83 };
84 };
85
86 for (size_t i=0; i < imageInfo.size(); i++)
87 {
88 ImagePtr img = ImagePtr(new ImageType());
89 deghosting::BImagePtr weight = deghosting::BImagePtr(new vigra::BImage());
90
91 if (g_verbose > 0)
92 {
93 std::cout << "Loading image: " << inputFiles[i] << std::endl;
94 }
95 //calculate offset
96 vigra::Point2D offset = vigra::Point2D(imageInfo[i].getPosition());
97 offset -= outputROI.upperLeft();
98 // now load images with offset
99 img->resize(outputROI.size());
100 weight->resize(img->size().width(), img->size().height(), 0);
101 if (imageInfo[i].numBands() == 4)
102 {
103 importImageAlpha(imageInfo[i], destImage(*img, offset), destImage(*weight, offset));
104 }
105 else
106 {
107 importImage(imageInfo[i], destImage(*img, offset));
108 vigra::initImage(vigra::destImageRange(*weight, vigra::Rect2D(offset, imageInfo[i].size())), 255);
109 }
110 images.push_back(img);
111 weightImages.push_back(weight);
112 };
113
114 output.resize(outputROI.size());
115 alpha.resize(output.width(), output.height(), 0);
116 if (g_verbose > 0)
117 {
118 std::cout << "Calculating weighted average " << std::endl;
119 }
120 // apply weighted average functor with
121 // heuristic to deal with pixels that are overexposed in all images
123
124 // loop over all pixels in the image (very low level access)
125 for (int y=0; y < output.height(); y++)
126 {
127 for (int x=0; x < output.width(); x++)
128 {
129 waverage.reset();
130 // loop over all exposures
131 bool hasValues = false;
132 for (unsigned imgNr=0; imgNr < images.size(); imgNr++)
133 {
134 // add pixel to weighted average
135 const vigra::UInt8 weight = (*weightImages[imgNr])(x, y);
136 waverage( (*images[imgNr])(x,y), weight);
137 hasValues |= (weight > 0);
138 }
139 // get result
140 if(hasValues)
141 {
142 output(x,y) = waverage();
143 alpha(x, y) = 255;
144 };
145 }
146 }
147 return true;
148}
149
151bool weightedAverageOfImageFiles(const std::vector<std::string>& inputFiles,
152 const std::vector<deghosting::FImagePtr>& weights,
153 vigra::FRGBImage& output,
154 vigra::BImage& alpha,
155 const vigra::Rect2D outputROI)
156{
157 if(g_verbose > 0)
158 {
159 std::cout << "Merging input images" << std::endl;
160 }
161 int width = (weights[0])->width();
162 int height = (weights[0])->height();
163
164 assert(inputFiles.size() == weights.size());
165
166 vigra::BasicImage<vigra::NumericTraits<vigra::FRGBImage::PixelType>::Promote> weightedImg(width, height);
167 vigra::BasicImage<vigra::NumericTraits<vigra::FImage::PixelType>::Promote> weightAdded(width, height);
168 for(unsigned i = 0; i < inputFiles.size(); i++)
169 {
170 vigra::ImageImportInfo inputInfo(inputFiles[i].c_str());
171 vigra::BasicImage<vigra::NumericTraits<vigra::FRGBImage::PixelType>::Promote> tmpImg(outputROI.size());
172
173 //load image
174 vigra::Point2D offset = vigra::Point2D(inputInfo.getPosition());
175 offset -= outputROI.upperLeft();
176 if (inputInfo.numBands() == 4)
177 {
178 vigra::BImage tmpMask(tmpImg.size());
179 vigra::importImageAlpha(inputInfo, vigra::destImage(tmpImg, offset), vigra::destImage(tmpMask, offset));
180 }
181 else
182 {
183 vigra::importImage(inputInfo, vigra::destImage(tmpImg, offset));
184 }
185
186 //combine with weight
187 vigra::combineThreeImages(srcImageRange(tmpImg), srcImage(*(weights[i])), srcImage(weightedImg), destImage(weightedImg), vigra::functor::Arg1() *vigra::functor::Arg2() + vigra::functor::Arg3());
188 vigra::combineTwoImages(srcImageRange(weightAdded), srcImage(*(weights[i])), destImage(weightAdded), vigra::functor::Arg1() + vigra::functor::Arg2());
189 }
190 output.resize(width, height);
191 alpha.resize(width, height, 0);
192 vigra::combineTwoImages(srcImageRange(weightedImg), srcImage(weightAdded), destImage(output), vigra::functor::Arg1() / vigra::functor::Arg2());
193 vigra::transformImage(vigra::srcImageRange(weightAdded), vigra::destImage(alpha),
194 vigra::Threshold<vigra::FImage::PixelType, vigra::BImage::PixelType>(1e-7f, FLT_MAX, 0, 255));
195 return true;
196}
197
198static void usage(const char* name)
199{
200 std::cout << name << ": merge overlapping images" << std::endl
201 << std::endl
202 << "hugin_hdrmerge version " << hugin_utils::GetHuginVersion() << std::endl
203 << std::endl
204 << "Usage: " << name << " [options] -o output.exr <input-files>" << std::endl
205 << "Valid options are:" << std::endl
206 << " -o|--output prefix output file" << std::endl
207 << " -m mode merge mode, can be one of: avg (default), avg_slow, khan, if avg, no" << std::endl
208 << " -i and -s options apply" << std::endl
209 << " -i iter number of iterations to execute (default is 4). Khan only" << std::endl
210 << " -s sigma standard deviation of Gaussian weighting" << std::endl
211 << " function (sigma > 0); default: 30. Khan only" << std::endl
212 << " -a set advanced settings. Possible options are:" << std::endl
213 << " f use gray images for computation. It's about two times faster" << std::endl
214 << " but it usually returns worse results." << std::endl
215 << " g use gamma 2.2 correction instead of logarithm" << std::endl
216 << " m do not scale image, NOTE: slows down process" << std::endl
217 << " -c Only consider pixels that are defined in all images (avg mode only)" << std::endl
218 << " -v|--verbose Verbose, print progress messages, repeat for" << std::endl
219 << " even more verbose output" << std::endl
220 << " -h|help Display help (this text)" << std::endl
221 << std::endl;
222}
223
224
225int main(int argc, char* argv[])
226{
227
228 // parse arguments
229 const char* optstring = "chvo:m:i:s:a:el";
230 static struct option longOptions[] =
231 {
232 { "output", required_argument, NULL, 'o' },
233 { "verbose", no_argument, NULL, 'v' },
234 { "help", no_argument, NULL, 'h' },
235 0
236 };
237 int c;
238
239 g_verbose = 0;
240 std::string outputFile = "merged.exr";
241 std::string mode = "avg";
242 bool onlyCompleteOverlap = false;
243 int iterations = 4;
244 double sigma = 30;
245 uint16_t flags = 0;
247
248 while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
249 {
250 switch (c)
251 {
252 case 'm':
253 mode = optarg;
254 break;
255 case 'i':
257 break;
258 case 's':
259 sigma = atof(optarg);
260 break;
261 case 'a':
262 for(char* c = optarg; *c; c++)
263 {
264 switch(*c)
265 {
266 case 'f':
268 break;
269 case 'g':
271 break;
272 case 'm':
274 break;
275 default:
276 std::cerr << hugin_utils::stripPath(argv[0]) << ": unknown option" << std::endl;
277 exit(1);
278 }
279 }
280 case 'c':
281 onlyCompleteOverlap = true;
282 break;
283 case 'o':
285 break;
286 case 'v':
287 g_verbose++;
288 break;
289 case 'h':
291 return 0;
292 case ':':
293 case '?':
294 // missing argument or invalid switch
295 return 1;
296 break;
297 default:
298 // this should not happen
299 abort();
300 }
301 }//end while
302
303 unsigned nFiles = argc - optind;
304 if (nFiles == 0)
305 {
306 std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one input image needed" << std::endl;
307 return 1;
308 }
309 else if (nFiles == 1)
310 {
311 std::cout << std::endl << "Only one input image given. Copying input image to output image." << std::endl;
312 // simply copy image file
313 std::ifstream infile(argv[optind], std::ios_base::binary);
314 std::ofstream outfile(outputFile.c_str(), std::ios_base::binary);
315 outfile << infile.rdbuf();
316 return 0;
317 }
318
319 // load all images
320 std::vector<std::string> inputFiles;
321 for (size_t i=optind; i < (size_t)argc; i++)
322 {
323 inputFiles.push_back(argv[i]);
324 }
325
326 // output image
328 try
329 {
330 if (mode == "avg_slow")
331 {
332 // use a weighted average, with special consideration of pixels
333 // that are completely over or underexposed in all exposures.
334 if (g_verbose > 0)
335 {
336 std::cout << "Running simple weighted avg algorithm" << std::endl;
337 }
338 vigra::BImage alpha;
339 vigra::Rect2D outputROI;
340 mergeWeightedAverage(inputFiles, output, alpha, outputROI);
341 // save output file
342 if (g_verbose > 0)
343 {
344 std::cout << "Writing " << outputFile << std::endl;
345 }
346 vigra::ImageExportInfo exinfo(outputFile.c_str());
347 exinfo.setPixelType("FLOAT");
348 exinfo.setPosition(outputROI.upperLeft());
349 exinfo.setCanvasSize(vigra::Size2D(outputROI.lowerRight().x, outputROI.lowerRight().y));
350 vigra::exportImageAlpha(srcImageRange(output), srcImage(alpha), exinfo);
351 }
352 else if (mode == "avg")
353 {
354 // apply weighted average functor with
355 // heuristic to deal with pixels that are overexposed in all images
357 // calc weighted average without loading the whole images into memory
359 }
360 else if (mode == "khan")
361 {
362 if (g_verbose > 0)
363 {
364 std::cout << "Running Khan deghosting algorithm" << std::endl;
365 }
366 vigra::Rect2D outputROI;
367 std::vector<deghosting::FImagePtr> weights;
368
370 {
372 weights = deghoster.createWeightMasks();
373 outputROI = deghoster.getOutputROI();
374 }
375 else
376 {
378 weights = deghoster.createWeightMasks();
379 outputROI = deghoster.getOutputROI();
380 };
381 vigra::BImage alpha;
382 weightedAverageOfImageFiles(inputFiles, weights, output, alpha, outputROI);
383 if (g_verbose > 0)
384 {
385 std::cout << "Writing " << outputFile << std::endl;
386 }
387 vigra::ImageExportInfo exinfo(outputFile.c_str());
388 exinfo.setPixelType("FLOAT");
389 exinfo.setPosition(outputROI.upperLeft());
390 exinfo.setCanvasSize(vigra::Size2D(outputROI.lowerRight().x, outputROI.lowerRight().y));
391 vigra::exportImageAlpha(vigra::srcImageRange(output), vigra::srcImage(alpha), exinfo);
392 }
393 else
394 {
395 std::cerr << "Unknown merge mode, see help for a list of possible modes" << std::endl;
396 return 1;
397 }
398 }
399 catch (std::exception& e)
400 {
401 std::cerr << "caught exception: " << e.what() << std::endl;
402 abort();
403 }
404
405 return 0;
406}
407
408
static void usage()
Definition Main.cpp:32
void reduceFilesToHDR(std::vector< std::string > input, std::string output, bool onlyCompleteOverlap, Functor &reduce)
static int iterations
static uint16_t flags
static double sigma
static uint16_t otherFlags
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
class AlphaIterator class AlphaAccessor inline void exportImageAlpha(ImageIterator image_upper_left, ImageIterator image_lower_right, ImageAccessor image_accessor, AlphaIterator alpha_upper_left, AlphaAccessor alpha_accessor, const ImageExportInfo &export_info)
int main(int argc, char *argv[])
vigra::FRGBImage ImageType
std::shared_ptr< ImageType > ImagePtr
bool mergeWeightedAverage(std::vector< std::string > inputFiles, vigra::FRGBImage &output, vigra::BImage &alpha, vigra::Rect2D &outputROI)
const uint16_t OTHER_GRAY
static int g_verbose
bool weightedAverageOfImageFiles(const std::vector< std::string > &inputFiles, const std::vector< deghosting::FImagePtr > &weights, vigra::FRGBImage &output, vigra::BImage &alpha, const vigra::Rect2D outputROI)
compute output image when given source images
const uint16_t ADV_MULTIRES
Definition deghosting.h:53
std::shared_ptr< vigra::BImage > BImagePtr
Definition deghosting.h:45
const uint16_t ADV_GAMMA
Definition deghosting.h:51
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41