Ghostbusters

This commit is contained in:
Fedor Morozov 2013-09-02 16:03:10 +04:00
parent 833f8d16fa
commit e15eabe3fa
7 changed files with 427 additions and 75 deletions

View File

@ -164,7 +164,7 @@ createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation =
class CV_EXPORTS_W ExposureAlign : public Algorithm class CV_EXPORTS_W ExposureAlign : public Algorithm
{ {
public: public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst, CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
const std::vector<float>& times, InputArray response) = 0; const std::vector<float>& times, InputArray response) = 0;
}; };
@ -173,22 +173,26 @@ public:
class CV_EXPORTS_W AlignMTB : public ExposureAlign class CV_EXPORTS_W AlignMTB : public ExposureAlign
{ {
public: public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArrayOfArrays dst, CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
const std::vector<float>& times, InputArray response) = 0; 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, std::vector<Mat>& dst) = 0;
CV_WRAP virtual void calculateShift(InputArray img0, InputArray img1, 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 void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
CV_WRAP virtual void computeBitmaps(Mat& img, Mat& tb, Mat& eb) = 0;
CV_WRAP virtual int getMaxBits() const = 0; CV_WRAP virtual int getMaxBits() const = 0;
CV_WRAP virtual void setMaxBits(int max_bits) = 0; CV_WRAP virtual void setMaxBits(int max_bits) = 0;
CV_WRAP virtual int getExcludeRange() const = 0; CV_WRAP virtual int getExcludeRange() const = 0;
CV_WRAP virtual void setExcludeRange(int exclude_range) = 0; CV_WRAP virtual void setExcludeRange(int exclude_range) = 0;
CV_WRAP virtual bool getCut() const = 0;
CV_WRAP virtual void setCut(bool value) = 0;
}; };
CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4); CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true);
class CV_EXPORTS_W ExposureCalibrate : public Algorithm class CV_EXPORTS_W ExposureCalibrate : public Algorithm
{ {
@ -206,9 +210,6 @@ public:
CV_WRAP virtual int getSamples() const = 0; CV_WRAP virtual int getSamples() const = 0;
CV_WRAP virtual void setSamples(int samples) = 0; CV_WRAP virtual void setSamples(int samples) = 0;
CV_WRAP virtual bool getTest() const = 0;
CV_WRAP virtual void setTest(bool val) = 0;
}; };
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 50, float lambda = 10.0f); CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 50, float lambda = 10.0f);
@ -278,6 +279,65 @@ public:
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) = 0;
}; };
CV_EXPORTS_W Ptr<MergeRobertson> createMergeRobertson();
class CV_EXPORTS_W Ghostbuster : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response) = 0;
};
// "Ghost Detection and Removal in High Dynamic Range Images", Sidibe et al., 2009
class CV_EXPORTS_W GhostbusterOrder : public Ghostbuster
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
CV_WRAP virtual int getUnderexp() = 0;
CV_WRAP virtual void setUnderexp(int value) = 0;
CV_WRAP virtual int getOverexp() = 0;
CV_WRAP virtual void setOverexp(int value) = 0;
};
CV_EXPORTS_W Ptr<GhostbusterOrder> createGhostbusterOrder(int underexp = 20, int overexp = 240);
// "Fast and Robust High Dynamic Range Image Generation with Camera and Object Movement", Grosch, 2006
class CV_EXPORTS_W GhostbusterPredict : public Ghostbuster
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times) = 0;
CV_WRAP virtual int getThreshold() = 0;
CV_WRAP virtual void setThreshold(int value) = 0;
CV_WRAP virtual int getUnderexp() = 0;
CV_WRAP virtual void setUnderexp(int value) = 0;
CV_WRAP virtual int getOverexp() = 0;
CV_WRAP virtual void setOverexp(int value) = 0;
};
CV_EXPORTS_W Ptr<GhostbusterPredict> createGhostbusterPredict(int thresh = 10, int underexp = 20, int overexp = 240);
// "Bitmap Movement Detection: HDR for Dynamic Scenes", Pece, Kautz, 2010
class CV_EXPORTS_W GhostbusterBitmap : public Ghostbuster
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
CV_WRAP virtual int getExclude() = 0;
CV_WRAP virtual void setExclude(int value) = 0;
};
CV_EXPORTS_W Ptr<GhostbusterBitmap> createGhostbusterBitmap(int exclude = 4);
} // cv } // cv
#endif #endif

