Skip to content

Commit 235f2cc

Browse files
committed
Merge pull request opencv#8750 from amroamroamro:photo-debevec
2 parents 0bc30d3 + 39954cc commit 235f2cc

File tree

5 files changed

+66
-39
lines changed

5 files changed

+66
-39
lines changed

modules/photo/include/opencv2/photo.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ class CV_EXPORTS_W CalibrateDebevec : public CalibrateCRF
591591
@param samples number of pixel locations to use
592592
@param lambda smoothness term weight. Greater values produce smoother results, but can alter the
593593
response.
594-
@param random if true sample pixel locations are chosen at random, otherwise the form a
594+
@param random if true sample pixel locations are chosen at random, otherwise they form a
595595
rectangular grid.
596596
*/
597597
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false);

modules/photo/src/calibrate.cpp

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
#include "precomp.hpp"
4444
#include "opencv2/photo.hpp"
4545
#include "opencv2/imgproc.hpp"
46-
//#include "opencv2/highgui.hpp"
4746
#include "hdr_common.hpp"
4847

4948
namespace cv
@@ -57,77 +56,104 @@ class CalibrateDebevecImpl : public CalibrateDebevec
5756
samples(_samples),
5857
lambda(_lambda),
5958
random(_random),
60-
w(tringleWeights())
59+
w(triangleWeights())
6160
{
6261
}
6362

6463
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times)
6564
{
6665
CV_INSTRUMENT_REGION()
6766

67+
// check inputs
6868
std::vector<Mat> images;
6969
src.getMatVector(images);
7070
Mat times = _times.getMat();
7171

7272
CV_Assert(images.size() == times.total());
7373
checkImageDimensions(images);
7474
CV_Assert(images[0].depth() == CV_8U);
75+
CV_Assert(times.type() == CV_32FC1);
7576

77+
// create output
7678
int channels = images[0].channels();
7779
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
80+
int rows = images[0].rows;
81+
int cols = images[0].cols;
7882

7983
dst.create(LDR_SIZE, 1, CV_32FCC);
8084
Mat result = dst.getMat();
8185

82-
std::vector<Point> sample_points;
86+
// pick pixel locations (either random or in a rectangular grid)
87+
std::vector<Point> points;
88+
points.reserve(samples);
8389
if(random) {
8490
for(int i = 0; i < samples; i++) {
85-
sample_points.push_back(Point(rand() % images[0].cols, rand() % images[0].rows));
91+
points.push_back(Point(rand() % cols, rand() % rows));
8692
}
8793
} else {
88-
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * images[0].cols / images[0].rows));
94+
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * cols / rows));
95+
CV_Assert(0 < x_points && x_points <= cols);
8996
int y_points = samples / x_points;
90-
int step_x = images[0].cols / x_points;
91-
int step_y = images[0].rows / y_points;
92-
97+
CV_Assert(0 < y_points && y_points <= rows);
98+
int step_x = cols / x_points;
99+
int step_y = rows / y_points;
93100
for(int i = 0, x = step_x / 2; i < x_points; i++, x += step_x) {
94101
for(int j = 0, y = step_y / 2; j < y_points; j++, y += step_y) {
95-
if( 0 <= x && x < images[0].cols &&
96-
0 <= y && y < images[0].rows )
97-
sample_points.push_back(Point(x, y));
102+
if( 0 <= x && x < cols && 0 <= y && y < rows ) {
103+
points.push_back(Point(x, y));
104+
}
98105
}
99106
}
107+
// we can have slightly less grid points than specified
108+
//samples = static_cast<int>(points.size());
100109
}
101110

