Refactor fast NL-means denoising
authorAndrey Kamaev <andrey.kamaev@itseez.com>
Wed, 19 Sep 2012 12:42:39 +0000 (16:42 +0400)
committerAndrey Kamaev <andrey.kamaev@itseez.com>
Wed, 19 Sep 2012 12:50:56 +0000 (16:50 +0400)
* reorder arguments
* rewrite accuracy tests
* replace doubles with integer arithmetic inside the main loop

modules/photo/include/opencv2/photo/photo.hpp
modules/photo/src/denoising.cpp
modules/photo/src/fast_nlmeans_denoising_invoker.hpp
modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp
modules/photo/test/test_denoising.cpp

index bae83bb..07b27ab 100644 (file)
@@ -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
index d3f1c09..f71c37c 100644 (file)
@@ -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());
index 07159bb..5f7d1fe 100644 (file)
@@ -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);
 
index b9b8e7f..4151f32 100644 (file)
@@ -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);
 
index 6b202f1..d52d8b4 100644 (file)
 using namespace cv;
 using namespace std;
 
-class CV_DenoisingGrayscaleTest : public cvtest::BaseTest
-{
-public:
-    CV_DenoisingGrayscaleTest();
-    ~CV_DenoisingGrayscaleTest();
-protected:
-    void run(int);
-};
+//#define DUMP_RESULTS
+
+#ifdef DUMP_RESULTS
+#  define DUMP(image, path) imwrite(path, image)
+#else
+#  define FUMP(image, path)
+#endif
 
-CV_DenoisingGrayscaleTest::CV_DenoisingGrayscaleTest() {}
-CV_DenoisingGrayscaleTest::~CV_DenoisingGrayscaleTest() {}
 
-void CV_DenoisingGrayscaleTest::run( int )
+TEST(Imgproc_DenoisingGrayscale, regression)
 {
-    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);
+    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 (orig.empty() || exp.empty())
-    {
-        ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
-        return;
-    }
+    Mat original = imread(original_path, CV_LOAD_IMAGE_GRAYSCALE);
+    Mat expected = imread(expected_path, CV_LOAD_IMAGE_GRAYSCALE);
 
-    Mat res;
-    fastNlMeansDenoising(orig, res, 7, 21, 10);
+    ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
+    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);
-    }
+    Mat result;
+    fastNlMeansDenoising(original, result, 10);
+
+    DUMP(result, expected_path + ".res.png");
+
+    ASSERT_EQ(0, norm(result != expected));
 }
 
-class CV_DenoisingColoredTest : public cvtest::BaseTest
+TEST(Imgproc_DenoisingColored, regression)
 {
-public:
-    CV_DenoisingColoredTest();
-    ~CV_DenoisingColoredTest();
-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_DenoisingColoredTest::CV_DenoisingColoredTest() {}
-CV_DenoisingColoredTest::~CV_DenoisingColoredTest() {}
+    Mat original = imread(original_path, CV_LOAD_IMAGE_COLOR);
+    Mat expected = imread(expected_path, CV_LOAD_IMAGE_COLOR);
 
-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);
+    ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
+    ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
 
-    if (orig.empty() || exp.empty())
-    {
-        ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
-        return;
-    }
+    Mat result;
+    fastNlMeansDenoisingColored(original, result, 10, 10);
 
-    Mat res;
-    fastNlMeansDenoisingColored(orig, res, 7, 21, 10, 10);
+    DUMP(result, expected_path + ".res.png");
 
-    if (norm(res - exp) > 0) {        
-        ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
-    } else {
-        ts->set_failed_test_info(cvtest::TS::OK);
-    }
+    ASSERT_EQ(0, norm(result != expected));
 }
 
-class CV_DenoisingGrayscaleMultiTest : public cvtest::BaseTest
+TEST(Imgproc_DenoisingGrayscaleMulti, regression)
 {
-public:
-    CV_DenoisingGrayscaleMultiTest();
-    ~CV_DenoisingGrayscaleMultiTest();
-protected:
-    void run(int);
-};
-
-CV_DenoisingGrayscaleMultiTest::CV_DenoisingGrayscaleMultiTest() {}
-CV_DenoisingGrayscaleMultiTest::~CV_DenoisingGrayscaleMultiTest() {}
-
-void CV_DenoisingGrayscaleMultiTest::run( int )
-{        
-    string folder = string(ts->get_data_path()) + "denoising/";
-
     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 exp = imread(folder + "lena_noised_denoised_multi_tw=7_sw=21_h=15.png", 0);
-
-    bool have_empty_src = false;
-    for (int i = 0; i < imgs_count; i++) {
-        have_empty_src |= src_imgs[i].empty();
-    }
+    string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
+
+    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 (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_GRAYSCALE);
+        ASSERT_FALSE(original[i].empty()) << "Could not load input image " << original_path;
     }
 
-    Mat res;
-    fastNlMeansDenoisingMulti(src_imgs, imgs_count / 2, imgs_count, res, 7, 21, 15);
+    Mat result;
+    fastNlMeansDenoisingMulti(original, result, imgs_count / 2, imgs_count, 15);
 
-    if (norm(res - exp) > 0) {        
-        ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
-    } else {
-        ts->set_failed_test_info(cvtest::TS::OK);
-    }
+    DUMP(result, expected_path + ".res.png");
+
+    ASSERT_EQ(0, norm(result != expected));
 }
 
-class CV_DenoisingColoredMultiTest : public cvtest::BaseTest
+TEST(Imgproc_DenoisingColoredMulti, regression)
 {
-public:
-    CV_DenoisingColoredMultiTest();
-    ~CV_DenoisingColoredMultiTest();
-protected:
-    void run(int);
-};
-
-CV_DenoisingColoredMultiTest::CV_DenoisingColoredMultiTest() {}
-CV_DenoisingColoredMultiTest::~CV_DenoisingColoredMultiTest() {}
-
-void CV_DenoisingColoredMultiTest::run( int )
-{        
-    string folder = string(ts->get_data_path()) + "denoising/";
-
     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);
-    
-    bool have_empty_src = false;
-    for (int i = 0; i < imgs_count; i++) {
-        have_empty_src |= src_imgs[i].empty();
-    }
+    string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
+
+    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));
+}