View File

@ -50,24 +50,24 @@ namespace cv
class AlignMTBImpl : public AlignMTB class AlignMTBImpl : public AlignMTB
{ {
public: public:
AlignMTBImpl(int max_bits, int exclude_range) : AlignMTBImpl(int max_bits, int exclude_range, bool cut) :
max_bits(max_bits), max_bits(max_bits),
exclude_range(exclude_range), exclude_range(exclude_range),
cut(cut),
name("AlignMTB") name("AlignMTB")
{ {
} }
void process(InputArrayOfArrays src, OutputArrayOfArrays dst, void process(InputArrayOfArrays src, std::vector<Mat>& dst,
const std::vector<float>& times, InputArray response) const std::vector<float>& times, InputArray response)
{ {
process(src, dst); process(src, dst);
} }
void process(InputArrayOfArrays _src, OutputArray _dst) void process(InputArrayOfArrays _src, std::vector<Mat>& dst)
{ {
std::vector<Mat> src, dst; std::vector<Mat> src;
_src.getMatVector(src); _src.getMatVector(src);
_dst.getMatVector(dst);
checkImageDimensions(src); checkImageDimensions(src);
dst.resize(src.size()); dst.resize(src.size());
@ -76,17 +76,41 @@ public:
dst[pivot] = src[pivot]; dst[pivot] = src[pivot];
Mat gray_base; Mat gray_base;
cvtColor(src[pivot], gray_base, COLOR_RGB2GRAY); cvtColor(src[pivot], gray_base, COLOR_RGB2GRAY);
std::vector<Point> shifts;
for(size_t i = 0; i < src.size(); i++) { for(size_t i = 0; i < src.size(); i++) {
if(i == pivot) { if(i == pivot) {
shifts.push_back(Point(0, 0));
continue; continue;
} }
Mat gray; Mat gray;
cvtColor(src[i], gray, COLOR_RGB2GRAY); cvtColor(src[i], gray, COLOR_RGB2GRAY);
Point shift; Point shift;
calculateShift(gray_base, gray, shift); calculateShift(gray_base, gray, shift);
shifts.push_back(shift);
shiftMat(src[i], dst[i], shift); shiftMat(src[i], dst[i], shift);
} }
if(cut) {
Point max(0, 0), min(0, 0);
for(size_t i = 0; i < shifts.size(); i++) {
if(shifts[i].x > max.x) {
max.x = shifts[i].x;
}
if(shifts[i].y > max.y) {
max.y = shifts[i].y;
}
if(shifts[i].x < min.x) {
min.x = shifts[i].x;
}
if(shifts[i].y < min.y) {
min.y = shifts[i].y;
}
}
Point size = dst[0].size();
for(size_t i = 0; i < dst.size(); i++) {
dst[i] = dst[i](Rect(max, min + size));
}
}
} }
void calculateShift(InputArray _img0, InputArray _img1, Point& shift) void calculateShift(InputArray _img0, InputArray _img1, Point& shift)
@ -109,8 +133,8 @@ public:
shift *= 2; shift *= 2;
Mat tb1, tb2, eb1, eb2; Mat tb1, tb2, eb1, eb2;
computeBitmaps(pyr0[level], tb1, eb1, exclude_range); computeBitmaps(pyr0[level], tb1, eb1);
computeBitmaps(pyr1[level], tb2, eb2, exclude_range); computeBitmaps(pyr1[level], tb2, eb2);
int min_err = pyr0[level].total(); int min_err = pyr0[level].total();
Point new_shift(shift); Point new_shift(shift);
@ -140,12 +164,13 @@ public:
_dst.create(src.size(), src.type()); _dst.create(src.size(), src.type());
Mat dst = _dst.getMat(); Mat dst = _dst.getMat();
dst = Mat::zeros(src.size(), src.type()); Mat res = Mat::zeros(src.size(), src.type());
int width = src.cols - abs(shift.x); int width = src.cols - abs(shift.x);
int height = src.rows - abs(shift.y); int height = src.rows - abs(shift.y);
Rect dst_rect(max(shift.x, 0), max(shift.y, 0), width, height); Rect dst_rect(max(shift.x, 0), max(shift.y, 0), width, height);
Rect src_rect(max(-shift.x, 0), max(-shift.y, 0), width, height); Rect src_rect(max(-shift.x, 0), max(-shift.y, 0), width, height);
src(src_rect).copyTo(dst(dst_rect)); src(src_rect).copyTo(res(dst_rect));
res.copyTo(dst);
} }
int getMaxBits() const { return max_bits; } int getMaxBits() const { return max_bits; }
@ -154,11 +179,15 @@ public:
int getExcludeRange() const { return exclude_range; } int getExcludeRange() const { return exclude_range; }
void setExcludeRange(int val) { exclude_range = val; } void setExcludeRange(int val) { exclude_range = val; }
bool getCut() const { return cut; }
void setCut(bool val) { cut = val; }
void write(FileStorage& fs) const void write(FileStorage& fs) const
{ {
fs << "name" << name fs << "name" << name
<< "max_bits" << max_bits << "max_bits" << max_bits
<< "exclude_range" << exclude_range; << "exclude_range" << exclude_range
<< "cut" << static_cast<int>(cut);
} }
void read(const FileNode& fn) void read(const FileNode& fn)
@ -167,11 +196,21 @@ public:
CV_Assert(n.isString() && String(n) == name); CV_Assert(n.isString() && String(n) == name);
max_bits = fn["max_bits"]; max_bits = fn["max_bits"];
exclude_range = fn["exclude_range"]; exclude_range = fn["exclude_range"];
int cut_val = fn["cut"];
cut = static_cast<bool>(cut_val);
}
void computeBitmaps(Mat& img, Mat& tb, Mat& eb)
{
int median = getMedian(img);
compare(img, median, tb, CMP_GT);
compare(abs(img - median), exclude_range, eb, CMP_GT);
} }
protected: protected:
String name; String name;
int max_bits, exclude_range; int max_bits, exclude_range;
bool cut;
void downsample(Mat& src, Mat& dst) void downsample(Mat& src, Mat& dst)
{ {
@ -217,18 +256,289 @@ protected:
} }
return median; return median;
} }
};
void computeBitmaps(Mat& img, Mat& tb, Mat& eb, int exclude_range) Ptr<AlignMTB> createAlignMTB(int max_bits, int exclude_range, bool cut)
{ {
int median = getMedian(img); return new AlignMTBImpl(max_bits, exclude_range, cut);
compare(img, median, tb, CMP_GT); }
compare(abs(img - median), exclude_range, eb, CMP_GT);
class floatIndexCmp {
public:
floatIndexCmp(std::vector<float> data) :
data(data)
{
}
bool operator() (int i,int j)
{
return data[i] < data[j];
}
protected:
std::vector<float> data;
};
class GhostbusterOrderImpl : public GhostbusterOrder
{
public:
GhostbusterOrderImpl(int underexp, int overexp) :
underexp(underexp),
overexp(overexp),
name("GhostbusterOrder")
{
}
void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response)
{
process(src, dst);
}
void process(InputArrayOfArrays src, OutputArray dst)
{
std::vector<Mat> unsorted_images;
src.getMatVector(unsorted_images);
checkImageDimensions(unsorted_images);
std::vector<Mat> images;
sortImages(unsorted_images, images);
int channels = images[0].channels();
dst.create(images[0].size(), CV_8U);
Mat res = Mat::zeros(images[0].size(), CV_8U);
std::vector<Mat> splitted(channels);
split(images[0], splitted);
for(int i = 0; i < images.size() - 1; i++) {
std::vector<Mat> next_splitted(channels);
split(images[i + 1], next_splitted);
for(int c = 0; c < channels; c++) {
Mat exposed = (splitted[c] >= underexp) & (splitted[c] <= overexp);
exposed &= (next_splitted[c] >= underexp) & (next_splitted[c] <= overexp);
Mat ghost = (splitted[c] > next_splitted[c]) & exposed;
res |= ghost;
}
splitted = next_splitted;
}
res.copyTo(dst.getMat());
}
int getUnderexp() {return underexp;}
void setUnderexp(int value) {underexp = value;}
int getOverexp() {return overexp;}
void setOverexp(int value) {overexp = value;}
void write(FileStorage& fs) const
{
fs << "name" << name
<< "overexp" << overexp
<< "underexp" << underexp;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
overexp = fn["overexp"];
underexp = fn["underexp"];
}
protected:
int overexp, underexp;
String name;
void sortImages(std::vector<Mat>& images, std::vector<Mat>& sorted)
{
std::vector<int>indices(images.size());
std::vector<float>means(images.size());
for(size_t i = 0; i < images.size(); i++) {
indices[i] = i;
means[i] = mean(mean(images[i]))[0];
}
sort(indices.begin(), indices.end(), floatIndexCmp(means));
sorted.resize(images.size());
for(size_t i = 0; i < images.size(); i++) {
sorted[i] = images[indices[i]];
}
} }
}; };
CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits, int exclude_range) Ptr<GhostbusterOrder> createGhostbusterOrder(int underexp, int overexp)
{ {
return new AlignMTBImpl(max_bits, exclude_range); return new GhostbusterOrderImpl(underexp, overexp);
}
class GhostbusterPredictImpl : public GhostbusterPredict
{
public:
GhostbusterPredictImpl(int thresh, int underexp, int overexp) :
thresh(thresh),
underexp(underexp),
overexp(overexp),
name("GhostbusterPredict")
{
}
void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response)
{
std::vector<Mat> images;
src.getMatVector(images);
checkImageDimensions(images);
int channels = images[0].channels();
dst.create(images[0].size(), CV_8U);
Mat res = Mat::zeros(images[0].size(), CV_8U);
Mat radiance;
LUT(images[0], response, radiance);
std::vector<Mat> splitted(channels);
split(radiance, splitted);
std::vector<Mat> resp_split(channels);
split(response, resp_split);
for(int i = 0; i < images.size() - 1; i++) {
std::vector<Mat> next_splitted(channels);
LUT(images[i + 1], response, radiance);
split(radiance, next_splitted);
for(int c = 0; c < channels; c++) {
Mat predicted = splitted[c] / times[i] * times[i + 1];
Mat low = max(thresh, next_splitted[c]) - thresh;
Mat high = min(255 - thresh, next_splitted[c]) + thresh;
low.convertTo(low, CV_8U);
high.convertTo(high, CV_8U);
LUT(low, resp_split[c], low);
LUT(high, resp_split[c], high);
Mat exposed = (splitted[c] >= underexp) & (splitted[c] <= overexp);
exposed &= (next_splitted[c] >= underexp) & (next_splitted[c] <= overexp);
Mat ghost = (low < predicted) & (predicted < high);
ghost &= exposed;
res |= ghost;
}
splitted = next_splitted;
}
res.copyTo(dst.getMat());
}
virtual void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times)
{
process(src, dst, times, linearResponse(3));
}
CV_WRAP virtual int getThreshold() {return thresh;}
CV_WRAP virtual void setThreshold(int value) {thresh = value;}
int getUnderexp() {return underexp;}
void setUnderexp(int value) {underexp = value;}
int getOverexp() {return overexp;}
void setOverexp(int value) {overexp = value;}
void write(FileStorage& fs) const
{
fs << "name" << name
<< "overexp" << overexp
<< "underexp" << underexp
<< "thresh" << thresh;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
overexp = fn["overexp"];
underexp = fn["underexp"];
thresh = fn["thresh"];
}
protected:
int thresh, underexp, overexp;
String name;
};
Ptr<GhostbusterPredict> createGhostbusterPredict(int thresh, int underexp, int overexp)
{
return new GhostbusterPredictImpl(thresh, underexp, overexp);
}
class GhostbusterBitmapImpl : public GhostbusterBitmap
{
public:
GhostbusterBitmapImpl(int exclude) :
exclude(exclude),
name("GhostbusterBitmap")
{
}
void process(InputArrayOfArrays src, OutputArray dst, std::vector<float>& times, Mat response)
{
process(src, dst);
}
void process(InputArrayOfArrays src, OutputArray dst)
{
std::vector<Mat> images;
src.getMatVector(images);
checkImageDimensions(images);
int channels = images[0].channels();
dst.create(images[0].size(), CV_8U);
Mat res = Mat::zeros(images[0].size(), CV_8U);
Ptr<AlignMTB> MTB = createAlignMTB();
MTB->setExcludeRange(exclude);
for(size_t i = 0; i < images.size(); i++) {
Mat gray;
if(channels == 1) {
gray = images[i];
} else {
cvtColor(images[i], gray, COLOR_RGB2GRAY);
}
Mat tb, eb;
MTB->computeBitmaps(gray, tb, eb);
tb &= eb & 1;
res += tb;
}
res = (res > 0) & (res < images.size());
res.copyTo(dst.getMat());
}
int getExclude() {return exclude;}
void setExclude(int value) {exclude = value;}
void write(FileStorage& fs) const
{
fs << "name" << name
<< "exclude" << exclude;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
exclude = fn["exclude"];
}
protected:
int exclude;
String name;
};
Ptr<GhostbusterBitmap> createGhostbusterBitmap(int exclude)
{
return new GhostbusterBitmapImpl(exclude);
} }
} }

