Refactor fast NL-means denoising

* reorder arguments
* rewrite accuracy tests
* replace doubles with integer arithmetic inside the main loop
pull/32/head
Andrey Kamaev 12 years ago
parent 87f282eed2
commit f42b38ac44
  1. 22
      modules/photo/include/opencv2/photo/photo.hpp
  2. 32
      modules/photo/src/denoising.cpp
  3. 6
      modules/photo/src/fast_nlmeans_denoising_invoker.hpp
  4. 6
      modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp
  5. 191
      modules/photo/test/test_denoising.cpp

@ -68,26 +68,24 @@ CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask,
OutputArray dst, double inpaintRadius, int flags );
CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst,
int templateWindowSize, int searchWindowSize, int h);
CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, int h = 3,
int templateWindowSize = 7, int searchWindowSize = 21);
CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst,
int templateWindowSize, int searchWindowSize,
int h, int hForColorComponents);
int h = 3, int hColor = 3,
int templateWindowSize = 7, int searchWindowSize = 21);
CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs,
CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, OutputArray dst,
int imgToDenoiseIndex, int temporalWindowSize,
OutputArray dst,
int templateWindowSize, int searchWindowSize, int h);
int h = 3, int templateWindowSize = 7, int searchWindowSize = 21);
CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs,
CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, OutputArray dst,
int imgToDenoiseIndex, int temporalWindowSize,
OutputArray dst,
int templateWindowSize, int searchWindowSize,
int h, int hForColorComponents);
int h = 3, int hColor = 3,
int templateWindowSize = 7, int searchWindowSize = 21);
}
#endif
#endif //__cplusplus
#endif

@ -45,8 +45,8 @@
#include "fast_nlmeans_denoising_invoker.hpp"
#include "fast_nlmeans_multi_denoising_invoker.hpp"
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst,
int templateWindowSize, int searchWindowSize, int h)
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, int h,
int templateWindowSize, int searchWindowSize)
{
Mat src = _src.getMat();
_dst.create(src.size(), src.type());
@ -75,8 +75,8 @@ void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst,
}
void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
int templateWindowSize, int searchWindowSize,
int h, int hForColorComponents)
int h, int hForColorComponents,
int templateWindowSize, int searchWindowSize)
{
Mat src = _src.getMat();
_dst.create(src.size(), src.type());
@ -96,8 +96,8 @@ void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels(&src_lab, 1, l_ab, 2, from_to, 3);
fastNlMeansDenoising(l, l, templateWindowSize, searchWindowSize, h);
fastNlMeansDenoising(ab, ab, templateWindowSize, searchWindowSize, hForColorComponents);
fastNlMeansDenoising(l, l, h, templateWindowSize, searchWindowSize);
fastNlMeansDenoising(ab, ab, hForColorComponents, templateWindowSize, searchWindowSize);
Mat l_ab_denoised[] = { l, ab };
Mat dst_lab(src.size(), src.type());
@ -138,10 +138,9 @@ static void fastNlMeansDenoisingMultiCheckPreconditions(
}
}
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs,
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
int imgToDenoiseIndex, int temporalWindowSize,
OutputArray _dst,
int templateWindowSize, int searchWindowSize, int h)
int h, int templateWindowSize, int searchWindowSize)
{
vector<Mat> srcImgs;
_srcImgs.getMatVector(srcImgs);
@ -178,11 +177,10 @@ void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs,
}
}
void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs,
void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
int imgToDenoiseIndex, int temporalWindowSize,
OutputArray _dst,
int templateWindowSize, int searchWindowSize,
int h, int hForColorComponents)
int h, int hForColorComponents,
int templateWindowSize, int searchWindowSize)
{
vector<Mat> srcImgs;
_srcImgs.getMatVector(srcImgs);
@ -222,12 +220,12 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs,
Mat dst_ab;
fastNlMeansDenoisingMulti(
l, imgToDenoiseIndex, temporalWindowSize,
dst_l, templateWindowSize, searchWindowSize, h);
l, dst_l, imgToDenoiseIndex, temporalWindowSize,
h, templateWindowSize, searchWindowSize);
fastNlMeansDenoisingMulti(
ab, imgToDenoiseIndex, temporalWindowSize,
dst_ab, templateWindowSize, searchWindowSize, hForColorComponents);
ab, dst_ab, imgToDenoiseIndex, temporalWindowSize,
hForColorComponents, templateWindowSize, searchWindowSize);
Mat l_ab_denoised[] = { dst_l, dst_ab };
Mat dst_lab(srcImgs[0].size(), srcImgs[0].type());

