Tonemap as Algorithm
This commit is contained in:
parent
af2c9077f7
commit
258b98d15b
@ -59,20 +59,6 @@ enum
|
|||||||
INPAINT_TELEA = 1 // A. Telea algorithm
|
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
|
//! restores the damaged image areas using one of the available intpainting algorithms
|
||||||
CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask,
|
CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask,
|
||||||
OutputArray dst, double inpaintRadius, int flags );
|
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<float>& exp_times, OutputArray dst, Mat response = Mat());
|
CV_EXPORTS_W void makeHDR(InputArrayOfArrays srcImgs, const std::vector<float>& exp_times, OutputArray dst, Mat response = Mat());
|
||||||
|
|
||||||
CV_EXPORTS_W void tonemap(InputArray src, OutputArray dst, int algorithm,
|
|
||||||
const std::vector<float>& params = std::vector<float>());
|
|
||||||
|
|
||||||
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 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);
|
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<float>& exp_times, OutputArray dst, int samples = 50, float lambda = 10);
|
CV_EXPORTS_W void estimateResponse(InputArrayOfArrays srcImgs, const std::vector<float>& exp_times, OutputArray dst, int samples = 50, float lambda = 10);
|
||||||
|
|
||||||
CV_EXPORTS_W void alignImages(InputArrayOfArrays src, std::vector<Mat>& dst);
|
CV_EXPORTS_W void alignImages(InputArrayOfArrays src, std::vector<Mat>& dst);
|
||||||
|
|
||||||
|
class CV_EXPORTS_W Tonemap : public Algorithm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Tonemap(float gamma);
|
||||||
|
virtual ~Tonemap();
|
||||||
|
void process(InputArray src, OutputArray dst);
|
||||||
|
static Ptr<Tonemap> 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
|
} // cv
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -161,7 +161,6 @@ void makeHDR(InputArrayOfArrays _images, const std::vector<float>& _exp_times, O
|
|||||||
res_ptr[channel] = exp(sum[channel] / weight_sum);
|
res_ptr[channel] = exp(sum[channel] / weight_sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tonemap(result, result, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void exposureFusion(InputArrayOfArrays _images, OutputArray _dst, float wc, float ws, float we)
|
void exposureFusion(InputArrayOfArrays _images, OutputArray _dst, float wc, float ws, float we)
|
||||||
|
@ -47,20 +47,58 @@
|
|||||||
namespace cv
|
namespace cv
|
||||||
{
|
{
|
||||||
|
|
||||||
static float getParam(const std::vector<float>& params, size_t i, float defval)
|
Tonemap::Tonemap(float gamma) : gamma(gamma)
|
||||||
{
|
{
|
||||||
if(params.size() > i) {
|
}
|
||||||
return params[i];
|
|
||||||
} else {
|
Tonemap::~Tonemap()
|
||||||
return defval;
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector<float>& params)
|
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;
|
Mat gray_img;
|
||||||
cvtColor(src_img, gray_img, COLOR_RGB2GRAY);
|
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||||
Mat log_img;
|
Mat log_img;
|
||||||
log(gray_img, log_img);
|
log(gray_img, log_img);
|
||||||
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
|
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
|
||||||
@ -73,7 +111,7 @@ static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector<float>& param
|
|||||||
Mat map;
|
Mat map;
|
||||||
log(gray_img + 1.0f, map);
|
log(gray_img + 1.0f, map);
|
||||||
Mat div;
|
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);
|
log(2.0f + 8.0f * div, div);
|
||||||
map = map.mul(1.0f / div);
|
map = map.mul(1.0f / div);
|
||||||
map = map.mul(1.0f / gray_img);
|
map = map.mul(1.0f / gray_img);
|
||||||
@ -81,58 +119,27 @@ static void DragoMap(Mat& src_img, Mat &dst_img, const std::vector<float>& param
|
|||||||
gray_img.release();
|
gray_img.release();
|
||||||
|
|
||||||
std::vector<Mat> channels(3);
|
std::vector<Mat> channels(3);
|
||||||
split(src_img, channels);
|
split(img, channels);
|
||||||
for(int i = 0; i < 3; i++) {
|
for(int i = 0; i < 3; i++) {
|
||||||
channels[i] = channels[i].mul(map);
|
channels[i] = channels[i].mul(map);
|
||||||
}
|
}
|
||||||
map.release();
|
map.release();
|
||||||
merge(channels, dst_img);
|
merge(channels, img);
|
||||||
|
linearMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReinhardDevlinMap(Mat& src_img, Mat &dst_img, const std::vector<float>& 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);
|
|
||||||
Mat log_img;
|
|
||||||
log(gray_img, log_img);
|
|
||||||
|
|
||||||
float log_mean = (float)sum(log_img)[0] / log_img.total();
|
|
||||||
double log_min, log_max;
|
|
||||||
minMaxLoc(log_img, &log_min, &log_max);
|
|
||||||
log_img.release();
|
|
||||||
|
|
||||||
double key = (float)((log_max - log_mean) / (log_max - log_min));
|
|
||||||
float map_key = 0.3f + 0.7f * pow((float)key, 1.4f);
|
|
||||||
intensity = exp(-intensity);
|
|
||||||
Scalar chan_mean = mean(src_img);
|
|
||||||
float gray_mean = (float)mean(gray_img)[0];
|
|
||||||
|
|
||||||
std::vector<Mat> channels(3);
|
|
||||||
split(src_img, channels);
|
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
float global = color_adapt * (float)chan_mean[i] + (1.0f - color_adapt) * gray_mean;
|
|
||||||
Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
|
|
||||||
adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
|
|
||||||
pow(intensity * adapt, map_key, adapt);
|
|
||||||
channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
|
|
||||||
}
|
|
||||||
gray_img.release();
|
|
||||||
merge(channels, dst_img);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DurandMap(Mat& src_img, Mat& dst_img, const std::vector<float>& params)
|
void TonemapDurand::tonemap()
|
||||||
{
|
{
|
||||||
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;
|
Mat gray_img;
|
||||||
cvtColor(src_img, gray_img, COLOR_RGB2GRAY);
|
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||||
Mat log_img;
|
Mat log_img;
|
||||||
log(gray_img, log_img);
|
log(gray_img, log_img);
|
||||||
Mat map_img;
|
Mat map_img;
|
||||||
@ -148,39 +155,75 @@ static void DurandMap(Mat& src_img, Mat& dst_img, const std::vector<float>& para
|
|||||||
gray_img.release();
|
gray_img.release();
|
||||||
|
|
||||||
std::vector<Mat> channels(3);
|
std::vector<Mat> channels(3);
|
||||||
split(src_img, channels);
|
split(img, channels);
|
||||||
for(int i = 0; i < 3; i++) {
|
for(int i = 0; i < 3; i++) {
|
||||||
channels[i] = channels[i].mul(map_img);
|
channels[i] = channels[i].mul(map_img);
|
||||||
}
|
}
|
||||||
merge(channels, dst_img);
|
merge(channels, img);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tonemap(InputArray _src, OutputArray _dst, int algorithm,
|
TonemapReinhardDevlin::TonemapReinhardDevlin(float gamma, float intensity, float color_adapt, float light_adapt) :
|
||||||
const std::vector<float>& params)
|
Tonemap(gamma),
|
||||||
|
intensity(intensity),
|
||||||
|
color_adapt(color_adapt),
|
||||||
|
light_adapt(light_adapt)
|
||||||
{
|
{
|
||||||
typedef void (*tonemap_func)(Mat&, Mat&, const std::vector<float>&);
|
}
|
||||||
tonemap_func functions[TONEMAP_COUNT] = {
|
|
||||||
NULL, DragoMap, ReinhardDevlinMap, DurandMap};
|
|
||||||
|
|
||||||
Mat src = _src.getMat();
|
void TonemapReinhardDevlin::tonemap()
|
||||||
CV_Assert(!src.empty());
|
{
|
||||||
CV_Assert(0 <= algorithm && algorithm < TONEMAP_COUNT);
|
Mat gray_img;
|
||||||
_dst.create(src.size(), CV_32FC3);
|
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||||
Mat dst = _dst.getMat();
|
Mat log_img;
|
||||||
src.copyTo(dst);
|
log(gray_img, log_img);
|
||||||
|
|
||||||
double min, max;
|
float log_mean = (float)sum(log_img)[0] / log_img.total();
|
||||||
minMaxLoc(dst, &min, &max);
|
double log_min, log_max;
|
||||||
if(max - min < DBL_EPSILON) {
|
minMaxLoc(log_img, &log_min, &log_max);
|
||||||
return;
|
log_img.release();
|
||||||
|
|
||||||
|
double key = (float)((log_max - log_mean) / (log_max - log_min));
|
||||||
|
float map_key = 0.3f + 0.7f * pow((float)key, 1.4f);
|
||||||
|
intensity = exp(-intensity);
|
||||||
|
Scalar chan_mean = mean(img);
|
||||||
|
float gray_mean = (float)mean(gray_img)[0];
|
||||||
|
|
||||||
|
std::vector<Mat> channels(3);
|
||||||
|
split(img, channels);
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
float global = color_adapt * (float)chan_mean[i] + (1.0f - color_adapt) * gray_mean;
|
||||||
|
Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
|
||||||
|
adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
|
||||||
|
pow(intensity * adapt, map_key, adapt);
|
||||||
|
channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
|
||||||
}
|
}
|
||||||
dst = (dst - min) / (max - min);
|
gray_img.release();
|
||||||
if(functions[algorithm]) {
|
merge(channels, img);
|
||||||
functions[algorithm](dst, dst, params);
|
linearMap();
|
||||||
}
|
}
|
||||||
minMaxLoc(dst, &min, &max);
|
|
||||||
dst = (dst - min) / (max - min);
|
Ptr<Tonemap> Tonemap::create(const String& TonemapType)
|
||||||
float gamma = getParam(params, 0, 1.0f);
|
{
|
||||||
pow(dst, 1.0f / gamma, dst);
|
return Algorithm::create<Tonemap>("Tonemap." + TonemapType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CV_INIT_ALGORITHM(TonemapLinear, "Tonemap.Linear",
|
||||||
|
obj.info()->addParam(obj, "gamma", obj.gamma));
|
||||||
|
|
||||||
|
CV_INIT_ALGORITHM(TonemapDrago, "Tonemap.Drago",
|
||||||
|
obj.info()->addParam(obj, "gamma", obj.gamma);
|
||||||
|
obj.info()->addParam(obj, "bias", obj.bias));
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ TEST(Photo_HdrFusion, regression)
|
|||||||
vector<Mat> images;
|
vector<Mat> images;
|
||||||
|
|
||||||
ifstream list_file(fuse_path + "list.txt");
|
ifstream list_file(fuse_path + "list.txt");
|
||||||
|
ASSERT_TRUE(list_file.is_open());
|
||||||
string name;
|
string name;
|
||||||
float val;
|
float val;
|
||||||
while(list_file >> name >> val) {
|
while(list_file >> name >> val) {
|
||||||
@ -110,48 +111,48 @@ TEST(Photo_HdrFusion, regression)
|
|||||||
|
|
||||||
TEST(Photo_Tonemap, regression)
|
TEST(Photo_Tonemap, regression)
|
||||||
{
|
{
|
||||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
|
||||||
vector<Mat>images(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]);
|
|
||||||
}
|
|
||||||
Mat img;
|
Mat img;
|
||||||
loadImage(folder + "rle.hdr", img);
|
loadImage(test_path + "../rle.hdr", img);
|
||||||
vector<float> param(1);
|
ifstream list_file(test_path + "list.txt");
|
||||||
param[0] = 2.2f;
|
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<Tonemap> mapper = Tonemap::create(name);
|
||||||
|
ASSERT_FALSE(mapper.empty()) << "Could not find mapper " << name;
|
||||||
Mat result;
|
Mat result;
|
||||||
tonemap(img, result, i, param);
|
mapper->process(img, result);
|
||||||
result.convertTo(result, CV_8UC3, 255);
|
result.convertTo(result, CV_8UC3, 255);
|
||||||
checkEqual(images[i], result, 0);
|
checkEqual(expected, result, 0);
|
||||||
}
|
}
|
||||||
|
list_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Photo_Align, regression)
|
TEST(Photo_Align, regression)
|
||||||
{
|
{
|
||||||
const int TESTS_COUNT = 100;
|
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);
|
Mat img = imread(file_name);
|
||||||
ASSERT_FALSE(img.empty()) << "Could not load input image " << file_name;
|
ASSERT_FALSE(img.empty()) << "Could not load input image " << file_name;
|
||||||
cvtColor(img, img, COLOR_RGB2GRAY);
|
cvtColor(img, img, COLOR_RGB2GRAY);
|
||||||
|
|
||||||
int max_bits = 6;
|
int max_bits = 5;
|
||||||
int max_shift = 64;
|
int max_shift = 32;
|
||||||
srand(time(0));
|
srand(static_cast<unsigned>(time(0)));
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
for(int i = 0; i < TESTS_COUNT; i++) {
|
for(int i = 0; i < TESTS_COUNT; i++) {
|
||||||
Point shift(rand() % max_shift, rand() % max_shift);
|
Point shift(rand() % max_shift, rand() % max_shift);
|
||||||
Mat res;
|
Mat res;
|
||||||
shiftMat(img, shift, res);
|
shiftMat(img, shift, res);
|
||||||
Point calc = getExpShift(img, res, max_bits);
|
Point calc = getExpShift(img, res, max_bits);
|
||||||
ASSERT_TRUE(calc == -shift);
|
errors += (calc != -shift);
|
||||||
}
|
}
|
||||||
|
ASSERT_TRUE(errors < 5);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user