View File

@ -55,8 +55,7 @@ public:
samples(samples), samples(samples),
lambda(lambda), lambda(lambda),
name("CalibrateDebevec"), name("CalibrateDebevec"),
w(tringleWeights()), w(tringleWeights())
test(false)
{ {
} }
@ -84,9 +83,6 @@ public:
for(int i = 0; i < samples; i++) { for(int i = 0; i < samples; i++) {
int pos = 3 * (rand() % images[0].total()) + channel; int pos = 3 * (rand() % images[0].total()) + channel;
if(test) {
pos = 3 * i + channel;
}
for(size_t j = 0; j < images.size(); j++) { for(size_t j = 0; j < images.size(); j++) {
int val = (images[j].ptr() + pos)[0]; int val = (images[j].ptr() + pos)[0];
@ -113,9 +109,6 @@ public:
exp(result, result); exp(result, result);
} }
bool getTest() const { return test; }
void setTest(bool val) { test = val; }
int getSamples() const { return samples; } int getSamples() const { return samples; }
void setSamples(int val) { samples = val; } void setSamples(int val) { samples = val; }
@ -141,7 +134,6 @@ protected:
String name; String name;
int samples; int samples;
float lambda; float lambda;
bool test;
Mat w; Mat w;
}; };

View File

@ -92,4 +92,21 @@ void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation)
merge(channels, dst); merge(channels, dst);
} }
Mat linearResponse(int channels)
{
Mat single_response = Mat(256, 1, CV_32F);
for(int i = 1; i < 256; i++) {
single_response.at<float>(i) = static_cast<float>(i);
}
single_response.at<float>(0) = static_cast<float>(1);
std::vector<Mat> splitted(channels);
for(int c = 0; c < channels; c++) {
splitted[c] = single_response;
}
Mat result;
merge(splitted, result);
return result;
}
}; };

