diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index aeb54d5fe7..dc7684ac09 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -59,20 +59,6 @@ enum INPAINT_TELEA = 1 // A. Telea algorithm }; -//! the tonemapping algorithm -enum -{ - TONEMAP_LINEAR, - - TONEMAP_DRAGO, // Adaptive Logarithmic Mapping For - // Displaying High Contrast Scenes - TONEMAP_REINHARD, // Dynamic Range Reduction Inspired - // by Photoreceptor Physiology - TONEMAP_DURAND, // Fast Bilateral Filtering for the - // Display of High-Dynamic-Range Images - TONEMAP_COUNT -}; - //! restores the damaged image areas using one of the available intpainting algorithms CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags ); @@ -96,9 +82,6 @@ CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, CV_EXPORTS_W void makeHDR(InputArrayOfArrays srcImgs, const std::vector& exp_times, OutputArray dst, Mat response = Mat()); -CV_EXPORTS_W void tonemap(InputArray src, OutputArray dst, int algorithm, - const std::vector& params = std::vector()); - CV_EXPORTS_W void exposureFusion(InputArrayOfArrays srcImgs, OutputArray dst, float wc = 1.0f, float ws = 1.0f, float we = 0.0f); CV_EXPORTS_W void shiftMat(InputArray src, Point shift, OutputArray dst); @@ -108,6 +91,66 @@ CV_EXPORTS_W Point getExpShift(InputArray img0, InputArray img1, int max_bits = CV_EXPORTS_W void estimateResponse(InputArrayOfArrays srcImgs, const std::vector& exp_times, OutputArray dst, int samples = 50, float lambda = 10); CV_EXPORTS_W void alignImages(InputArrayOfArrays src, std::vector& dst); + +class CV_EXPORTS_W Tonemap : public Algorithm +{ +public: + Tonemap(float gamma); + virtual ~Tonemap(); + void process(InputArray src, OutputArray dst); + static Ptr create(const String& name); +protected: + float gamma; + Mat img; + void linearMap(); + void gammaCorrection(); + + virtual void tonemap() = 0; +}; + +class CV_EXPORTS_W TonemapLinear : public Tonemap +{ +public: + TonemapLinear(float gamma = 2.2f); + AlgorithmInfo* info() const; +protected: + void tonemap(); +}; + +class CV_EXPORTS_W TonemapDrago : public Tonemap +{ +public: + TonemapDrago(float gamma = 2.2f, float bias = 0.85f); + AlgorithmInfo* info() const; +protected: + float bias; + void tonemap(); +}; + +class CV_EXPORTS_W TonemapDurand : public Tonemap +{ +public: + TonemapDurand(float gamma = 2.2f, float contrast = 4.0f, float sigma_color = 2.0f, float sigma_space = 2.0f); + AlgorithmInfo* info() const; +protected: + float contrast; + float sigma_color; + float sigma_space; + void tonemap(); +}; + +class CV_EXPORTS_W TonemapReinhardDevlin : public Tonemap +{ +public: + TonemapReinhardDevlin(float gamma = 2.2f, float intensity = 0.0f, float color_adapt = 0.0f, float light_adapt = 1.0f); + AlgorithmInfo* info() const; +protected: + float intensity; + float color_adapt; + float light_adapt; + void tonemap(); +}; + } // cv #endif diff --git a/modules/photo/src/hdr_fusion.cpp b/modules/photo/src/hdr_fusion.cpp index 1a4d39ab5e..c088933a7a 100644 --- a/modules/photo/src/hdr_fusion.cpp +++ b/modules/photo/src/hdr_fusion.cpp @@ -161,7 +161,6 @@ void makeHDR(InputArrayOfArrays _images, const std::vector& _exp_times, O res_ptr[channel] = exp(sum[channel] / weight_sum); } } - tonemap(result, result, 0); } void exposureFusion(InputArrayOfArrays _images, OutputArray _dst, float wc, float ws, float we) diff --git a/modules/photo/src/tonemap.cpp b/modules/photo/src/tonemap.cpp index 19db4a47fd..d515cefdad 100644 --- a/modules/photo/src/tonemap.cpp +++ b/modules/photo/src/tonemap.cpp @@ -47,20 +47,58 @@ namespace cv { -static float getParam(const std::vector& params, size_t i, float defval) +Tonemap::Tonemap(float gamma) : gamma(gamma) { - if(params.size() > i) { - return params[i]; - } else { - return defval; - } } -static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector& params) +Tonemap::~Tonemap() +{ +} + +void Tonemap::process(InputArray src, OutputArray dst) +{ + Mat srcMat = src.getMat(); + CV_Assert(!srcMat.empty()); + dst.create(srcMat.size(), CV_32FC3); + img = dst.getMat(); + srcMat.copyTo(img); + linearMap(); + tonemap(); + gammaCorrection(); +} + +void Tonemap::linearMap() +{ + double min, max; + minMaxLoc(img, &min, &max); + if(max - min > DBL_EPSILON) { + img = (img - min) / (max - min); + } +} + +void Tonemap::gammaCorrection() +{ + pow(img, 1.0f / gamma, img); +} + +void TonemapLinear::tonemap() +{ +} + +TonemapLinear::TonemapLinear(float gamma) : Tonemap(gamma) +{ +} + +TonemapDrago::TonemapDrago(float gamma, float bias) : + Tonemap(gamma), + bias(bias) +{ +} + +void TonemapDrago::tonemap() { - float bias_value = getParam(params, 1, 0.85f); Mat gray_img; - cvtColor(src_img, gray_img, COLOR_RGB2GRAY); + cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; log(gray_img, log_img); float mean = expf(static_cast(sum(log_img)[0]) / log_img.total()); @@ -73,7 +111,7 @@ static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector& param Mat map; log(gray_img + 1.0f, map); Mat div; - pow(gray_img / (float)max, logf(bias_value) / logf(0.5f), div); + pow(gray_img / (float)max, logf(bias) / logf(0.5f), div); log(2.0f + 8.0f * div, div); map = map.mul(1.0f / div); map = map.mul(1.0f / gray_img); @@ -81,22 +119,61 @@ static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector& param gray_img.release(); std::vector channels(3); - split(src_img, channels); + split(img, channels); for(int i = 0; i < 3; i++) { channels[i] = channels[i].mul(map); } map.release(); - merge(channels, dst_img); + merge(channels, img); + linearMap(); } -static void ReinhardDevlinMap(Mat& src_img, Mat &dst_img, const std::vector& params) +TonemapDurand::TonemapDurand(float gamma, float contrast, float sigma_color, float sigma_space) : + Tonemap(gamma), + contrast(contrast), + sigma_color(sigma_color), + sigma_space(sigma_space) { - float intensity = getParam(params, 1, 0.0f); - float color_adapt = getParam(params, 2, 0.0f); - float light_adapt = getParam(params, 3, 1.0f); +} - Mat gray_img; - cvtColor(src_img, gray_img, COLOR_RGB2GRAY); +void TonemapDurand::tonemap() +{ + Mat gray_img; + cvtColor(img, gray_img, COLOR_RGB2GRAY); + Mat log_img; + log(gray_img, log_img); + Mat map_img; + bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space); + + double min, max; + minMaxLoc(map_img, &min, &max); + float scale = contrast / (float)(max - min); + + exp(map_img * (scale - 1.0f) + log_img, map_img); + log_img.release(); + map_img = map_img.mul(1.0f / gray_img); + gray_img.release(); + + std::vector channels(3); + split(img, channels); + for(int i = 0; i < 3; i++) { + channels[i] = channels[i].mul(map_img); + } + merge(channels, img); +} + +TonemapReinhardDevlin::TonemapReinhardDevlin(float gamma, float intensity, float color_adapt, float light_adapt) : + Tonemap(gamma), + intensity(intensity), + color_adapt(color_adapt), + light_adapt(light_adapt) +{ +} + +void TonemapReinhardDevlin::tonemap() +{ + Mat gray_img; + cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; log(gray_img, log_img); @@ -108,11 +185,11 @@ static void ReinhardDevlinMap(Mat& src_img, Mat &dst_img, const std::vector channels(3); - split(src_img, channels); + split(img, channels); for(int i = 0; i < 3; i++) { float global = color_adapt * (float)chan_mean[i] + (1.0f - color_adapt) * gray_mean; @@ -122,65 +199,31 @@ static void ReinhardDevlinMap(Mat& src_img, Mat &dst_img, const std::vector& params) +Ptr Tonemap::create(const String& TonemapType) { - float contrast = getParam(params, 1, 4.0f); - float sigma_color = getParam(params, 2, 2.0f); - float sigma_space = getParam(params, 3, 2.0f); - - Mat gray_img; - cvtColor(src_img, gray_img, COLOR_RGB2GRAY); - Mat log_img; - log(gray_img, log_img); - Mat map_img; - bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space); - - double min, max; - minMaxLoc(map_img, &min, &max); - float scale = contrast / (float)(max - min); - - exp(map_img * (scale - 1.0f) + log_img, map_img); - log_img.release(); - map_img = map_img.mul(1.0f / gray_img); - gray_img.release(); - - std::vector channels(3); - split(src_img, channels); - for(int i = 0; i < 3; i++) { - channels[i] = channels[i].mul(map_img); - } - merge(channels, dst_img); + return Algorithm::create("Tonemap." + TonemapType); } -void tonemap(InputArray _src, OutputArray _dst, int algorithm, - const std::vector& params) -{ - typedef void (*tonemap_func)(Mat&, Mat&, const std::vector&); - tonemap_func functions[TONEMAP_COUNT] = { - NULL, DragoMap, ReinhardDevlinMap, DurandMap}; +CV_INIT_ALGORITHM(TonemapLinear, "Tonemap.Linear", + obj.info()->addParam(obj, "gamma", obj.gamma)); - Mat src = _src.getMat(); - CV_Assert(!src.empty()); - CV_Assert(0 <= algorithm && algorithm < TONEMAP_COUNT); - _dst.create(src.size(), CV_32FC3); - Mat dst = _dst.getMat(); - src.copyTo(dst); +CV_INIT_ALGORITHM(TonemapDrago, "Tonemap.Drago", + obj.info()->addParam(obj, "gamma", obj.gamma); + obj.info()->addParam(obj, "bias", obj.bias)); - double min, max; - minMaxLoc(dst, &min, &max); - if(max - min < DBL_EPSILON) { - return; - } - dst = (dst - min) / (max - min); - if(functions[algorithm]) { - functions[algorithm](dst, dst, params); - } - minMaxLoc(dst, &min, &max); - dst = (dst - min) / (max - min); - float gamma = getParam(params, 0, 1.0f); - pow(dst, 1.0f / gamma, dst); -} +CV_INIT_ALGORITHM(TonemapDurand, "Tonemap.Durand", + obj.info()->addParam(obj, "gamma", obj.gamma); + obj.info()->addParam(obj, "contrast", obj.contrast); + obj.info()->addParam(obj, "sigma_color", obj.sigma_color); + obj.info()->addParam(obj, "sigma_space", obj.sigma_space)); + +CV_INIT_ALGORITHM(TonemapReinhardDevlin, "Tonemap.ReinhardDevlin", + obj.info()->addParam(obj, "gamma", obj.gamma); + obj.info()->addParam(obj, "intensity", obj.intensity); + obj.info()->addParam(obj, "color_adapt", obj.color_adapt); + obj.info()->addParam(obj, "light_adapt", obj.light_adapt)); } diff --git a/modules/photo/test/test_hdr.cpp b/modules/photo/test/test_hdr.cpp index 2e3b257314..9420e971e2 100644 --- a/modules/photo/test/test_hdr.cpp +++ b/modules/photo/test/test_hdr.cpp @@ -70,6 +70,7 @@ TEST(Photo_HdrFusion, regression) vector images; ifstream list_file(fuse_path + "list.txt"); + ASSERT_TRUE(list_file.is_open()); string name; float val; while(list_file >> name >> val) { @@ -110,48 +111,48 @@ TEST(Photo_HdrFusion, regression) TEST(Photo_Tonemap, regression) { - string folder = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; - vectorimages(TONEMAP_COUNT); - for(int i = 0; i < TONEMAP_COUNT; i++) { - stringstream stream; - stream << "tonemap" << i << ".png"; - string file_name; - stream >> file_name; - loadImage(folder + "tonemap/" + file_name ,images[i]); - } + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/"; + Mat img; - loadImage(folder + "rle.hdr", img); - vector param(1); - param[0] = 2.2f; + loadImage(test_path + "../rle.hdr", img); + ifstream list_file(test_path + "list.txt"); + ASSERT_TRUE(list_file.is_open()); - for(int i = 0; i < TONEMAP_COUNT; i++) { - + string name; + while(list_file >> name) { + Mat expected = imread(test_path + name + ".png"); + ASSERT_FALSE(img.empty()) << "Could not load input image " << test_path + name + ".png"; + Ptr mapper = Tonemap::create(name); + ASSERT_FALSE(mapper.empty()) << "Could not find mapper " << name; Mat result; - tonemap(img, result, i, param); + mapper->process(img, result); result.convertTo(result, CV_8UC3, 255); - checkEqual(images[i], result, 0); + checkEqual(expected, result, 0); } + list_file.close(); } TEST(Photo_Align, regression) { const int TESTS_COUNT = 100; - string folder = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; + string folder = string(cvtest::TS::ptr()->get_data_path()) + "shared/"; - string file_name = folder + "exp_fusion.png"; + string file_name = folder + "lena.png"; Mat img = imread(file_name); ASSERT_FALSE(img.empty()) << "Could not load input image " << file_name; cvtColor(img, img, COLOR_RGB2GRAY); - int max_bits = 6; - int max_shift = 64; - srand(time(0)); + int max_bits = 5; + int max_shift = 32; + srand(static_cast(time(0))); + int errors = 0; for(int i = 0; i < TESTS_COUNT; i++) { Point shift(rand() % max_shift, rand() % max_shift); Mat res; shiftMat(img, shift, res); Point calc = getExpShift(img, res, max_bits); - ASSERT_TRUE(calc == -shift); + errors += (calc != -shift); } + ASSERT_TRUE(errors < 5); }