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