View File

@ -56,6 +56,7 @@ void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
Mat RobertsonWeights(); Mat RobertsonWeights();
Mat linearResponse(int channels);
}; };
#endif #endif

View File

@ -125,23 +125,6 @@ public:
protected: protected:
String name; String name;
Mat weights; Mat weights;
Mat linearResponse(int channels)
{
Mat single_response = Mat(256, 1, CV_32F);
for(int i = 1; i < 256; i++) {
single_response.at<float>(i) = static_cast<float>(i);
}
single_response.at<float>(0) = static_cast<float>(1);
std::vector<Mat> splitted(channels);
for(int c = 0; c < channels; c++) {
splitted[c] = single_response;
}
Mat result;
merge(splitted, result);
return result;
}
}; };
Ptr<MergeDebevec> createMergeDebevec() Ptr<MergeDebevec> createMergeDebevec()
@ -329,7 +312,7 @@ public:
Mat response = input_response.getMat(); Mat response = input_response.getMat();
if(response.empty()) { if(response.empty()) {
response = linearResponse(channels); response = linearResponse(channels) / 128.0f;
} }
CV_Assert(response.rows == 256 && response.cols == 1 && CV_Assert(response.rows == 256 && response.cols == 1 &&
response.channels() == channels); response.channels() == channels);
@ -355,17 +338,6 @@ public:
protected: protected:
String name; String name;
Mat weight; Mat weight;
Mat linearResponse(int channels)
{
Mat response = Mat::zeros(256, 1, CV_32FC3);
for(int i = 0; i < 256; i++) {
for(int c = 0; c < 3; c++) {
response.at<Vec3f>(i)[c] = static_cast<float>(i) / 128.0f;
}
}
return response;
}
}; };
Ptr<MergeRobertson> createMergeRobertson() Ptr<MergeRobertson> createMergeRobertson()

