diff --git a/modules/xphoto/CMakeLists.txt b/modules/xphoto/CMakeLists.txt index 0213f0ad1..a05848d38 100644 --- a/modules/xphoto/CMakeLists.txt +++ b/modules/xphoto/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Addon to basic photo module") -ocv_define_module(xphoto opencv_core opencv_imgproc WRAP python java) +ocv_define_module(xphoto opencv_core opencv_imgproc opencv_photo WRAP python java) diff --git a/modules/xphoto/doc/xphoto.bib b/modules/xphoto/doc/xphoto.bib index b57471ceb..d89e90723 100644 --- a/modules/xphoto/doc/xphoto.bib +++ b/modules/xphoto/doc/xphoto.bib @@ -6,7 +6,6 @@ year={2012}, publisher={Springer} } - @inproceedings{Cheng2015, title={Effective learning-based illuminant estimation using simple features}, author={Cheng, Dongliang and Price, Brian and Cohen, Scott and Brown, Michael S}, @@ -14,3 +13,14 @@ pages={1000--1008}, year={2015} } +@inproceedings{DD02, + author = {Durand, Fr{\'e}do and Dorsey, Julie}, + title = {Fast bilateral filtering for the display of high-dynamic-range images}, + booktitle = {ACM Transactions on Graphics (TOG)}, + year = {2002}, + pages = {257--266}, + volume = {21}, + number = {3}, + publisher = {ACM}, + url = {https://www.researchgate.net/profile/Julie_Dorsey/publication/220184746_Fast_Bilateral_Filtering_for_the_Display_of_High_-_dynamic_-_range_Images/links/54566b000cf26d5090a95f96/Fast-Bilateral-Filtering-for-the-Display-of-High-dynamic-range-Images.pdf} +} diff --git a/modules/xphoto/include/opencv2/xphoto.hpp b/modules/xphoto/include/opencv2/xphoto.hpp index 73b1e0bb3..14d5d5311 100644 --- a/modules/xphoto/include/opencv2/xphoto.hpp +++ b/modules/xphoto/include/opencv2/xphoto.hpp @@ -50,4 +50,6 @@ #include "xphoto/white_balance.hpp" #include "xphoto/dct_image_denoising.hpp" #include "xphoto/bm3d_image_denoising.hpp" +#include "xphoto/tonemap.hpp" + #endif diff --git a/modules/xphoto/include/opencv2/xphoto/tonemap.hpp b/modules/xphoto/include/opencv2/xphoto/tonemap.hpp new file mode 100644 index 000000000..8d977d607 --- /dev/null +++ b/modules/xphoto/include/opencv2/xphoto/tonemap.hpp @@ -0,0 +1,56 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_XPHOTO_TONEMAP_HPP +#define OPENCV_XPHOTO_TONEMAP_HPP + +#include "opencv2/photo.hpp" + +namespace cv { namespace xphoto { + +//! @addtogroup xphoto +//! @{ + +/** @brief This algorithm decomposes image into two layers: base layer and detail layer using bilateral filter +and compresses contrast of the base layer thus preserving all the details. + +This implementation uses regular bilateral filter from OpenCV. + +Saturation enhancement is possible as in cv::TonemapDrago. + +For more information see @cite DD02 . + */ +class CV_EXPORTS_W TonemapDurand : public Tonemap +{ +public: + + CV_WRAP virtual float getSaturation() const = 0; + CV_WRAP virtual void setSaturation(float saturation) = 0; + + CV_WRAP virtual float getContrast() const = 0; + CV_WRAP virtual void setContrast(float contrast) = 0; + + CV_WRAP virtual float getSigmaSpace() const = 0; + CV_WRAP virtual void setSigmaSpace(float sigma_space) = 0; + + CV_WRAP virtual float getSigmaColor() const = 0; + CV_WRAP virtual void setSigmaColor(float sigma_color) = 0; +}; + +/** @brief Creates TonemapDurand object + +You need to set the OPENCV_ENABLE_NONFREE option in cmake to use those. Use them at your own risk. + +@param gamma gamma value for gamma correction. See createTonemap +@param contrast resulting contrast on logarithmic scale, i. e. log(max / min), where max and min +are maximum and minimum luminance values of the resulting image. +@param saturation saturation enhancement value. See createTonemapDrago +@param sigma_space bilateral filter sigma in color space +@param sigma_color bilateral filter sigma in coordinate space + */ +CV_EXPORTS_W Ptr +createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_space = 2.0f, float sigma_color = 2.0f); + +}} // namespace +#endif // OPENCV_XPHOTO_TONEMAP_HPP diff --git a/modules/xphoto/src/tonemap.cpp b/modules/xphoto/src/tonemap.cpp new file mode 100644 index 000000000..038017ea6 --- /dev/null +++ b/modules/xphoto/src/tonemap.cpp @@ -0,0 +1,129 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include +#include +#include "opencv2/imgproc.hpp" +#include "opencv2/xphoto.hpp" + +namespace cv { namespace xphoto { + +#ifdef OPENCV_ENABLE_NONFREE +static inline +void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation) +{ + std::vector channels(3); + split(src, channels); + for(int i = 0; i < 3; i++) { + channels[i] = channels[i].mul(1.0f / lum); + pow(channels[i], saturation, channels[i]); + channels[i] = channels[i].mul(new_lum); + } + merge(channels, dst); +} + +static inline +void log_(const Mat& src, Mat& dst) +{ + max(src, Scalar::all(1e-4), dst); + log(dst, dst); +} + +class TonemapDurandImpl CV_FINAL : public TonemapDurand +{ +public: + TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) : + name("TonemapDurand"), + gamma(_gamma), + contrast(_contrast), + saturation(_saturation), + sigma_color(_sigma_color), + sigma_space(_sigma_space) + { + } + + void process(InputArray _src, OutputArray _dst) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + + Mat src = _src.getMat(); + CV_Assert(!src.empty()); + _dst.create(src.size(), CV_32FC3); + Mat img = _dst.getMat(); + Ptr linear = createTonemap(1.0f); + linear->process(src, img); + + 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 / static_cast(max - min); + exp(map_img * (scale - 1.0f) + log_img, map_img); + log_img.release(); + + mapLuminance(img, img, gray_img, map_img, saturation); + pow(img, 1.0f / gamma, img); + } + + float getGamma() const CV_OVERRIDE { return gamma; } + void setGamma(float val) CV_OVERRIDE { gamma = val; } + + float getSaturation() const CV_OVERRIDE { return saturation; } + void setSaturation(float val) CV_OVERRIDE { saturation = val; } + + float getContrast() const CV_OVERRIDE { return contrast; } + void setContrast(float val) CV_OVERRIDE { contrast = val; } + + float getSigmaColor() const CV_OVERRIDE { return sigma_color; } + void setSigmaColor(float val) CV_OVERRIDE { sigma_color = val; } + + float getSigmaSpace() const CV_OVERRIDE { return sigma_space; } + void setSigmaSpace(float val) CV_OVERRIDE { sigma_space = val; } + + void write(FileStorage& fs) const CV_OVERRIDE + { + writeFormat(fs); + fs << "name" << name + << "gamma" << gamma + << "contrast" << contrast + << "sigma_color" << sigma_color + << "sigma_space" << sigma_space + << "saturation" << saturation; + } + + void read(const FileNode& fn) CV_OVERRIDE + { + FileNode n = fn["name"]; + CV_Assert(n.isString() && String(n) == name); + gamma = fn["gamma"]; + contrast = fn["contrast"]; + sigma_color = fn["sigma_color"]; + sigma_space = fn["sigma_space"]; + saturation = fn["saturation"]; + } + +protected: + String name; + float gamma, contrast, saturation, sigma_color, sigma_space; +}; + +Ptr createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space) +{ + return makePtr(gamma, contrast, saturation, sigma_color, sigma_space); +} +#else +Ptr createTonemapDurand(float /*gamma*/, float /*contrast*/, float /*saturation*/, float /*sigma_color*/, float /*sigma_space*/) +{ + CV_Error(Error::StsNotImplemented, + "This algorithm is patented and is excluded in this configuration; " + "Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library"); +} +#endif // OPENCV_ENABLE_NONFREE + +}} // namespace diff --git a/modules/xphoto/test/test_hdr.cpp b/modules/xphoto/test/test_hdr.cpp new file mode 100644 index 000000000..bfc0d5eb1 --- /dev/null +++ b/modules/xphoto/test/test_hdr.cpp @@ -0,0 +1,43 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +using namespace cv::xphoto; + +#ifdef OPENCV_ENABLE_NONFREE + +void loadImage(string path, Mat &img) +{ + img = imread(path, -1); + ASSERT_FALSE(img.empty()) << "Could not load input image " << path; +} + +void checkEqual(Mat img0, Mat img1, double threshold, const string& name) +{ + double max = 1.0; + minMaxLoc(abs(img0 - img1), NULL, &max); + ASSERT_FALSE(max > threshold) << "max=" << max << " threshold=" << threshold << " method=" << name; +} + +TEST(Photo_Tonemap, Durand_regression) +{ + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "cv/hdr/tonemap/"; + + Mat img, expected, result; + loadImage(test_path + "image.hdr", img); + float gamma = 2.2f; + + Ptr durand = createTonemapDurand(gamma); + durand->process(img, result); + loadImage(test_path + "durand.png", expected); + result.convertTo(result, CV_8UC3, 255); + checkEqual(result, expected, 3, "Durand"); +} + +#endif // OPENCV_ENABLE_NONFREE + +}} // namespace