From c118f3c529642ed7c87c07722af175fadda22838 Mon Sep 17 00:00:00 2001 From: Fedor Morozov Date: Fri, 6 Sep 2013 17:30:43 +0400 Subject: [PATCH] Robertson update --- modules/photo/include/opencv2/photo.hpp | 6 ++- modules/photo/src/align.cpp | 14 ++++--- modules/photo/src/calibrate.cpp | 74 +++++++++++++-------------------- modules/photo/src/hdr_common.cpp | 35 +++++++--------- modules/photo/src/merge.cpp | 8 ++-- modules/photo/test/test_hdr.cpp | 32 +++++++++++++- 6 files changed, 92 insertions(+), 77 deletions(-) diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index 70d472e..ca3975f 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -80,6 +80,8 @@ CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, float h = 3, float hColor = 3, int templateWindowSize = 7, int searchWindowSize = 21); +enum { LDR_SIZE = 256 }; + class CV_EXPORTS_W Tonemap : public Algorithm { public: @@ -227,9 +229,11 @@ public: CV_WRAP virtual float getThreshold() const = 0; CV_WRAP virtual void setThreshold(float threshold) = 0; + + CV_WRAP virtual Mat getRadiance() const = 0; }; -CV_EXPORTS_W Ptr createCalibrateRobertson(int samples = 50, float lambda = 10.0f); +CV_EXPORTS_W Ptr createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f); class CV_EXPORTS_W ExposureMerge : public Algorithm { diff --git a/modules/photo/src/align.cpp b/modules/photo/src/align.cpp index 7eb9c6f..295fea3 100644 --- a/modules/photo/src/align.cpp +++ b/modules/photo/src/align.cpp @@ -243,14 +243,14 @@ protected: { int channels = 0; Mat hist; - int hist_size = 256; - float range[] = {0, 256} ; + int hist_size = LDR_SIZE; + float range[] = {0, LDR_SIZE} ; const float* ranges[] = {range}; calcHist(&img, 1, &channels, Mat(), hist, 1, &hist_size, ranges); float *ptr = hist.ptr(); int median = 0, sum = 0; int thresh = img.total() / 2; - while(sum < thresh && median < 256) { + while(sum < thresh && median < LDR_SIZE) { sum += static_cast(ptr[median]); median++; } @@ -309,7 +309,7 @@ public: std::vector splitted(channels); split(images[0], splitted); - for(int i = 0; i < images.size() - 1; i++) { + for(size_t i = 0; i < images.size() - 1; i++) { std::vector next_splitted(channels); split(images[i + 1], next_splitted); @@ -399,7 +399,7 @@ public: split(radiance, splitted); std::vector resp_split(channels); split(response, resp_split); - for(int i = 0; i < images.size() - 1; i++) { + for(size_t i = 0; i < images.size() - 1; i++) { std::vector next_splitted(channels); LUT(images[i + 1], response, radiance); @@ -430,7 +430,9 @@ public: virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector& times) { - process(src, dst, times, linearResponse(3)); + Mat response = linearResponse(3); + response.at(0) = response.at(1); + process(src, dst, times, response); } CV_WRAP virtual int getThreshold() {return thresh;} diff --git a/modules/photo/src/calibrate.cpp b/modules/photo/src/calibrate.cpp index 4701d4a..0b08a50 100644 --- a/modules/photo/src/calibrate.cpp +++ b/modules/photo/src/calibrate.cpp @@ -45,7 +45,6 @@ #include "opencv2/imgproc.hpp" //#include "opencv2/highgui.hpp" #include "hdr_common.hpp" -#include namespace cv { @@ -74,7 +73,7 @@ public: int channels = images[0].channels(); int CV_32FCC = CV_MAKETYPE(CV_32F, channels); - dst.create(256, 1, CV_32FCC); + dst.create(LDR_SIZE, 1, CV_32FCC); Mat result = dst.getMat(); std::vector sample_points; @@ -97,7 +96,7 @@ public: std::vector result_split(channels); for(int channel = 0; channel < channels; channel++) { - Mat A = Mat::zeros(sample_points.size() * images.size() + 257, 256 + sample_points.size(), CV_32F); + Mat A = Mat::zeros(sample_points.size() * images.size() + LDR_SIZE + 1, LDR_SIZE + sample_points.size(), CV_32F); Mat B = Mat::zeros(A.rows, 1, CV_32F); int eq = 0; @@ -107,12 +106,12 @@ public: int val = images[j].ptr()[3*(sample_points[i].y * images[j].cols + sample_points[j].x) + channel]; A.at(eq, val) = w.at(val); - A.at(eq, 256 + i) = -w.at(val); + A.at(eq, LDR_SIZE + i) = -w.at(val); B.at(eq, 0) = w.at(val) * log(times[j]); eq++; } } - A.at(eq, 128) = 1; + A.at(eq, LDR_SIZE / 2) = 1; eq++; for(int i = 0; i < 254; i++) { @@ -123,7 +122,7 @@ public: } Mat solution; solve(A, B, solution, DECOMP_SVD); - solution.rowRange(0, 256).copyTo(result_split[channel]); + solution.rowRange(0, LDR_SIZE).copyTo(result_split[channel]); } merge(result_split, result); exp(result, result); @@ -192,20 +191,14 @@ public: int channels = images[0].channels(); int CV_32FCC = CV_MAKETYPE(CV_32F, channels); - dst.create(256, 1, CV_32FCC); + dst.create(LDR_SIZE, 1, CV_32FCC); Mat response = dst.getMat(); - - response = Mat::zeros(256, 1, CV_32FCC); - for(int i = 0; i < 256; i++) { - for(int c = 0; c < channels; c++) { - response.at(i)[c] = i / 128.0; - } - } + response = linearResponse(3) / (LDR_SIZE / 2.0f); - Mat card = Mat::zeros(256, 1, CV_32FCC); - for(int i = 0; i < images.size(); i++) { + Mat card = Mat::zeros(LDR_SIZE, 1, CV_32FCC); + for(size_t i = 0; i < images.size(); i++) { uchar *ptr = images[i].ptr(); - for(int pos = 0; pos < images[i].total(); pos++) { + for(size_t pos = 0; pos < images[i].total(); pos++) { for(int c = 0; c < channels; c++, ptr++) { card.at(*ptr)[c] += 1; } @@ -213,43 +206,34 @@ public: } card = 1.0 / card; + Ptr merge = createMergeRobertson(); for(int iter = 0; iter < max_iter; iter++) { - Scalar channel_err(0, 0, 0); - Mat radiance = Mat::zeros(images[0].size(), CV_32FCC); - Mat wsum = Mat::zeros(images[0].size(), CV_32FCC); - for(int i = 0; i < images.size(); i++) { - Mat im, w; - LUT(images[i], weight, w); - LUT(images[i], response, im); - - Mat err_mat; - pow(im - times[i] * radiance, 2.0f, err_mat); - err_mat = w.mul(err_mat); - channel_err += sum(err_mat); - - radiance += times[i] * w.mul(im); - wsum += pow(times[i], 2) * w; - } - float err = (channel_err[0] + channel_err[1] + channel_err[2]) / (channels * radiance.total()); - radiance = radiance.mul(1 / wsum); + radiance = Mat::zeros(images[0].size(), CV_32FCC); + merge->process(images, radiance, times, response); - float* rad_ptr = radiance.ptr(); - response = Mat::zeros(256, 1, CV_32FC3); - for(int i = 0; i < images.size(); i++) { + Mat new_response = Mat::zeros(LDR_SIZE, 1, CV_32FC3); + for(size_t i = 0; i < images.size(); i++) { uchar *ptr = images[i].ptr(); - for(int pos = 0; pos < images[i].total(); pos++) { + float* rad_ptr = radiance.ptr(); + for(size_t pos = 0; pos < images[i].total(); pos++) { for(int c = 0; c < channels; c++, ptr++, rad_ptr++) { - response.at(*ptr)[c] += times[i] * *rad_ptr; + new_response.at(*ptr)[c] += times[i] * *rad_ptr; } } } - response = response.mul(card); + new_response = new_response.mul(card); for(int c = 0; c < 3; c++) { - for(int i = 0; i < 256; i++) { - response.at(i)[c] /= response.at(128)[c]; + float middle = new_response.at(LDR_SIZE / 2)[c]; + for(int i = 0; i < LDR_SIZE; i++) { + new_response.at(i)[c] /= middle; } } + float diff = sum(sum(abs(new_response - response)))[0] / channels; + new_response.copyTo(response); + if(diff < threshold) { + break; + } } } @@ -259,6 +243,8 @@ public: float getThreshold() const { return threshold; } void setThreshold(float val) { threshold = val; } + Mat getRadiance() const { return radiance; } + void write(FileStorage& fs) const { fs << "name" << name @@ -278,7 +264,7 @@ protected: String name; int max_iter; float threshold; - Mat weight; + Mat weight, radiance; }; Ptr createCalibrateRobertson(int max_iter, float threshold) diff --git a/modules/photo/src/hdr_common.cpp b/modules/photo/src/hdr_common.cpp index 4a0b320..2751258 100644 --- a/modules/photo/src/hdr_common.cpp +++ b/modules/photo/src/hdr_common.cpp @@ -61,21 +61,22 @@ void checkImageDimensions(const std::vector& images) Mat tringleWeights() { - Mat w(256, 1, CV_32F); - for(int i = 0; i < 256; i++) { - w.at(i) = i < 128 ? i + 1.0f : 256.0f - i; + Mat w(LDR_SIZE, 1, CV_32F); + int half = LDR_SIZE / 2; + for(int i = 0; i < LDR_SIZE; i++) { + w.at(i) = i < half ? i + 1.0f : LDR_SIZE - i; } return w; } Mat RobertsonWeights() { - Mat weight(256, 1, CV_32FC3); - for(int i = 0; i < 256; i++) { - float value = exp(-4.0f * pow(i - 127.5f, 2.0f) / pow(127.5f, 2.0f)); - for(int c = 0; c < 3; c++) { - weight.at(i)[c] = value; - } + Mat weight(LDR_SIZE, 1, CV_32FC3); + float q = (LDR_SIZE - 1) / 4.0f; + for(int i = 0; i < LDR_SIZE; i++) { + float value = i / q - 2.0f; + value = exp(-value * value); + weight.at(i) = Vec3f::all(value); } return weight; } @@ -94,19 +95,11 @@ void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation) Mat linearResponse(int channels) { - Mat single_response = Mat(256, 1, CV_32F); - for(int i = 1; i < 256; i++) { - single_response.at(i) = static_cast(i); + Mat response = Mat(LDR_SIZE, 1, CV_MAKETYPE(CV_32F, channels)); + for(int i = 0; i < LDR_SIZE; i++) { + response.at(i) = Vec3f::all(i); } - single_response.at(0) = static_cast(1); - - std::vector splitted(channels); - for(int c = 0; c < channels; c++) { - splitted[c] = single_response; - } - Mat result; - merge(splitted, result); - return result; + return response; } }; diff --git a/modules/photo/src/merge.cpp b/modules/photo/src/merge.cpp index 0ee3518..c247021 100644 --- a/modules/photo/src/merge.cpp +++ b/modules/photo/src/merge.cpp @@ -43,7 +43,6 @@ #include "opencv2/photo.hpp" #include "opencv2/imgproc.hpp" #include "hdr_common.hpp" -#include namespace cv { @@ -77,9 +76,10 @@ public: if(response.empty()) { response = linearResponse(channels); + response.at(0) = response.at(1); } log(response, response); - CV_Assert(response.rows == 256 && response.cols == 1 && + CV_Assert(response.rows == LDR_SIZE && response.cols == 1 && response.channels() == channels); Mat exp_values(times); @@ -312,9 +312,9 @@ public: Mat response = input_response.getMat(); if(response.empty()) { - response = linearResponse(channels) / 128.0f; + response = linearResponse(channels) / (LDR_SIZE / 2.0f); } - CV_Assert(response.rows == 256 && response.cols == 1 && + CV_Assert(response.rows == LDR_SIZE && response.cols == 1 && response.channels() == channels); result = Mat::zeros(images[0].size(), CV_32FCC); diff --git a/modules/photo/test/test_hdr.cpp b/modules/photo/test/test_hdr.cpp index c4c376f..a86f119 100644 --- a/modules/photo/test/test_hdr.cpp +++ b/modules/photo/test/test_hdr.cpp @@ -187,7 +187,22 @@ TEST(Photo_MergeDebevec, regression) Mat result, expected; loadImage(test_path + "merge/debevec.exr", expected); merge->process(images, result, times, response); - imwrite("test.exr", result); + checkEqual(expected, result, 1e-2f); +} + +TEST(Photo_MergeRobertson, regression) +{ + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; + + vector images; + vector times; + loadExposureSeq(test_path + "exposures/", images, times); + + Ptr merge = createMergeRobertson(); + + Mat result, expected; + loadImage(test_path + "merge/robertson.exr", expected); + merge->process(images, result, times); checkEqual(expected, result, 1e-2f); } @@ -208,3 +223,18 @@ TEST(Photo_CalibrateDebevec, regression) minMaxLoc(diff, NULL, &max); ASSERT_FALSE(max > 0.1); } + +TEST(Photo_CalibrateRobertson, regression) +{ + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; + + vector images; + vector times; + Mat response, expected; + loadExposureSeq(test_path + "exposures/", images, times); + loadResponseCSV(test_path + "calibrate/robertson.csv", expected); + + Ptr calibrate = createCalibrateRobertson(); + calibrate->process(images, response, times); + checkEqual(expected, response, 1e-3f); +} \ No newline at end of file -- 2.7.4