Commit 17609b90 authored by Fedor Morozov's avatar Fedor Morozov

Mantiuk's tonemapping

parent c51b50b4
......@@ -123,10 +123,9 @@ HdrEncoder::~HdrEncoder()
bool HdrEncoder::write( const Mat& _img, const std::vector<int>& params )
{
CV_Assert(_img.channels() == 3);
Mat img;
if(_img.depth() == CV_32F) {
_img.convertTo(img, CV_32FC3);
} else {
if(_img.depth() != CV_32F) {
_img.convertTo(img, CV_32FC3, 1/255.0f);
}
CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE);
......
......@@ -87,8 +87,8 @@ class CV_EXPORTS_W Tonemap : public Algorithm
public:
CV_WRAP virtual void process(InputArray src, OutputArray dst) = 0;
CV_WRAP virtual float getGamma() const = 0;
CV_WRAP virtual void setGamma(float gamma) = 0;
CV_WRAP virtual float getGamma() const = 0;
CV_WRAP virtual void setGamma(float gamma) = 0;
};
class CV_EXPORTS_W TonemapLinear : public Tonemap
......@@ -102,71 +102,92 @@ CV_EXPORTS_W Ptr<TonemapLinear> createTonemapLinear(float gamma = 1.0f);
class CV_EXPORTS_W TonemapDrago : public Tonemap
{
public:
CV_WRAP virtual float getBias() const = 0;
CV_WRAP virtual void setBias(float bias) = 0;
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
CV_WRAP virtual float getBias() const = 0;
CV_WRAP virtual void setBias(float bias) = 0;
};
CV_EXPORTS_W Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float bias = 0.85f);
CV_EXPORTS_W Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f);
// "Fast Bilateral Filtering for the Display of High-Dynamic-Range Images", Durand, Dorsey, 2002
class CV_EXPORTS_W TonemapDurand : public Tonemap
{
public:
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 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 getSigmaColor() const = 0;
CV_WRAP virtual void setSigmaColor(float sigma_color) = 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;
};
CV_EXPORTS_W Ptr<TonemapDurand>
createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float sigma_space = 2.0f, float sigma_color = 2.0f);
createTonemapDurand(float gamma = 1.0f, float saturation = 1.0f, float contrast = 4.0f, float sigma_space = 2.0f, float sigma_color = 2.0f);
// "Dynamic Range Reduction Inspired by Photoreceptor Physiology", Reinhard, Devlin, 2005
class CV_EXPORTS_W TonemapReinhardDevlin : public Tonemap
{
public:
CV_WRAP virtual float getIntensity() const = 0;
CV_WRAP virtual void setIntensity(float intensity) = 0;
CV_WRAP virtual float getIntensity() const = 0;
CV_WRAP virtual void setIntensity(float intensity) = 0;
CV_WRAP virtual float getLightAdaptation() const = 0;
CV_WRAP virtual void setLightAdaptation(float light_adapt) = 0;
CV_WRAP virtual float getLightAdaptation() const = 0;
CV_WRAP virtual void setLightAdaptation(float light_adapt) = 0;
CV_WRAP virtual float getColorAdaptation() const = 0;
CV_WRAP virtual void setColorAdaptation(float color_adapt) = 0;
CV_WRAP virtual float getColorAdaptation() const = 0;
CV_WRAP virtual void setColorAdaptation(float color_adapt) = 0;
};
CV_EXPORTS_W Ptr<TonemapReinhardDevlin>
createTonemapReinhardDevlin(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f);
class CV_EXPORTS_W TonemapMantiuk : public Tonemap
{
public:
CV_WRAP virtual float getScale() const = 0;
CV_WRAP virtual void setScale(float scale) = 0;
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
};
CV_EXPORTS_W Ptr<TonemapMantiuk>
createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f);
class CV_EXPORTS_W ExposureAlign : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst,
const std::vector<float>& times, InputArray response) = 0;
};
class CV_EXPORTS_W AlignMTB : public ExposureAlign
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst) = 0;
CV_WRAP virtual void calculateShift(InputArray img0, InputArray img1, Point& shift) = 0;
CV_WRAP virtual void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
CV_WRAP virtual void calculateShift(InputArray img0, InputArray img1, Point& shift) = 0;
CV_WRAP virtual void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
CV_WRAP virtual int getMaxBits() const = 0;
CV_WRAP virtual void setMaxBits(int max_bits) = 0;
CV_WRAP virtual int getMaxBits() const = 0;
CV_WRAP virtual void setMaxBits(int max_bits) = 0;
CV_WRAP virtual int getExcludeRange() const = 0;
CV_WRAP virtual void setExcludeRange(int exclude_range) = 0;
CV_WRAP virtual int getExcludeRange() const = 0;
CV_WRAP virtual void setExcludeRange(int exclude_range) = 0;
};
// "Fast, Robust Image Registration for Compositing High Dynamic Range Photographs from Handheld Exposures", Ward, 2003
......@@ -176,7 +197,7 @@ CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range =
class CV_EXPORTS_W ExposureCalibrate : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times) = 0;
};
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
......@@ -184,11 +205,11 @@ public:
class CV_EXPORTS_W CalibrateDebevec : public ExposureCalibrate
{
public:
CV_WRAP virtual float getLambda() const = 0;
CV_WRAP virtual float getLambda() const = 0;
CV_WRAP virtual void setLambda(float lambda) = 0;
CV_WRAP virtual int getSamples() const = 0;
CV_WRAP virtual void setSamples(int samples) = 0;
CV_WRAP virtual int getSamples() const = 0;
CV_WRAP virtual void setSamples(int samples) = 0;
};
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 50, float lambda = 10.0f);
......@@ -196,8 +217,8 @@ CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 50, floa
class CV_EXPORTS_W ExposureMerge : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
};
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
......@@ -205,9 +226,9 @@ public:
class CV_EXPORTS_W MergeDebevec : public ExposureMerge
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, const std::vector<float>& times) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, const std::vector<float>& times) = 0;
};
CV_EXPORTS_W Ptr<MergeDebevec> createMergeDebevec();
......@@ -217,18 +238,18 @@ CV_EXPORTS_W Ptr<MergeDebevec> createMergeDebevec();
class CV_EXPORTS_W MergeMertens : public ExposureMerge
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
const std::vector<float>& times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
CV_WRAP virtual float getContrastWeight() const = 0;
CV_WRAP virtual void setContrastWeight(float contrast_weiht) = 0;
CV_WRAP virtual float getContrastWeight() const = 0;
CV_WRAP virtual void setContrastWeight(float contrast_weiht) = 0;
CV_WRAP virtual float getSaturationWeight() const = 0;
CV_WRAP virtual void setSaturationWeight(float saturation_weight) = 0;
CV_WRAP virtual float getSaturationWeight() const = 0;
CV_WRAP virtual void setSaturationWeight(float saturation_weight) = 0;
CV_WRAP virtual float getExposureWeight() const = 0;
CV_WRAP virtual void setExposureWeight(float exposure_weight) = 0;
CV_WRAP virtual float getExposureWeight() const = 0;
CV_WRAP virtual void setExposureWeight(float exposure_weight) = 0;
};
CV_EXPORTS_W Ptr<MergeMertens>
......
This diff is collapsed.
......@@ -47,73 +47,73 @@
namespace cv
{
class CalibrateDebevecImpl : public CalibrateDebevec
{
public:
CalibrateDebevecImpl(int samples, float lambda) :
samples(samples),
lambda(lambda),
name("CalibrateDebevec"),
w(tringleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times)
{
std::vector<Mat> images;
src.getMatVector(images);
dst.create(256, images[0].channels(), CV_32F);
Mat response = dst.getMat();
CalibrateDebevecImpl(int samples, float lambda) :
samples(samples),
lambda(lambda),
name("CalibrateDebevec"),
w(tringleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times)
{
std::vector<Mat> images;
src.getMatVector(images);
dst.create(256, images[0].channels(), CV_32F);
Mat response = dst.getMat();
CV_Assert(!images.empty() && images.size() == times.size());
CV_Assert(images[0].depth() == CV_8U);
checkImageDimensions(images);
CV_Assert(!images.empty() && images.size() == times.size());
CV_Assert(images[0].depth() == CV_8U);
checkImageDimensions(images);
for(int channel = 0; channel < images[0].channels(); channel++) {
Mat A = Mat::zeros(samples * images.size() + 257, 256 + samples, CV_32F);
Mat B = Mat::zeros(A.rows, 1, CV_32F);
for(int channel = 0; channel < images[0].channels(); channel++) {
Mat A = Mat::zeros(samples * images.size() + 257, 256 + samples, CV_32F);
Mat B = Mat::zeros(A.rows, 1, CV_32F);
int eq = 0;
for(int i = 0; i < samples; i++) {
int eq = 0;
for(int i = 0; i < samples; i++) {
int pos = 3 * (rand() % images[0].total()) + channel;
for(size_t j = 0; j < images.size(); j++) {
int pos = 3 * (rand() % images[0].total()) + channel;
for(size_t j = 0; j < images.size(); j++) {
int val = (images[j].ptr() + pos)[0];
A.at<float>(eq, val) = w.at<float>(val);
A.at<float>(eq, 256 + i) = -w.at<float>(val);
B.at<float>(eq, 0) = w.at<float>(val) * log(times[j]);
eq++;
}
}
A.at<float>(eq, 128) = 1;
eq++;
int val = (images[j].ptr() + pos)[0];
A.at<float>(eq, val) = w.at<float>(val);
A.at<float>(eq, 256 + i) = -w.at<float>(val);
B.at<float>(eq, 0) = w.at<float>(val) * log(times[j]);
eq++;
}
}
A.at<float>(eq, 128) = 1;
eq++;
for(int i = 0; i < 254; i++) {
A.at<float>(eq, i) = lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 1) = -2 * lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 2) = lambda * w.at<float>(i + 1);
eq++;
}
Mat solution;
solve(A, B, solution, DECOMP_SVD);
solution.rowRange(0, 256).copyTo(response.col(channel));
}
exp(response, response);
}
for(int i = 0; i < 254; i++) {
A.at<float>(eq, i) = lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 1) = -2 * lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 2) = lambda * w.at<float>(i + 1);
eq++;
}
Mat solution;
solve(A, B, solution, DECOMP_SVD);
solution.rowRange(0, 256).copyTo(response.col(channel));
}
exp(response, response);
}
int getSamples() const { return samples; }
void setSamples(int val) { samples = val; }
int getSamples() const { return samples; }
void setSamples(int val) { samples = val; }
float getLambda() const { return lambda; }
void setLambda(float val) { lambda = val; }
float getLambda() const { return lambda; }
void setLambda(float val) { lambda = val; }
void write(FileStorage& fs) const
void write(FileStorage& fs) const
{
fs << "name" << name
<< "samples" << samples
<< "lambda" << lambda;
<< "samples" << samples
<< "lambda" << lambda;
}
void read(const FileNode& fn)
......@@ -121,19 +121,19 @@ public:
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
samples = fn["samples"];
lambda = fn["lambda"];
lambda = fn["lambda"];
}
protected:
String name;
int samples;
float lambda;
Mat w;
String name;
int samples;
float lambda;
Mat w;
};
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda)
{
return new CalibrateDebevecImpl(samples, lambda);
return new CalibrateDebevecImpl(samples, lambda);
}
}
\ No newline at end of file
......@@ -49,26 +49,38 @@ namespace cv
void checkImageDimensions(const std::vector<Mat>& images)
{
CV_Assert(!images.empty());
int width = images[0].cols;
int height = images[0].rows;
int type = images[0].type();
CV_Assert(!images.empty());
int width = images[0].cols;
int height = images[0].rows;
int type = images[0].type();
for(size_t i = 0; i < images.size(); i++) {
CV_Assert(images[i].cols == width && images[i].rows == height);
CV_Assert(images[i].type() == type);
}
for(size_t i = 0; i < images.size(); i++) {
CV_Assert(images[i].cols == width && images[i].rows == height);
CV_Assert(images[i].type() == type);
}
}
Mat tringleWeights()
{
Mat w(256, 3, CV_32F);
for(int i = 0; i < 256; i++) {
for(int j = 0; j < 3; j++) {
w.at<float>(i, j) = i < 128 ? i + 1.0f : 256.0f - i;
}
}
return w;
Mat w(256, 3, CV_32F);
for(int i = 0; i < 256; i++) {
for(int j = 0; j < 3; j++) {
w.at<float>(i, j) = i < 128 ? i + 1.0f : 256.0f - i;
}
}
return w;
}
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation)
{
std::vector<Mat> 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);
}
};
\ No newline at end of file
......@@ -53,6 +53,8 @@ void checkImageDimensions(const std::vector<Mat>& images);
Mat tringleWeights();
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
};
#endif
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -91,12 +91,11 @@ void loadResponseCSV(String path, Mat& response)
TEST(Photo_Tonemap, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
Mat img, expected, result;
loadImage(test_path + "rle.hdr", img);
loadImage(test_path + "image.hdr", img);
float gamma = 2.2f;
test_path += "tonemap/";
Ptr<TonemapLinear> linear = createTonemapLinear(gamma);
linear->process(img, result);
......@@ -121,6 +120,12 @@ TEST(Photo_Tonemap, regression)
loadImage(test_path + "reinharddevlin.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0);
Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma);
mantiuk->process(img, result);
loadImage(test_path + "mantiuk.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0);
}
TEST(Photo_AlignMTB, regression)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment