Home | History | Annotate | Download | only in test
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
     14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
     15 // Third party copyrights are property of their respective owners.
     16 //
     17 // Redistribution and use in source and binary forms, with or without modification,
     18 // are permitted provided that the following conditions are met:
     19 //
     20 //   * Redistribution's of source code must retain the above copyright notice,
     21 //     this list of conditions and the following disclaimer.
     22 //
     23 //   * Redistribution's in binary form must reproduce the above copyright notice,
     24 //     this list of conditions and the following disclaimer in the documentation
     25 //     and/or other materials provided with the distribution.
     26 //
     27 //   * The name of the copyright holders may not be used to endorse or promote products
     28 //     derived from this software without specific prior written permission.
     29 //
     30 // This software is provided by the copyright holders and contributors "as is" and
     31 // any express or implied warranties, including, but not limited to, the implied
     32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     33 // In no event shall the Intel Corporation or contributors be liable for any direct,
     34 // indirect, incidental, special, exemplary, or consequential damages
     35 // (including, but not limited to, procurement of substitute goods or services;
     36 // loss of use, data, or profits; or business interruption) however caused
     37 // and on any theory of liability, whether in contract, strict liability,
     38 // or tort (including negligence or otherwise) arising in any way out of
     39 // the use of this software, even if advised of the possibility of such damage.
     40 //
     41 //M*/
     42 
     43 #include "test_precomp.hpp"
     44 
     45 using namespace cv;
     46 
     47 namespace cvtest
     48 {
     49     class CV_BilateralFilterTest :
     50         public cvtest::BaseTest
     51     {
     52     public:
     53         enum
     54         {
     55             MAX_WIDTH = 1920, MIN_WIDTH = 1,
     56             MAX_HEIGHT = 1080, MIN_HEIGHT = 1
     57         };
     58 
     59         CV_BilateralFilterTest();
     60         ~CV_BilateralFilterTest();
     61 
     62     protected:
     63         virtual void run_func();
     64         virtual int prepare_test_case(int test_case_index);
     65         virtual int validate_test_results(int test_case_index);
     66 
     67     private:
     68         void reference_bilateral_filter(const Mat& src, Mat& dst, int d, double sigma_color,
     69             double sigma_space, int borderType = BORDER_DEFAULT);
     70 
     71         int getRandInt(RNG& rng, int min_value, int max_value) const;
     72 
     73         double _sigma_color;
     74         double _sigma_space;
     75 
     76         Mat _src;
     77         Mat _parallel_dst;
     78         int _d;
     79     };
     80 
     81     CV_BilateralFilterTest::CV_BilateralFilterTest() :
     82         cvtest::BaseTest(), _src(), _parallel_dst(), _d()
     83     {
     84         test_case_count = 1000;
     85     }
     86 
     87     CV_BilateralFilterTest::~CV_BilateralFilterTest()
     88     {
     89     }
     90 
     91     int CV_BilateralFilterTest::getRandInt(RNG& rng, int min_value, int max_value) const
     92     {
     93         double rand_value = rng.uniform(log((double)min_value), log((double)max_value + 1));
     94         return cvRound(exp((double)rand_value));
     95     }
     96 
     97     void CV_BilateralFilterTest::reference_bilateral_filter(const Mat &src, Mat &dst, int d,
     98         double sigma_color, double sigma_space, int borderType)
     99     {
    100         int cn = src.channels();
    101         int i, j, k, maxk, radius;
    102         double minValSrc = -1, maxValSrc = 1;
    103         const int kExpNumBinsPerChannel = 1 << 12;
    104         int kExpNumBins = 0;
    105         float lastExpVal = 1.f;
    106         float len, scale_index;
    107         Size size = src.size();
    108 
    109         dst.create(size, src.type());
    110 
    111         CV_Assert( (src.type() == CV_32FC1 || src.type() == CV_32FC3) &&
    112             src.type() == dst.type() && src.size() == dst.size() &&
    113             src.data != dst.data );
    114 
    115         if( sigma_color <= 0 )
    116             sigma_color = 1;
    117         if( sigma_space <= 0 )
    118             sigma_space = 1;
    119 
    120         double gauss_color_coeff = -0.5/(sigma_color*sigma_color);
    121         double gauss_space_coeff = -0.5/(sigma_space*sigma_space);
    122 
    123         if( d <= 0 )
    124             radius = cvRound(sigma_space*1.5);
    125         else
    126             radius = d/2;
    127         radius = MAX(radius, 1);
    128         d = radius*2 + 1;
    129         // compute the min/max range for the input image (even if multichannel)
    130 
    131         minMaxLoc( src.reshape(1), &minValSrc, &maxValSrc );
    132         if(std::abs(minValSrc - maxValSrc) < FLT_EPSILON)
    133         {
    134             src.copyTo(dst);
    135             return;
    136         }
    137 
    138         // temporary copy of the image with borders for easy processing
    139         Mat temp;
    140         copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
    141         patchNaNs(temp);
    142 
    143         // allocate lookup tables
    144         vector<float> _space_weight(d*d);
    145         vector<int> _space_ofs(d*d);
    146         float* space_weight = &_space_weight[0];
    147         int* space_ofs = &_space_ofs[0];
    148 
    149         // assign a length which is slightly more than needed
    150         len = (float)(maxValSrc - minValSrc) * cn;
    151         kExpNumBins = kExpNumBinsPerChannel * cn;
    152         vector<float> _expLUT(kExpNumBins+2);
    153         float* expLUT = &_expLUT[0];
    154 
    155         scale_index = kExpNumBins/len;
    156 
    157         // initialize the exp LUT
    158         for( i = 0; i < kExpNumBins+2; i++ )
    159         {
    160             if( lastExpVal > 0.f )
    161             {
    162                 double val =  i / scale_index;
    163                 expLUT[i] = (float)std::exp(val * val * gauss_color_coeff);
    164                 lastExpVal = expLUT[i];
    165             }
    166             else
    167                 expLUT[i] = 0.f;
    168         }
    169 
    170         // initialize space-related bilateral filter coefficients
    171         for( i = -radius, maxk = 0; i <= radius; i++ )
    172             for( j = -radius; j <= radius; j++ )
    173             {
    174                 double r = std::sqrt((double)i*i + (double)j*j);
    175                 if( r > radius )
    176                     continue;
    177                 space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);
    178                 space_ofs[maxk++] = (int)(i*(temp.step/sizeof(float)) + j*cn);
    179             }
    180 
    181         for( i = 0; i < size.height; i++ )
    182         {
    183             const float* sptr = temp.ptr<float>(i+radius) + radius*cn;
    184             float* dptr = dst.ptr<float>(i);
    185 
    186             if( cn == 1 )
    187             {
    188                 for( j = 0; j < size.width; j++ )
    189                 {
    190                     float sum = 0, wsum = 0;
    191                     float val0 = sptr[j];
    192                     for( k = 0; k < maxk; k++ )
    193                     {
    194                         float val = sptr[j + space_ofs[k]];
    195                         float alpha = (float)(std::abs(val - val0)*scale_index);
    196                         int idx = cvFloor(alpha);
    197                         alpha -= idx;
    198                         float w = space_weight[k]*(expLUT[idx] + alpha*(expLUT[idx+1] - expLUT[idx]));
    199                         sum += val*w;
    200                         wsum += w;
    201                     }
    202                     dptr[j] = (float)(sum/wsum);
    203                 }
    204             }
    205             else
    206             {
    207                 assert( cn == 3 );
    208                 for( j = 0; j < size.width*3; j += 3 )
    209                 {
    210                     float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
    211                     float b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2];
    212                     for( k = 0; k < maxk; k++ )
    213                     {
    214                         const float* sptr_k = sptr + j + space_ofs[k];
    215                         float b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];
    216                         float alpha = (float)((std::abs(b - b0) +
    217                             std::abs(g - g0) + std::abs(r - r0))*scale_index);
    218                         int idx = cvFloor(alpha);
    219                         alpha -= idx;
    220                         float w = space_weight[k]*(expLUT[idx] + alpha*(expLUT[idx+1] - expLUT[idx]));
    221                         sum_b += b*w; sum_g += g*w; sum_r += r*w;
    222                         wsum += w;
    223                     }
    224                     wsum = 1.f/wsum;
    225                     b0 = sum_b*wsum;
    226                     g0 = sum_g*wsum;
    227                     r0 = sum_r*wsum;
    228                     dptr[j] = b0; dptr[j+1] = g0; dptr[j+2] = r0;
    229                 }
    230             }
    231         }
    232     }
    233 
    234     int CV_BilateralFilterTest::prepare_test_case(int /* test_case_index */)
    235     {
    236         const static int types[] = { CV_32FC1, CV_32FC3, CV_8UC1, CV_8UC3 };
    237         RNG& rng = ts->get_rng();
    238         Size size(getRandInt(rng, MIN_WIDTH, MAX_WIDTH), getRandInt(rng, MIN_HEIGHT, MAX_HEIGHT));
    239         int type = types[rng(sizeof(types) / sizeof(types[0]))];
    240 
    241         _d = rng.uniform(0., 1.) > 0.5 ? 5 : 3;
    242 
    243         _src.create(size, type);
    244 
    245         rng.fill(_src, RNG::UNIFORM, 0, 256);
    246 
    247         _sigma_color = _sigma_space = 1.;
    248 
    249         return 1;
    250     }
    251 
    252     int CV_BilateralFilterTest::validate_test_results(int test_case_index)
    253     {
    254         static const double eps = 4;
    255 
    256         Mat reference_dst, reference_src;
    257         if (_src.depth() == CV_32F)
    258             reference_bilateral_filter(_src, reference_dst, _d, _sigma_color, _sigma_space);
    259         else
    260         {
    261             int type = _src.type();
    262             _src.convertTo(reference_src, CV_32F);
    263             reference_bilateral_filter(reference_src, reference_dst, _d, _sigma_color, _sigma_space);
    264             reference_dst.convertTo(reference_dst, type);
    265         }
    266 
    267         double e = cvtest::norm(reference_dst, _parallel_dst, NORM_L2);
    268         if (e > eps)
    269         {
    270             ts->printf(cvtest::TS::CONSOLE, "actual error: %g, expected: %g", e, eps);
    271             ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
    272         }
    273         else
    274             ts->set_failed_test_info(cvtest::TS::OK);
    275 
    276         return BaseTest::validate_test_results(test_case_index);
    277     }
    278 
    279     void CV_BilateralFilterTest::run_func()
    280     {
    281         bilateralFilter(_src, _parallel_dst, _d, _sigma_color, _sigma_space);
    282     }
    283 
    284     TEST(Imgproc_BilateralFilter, accuracy)
    285     {
    286         CV_BilateralFilterTest test;
    287         test.safe_run();
    288     }
    289 
    290 } // end of namespace cvtest
    291