111+
// we need enough equations to ensure a sufficiently overdetermined system
112+
// (maybe only as a warning)
113+
//CV_Assert(points.size() * (images.size() - 1) >= LDR_SIZE);
114+
115+
// solve for imaging system response function, over each channel separately
102116
std::vector<Mat> result_split(channels);
103-
for(int channel = 0; channel < channels; channel++) {
104-
Mat A = Mat::zeros((int)sample_points.size() * (int)images.size() + LDR_SIZE + 1, LDR_SIZE + (int)sample_points.size(), CV_32F);
117+
for(int ch = 0; ch < channels; ch++) {
118+
// initialize system of linear equations
119+
Mat A = Mat::zeros((int)points.size() * (int)images.size() + LDR_SIZE + 1,
120+
LDR_SIZE + (int)points.size(), CV_32F);
105121
Mat B = Mat::zeros(A.rows, 1, CV_32F);
106122

107-
int eq = 0;
108-
for(size_t i = 0; i < sample_points.size(); i++) {
123+
// include the data−fitting equations
124+
int k = 0;
125+
for(size_t i = 0; i < points.size(); i++) {
109126
for(size_t j = 0; j < images.size(); j++) {
110-
111-
int val = images[j].ptr()[3*(sample_points[i].y * images[j].cols + sample_points[i].x) + channel];
112-
A.at<float>(eq, val) = w.at<float>(val);
113-
A.at<float>(eq, LDR_SIZE + (int)i) = -w.at<float>(val);
114-
B.at<float>(eq, 0) = w.at<float>(val) * log(times.at<float>((int)j));
115-
eq++;
127+
// val = images[j].at<Vec3b>(points[i].y, points[i].x)[ch]
128+
int val = images[j].ptr()[channels*(points[i].y * cols + points[i].x) + ch];
129+
float wij = w.at<float>(val);
130+
A.at<float>(k, val) = wij;
131+
A.at<float>(k, LDR_SIZE + (int)i) = -wij;
132+
B.at<float>(k, 0) = wij * log(times.at<float>((int)j));
133+
k++;
116134
}
117135
}
118-
A.at<float>(eq, LDR_SIZE / 2) = 1;
119-
eq++;
120-
121-
for(int i = 0; i < 254; i++) {
122-
A.at<float>(eq, i) = lambda * w.at<float>(i + 1);
123-
A.at<float>(eq, i + 1) = -2 * lambda * w.at<float>(i + 1);
124-
A.at<float>(eq, i + 2) = lambda * w.at<float>(i + 1);
125-
eq++;
136+
137+
// fix the curve by setting its middle value to 0
138+
A.at<float>(k, LDR_SIZE / 2) = 1;
139+
k++;
140+
141+
// include the smoothness equations
142+
for(int i = 0; i < (LDR_SIZE - 2); i++) {
143+
float wi = w.at<float>(i + 1);
144+
A.at<float>(k, i) = lambda * wi;
145+
A.at<float>(k, i + 1) = -2 * lambda * wi;
146+
A.at<float>(k, i + 2) = lambda * wi;
147+
k++;
126148
}
149+
150+
// solve the overdetermined system using SVD (least-squares problem)
127151
Mat solution;
128152
solve(A, B, solution, DECOMP_SVD);
129-
solution.rowRange(0, LDR_SIZE).copyTo(result_split[channel]);
153+
solution.rowRange(0, LDR_SIZE).copyTo(result_split[ch]);
130154
}
155+
156+
// combine log-exposures and take its exponent
131157
merge(result_split, result);
132158
exp(result, result);
133159
}
@@ -161,11 +187,11 @@ class CalibrateDebevecImpl : public CalibrateDebevec
161187
}
162188

163189
protected:
164-
String name;
165-
int samples;
166-
float lambda;
167-
bool random;
168-
Mat w;
190+
String name; // calibration algorithm identifier
191+
int samples; // number of pixel locations to sample
192+
float lambda; // constant that determines the amount of smoothness
193+
bool random; // whether to sample locations randomly or in a grid shape
194+
Mat w; // weighting function for corresponding pixel values
169195
};
170196

171197
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda, bool random)

modules/photo/src/hdr_common.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ void checkImageDimensions(const std::vector<Mat>& images)
5959
}
6060
}
6161

62-
Mat tringleWeights()
62+
Mat triangleWeights()
6363
{
64+
// hat function
6465
Mat w(LDR_SIZE, 1, CV_32F);
6566
int half = LDR_SIZE / 2;
6667
for(int i = 0; i < LDR_SIZE; i++) {

modules/photo/src/hdr_common.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace cv
5050

5151
void checkImageDimensions(const std::vector<Mat>& images);
5252

53-
Mat tringleWeights();
53+
Mat triangleWeights();
5454

5555
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
5656

modules/photo/src/merge.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class MergeDebevecImpl : public MergeDebevec
5252
public:
5353
MergeDebevecImpl() :
5454
name("MergeDebevec"),
55-
weights(tringleWeights())
55+
weights(triangleWeights())
5656
{
5757
}
5858

0 commit comments

Comments
 (0)