@ -257,10 +257,8 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const BlockedRange& range) cons
}
if (weights_sum > 0) {
for (int channel_num = 0; channel_num < src_.channels(); channel_num++) {
estimation[channel_num] =
cvRound(((double)estimation[channel_num]) / weights_sum);
}
for (int channel_num = 0; channel_num < src_.channels(); channel_num++)
estimation[channel_num] = (estimation[channel_num] + weights_sum/2) / weights_sum;
dst_.at<T>(i,j) = saturateCastFromArray<T>(estimation);

@ -290,10 +290,8 @@ void FastNlMeansMultiDenoisingInvoker<T>::operator() (const BlockedRange& range)
}
if (weights_sum > 0) {
for (int channel_num = 0; channel_num < channels_count_; channel_num++) {
estimation[channel_num] =
cvRound(((double)estimation[channel_num]) / weights_sum);
}
for (int channel_num = 0; channel_num < channels_count_; channel_num++)
estimation[channel_num] = (estimation[channel_num] + weights_sum / 2) / weights_sum;
dst_.at<T>(i,j) = saturateCastFromArray<T>(estimation);

@ -47,167 +47,102 @@
using namespace cv;
using namespace std;
class CV_DenoisingGrayscaleTest : public cvtest::BaseTest
{
public:
CV_DenoisingGrayscaleTest();
~CV_DenoisingGrayscaleTest();
protected:
void run(int);
};
//#define DUMP_RESULTS
CV_DenoisingGrayscaleTest::CV_DenoisingGrayscaleTest() {}
CV_DenoisingGrayscaleTest::~CV_DenoisingGrayscaleTest() {}
#ifdef DUMP_RESULTS
# define DUMP(image, path) imwrite(path, image)
#else
# define FUMP(image, path)
#endif
void CV_DenoisingGrayscaleTest::run( int )
{
string folder = string(ts->get_data_path()) + "denoising/";
Mat orig = imread(folder + "lena_noised_gaussian_sigma=10.png", 0);
Mat exp = imread(folder + "lena_noised_denoised_grayscale_tw=7_sw=21_h=10.png", 0);
if (orig.empty() || exp.empty())
TEST(Imgproc_DenoisingGrayscale, regression)
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
Mat res;
fastNlMeansDenoising(orig, res, 7, 21, 10);
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
string original_path = folder + "lena_noised_gaussian_sigma=10.png";
string expected_path = folder + "lena_noised_denoised_grayscale_tw=7_sw=21_h=10.png";
if (norm(res - exp) > 0) {
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
} else {
ts->set_failed_test_info(cvtest::TS::OK);
}
}
Mat original = imread(original_path, CV_LOAD_IMAGE_GRAYSCALE);
Mat expected = imread(expected_path, CV_LOAD_IMAGE_GRAYSCALE);
class CV_DenoisingColoredTest : public cvtest::BaseTest
{
public:
CV_DenoisingColoredTest();
~CV_DenoisingColoredTest();
protected:
void run(int);
};
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
CV_DenoisingColoredTest::CV_DenoisingColoredTest() {}
CV_DenoisingColoredTest::~CV_DenoisingColoredTest() {}
Mat result;
fastNlMeansDenoising(original, result, 10);
void CV_DenoisingColoredTest::run( int )
{
string folder = string(ts->get_data_path()) + "denoising/";
Mat orig = imread(folder + "lena_noised_gaussian_sigma=10.png", 1);
Mat exp = imread(folder + "lena_noised_denoised_lab12_tw=7_sw=21_h=10_h2=10.png", 1);
DUMP(result, expected_path + ".res.png");
if (orig.empty() || exp.empty())
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
ASSERT_EQ(0, norm(result != expected));
}
Mat res;
fastNlMeansDenoisingColored(orig, res, 7, 21, 10, 10);
if (norm(res - exp) > 0) {
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
} else {
ts->set_failed_test_info(cvtest::TS::OK);
}
}
class CV_DenoisingGrayscaleMultiTest : public cvtest::BaseTest
TEST(Imgproc_DenoisingColored, regression)
{
public:
CV_DenoisingGrayscaleMultiTest();
~CV_DenoisingGrayscaleMultiTest();
protected:
void run(int);
};
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
string original_path = folder + "lena_noised_gaussian_sigma=10.png";
string expected_path = folder + "lena_noised_denoised_lab12_tw=7_sw=21_h=10_h2=10.png";
CV_DenoisingGrayscaleMultiTest::CV_DenoisingGrayscaleMultiTest() {}
CV_DenoisingGrayscaleMultiTest::~CV_DenoisingGrayscaleMultiTest() {}
Mat original = imread(original_path, CV_LOAD_IMAGE_COLOR);
Mat expected = imread(expected_path, CV_LOAD_IMAGE_COLOR);
void CV_DenoisingGrayscaleMultiTest::run( int )
{
string folder = string(ts->get_data_path()) + "denoising/";
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
const int imgs_count = 3;
vector<Mat> src_imgs(imgs_count);
src_imgs[0] = imread(folder + "lena_noised_gaussian_sigma=20_multi_0.png", 0);
src_imgs[1] = imread(folder + "lena_noised_gaussian_sigma=20_multi_1.png", 0);
src_imgs[2] = imread(folder + "lena_noised_gaussian_sigma=20_multi_2.png", 0);
Mat result;
fastNlMeansDenoisingColored(original, result, 10, 10);
Mat exp = imread(folder + "lena_noised_denoised_multi_tw=7_sw=21_h=15.png", 0);
DUMP(result, expected_path + ".res.png");
bool have_empty_src = false;
for (int i = 0; i < imgs_count; i++) {
have_empty_src |= src_imgs[i].empty();
ASSERT_EQ(0, norm(result != expected));
}
if (have_empty_src || exp.empty())
TEST(Imgproc_DenoisingGrayscaleMulti, regression)
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
const int imgs_count = 3;
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
Mat res;
fastNlMeansDenoisingMulti(src_imgs, imgs_count / 2, imgs_count, res, 7, 21, 15);
string expected_path = folder + "lena_noised_denoised_multi_tw=7_sw=21_h=15.png";
Mat expected = imread(expected_path, CV_LOAD_IMAGE_GRAYSCALE);
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
if (norm(res - exp) > 0) {
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
} else {
ts->set_failed_test_info(cvtest::TS::OK);
}
vector<Mat> original(imgs_count);
for (int i = 0; i < imgs_count; i++)
{
string original_path = format("%slena_noised_gaussian_sigma=20_multi_%d.png", folder.c_str(), i);
original[i] = imread(original_path, CV_LOAD_IMAGE_GRAYSCALE);
ASSERT_FALSE(original[i].empty()) << "Could not load input image " << original_path;
}
class CV_DenoisingColoredMultiTest : public cvtest::BaseTest
{
public:
CV_DenoisingColoredMultiTest();
~CV_DenoisingColoredMultiTest();
protected:
void run(int);
};
Mat result;
fastNlMeansDenoisingMulti(original, result, imgs_count / 2, imgs_count, 15);
CV_DenoisingColoredMultiTest::CV_DenoisingColoredMultiTest() {}
CV_DenoisingColoredMultiTest::~CV_DenoisingColoredMultiTest() {}
DUMP(result, expected_path + ".res.png");
void CV_DenoisingColoredMultiTest::run( int )
{
string folder = string(ts->get_data_path()) + "denoising/";
ASSERT_EQ(0, norm(result != expected));
}
TEST(Imgproc_DenoisingColoredMulti, regression)
{
const int imgs_count = 3;
vector<Mat> src_imgs(imgs_count);
src_imgs[0] = imread(folder + "lena_noised_gaussian_sigma=20_multi_0.png", 1);
src_imgs[1] = imread(folder + "lena_noised_gaussian_sigma=20_multi_1.png", 1);
src_imgs[2] = imread(folder + "lena_noised_gaussian_sigma=20_multi_2.png", 1);
Mat exp = imread(folder + "lena_noised_denoised_multi_lab12_tw=7_sw=21_h=10_h2=15.png", 1);
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
bool have_empty_src = false;
for (int i = 0; i < imgs_count; i++) {
have_empty_src |= src_imgs[i].empty();
}
string expected_path = folder + "lena_noised_denoised_multi_lab12_tw=7_sw=21_h=10_h2=15.png";
Mat expected = imread(expected_path, CV_LOAD_IMAGE_COLOR);
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
if (have_empty_src || exp.empty())
vector<Mat> original(imgs_count);
for (int i = 0; i < imgs_count; i++)
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
string original_path = format("%slena_noised_gaussian_sigma=20_multi_%d.png", folder.c_str(), i);
original[i] = imread(original_path, CV_LOAD_IMAGE_COLOR);
ASSERT_FALSE(original[i].empty()) << "Could not load input image " << original_path;
}
Mat res;
fastNlMeansDenoisingColoredMulti(src_imgs, imgs_count / 2, imgs_count, res, 7, 21, 10, 15);
if (norm(res - exp) > 0) {
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
} else {
ts->set_failed_test_info(cvtest::TS::OK);
}
}
Mat result;
fastNlMeansDenoisingColoredMulti(original, result, imgs_count / 2, imgs_count, 10, 15);
DUMP(result, expected_path + ".res.png");
TEST(Imgproc_DenoisingGrayscale, regression) { CV_DenoisingGrayscaleTest test; test.safe_run(); }
TEST(Imgproc_DenoisingColored, regression) { CV_DenoisingColoredTest test; test.safe_run(); }
TEST(Imgproc_DenoisingGrayscaleMulti, regression) { CV_DenoisingGrayscaleMultiTest test; test.safe_run(); }
TEST(Imgproc_DenoisingColoredMulti, regression) { CV_DenoisingColoredMultiTest test; test.safe_run(); }
ASSERT_EQ(0, norm(result != expected));
}

Loading…
Cancel
Save