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// Intel License Agreement 11// For Open Source Computer Vision Library 12// 13// Copyright (C) 2000, Intel Corporation, all rights reserved. 14// Third party copyrights are property of their respective owners. 15// 16// Redistribution and use in source and binary forms, with or without modification, 17// are permitted provided that the following conditions are met: 18// 19// * Redistribution's of source code must retain the above copyright notice, 20// this list of conditions and the following disclaimer. 21// 22// * Redistribution's in binary form must reproduce the above copyright notice, 23// this list of conditions and the following disclaimer in the documentation 24// and/or other materials provided with the distribution. 25// 26// * The name of Intel Corporation may not be used to endorse or promote products 27// derived from this software without specific prior written permission. 28// 29// This software is provided by the copyright holders and contributors "as is" and 30// any express or implied warranties, including, but not limited to, the implied 31// warranties of merchantability and fitness for a particular purpose are disclaimed. 32// In no event shall the Intel Corporation or contributors be liable for any direct, 33// indirect, incidental, special, exemplary, or consequential damages 34// (including, but not limited to, procurement of substitute goods or services; 35// loss of use, data, or profits; or business interruption) however caused 36// and on any theory of liability, whether in contract, strict liability, 37// or tort (including negligence or otherwise) arising in any way out of 38// the use of this software, even if advised of the possibility of such damage. 39// 40//M*/ 41#include "test_precomp.hpp" 42 43using namespace cv; 44using namespace std; 45 46#define OCL_TUNING_MODE 0 47#if OCL_TUNING_MODE 48#define OCL_TUNING_MODE_ONLY(code) code 49#else 50#define OCL_TUNING_MODE_ONLY(code) 51#endif 52 53// image moments 54class CV_MomentsTest : public cvtest::ArrayTest 55{ 56public: 57 CV_MomentsTest(); 58 59protected: 60 61 enum { MOMENT_COUNT = 25 }; 62 int prepare_test_case( int test_case_idx ); 63 void prepare_to_validation( int /*test_case_idx*/ ); 64 void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); 65 void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); 66 double get_success_error_level( int test_case_idx, int i, int j ); 67 void run_func(); 68 int coi; 69 bool is_binary; 70 bool try_umat; 71}; 72 73 74CV_MomentsTest::CV_MomentsTest() 75{ 76 test_array[INPUT].push_back(NULL); 77 test_array[OUTPUT].push_back(NULL); 78 test_array[REF_OUTPUT].push_back(NULL); 79 coi = -1; 80 is_binary = false; 81 OCL_TUNING_MODE_ONLY(test_case_count = 10); 82 //element_wise_relative_error = false; 83} 84 85 86void CV_MomentsTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) 87{ 88 cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); 89 int depth = CV_MAT_DEPTH(type); 90 91 if( depth == CV_16U ) 92 { 93 low = Scalar::all(0); 94 high = Scalar::all(1000); 95 } 96 else if( depth == CV_16S ) 97 { 98 low = Scalar::all(-1000); 99 high = Scalar::all(1000); 100 } 101 else if( depth == CV_32F ) 102 { 103 low = Scalar::all(-1); 104 high = Scalar::all(1); 105 } 106} 107 108void CV_MomentsTest::get_test_array_types_and_sizes( int test_case_idx, 109 vector<vector<Size> >& sizes, vector<vector<int> >& types ) 110{ 111 RNG& rng = ts->get_rng(); 112 cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); 113 int cn = (cvtest::randInt(rng) % 4) + 1; 114 int depth = cvtest::randInt(rng) % 4; 115 depth = depth == 0 ? CV_8U : depth == 1 ? CV_16U : depth == 2 ? CV_16S : CV_32F; 116 117 is_binary = cvtest::randInt(rng) % 2 != 0; 118 if( depth == 0 && !is_binary ) 119 try_umat = cvtest::randInt(rng) % 5 != 0; 120 else 121 try_umat = cvtest::randInt(rng) % 2 != 0; 122 123 if( cn == 2 || try_umat ) 124 cn = 1; 125 126 OCL_TUNING_MODE_ONLY( 127 cn = 1; 128 depth = CV_8U; 129 try_umat = true; 130 is_binary = false; 131 sizes[INPUT][0] = Size(1024,768) 132 ); 133 134 types[INPUT][0] = CV_MAKETYPE(depth, cn); 135 types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; 136 sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(MOMENT_COUNT,1); 137 if(CV_MAT_DEPTH(types[INPUT][0])>=CV_32S) 138 sizes[INPUT][0].width = MAX(sizes[INPUT][0].width, 3); 139 140 coi = 0; 141 cvmat_allowed = true; 142 if( cn > 1 ) 143 { 144 coi = cvtest::randInt(rng) % cn; 145 cvmat_allowed = false; 146 } 147} 148 149 150double CV_MomentsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) 151{ 152 int depth = test_mat[INPUT][0].depth(); 153 return depth != CV_32F ? FLT_EPSILON*10 : FLT_EPSILON*100; 154} 155 156int CV_MomentsTest::prepare_test_case( int test_case_idx ) 157{ 158 int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); 159 if( code > 0 ) 160 { 161 int cn = test_mat[INPUT][0].channels(); 162 if( cn > 1 ) 163 cvSetImageCOI( (IplImage*)test_array[INPUT][0], coi + 1 ); 164 } 165 166 return code; 167} 168 169 170void CV_MomentsTest::run_func() 171{ 172 CvMoments* m = (CvMoments*)test_mat[OUTPUT][0].ptr<double>(); 173 double* others = (double*)(m + 1); 174 if( try_umat ) 175 { 176 UMat u; 177 test_mat[INPUT][0].clone().copyTo(u); 178 OCL_TUNING_MODE_ONLY( 179 static double ttime = 0; 180 static int ncalls = 0; 181 moments(u, is_binary != 0); 182 double t = (double)getTickCount()); 183 Moments new_m = moments(u, is_binary != 0); 184 OCL_TUNING_MODE_ONLY( 185 ttime += (double)getTickCount() - t; 186 ncalls++; 187 printf("%g\n", ttime/ncalls/u.total())); 188 *m = new_m; 189 } 190 else 191 cvMoments( test_array[INPUT][0], m, is_binary ); 192 193 others[0] = cvGetNormalizedCentralMoment( m, 2, 0 ); 194 others[1] = cvGetNormalizedCentralMoment( m, 1, 1 ); 195 others[2] = cvGetNormalizedCentralMoment( m, 0, 2 ); 196 others[3] = cvGetNormalizedCentralMoment( m, 3, 0 ); 197 others[4] = cvGetNormalizedCentralMoment( m, 2, 1 ); 198 others[5] = cvGetNormalizedCentralMoment( m, 1, 2 ); 199 others[6] = cvGetNormalizedCentralMoment( m, 0, 3 ); 200} 201 202 203void CV_MomentsTest::prepare_to_validation( int /*test_case_idx*/ ) 204{ 205 Mat& src = test_mat[INPUT][0]; 206 CvMoments m; 207 double* mdata = test_mat[REF_OUTPUT][0].ptr<double>(); 208 int depth = src.depth(); 209 int cn = src.channels(); 210 int i, y, x, cols = src.cols; 211 double xc = 0., yc = 0.; 212 213 memset( &m, 0, sizeof(m)); 214 215 for( y = 0; y < src.rows; y++ ) 216 { 217 double s0 = 0, s1 = 0, s2 = 0, s3 = 0; 218 uchar* ptr = src.ptr(y); 219 for( x = 0; x < cols; x++ ) 220 { 221 double val; 222 if( depth == CV_8U ) 223 val = ptr[x*cn + coi]; 224 else if( depth == CV_16U ) 225 val = ((ushort*)ptr)[x*cn + coi]; 226 else if( depth == CV_16S ) 227 val = ((short*)ptr)[x*cn + coi]; 228 else 229 val = ((float*)ptr)[x*cn + coi]; 230 231 if( is_binary ) 232 val = val != 0; 233 234 s0 += val; 235 s1 += val*x; 236 s2 += val*x*x; 237 s3 += ((val*x)*x)*x; 238 } 239 240 m.m00 += s0; 241 m.m01 += s0*y; 242 m.m02 += (s0*y)*y; 243 m.m03 += ((s0*y)*y)*y; 244 245 m.m10 += s1; 246 m.m11 += s1*y; 247 m.m12 += (s1*y)*y; 248 249 m.m20 += s2; 250 m.m21 += s2*y; 251 252 m.m30 += s3; 253 } 254 255 if( m.m00 != 0 ) 256 { 257 xc = m.m10/m.m00, yc = m.m01/m.m00; 258 m.inv_sqrt_m00 = 1./sqrt(fabs(m.m00)); 259 } 260 261 for( y = 0; y < src.rows; y++ ) 262 { 263 double s0 = 0, s1 = 0, s2 = 0, s3 = 0, y1 = y - yc; 264 uchar* ptr = src.ptr(y); 265 for( x = 0; x < cols; x++ ) 266 { 267 double val, x1 = x - xc; 268 if( depth == CV_8U ) 269 val = ptr[x*cn + coi]; 270 else if( depth == CV_16U ) 271 val = ((ushort*)ptr)[x*cn + coi]; 272 else if( depth == CV_16S ) 273 val = ((short*)ptr)[x*cn + coi]; 274 else 275 val = ((float*)ptr)[x*cn + coi]; 276 277 if( is_binary ) 278 val = val != 0; 279 280 s0 += val; 281 s1 += val*x1; 282 s2 += val*x1*x1; 283 s3 += ((val*x1)*x1)*x1; 284 } 285 286 m.mu02 += s0*y1*y1; 287 m.mu03 += ((s0*y1)*y1)*y1; 288 289 m.mu11 += s1*y1; 290 m.mu12 += (s1*y1)*y1; 291 292 m.mu20 += s2; 293 m.mu21 += s2*y1; 294 295 m.mu30 += s3; 296 } 297 298 memcpy( mdata, &m, sizeof(m)); 299 mdata += sizeof(m)/sizeof(m.m00); 300 301 /* calc normalized moments */ 302 { 303 double inv_m00 = m.inv_sqrt_m00*m.inv_sqrt_m00; 304 double s2 = inv_m00*inv_m00; /* 1./(m00 ^ (2/2 + 1)) */ 305 double s3 = s2*m.inv_sqrt_m00; /* 1./(m00 ^ (3/2 + 1)) */ 306 307 mdata[0] = m.mu20 * s2; 308 mdata[1] = m.mu11 * s2; 309 mdata[2] = m.mu02 * s2; 310 311 mdata[3] = m.mu30 * s3; 312 mdata[4] = m.mu21 * s3; 313 mdata[5] = m.mu12 * s3; 314 mdata[6] = m.mu03 * s3; 315 } 316 317 double* a = test_mat[REF_OUTPUT][0].ptr<double>(); 318 double* b = test_mat[OUTPUT][0].ptr<double>(); 319 for( i = 0; i < MOMENT_COUNT; i++ ) 320 { 321 if( fabs(a[i]) < 1e-3 ) 322 a[i] = 0; 323 if( fabs(b[i]) < 1e-3 ) 324 b[i] = 0; 325 } 326} 327 328 329// Hu invariants 330class CV_HuMomentsTest : public cvtest::ArrayTest 331{ 332public: 333 CV_HuMomentsTest(); 334 335protected: 336 337 enum { MOMENT_COUNT = 18, HU_MOMENT_COUNT = 7 }; 338 339 int prepare_test_case( int test_case_idx ); 340 void prepare_to_validation( int /*test_case_idx*/ ); 341 void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types ); 342 void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ); 343 double get_success_error_level( int test_case_idx, int i, int j ); 344 void run_func(); 345}; 346 347 348CV_HuMomentsTest::CV_HuMomentsTest() 349{ 350 test_array[INPUT].push_back(NULL); 351 test_array[OUTPUT].push_back(NULL); 352 test_array[REF_OUTPUT].push_back(NULL); 353} 354 355 356void CV_HuMomentsTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high ) 357{ 358 cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high ); 359 low = Scalar::all(-10000); 360 high = Scalar::all(10000); 361} 362 363 364void CV_HuMomentsTest::get_test_array_types_and_sizes( int test_case_idx, 365 vector<vector<Size> >& sizes, vector<vector<int> >& types ) 366{ 367 cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); 368 types[INPUT][0] = types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1; 369 sizes[INPUT][0] = cvSize(MOMENT_COUNT,1); 370 sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(HU_MOMENT_COUNT,1); 371} 372 373 374double CV_HuMomentsTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) 375{ 376 return FLT_EPSILON; 377} 378 379 380 381int CV_HuMomentsTest::prepare_test_case( int test_case_idx ) 382{ 383 int code = cvtest::ArrayTest::prepare_test_case( test_case_idx ); 384 if( code > 0 ) 385 { 386 // ... 387 } 388 389 return code; 390} 391 392 393void CV_HuMomentsTest::run_func() 394{ 395 cvGetHuMoments( test_mat[INPUT][0].ptr<CvMoments>(), 396 test_mat[OUTPUT][0].ptr<CvHuMoments>() ); 397} 398 399 400void CV_HuMomentsTest::prepare_to_validation( int /*test_case_idx*/ ) 401{ 402 CvMoments* m = test_mat[INPUT][0].ptr<CvMoments>(); 403 CvHuMoments* hu = test_mat[REF_OUTPUT][0].ptr<CvHuMoments>(); 404 405 double inv_m00 = m->inv_sqrt_m00*m->inv_sqrt_m00; 406 double s2 = inv_m00*inv_m00; /* 1./(m00 ^ (2/2 + 1)) */ 407 double s3 = s2*m->inv_sqrt_m00; /* 1./(m00 ^ (3/2 + 1)) */ 408 409 double nu20 = m->mu20 * s2; 410 double nu11 = m->mu11 * s2; 411 double nu02 = m->mu02 * s2; 412 413 double nu30 = m->mu30 * s3; 414 double nu21 = m->mu21 * s3; 415 double nu12 = m->mu12 * s3; 416 double nu03 = m->mu03 * s3; 417 418 #undef sqr 419 #define sqr(a) ((a)*(a)) 420 421 hu->hu1 = nu20 + nu02; 422 hu->hu2 = sqr(nu20 - nu02) + 4*sqr(nu11); 423 hu->hu3 = sqr(nu30 - 3*nu12) + sqr(3*nu21 - nu03); 424 hu->hu4 = sqr(nu30 + nu12) + sqr(nu21 + nu03); 425 hu->hu5 = (nu30 - 3*nu12)*(nu30 + nu12)*(sqr(nu30 + nu12) - 3*sqr(nu21 + nu03)) + 426 (3*nu21 - nu03)*(nu21 + nu03)*(3*sqr(nu30 + nu12) - sqr(nu21 + nu03)); 427 hu->hu6 = (nu20 - nu02)*(sqr(nu30 + nu12) - sqr(nu21 + nu03)) + 428 4*nu11*(nu30 + nu12)*(nu21 + nu03); 429 hu->hu7 = (3*nu21 - nu03)*(nu30 + nu12)*(sqr(nu30 + nu12) - 3*sqr(nu21 + nu03)) + 430 (3*nu12 - nu30)*(nu21 + nu03)*(3*sqr(nu30 + nu12) - sqr(nu21 + nu03)); 431} 432 433 434TEST(Imgproc_Moments, accuracy) { CV_MomentsTest test; test.safe_run(); } 435TEST(Imgproc_HuMoments, accuracy) { CV_HuMomentsTest test; test.safe_run(); } 436 437class CV_SmallContourMomentTest : public cvtest::BaseTest 438{ 439public: 440 CV_SmallContourMomentTest() {} 441 ~CV_SmallContourMomentTest() {} 442protected: 443 void run(int) 444 { 445 try 446 { 447 vector<Point> points; 448 points.push_back(Point(50, 56)); 449 points.push_back(Point(53, 53)); 450 points.push_back(Point(46, 54)); 451 points.push_back(Point(49, 51)); 452 453 Moments m = moments(points, false); 454 double area = contourArea(points); 455 456 CV_Assert( m.m00 == 0 && m.m01 == 0 && m.m10 == 0 && area == 0 ); 457 } 458 catch(...) 459 { 460 ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); 461 } 462 } 463}; 464 465TEST(Imgproc_ContourMoment, small) { CV_SmallContourMomentTest test; test.safe_run(); } 466