View File

@ -79,7 +79,7 @@ void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DE
void loadResponseCSV(String path, Mat& response) void loadResponseCSV(String path, Mat& response)
{ {
response = Mat(256, 1, CV_32FC3); response = Mat(256, 1, CV_32FC3);
ifstream resp_file(path.c_str()); ifstream resp_file(path);
for(int i = 0; i < 256; i++) { for(int i = 0; i < 256; i++) {
for(int c = 0; c < 3; c++) { for(int c = 0; c < 3; c++) {
resp_file >> response.at<Vec3f>(i)[c]; resp_file >> response.at<Vec3f>(i)[c];
@ -101,31 +101,31 @@ TEST(Photo_Tonemap, regression)
linear->process(img, result); linear->process(img, result);
loadImage(test_path + "linear.png", expected); loadImage(test_path + "linear.png", expected);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0); checkEqual(result, expected, 3);
Ptr<TonemapDrago> drago = createTonemapDrago(gamma); Ptr<TonemapDrago> drago = createTonemapDrago(gamma);
drago->process(img, result); drago->process(img, result);
loadImage(test_path + "drago.png", expected); loadImage(test_path + "drago.png", expected);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0); checkEqual(result, expected, 3);
Ptr<TonemapDurand> durand = createTonemapDurand(gamma); Ptr<TonemapDurand> durand = createTonemapDurand(gamma);
durand->process(img, result); durand->process(img, result);
loadImage(test_path + "durand.png", expected); loadImage(test_path + "durand.png", expected);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0); checkEqual(result, expected, 3);
Ptr<TonemapReinhardDevlin> reinhard_devlin = createTonemapReinhardDevlin(gamma); Ptr<TonemapReinhardDevlin> reinhard_devlin = createTonemapReinhardDevlin(gamma);
reinhard_devlin->process(img, result); reinhard_devlin->process(img, result);
loadImage(test_path + "reinharddevlin.png", expected); loadImage(test_path + "reinharddevlin.png", expected);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0); checkEqual(result, expected, 3);
Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma); Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma);
mantiuk->process(img, result); mantiuk->process(img, result);
loadImage(test_path + "mantiuk.png", expected); loadImage(test_path + "mantiuk.png", expected);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 0); checkEqual(result, expected, 3);
} }
TEST(Photo_AlignMTB, regression) TEST(Photo_AlignMTB, regression)
@ -169,7 +169,7 @@ TEST(Photo_MergeMertens, regression)
loadImage(test_path + "merge/mertens.png", expected); loadImage(test_path + "merge/mertens.png", expected);
merge->process(images, result); merge->process(images, result);
result.convertTo(result, CV_8UC3, 255); result.convertTo(result, CV_8UC3, 255);
checkEqual(expected, result, 0); checkEqual(expected, result, 3);
} }
TEST(Photo_MergeDebevec, regression) TEST(Photo_MergeDebevec, regression)
@ -188,7 +188,7 @@ TEST(Photo_MergeDebevec, regression)
loadImage(test_path + "merge/debevec.exr", expected); loadImage(test_path + "merge/debevec.exr", expected);
merge->process(images, result, times, response); merge->process(images, result, times, response);
imwrite("test.exr", result); imwrite("test.exr", result);
checkEqual(expected, result, 1e-3f); checkEqual(expected, result, 1e-2f);
} }
TEST(Photo_CalibrateDebevec, regression) TEST(Photo_CalibrateDebevec, regression)
@ -197,11 +197,11 @@ TEST(Photo_CalibrateDebevec, regression)
vector<Mat> images; vector<Mat> images;
vector<float> times; vector<float> times;
Mat expected, response; Mat response, expected;
loadExposureSeq(test_path + "exposures/", images, times); loadExposureSeq(test_path + "exposures/", images, times);
loadResponseCSV(test_path + "calibrate/debevec.csv", expected); loadResponseCSV(test_path + "calibrate/debevec.csv", expected);
Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec(); Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
calibrate->setTest(true); srand(1);
calibrate->process(images, response, times); calibrate->process(images, response, times);
checkEqual(expected, response, 1e-3f); checkEqual(expected, response, 1e-3f);
} }