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 42 #include "test_precomp.hpp" 43 44 using namespace cv; 45 using namespace std; 46 47 /*static int 48 cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n ) 49 { 50 CvPoint2D32f v0 = v[n-1]; 51 int i, sign = 0; 52 53 for( i = 0; i < n; i++ ) 54 { 55 CvPoint2D32f v1 = v[i]; 56 float dx = pt.x - v0.x, dy = pt.y - v0.y; 57 float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y; 58 double t = (double)dx*dy1 - (double)dx1*dy; 59 if( fabs(t) > DBL_EPSILON ) 60 { 61 if( t*sign < 0 ) 62 break; 63 if( sign == 0 ) 64 sign = t < 0 ? -1 : 1; 65 } 66 else if( fabs(dx) + fabs(dy) < DBL_EPSILON ) 67 return i+1; 68 v0 = v1; 69 } 70 71 return i < n ? -1 : 0; 72 }*/ 73 74 CV_INLINE double 75 cvTsDist( CvPoint2D32f a, CvPoint2D32f b ) 76 { 77 double dx = a.x - b.x; 78 double dy = a.y - b.y; 79 return sqrt(dx*dx + dy*dy); 80 } 81 82 CV_INLINE double 83 cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b ) 84 { 85 double d0 = cvTsDist( pt, a ), d1; 86 double dd = cvTsDist( a, b ); 87 if( dd < FLT_EPSILON ) 88 return d0; 89 d1 = cvTsDist( pt, b ); 90 dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd; 91 d0 = MIN( d0, d1 ); 92 return MIN( d0, dd ); 93 } 94 95 static double 96 cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 ) 97 { 98 int i; 99 CvPoint2D32f v = vv[n-1], v0; 100 double min_dist_num = FLT_MAX, min_dist_denom = 1; 101 int min_dist_idx = -1, min_on_edge = 0; 102 int counter = 0; 103 double result; 104 105 for( i = 0; i < n; i++ ) 106 { 107 double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1; 108 int on_edge = 0, idx = i; 109 110 v0 = v; v = vv[i]; 111 dx = v.x - v0.x; dy = v.y - v0.y; 112 dx1 = pt.x - v0.x; dy1 = pt.y - v0.y; 113 dx2 = pt.x - v.x; dy2 = pt.y - v.y; 114 115 if( dx2*dx + dy2*dy >= 0 ) 116 dist_num = dx2*dx2 + dy2*dy2; 117 else if( dx1*dx + dy1*dy <= 0 ) 118 { 119 dist_num = dx1*dx1 + dy1*dy1; 120 idx = i - 1; 121 if( idx < 0 ) idx = n-1; 122 } 123 else 124 { 125 dist_num = (dy1*dx - dx1*dy); 126 dist_num *= dist_num; 127 dist_denom = dx*dx + dy*dy; 128 on_edge = 1; 129 } 130 131 if( dist_num*min_dist_denom < min_dist_num*dist_denom ) 132 { 133 min_dist_num = dist_num; 134 min_dist_denom = dist_denom; 135 min_dist_idx = idx; 136 min_on_edge = on_edge; 137 if( min_dist_num == 0 ) 138 break; 139 } 140 141 if( (v0.y <= pt.y && v.y <= pt.y) || 142 (v0.y > pt.y && v.y > pt.y) || 143 (v0.x < pt.x && v.x < pt.x) ) 144 continue; 145 146 dist_num = dy1*dx - dx1*dy; 147 if( dy < 0 ) 148 dist_num = -dist_num; 149 counter += dist_num > 0; 150 } 151 152 result = sqrt(min_dist_num/min_dist_denom); 153 if( counter % 2 == 0 ) 154 result = -result; 155 156 if( _idx ) 157 *_idx = min_dist_idx; 158 if( _on_edge ) 159 *_on_edge = min_on_edge; 160 161 return result; 162 } 163 164 static cv::Point2f 165 cvTsMiddlePoint(const cv::Point2f &a, const cv::Point2f &b) 166 { 167 return cv::Point2f((a.x + b.x) / 2, (a.y + b.y) / 2); 168 } 169 170 static bool 171 cvTsIsPointOnLineSegment(const cv::Point2f &x, const cv::Point2f &a, const cv::Point2f &b) 172 { 173 double d1 = cvTsDist(CvPoint2D32f(x.x, x.y), CvPoint2D32f(a.x, a.y)); 174 double d2 = cvTsDist(CvPoint2D32f(x.x, x.y), CvPoint2D32f(b.x, b.y)); 175 double d3 = cvTsDist(CvPoint2D32f(a.x, a.y), CvPoint2D32f(b.x, b.y)); 176 177 return (abs(d1 + d2 - d3) <= (1E-5)); 178 } 179 180 181 /****************************************************************************************\ 182 * Base class for shape descriptor tests * 183 \****************************************************************************************/ 184 185 class CV_BaseShapeDescrTest : public cvtest::BaseTest 186 { 187 public: 188 CV_BaseShapeDescrTest(); 189 virtual ~CV_BaseShapeDescrTest(); 190 void clear(); 191 192 protected: 193 int read_params( CvFileStorage* fs ); 194 void run_func(void); 195 int prepare_test_case( int test_case_idx ); 196 int validate_test_results( int test_case_idx ); 197 virtual void generate_point_set( void* points ); 198 virtual void extract_points(); 199 200 int min_log_size; 201 int max_log_size; 202 int dims; 203 bool enable_flt_points; 204 205 CvMemStorage* storage; 206 CvSeq* points1; 207 CvMat* points2; 208 void* points; 209 void* result; 210 double low_high_range; 211 CvScalar low, high; 212 213 bool test_cpp; 214 }; 215 216 217 CV_BaseShapeDescrTest::CV_BaseShapeDescrTest() 218 { 219 points1 = 0; 220 points2 = 0; 221 points = 0; 222 storage = 0; 223 test_case_count = 500; 224 min_log_size = 0; 225 max_log_size = 10; 226 low = high = cvScalarAll(0); 227 low_high_range = 50; 228 dims = 2; 229 enable_flt_points = true; 230 231 test_cpp = false; 232 } 233 234 235 CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest() 236 { 237 clear(); 238 } 239 240 241 void CV_BaseShapeDescrTest::clear() 242 { 243 cvtest::BaseTest::clear(); 244 cvReleaseMemStorage( &storage ); 245 cvReleaseMat( &points2 ); 246 points1 = 0; 247 points = 0; 248 } 249 250 251 int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs ) 252 { 253 int code = cvtest::BaseTest::read_params( fs ); 254 if( code < 0 ) 255 return code; 256 257 test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count ); 258 min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size ); 259 max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size ); 260 261 min_log_size = cvtest::clipInt( min_log_size, 0, 8 ); 262 max_log_size = cvtest::clipInt( max_log_size, 0, 10 ); 263 if( min_log_size > max_log_size ) 264 { 265 int t; 266 CV_SWAP( min_log_size, max_log_size, t ); 267 } 268 269 return 0; 270 } 271 272 273 void CV_BaseShapeDescrTest::generate_point_set( void* pointsSet ) 274 { 275 RNG& rng = ts->get_rng(); 276 int i, k, n, total, point_type; 277 CvSeqReader reader; 278 uchar* data = 0; 279 double a[4], b[4]; 280 281 for( k = 0; k < 4; k++ ) 282 { 283 a[k] = high.val[k] - low.val[k]; 284 b[k] = low.val[k]; 285 } 286 memset( &reader, 0, sizeof(reader) ); 287 288 if( CV_IS_SEQ(pointsSet) ) 289 { 290 CvSeq* ptseq = (CvSeq*)pointsSet; 291 total = ptseq->total; 292 point_type = CV_SEQ_ELTYPE(ptseq); 293 cvStartReadSeq( ptseq, &reader ); 294 } 295 else 296 { 297 CvMat* ptm = (CvMat*)pointsSet; 298 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); 299 total = ptm->rows + ptm->cols - 1; 300 point_type = CV_MAT_TYPE(ptm->type); 301 data = ptm->data.ptr; 302 } 303 304 n = CV_MAT_CN(point_type); 305 point_type = CV_MAT_DEPTH(point_type); 306 307 assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 ); 308 309 for( i = 0; i < total; i++ ) 310 { 311 int* pi; 312 float* pf; 313 if( reader.ptr ) 314 { 315 pi = (int*)reader.ptr; 316 pf = (float*)reader.ptr; 317 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); 318 } 319 else 320 { 321 pi = (int*)data + i*n; 322 pf = (float*)data + i*n; 323 } 324 if( point_type == CV_32S ) 325 for( k = 0; k < n; k++ ) 326 pi[k] = cvRound(cvtest::randReal(rng)*a[k] + b[k]); 327 else 328 for( k = 0; k < n; k++ ) 329 pf[k] = (float)(cvtest::randReal(rng)*a[k] + b[k]); 330 } 331 } 332 333 334 int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx ) 335 { 336 int size; 337 int use_storage = 0; 338 int point_type; 339 int i; 340 RNG& rng = ts->get_rng(); 341 342 cvtest::BaseTest::prepare_test_case( test_case_idx ); 343 344 clear(); 345 size = cvRound( exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) ); 346 use_storage = cvtest::randInt(rng) % 2; 347 point_type = CV_MAKETYPE(cvtest::randInt(rng) % 348 (enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims); 349 350 if( use_storage ) 351 { 352 storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); 353 points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage ); 354 cvSeqPushMulti( points1, 0, size ); 355 points = points1; 356 } 357 else 358 { 359 int rows = 1, cols = size; 360 if( cvtest::randInt(rng) % 2 ) 361 rows = size, cols = 1; 362 363 points2 = cvCreateMat( rows, cols, point_type ); 364 points = points2; 365 } 366 367 for( i = 0; i < 4; i++ ) 368 { 369 low.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; 370 high.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; 371 if( low.val[i] > high.val[i] ) 372 { 373 double t; 374 CV_SWAP( low.val[i], high.val[i], t ); 375 } 376 if( high.val[i] < low.val[i] + 1 ) 377 high.val[i] += 1; 378 } 379 380 generate_point_set( points ); 381 382 test_cpp = (cvtest::randInt(rng) & 16) == 0; 383 return 1; 384 } 385 386 387 void CV_BaseShapeDescrTest::extract_points() 388 { 389 if( points1 ) 390 { 391 points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) ); 392 cvCvtSeqToArray( points1, points2->data.ptr ); 393 } 394 395 if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points ) 396 { 397 CvMat tmp = cvMat( points2->rows, points2->cols, 398 (points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr ); 399 cvConvert( points2, &tmp ); 400 } 401 } 402 403 404 void CV_BaseShapeDescrTest::run_func(void) 405 { 406 } 407 408 409 int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ ) 410 { 411 extract_points(); 412 return 0; 413 } 414 415 416 /****************************************************************************************\ 417 * Convex Hull Test * 418 \****************************************************************************************/ 419 420 class CV_ConvHullTest : public CV_BaseShapeDescrTest 421 { 422 public: 423 CV_ConvHullTest(); 424 virtual ~CV_ConvHullTest(); 425 void clear(); 426 427 protected: 428 void run_func(void); 429 int prepare_test_case( int test_case_idx ); 430 int validate_test_results( int test_case_idx ); 431 432 CvSeq* hull1; 433 CvMat* hull2; 434 void* hull_storage; 435 int orientation; 436 int return_points; 437 }; 438 439 440 CV_ConvHullTest::CV_ConvHullTest() 441 { 442 hull1 = 0; 443 hull2 = 0; 444 hull_storage = 0; 445 orientation = return_points = 0; 446 } 447 448 449 CV_ConvHullTest::~CV_ConvHullTest() 450 { 451 clear(); 452 } 453 454 455 void CV_ConvHullTest::clear() 456 { 457 CV_BaseShapeDescrTest::clear(); 458 cvReleaseMat( &hull2 ); 459 hull1 = 0; 460 hull_storage = 0; 461 } 462 463 464 int CV_ConvHullTest::prepare_test_case( int test_case_idx ) 465 { 466 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); 467 int use_storage_for_hull = 0; 468 RNG& rng = ts->get_rng(); 469 470 if( code <= 0 ) 471 return code; 472 473 orientation = cvtest::randInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE; 474 return_points = cvtest::randInt(rng) % 2; 475 476 use_storage_for_hull = (cvtest::randInt(rng) % 2) && !test_cpp; 477 if( use_storage_for_hull ) 478 { 479 if( !storage ) 480 storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); 481 hull_storage = storage; 482 } 483 else 484 { 485 int rows, cols; 486 int sz = points1 ? points1->total : points2->cols + points2->rows - 1; 487 int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type); 488 489 if( cvtest::randInt(rng) % 2 ) 490 rows = sz, cols = 1; 491 else 492 rows = 1, cols = sz; 493 494 hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 ); 495 hull_storage = hull2; 496 } 497 498 return code; 499 } 500 501 502 void CV_ConvHullTest::run_func() 503 { 504 if(!test_cpp) 505 hull1 = cvConvexHull2( points, hull_storage, orientation, return_points ); 506 else 507 { 508 cv::Mat _points = cv::cvarrToMat(points); 509 bool clockwise = orientation == CV_CLOCKWISE; 510 size_t n = 0; 511 if( !return_points ) 512 { 513 std::vector<int> _hull; 514 cv::convexHull(_points, _hull, clockwise); 515 n = _hull.size(); 516 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); 517 } 518 else if(_points.type() == CV_32SC2) 519 { 520 std::vector<cv::Point> _hull; 521 cv::convexHull(_points, _hull, clockwise); 522 n = _hull.size(); 523 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); 524 } 525 else if(_points.type() == CV_32FC2) 526 { 527 std::vector<cv::Point2f> _hull; 528 cv::convexHull(_points, _hull, clockwise); 529 n = _hull.size(); 530 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); 531 } 532 if(hull2->rows > hull2->cols) 533 hull2->rows = (int)n; 534 else 535 hull2->cols = (int)n; 536 } 537 } 538 539 540 int CV_ConvHullTest::validate_test_results( int test_case_idx ) 541 { 542 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 543 CvMat* hull = 0; 544 CvMat* mask = 0; 545 int i, point_count, hull_count; 546 CvPoint2D32f *p, *h; 547 CvSeq header, hheader, *ptseq, *hseq; 548 CvSeqBlock block, hblock; 549 550 if( points1 ) 551 ptseq = points1; 552 else 553 ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type), 554 sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr, 555 points2->rows + points2->cols - 1, &header, &block ); 556 point_count = ptseq->total; 557 p = (CvPoint2D32f*)(points2->data.ptr); 558 559 if( hull1 ) 560 hseq = hull1; 561 else 562 hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type), 563 sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr, 564 hull2->rows + hull2->cols - 1, &hheader, &hblock ); 565 hull_count = hseq->total; 566 hull = cvCreateMat( 1, hull_count, CV_32FC2 ); 567 mask = cvCreateMat( 1, hull_count, CV_8UC1 ); 568 cvZero( mask ); 569 Mat _mask = cvarrToMat(mask); 570 571 h = (CvPoint2D32f*)(hull->data.ptr); 572 573 // extract convex hull points 574 if( return_points ) 575 { 576 cvCvtSeqToArray( hseq, hull->data.ptr ); 577 if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 ) 578 { 579 CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr ); 580 cvConvert( &tmp, hull ); 581 } 582 } 583 else 584 { 585 CvSeqReader reader; 586 cvStartReadSeq( hseq, &reader ); 587 588 for( i = 0; i < hull_count; i++ ) 589 { 590 schar* ptr = reader.ptr; 591 int idx; 592 CV_NEXT_SEQ_ELEM( hseq->elem_size, reader ); 593 594 if( hull1 ) 595 idx = cvSeqElemIdx( ptseq, *(uchar**)ptr ); 596 else 597 idx = *(int*)ptr; 598 599 if( idx < 0 || idx >= point_count ) 600 { 601 ts->printf( cvtest::TS::LOG, "Invalid convex hull point #%d\n", i ); 602 code = cvtest::TS::FAIL_INVALID_OUTPUT; 603 goto _exit_; 604 } 605 h[i] = p[idx]; 606 } 607 } 608 609 // check that the convex hull is a convex polygon 610 if( hull_count >= 3 ) 611 { 612 CvPoint2D32f pt0 = h[hull_count-1]; 613 for( i = 0; i < hull_count; i++ ) 614 { 615 int j = i+1; 616 CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0]; 617 float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y; 618 float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y; 619 double t = (double)dx0*dy1 - (double)dx1*dy0; 620 if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) ) 621 { 622 ts->printf( cvtest::TS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i ); 623 code = cvtest::TS::FAIL_INVALID_OUTPUT; 624 goto _exit_; 625 } 626 pt0 = pt1; 627 } 628 } 629 630 // check that all the points are inside the hull or on the hull edge 631 // and at least hull_point points are at the hull vertices 632 for( i = 0; i < point_count; i++ ) 633 { 634 int idx = 0, on_edge = 0; 635 double pptresult = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge ); 636 637 if( pptresult < 0 ) 638 { 639 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the convex hull\n", i ); 640 code = cvtest::TS::FAIL_BAD_ACCURACY; 641 goto _exit_; 642 } 643 644 if( pptresult < FLT_EPSILON && !on_edge ) 645 mask->data.ptr[idx] = (uchar)1; 646 } 647 648 if( cvtest::norm( _mask, Mat::zeros(_mask.dims, _mask.size, _mask.type()), NORM_L1 ) != hull_count ) 649 { 650 ts->printf( cvtest::TS::LOG, "Not every convex hull vertex coincides with some input point\n" ); 651 code = cvtest::TS::FAIL_BAD_ACCURACY; 652 goto _exit_; 653 } 654 655 _exit_: 656 657 cvReleaseMat( &hull ); 658 cvReleaseMat( &mask ); 659 if( code < 0 ) 660 ts->set_failed_test_info( code ); 661 return code; 662 } 663 664 665 /****************************************************************************************\ 666 * MinAreaRect Test * 667 \****************************************************************************************/ 668 669 class CV_MinAreaRectTest : public CV_BaseShapeDescrTest 670 { 671 public: 672 CV_MinAreaRectTest(); 673 674 protected: 675 void run_func(void); 676 int validate_test_results( int test_case_idx ); 677 678 CvBox2D box; 679 CvPoint2D32f box_pt[4]; 680 }; 681 682 683 CV_MinAreaRectTest::CV_MinAreaRectTest() 684 { 685 } 686 687 688 void CV_MinAreaRectTest::run_func() 689 { 690 if(!test_cpp) 691 { 692 box = cvMinAreaRect2( points, storage ); 693 cvBoxPoints( box, box_pt ); 694 } 695 else 696 { 697 cv::RotatedRect r = cv::minAreaRect(cv::cvarrToMat(points)); 698 box = (CvBox2D)r; 699 r.points((cv::Point2f*)box_pt); 700 } 701 } 702 703 704 int CV_MinAreaRectTest::validate_test_results( int test_case_idx ) 705 { 706 double eps = 1e-1; 707 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 708 int i, j, point_count = points2->rows + points2->cols - 1; 709 CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); 710 int mask[] = {0,0,0,0}; 711 712 // check that the bounding box is a rotated rectangle: 713 // 1. diagonals should be equal 714 // 2. they must intersect in their middle points 715 { 716 double d0 = cvTsDist( box_pt[0], box_pt[2] ); 717 double d1 = cvTsDist( box_pt[1], box_pt[3] ); 718 719 double x0 = (box_pt[0].x + box_pt[2].x)*0.5; 720 double y0 = (box_pt[0].y + box_pt[2].y)*0.5; 721 double x1 = (box_pt[1].x + box_pt[3].x)*0.5; 722 double y1 = (box_pt[1].y + box_pt[3].y)*0.5; 723 724 if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) ) 725 { 726 ts->printf( cvtest::TS::LOG, "The bounding box is not a rectangle\n" ); 727 code = cvtest::TS::FAIL_INVALID_OUTPUT; 728 goto _exit_; 729 } 730 } 731 732 #if 0 733 { 734 int n = 4; 735 double a = 8, c = 8, b = 100, d = 150; 736 CvPoint bp[4], *bpp = bp; 737 cvNamedWindow( "test", 1 ); 738 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); 739 cvZero(img); 740 for( i = 0; i < point_count; i++ ) 741 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 ); 742 for( i = 0; i < n; i++ ) 743 bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d)); 744 cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 ); 745 cvShowImage( "test", img ); 746 cvWaitKey(); 747 cvReleaseImage(&img); 748 } 749 #endif 750 751 // check that the box includes all the points 752 // and there is at least one point at (or very close to) every box side 753 for( i = 0; i < point_count; i++ ) 754 { 755 int idx = 0, on_edge = 0; 756 double pptresult = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge ); 757 if( pptresult < -eps ) 758 { 759 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the box\n", i ); 760 code = cvtest::TS::FAIL_BAD_ACCURACY; 761 goto _exit_; 762 } 763 764 if( pptresult < eps ) 765 { 766 for( j = 0; j < 4; j++ ) 767 { 768 double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] ); 769 if( d < eps ) 770 mask[j] = (uchar)1; 771 } 772 } 773 } 774 775 if( mask[0] + mask[1] + mask[2] + mask[3] != 4 ) 776 { 777 ts->printf( cvtest::TS::LOG, "Not every box side has a point nearby\n" ); 778 code = cvtest::TS::FAIL_BAD_ACCURACY; 779 goto _exit_; 780 } 781 782 _exit_: 783 784 if( code < 0 ) 785 ts->set_failed_test_info( code ); 786 return code; 787 } 788 789 790 /****************************************************************************************\ 791 * MinEnclosingTriangle Test * 792 \****************************************************************************************/ 793 794 class CV_MinTriangleTest : public CV_BaseShapeDescrTest 795 { 796 public: 797 CV_MinTriangleTest(); 798 799 protected: 800 void run_func(void); 801 int validate_test_results( int test_case_idx ); 802 std::vector<cv::Point2f> getTriangleMiddlePoints(); 803 804 std::vector<cv::Point2f> convexPolygon; 805 std::vector<cv::Point2f> triangle; 806 }; 807 808 809 CV_MinTriangleTest::CV_MinTriangleTest() 810 { 811 } 812 813 std::vector<cv::Point2f> CV_MinTriangleTest::getTriangleMiddlePoints() 814 { 815 std::vector<cv::Point2f> triangleMiddlePoints; 816 817 for (int i = 0; i < 3; i++) { 818 triangleMiddlePoints.push_back(cvTsMiddlePoint(triangle[i], triangle[(i + 1) % 3])); 819 } 820 821 return triangleMiddlePoints; 822 } 823 824 825 void CV_MinTriangleTest::run_func() 826 { 827 std::vector<cv::Point2f> pointsAsVector; 828 829 cv::cvarrToMat(points).convertTo(pointsAsVector, CV_32F); 830 831 cv::minEnclosingTriangle(pointsAsVector, triangle); 832 cv::convexHull(pointsAsVector, convexPolygon, true, true); 833 } 834 835 836 int CV_MinTriangleTest::validate_test_results( int test_case_idx ) 837 { 838 bool errorEnclosed = false, errorMiddlePoints = false, errorFlush = true; 839 double eps = 1e-4; 840 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 841 842 #if 0 843 { 844 int n = 3; 845 double a = 8, c = 8, b = 100, d = 150; 846 CvPoint bp[4], *bpp = bp; 847 cvNamedWindow( "test", 1 ); 848 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); 849 cvZero(img); 850 for( i = 0; i < point_count; i++ ) 851 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 ); 852 for( i = 0; i < n; i++ ) 853 bp[i] = cvPoint(cvRound(triangle[i].x*a+b),cvRound(triangle[i].y*c+d)); 854 cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 ); 855 cvShowImage( "test", img ); 856 cvWaitKey(); 857 cvReleaseImage(&img); 858 } 859 #endif 860 861 int polygonVertices = (int) convexPolygon.size(); 862 863 if (polygonVertices > 2) { 864 // Check if all points are enclosed by the triangle 865 for (int i = 0; (i < polygonVertices) && (!errorEnclosed); i++) 866 { 867 if (cv::pointPolygonTest(triangle, cv::Point2f(convexPolygon[i].x, convexPolygon[i].y), true) < (-eps)) 868 errorEnclosed = true; 869 } 870 871 // Check if triangle edges middle points touch the polygon 872 std::vector<cv::Point2f> middlePoints = getTriangleMiddlePoints(); 873 874 for (int i = 0; (i < 3) && (!errorMiddlePoints); i++) 875 { 876 bool isTouching = false; 877 878 for (int j = 0; (j < polygonVertices) && (!isTouching); j++) 879 { 880 if (cvTsIsPointOnLineSegment(middlePoints[i], convexPolygon[j], 881 convexPolygon[(j + 1) % polygonVertices])) 882 isTouching = true; 883 } 884 885 errorMiddlePoints = (isTouching) ? false : true; 886 } 887 888 // Check if at least one of the edges is flush 889 for (int i = 0; (i < 3) && (errorFlush); i++) 890 { 891 for (int j = 0; (j < polygonVertices) && (errorFlush); j++) 892 { 893 if ((cvTsIsPointOnLineSegment(convexPolygon[j], triangle[i], 894 triangle[(i + 1) % 3])) && 895 (cvTsIsPointOnLineSegment(convexPolygon[(j + 1) % polygonVertices], triangle[i], 896 triangle[(i + 1) % 3]))) 897 errorFlush = false; 898 } 899 } 900 901 // Report any found errors 902 if (errorEnclosed) 903 { 904 ts->printf( cvtest::TS::LOG, 905 "All points should be enclosed by the triangle.\n" ); 906 code = cvtest::TS::FAIL_BAD_ACCURACY; 907 } 908 else if (errorMiddlePoints) 909 { 910 ts->printf( cvtest::TS::LOG, 911 "All triangle edges middle points should touch the convex hull of the points.\n" ); 912 code = cvtest::TS::FAIL_INVALID_OUTPUT; 913 } 914 else if (errorFlush) 915 { 916 ts->printf( cvtest::TS::LOG, 917 "At least one edge of the enclosing triangle should be flush with one edge of the polygon.\n" ); 918 code = cvtest::TS::FAIL_INVALID_OUTPUT; 919 } 920 } 921 922 if ( code < 0 ) 923 ts->set_failed_test_info( code ); 924 925 return code; 926 } 927 928 929 /****************************************************************************************\ 930 * MinEnclosingCircle Test * 931 \****************************************************************************************/ 932 933 class CV_MinCircleTest : public CV_BaseShapeDescrTest 934 { 935 public: 936 CV_MinCircleTest(); 937 938 protected: 939 void run_func(void); 940 int validate_test_results( int test_case_idx ); 941 942 CvPoint2D32f center; 943 float radius; 944 }; 945 946 947 CV_MinCircleTest::CV_MinCircleTest() 948 { 949 } 950 951 952 void CV_MinCircleTest::run_func() 953 { 954 if(!test_cpp) 955 cvMinEnclosingCircle( points, ¢er, &radius ); 956 else 957 { 958 cv::Point2f tmpcenter; 959 cv::minEnclosingCircle(cv::cvarrToMat(points), tmpcenter, radius); 960 center = tmpcenter; 961 } 962 } 963 964 965 int CV_MinCircleTest::validate_test_results( int test_case_idx ) 966 { 967 double eps = 1.03; 968 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 969 int i, j = 0, point_count = points2->rows + points2->cols - 1; 970 CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); 971 CvPoint2D32f v[3]; 972 973 #if 0 974 { 975 double a = 2, b = 200, d = 400; 976 cvNamedWindow( "test", 1 ); 977 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); 978 cvZero(img); 979 for( i = 0; i < point_count; i++ ) 980 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 ); 981 cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)), 982 cvRound(radius*a), CV_RGB(255,255,0), 1 ); 983 cvShowImage( "test", img ); 984 cvWaitKey(); 985 cvReleaseImage(&img); 986 } 987 #endif 988 989 // check that the circle contains all the points inside and 990 // remember at most 3 points that are close to the boundary 991 for( i = 0; i < point_count; i++ ) 992 { 993 double d = cvTsDist( p[i], center ); 994 if( d > radius ) 995 { 996 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the circle\n", i ); 997 code = cvtest::TS::FAIL_BAD_ACCURACY; 998 goto _exit_; 999 } 1000 1001 if( radius - d < eps*radius && j < 3 ) 1002 v[j++] = p[i]; 1003 } 1004 1005 if( point_count >= 2 && (j < 2 || (j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps)) ) 1006 { 1007 ts->printf( cvtest::TS::LOG, 1008 "There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" ); 1009 code = cvtest::TS::FAIL_BAD_ACCURACY; 1010 goto _exit_; 1011 } 1012 1013 _exit_: 1014 1015 if( code < 0 ) 1016 ts->set_failed_test_info( code ); 1017 return code; 1018 } 1019 1020 1021 /****************************************************************************************\ 1022 * Perimeter Test * 1023 \****************************************************************************************/ 1024 1025 class CV_PerimeterTest : public CV_BaseShapeDescrTest 1026 { 1027 public: 1028 CV_PerimeterTest(); 1029 1030 protected: 1031 int prepare_test_case( int test_case_idx ); 1032 void run_func(void); 1033 int validate_test_results( int test_case_idx ); 1034 CvSlice slice; 1035 int is_closed; 1036 double result; 1037 }; 1038 1039 1040 CV_PerimeterTest::CV_PerimeterTest() 1041 { 1042 } 1043 1044 1045 int CV_PerimeterTest::prepare_test_case( int test_case_idx ) 1046 { 1047 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); 1048 RNG& rng = ts->get_rng(); 1049 int total; 1050 1051 if( code < 0 ) 1052 return code; 1053 1054 is_closed = cvtest::randInt(rng) % 2; 1055 1056 if( points1 ) 1057 { 1058 points1->flags |= CV_SEQ_KIND_CURVE; 1059 if( is_closed ) 1060 points1->flags |= CV_SEQ_FLAG_CLOSED; 1061 total = points1->total; 1062 } 1063 else 1064 total = points2->cols + points2->rows - 1; 1065 1066 if( (cvtest::randInt(rng) % 3) && !test_cpp ) 1067 { 1068 slice.start_index = cvtest::randInt(rng) % total; 1069 slice.end_index = cvtest::randInt(rng) % total; 1070 } 1071 else 1072 slice = CV_WHOLE_SEQ; 1073 1074 return 1; 1075 } 1076 1077 1078 void CV_PerimeterTest::run_func() 1079 { 1080 if(!test_cpp) 1081 result = cvArcLength( points, slice, points1 ? -1 : is_closed ); 1082 else 1083 result = cv::arcLength(cv::cvarrToMat(points), 1084 !points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0); 1085 } 1086 1087 1088 int CV_PerimeterTest::validate_test_results( int test_case_idx ) 1089 { 1090 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 1091 int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1; 1092 double result0 = 0; 1093 CvPoint2D32f prev_pt, pt, *ptr; 1094 1095 if( len < 0 ) 1096 len += total; 1097 1098 len = MIN( len, total ); 1099 //len -= !is_closed && len == total; 1100 1101 ptr = (CvPoint2D32f*)points2->data.fl; 1102 prev_pt = ptr[(is_closed ? slice.start_index+len-1 : slice.start_index) % total]; 1103 1104 for( i = 0; i < len + (len < total && (!is_closed || len==1)); i++ ) 1105 { 1106 pt = ptr[(i + slice.start_index) % total]; 1107 double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y; 1108 result0 += sqrt(dx*dx + dy*dy); 1109 prev_pt = pt; 1110 } 1111 1112 if( cvIsNaN(result) || cvIsInf(result) ) 1113 { 1114 ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result ); 1115 code = cvtest::TS::FAIL_INVALID_OUTPUT; 1116 } 1117 else if( fabs(result - result0) > FLT_EPSILON*100*result0 ) 1118 { 1119 ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 ); 1120 code = cvtest::TS::FAIL_BAD_ACCURACY; 1121 } 1122 1123 if( code < 0 ) 1124 ts->set_failed_test_info( code ); 1125 return code; 1126 } 1127 1128 1129 /****************************************************************************************\ 1130 * FitEllipse Test * 1131 \****************************************************************************************/ 1132 1133 class CV_FitEllipseTest : public CV_BaseShapeDescrTest 1134 { 1135 public: 1136 CV_FitEllipseTest(); 1137 1138 protected: 1139 int prepare_test_case( int test_case_idx ); 1140 void generate_point_set( void* points ); 1141 void run_func(void); 1142 int validate_test_results( int test_case_idx ); 1143 CvBox2D box0, box; 1144 double min_ellipse_size, max_noise; 1145 }; 1146 1147 1148 CV_FitEllipseTest::CV_FitEllipseTest() 1149 { 1150 min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least 1151 max_log_size = 10; 1152 min_ellipse_size = 10; 1153 max_noise = 0.05; 1154 } 1155 1156 1157 void CV_FitEllipseTest::generate_point_set( void* pointsSet ) 1158 { 1159 RNG& rng = ts->get_rng(); 1160 int i, total, point_type; 1161 CvSeqReader reader; 1162 uchar* data = 0; 1163 double a, b; 1164 1165 box0.center.x = (float)((low.val[0] + high.val[0])*0.5); 1166 box0.center.y = (float)((low.val[1] + high.val[1])*0.5); 1167 box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2); 1168 box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2); 1169 box0.angle = (float)(cvtest::randReal(rng)*180); 1170 a = cos(box0.angle*CV_PI/180.); 1171 b = sin(box0.angle*CV_PI/180.); 1172 1173 if( box0.size.width > box0.size.height ) 1174 { 1175 float t; 1176 CV_SWAP( box0.size.width, box0.size.height, t ); 1177 } 1178 memset( &reader, 0, sizeof(reader) ); 1179 1180 if( CV_IS_SEQ(pointsSet) ) 1181 { 1182 CvSeq* ptseq = (CvSeq*)pointsSet; 1183 total = ptseq->total; 1184 point_type = CV_SEQ_ELTYPE(ptseq); 1185 cvStartReadSeq( ptseq, &reader ); 1186 } 1187 else 1188 { 1189 CvMat* ptm = (CvMat*)pointsSet; 1190 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); 1191 total = ptm->rows + ptm->cols - 1; 1192 point_type = CV_MAT_TYPE(ptm->type); 1193 data = ptm->data.ptr; 1194 } 1195 1196 assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); 1197 1198 for( i = 0; i < total; i++ ) 1199 { 1200 CvPoint* pp; 1201 CvPoint2D32f p; 1202 double angle = cvtest::randReal(rng)*CV_PI*2; 1203 double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); 1204 double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); 1205 p.x = (float)(box0.center.x + a*x + b*y); 1206 p.y = (float)(box0.center.y - b*x + a*y); 1207 1208 if( reader.ptr ) 1209 { 1210 pp = (CvPoint*)reader.ptr; 1211 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); 1212 } 1213 else 1214 pp = ((CvPoint*)data) + i; 1215 if( point_type == CV_32SC2 ) 1216 { 1217 pp->x = cvRound(p.x); 1218 pp->y = cvRound(p.y); 1219 } 1220 else 1221 *(CvPoint2D32f*)pp = p; 1222 } 1223 } 1224 1225 1226 int CV_FitEllipseTest::prepare_test_case( int test_case_idx ) 1227 { 1228 min_log_size = MAX(min_log_size,4); 1229 max_log_size = MAX(min_log_size,max_log_size); 1230 return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); 1231 } 1232 1233 1234 void CV_FitEllipseTest::run_func() 1235 { 1236 if(!test_cpp) 1237 box = cvFitEllipse2( points ); 1238 else 1239 box = (CvBox2D)cv::fitEllipse(cv::cvarrToMat(points)); 1240 } 1241 1242 int CV_FitEllipseTest::validate_test_results( int test_case_idx ) 1243 { 1244 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 1245 double diff_angle; 1246 1247 if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) || 1248 cvIsNaN(box.center.y) || cvIsInf(box.center.y) || 1249 cvIsNaN(box.size.width) || cvIsInf(box.size.width) || 1250 cvIsNaN(box.size.height) || cvIsInf(box.size.height) || 1251 cvIsNaN(box.angle) || cvIsInf(box.angle) ) 1252 { 1253 ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n", 1254 box.center.x, box.center.y, box.size.width, box.size.height, box.angle ); 1255 code = cvtest::TS::FAIL_INVALID_OUTPUT; 1256 goto _exit_; 1257 } 1258 1259 box.angle = (float)(90-box.angle); 1260 if( box.angle < 0 ) 1261 box.angle += 360; 1262 if( box.angle > 360 ) 1263 box.angle -= 360; 1264 1265 if( fabs(box.center.x - box0.center.x) > 3 || 1266 fabs(box.center.y - box0.center.y) > 3 || 1267 fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) || 1268 fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) ) 1269 { 1270 ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t" 1271 "(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n", 1272 box.center.x, box.center.y, box.size.width, box.size.height, 1273 box0.center.x, box0.center.y, box0.size.width, box0.size.height ); 1274 code = cvtest::TS::FAIL_BAD_ACCURACY; 1275 goto _exit_; 1276 } 1277 1278 diff_angle = fabs(box0.angle - box.angle); 1279 diff_angle = MIN( diff_angle, fabs(diff_angle - 360)); 1280 diff_angle = MIN( diff_angle, fabs(diff_angle - 180)); 1281 1282 if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 ) 1283 { 1284 ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n", 1285 box.angle, box0.angle ); 1286 code = cvtest::TS::FAIL_BAD_ACCURACY; 1287 goto _exit_; 1288 } 1289 1290 _exit_: 1291 1292 #if 0 1293 if( code < 0 ) 1294 { 1295 cvNamedWindow( "test", 0 ); 1296 IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4), 1297 cvRound(low_high_range*4)), 8, 3 ); 1298 cvZero( img ); 1299 1300 box.center.x += (float)low_high_range*2; 1301 box.center.y += (float)low_high_range*2; 1302 cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 ); 1303 1304 for( int i = 0; i < points2->rows + points2->cols - 1; i++ ) 1305 { 1306 CvPoint pt; 1307 pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2); 1308 pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2); 1309 cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 ); 1310 } 1311 1312 cvShowImage( "test", img ); 1313 cvReleaseImage( &img ); 1314 cvWaitKey(0); 1315 } 1316 #endif 1317 1318 if( code < 0 ) 1319 { 1320 ts->set_failed_test_info( code ); 1321 } 1322 return code; 1323 } 1324 1325 1326 class CV_FitEllipseSmallTest : public cvtest::BaseTest 1327 { 1328 public: 1329 CV_FitEllipseSmallTest() {} 1330 ~CV_FitEllipseSmallTest() {} 1331 protected: 1332 void run(int) 1333 { 1334 Size sz(50, 50); 1335 vector<vector<Point> > c; 1336 c.push_back(vector<Point>()); 1337 int scale = 1; 1338 Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale; 1339 c[0].push_back(Point(2, 0)*scale+ofs); 1340 c[0].push_back(Point(0, 2)*scale+ofs); 1341 c[0].push_back(Point(0, 6)*scale+ofs); 1342 c[0].push_back(Point(2, 8)*scale+ofs); 1343 c[0].push_back(Point(6, 8)*scale+ofs); 1344 c[0].push_back(Point(8, 6)*scale+ofs); 1345 c[0].push_back(Point(8, 2)*scale+ofs); 1346 c[0].push_back(Point(6, 0)*scale+ofs); 1347 1348 RotatedRect e = fitEllipse(c[0]); 1349 CV_Assert( fabs(e.center.x - 4) <= 1. && 1350 fabs(e.center.y - 4) <= 1. && 1351 fabs(e.size.width - 9) <= 1. && 1352 fabs(e.size.height - 9) <= 1. ); 1353 } 1354 }; 1355 1356 1357 // Regression test for incorrect fitEllipse result reported in Bug #3989 1358 // Check edge cases for rotation angles of ellipse ([-180, 90, 0, 90, 180] degrees) 1359 class CV_FitEllipseParallelTest : public CV_FitEllipseTest 1360 { 1361 public: 1362 CV_FitEllipseParallelTest(); 1363 ~CV_FitEllipseParallelTest(); 1364 protected: 1365 void generate_point_set( void* points ); 1366 void run_func(void); 1367 Mat pointsMat; 1368 }; 1369 1370 CV_FitEllipseParallelTest::CV_FitEllipseParallelTest() 1371 { 1372 min_ellipse_size = 5; 1373 } 1374 1375 void CV_FitEllipseParallelTest::generate_point_set( void* ) 1376 { 1377 RNG& rng = ts->get_rng(); 1378 int height = (int)(MAX(high.val[0] - low.val[0], min_ellipse_size)); 1379 int width = (int)(MAX(high.val[1] - low.val[1], min_ellipse_size)); 1380 const int angle = ( (cvtest::randInt(rng) % 5) - 2 ) * 90; 1381 const int dim = max(height, width); 1382 const Point center = Point(dim*2, dim*2); 1383 1384 if( width > height ) 1385 { 1386 int t; 1387 CV_SWAP( width, height, t ); 1388 } 1389 1390 Mat image = Mat::zeros(dim*4, dim*4, CV_8UC1); 1391 ellipse(image, center, Size(height, width), angle, 1392 0, 360, Scalar(255, 0, 0), 1, 8); 1393 1394 box0.center.x = (float)center.x; 1395 box0.center.y = (float)center.y; 1396 box0.size.width = (float)width*2; 1397 box0.size.height = (float)height*2; 1398 box0.angle = (float)angle; 1399 1400 vector<vector<Point> > contours; 1401 findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); 1402 Mat(contours[0]).convertTo(pointsMat, CV_32F); 1403 } 1404 1405 void CV_FitEllipseParallelTest::run_func() 1406 { 1407 box = (CvBox2D)cv::fitEllipse(pointsMat); 1408 } 1409 1410 CV_FitEllipseParallelTest::~CV_FitEllipseParallelTest(){ 1411 pointsMat.release(); 1412 } 1413 1414 /****************************************************************************************\ 1415 * FitLine Test * 1416 \****************************************************************************************/ 1417 1418 class CV_FitLineTest : public CV_BaseShapeDescrTest 1419 { 1420 public: 1421 CV_FitLineTest(); 1422 1423 protected: 1424 int prepare_test_case( int test_case_idx ); 1425 void generate_point_set( void* points ); 1426 void run_func(void); 1427 int validate_test_results( int test_case_idx ); 1428 double max_noise; 1429 float line[6], line0[6]; 1430 int dist_type; 1431 double reps, aeps; 1432 }; 1433 1434 1435 CV_FitLineTest::CV_FitLineTest() 1436 { 1437 min_log_size = 5; // for robust line fitting a dozen of points is needed at least 1438 max_log_size = 10; 1439 max_noise = 0.05; 1440 } 1441 1442 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) 1443 # pragma GCC diagnostic push 1444 # pragma GCC diagnostic ignored "-Warray-bounds" 1445 #endif 1446 1447 void CV_FitLineTest::generate_point_set( void* pointsSet ) 1448 { 1449 RNG& rng = ts->get_rng(); 1450 int i, k, n, total, point_type; 1451 CvSeqReader reader; 1452 uchar* data = 0; 1453 double s = 0; 1454 1455 n = dims; 1456 for( k = 0; k < n; k++ ) 1457 { 1458 line0[k+n] = (float)((low.val[k] + high.val[k])*0.5); 1459 line0[k] = (float)(high.val[k] - low.val[k]); 1460 if( cvtest::randInt(rng) % 2 ) 1461 line0[k] = -line0[k]; 1462 s += (double)line0[k]*line0[k]; 1463 } 1464 1465 s = 1./sqrt(s); 1466 for( k = 0; k < n; k++ ) 1467 line0[k] = (float)(line0[k]*s); 1468 1469 memset( &reader, 0, sizeof(reader) ); 1470 1471 if( CV_IS_SEQ(pointsSet) ) 1472 { 1473 CvSeq* ptseq = (CvSeq*)pointsSet; 1474 total = ptseq->total; 1475 point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq)); 1476 cvStartReadSeq( ptseq, &reader ); 1477 } 1478 else 1479 { 1480 CvMat* ptm = (CvMat*)pointsSet; 1481 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); 1482 total = ptm->rows + ptm->cols - 1; 1483 point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type)); 1484 data = ptm->data.ptr; 1485 } 1486 1487 for( i = 0; i < total; i++ ) 1488 { 1489 int* pi; 1490 float* pf; 1491 float p[4], t; 1492 if( reader.ptr ) 1493 { 1494 pi = (int*)reader.ptr; 1495 pf = (float*)reader.ptr; 1496 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); 1497 } 1498 else 1499 { 1500 pi = (int*)data + i*n; 1501 pf = (float*)data + i*n; 1502 } 1503 1504 t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2); 1505 1506 for( k = 0; k < n; k++ ) 1507 { 1508 p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]); 1509 1510 if( point_type == CV_32S ) 1511 pi[k] = cvRound(p[k]); 1512 else 1513 pf[k] = p[k]; 1514 } 1515 } 1516 } 1517 1518 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) 1519 # pragma GCC diagnostic pop 1520 #endif 1521 1522 int CV_FitLineTest::prepare_test_case( int test_case_idx ) 1523 { 1524 RNG& rng = ts->get_rng(); 1525 dims = cvtest::randInt(rng) % 2 + 2; 1526 min_log_size = MAX(min_log_size,5); 1527 max_log_size = MAX(min_log_size,max_log_size); 1528 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); 1529 dist_type = cvtest::randInt(rng) % 6 + 1; 1530 dist_type += dist_type == CV_DIST_C; 1531 reps = 0.1; aeps = 0.01; 1532 return code; 1533 } 1534 1535 1536 void CV_FitLineTest::run_func() 1537 { 1538 if(!test_cpp) 1539 cvFitLine( points, dist_type, 0, reps, aeps, line ); 1540 else if(dims == 2) 1541 cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps); 1542 else 1543 cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps); 1544 } 1545 1546 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) 1547 # pragma GCC diagnostic push 1548 # pragma GCC diagnostic ignored "-Warray-bounds" 1549 #endif 1550 1551 int CV_FitLineTest::validate_test_results( int test_case_idx ) 1552 { 1553 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 1554 int k, max_k = 0; 1555 double vec_diff = 0, t; 1556 1557 for( k = 0; k < dims*2; k++ ) 1558 { 1559 if( cvIsNaN(line[k]) || cvIsInf(line[k]) ) 1560 { 1561 ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n", 1562 k, line[k] ); 1563 code = cvtest::TS::FAIL_INVALID_OUTPUT; 1564 goto _exit_; 1565 } 1566 } 1567 1568 if( fabs(line0[1]) > fabs(line0[0]) ) 1569 max_k = 1; 1570 if( fabs(line0[dims-1]) > fabs(line0[max_k]) ) 1571 max_k = dims-1; 1572 if( line0[max_k] < 0 ) 1573 for( k = 0; k < dims; k++ ) 1574 line0[k] = -line0[k]; 1575 if( line[max_k] < 0 ) 1576 for( k = 0; k < dims; k++ ) 1577 line[k] = -line[k]; 1578 1579 for( k = 0; k < dims; k++ ) 1580 { 1581 double dt = line[k] - line0[k]; 1582 vec_diff += dt*dt; 1583 } 1584 1585 if( sqrt(vec_diff) > 0.05 ) 1586 { 1587 if( dims == 2 ) 1588 ts->printf( cvtest::TS::LOG, 1589 "The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n", 1590 line[0], line[1], line0[0], line0[1] ); 1591 else 1592 ts->printf( cvtest::TS::LOG, 1593 "The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n", 1594 line[0], line[1], line[2], line0[0], line0[1], line0[2] ); 1595 code = cvtest::TS::FAIL_BAD_ACCURACY; 1596 goto _exit_; 1597 } 1598 1599 t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k]; 1600 for( k = 0; k < dims; k++ ) 1601 { 1602 double p = line0[k+dims] + t*line0[k] - line[k+dims]; 1603 vec_diff += p*p; 1604 } 1605 1606 if( sqrt(vec_diff) > 1*MAX(fabs(t),1) ) 1607 { 1608 if( dims == 2 ) 1609 ts->printf( cvtest::TS::LOG, 1610 "The computed line point (%.2f,%.2f) is too far from the actual line\n", 1611 line[2]+line0[2], line[3]+line0[3] ); 1612 else 1613 ts->printf( cvtest::TS::LOG, 1614 "The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n", 1615 line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] ); 1616 code = cvtest::TS::FAIL_BAD_ACCURACY; 1617 goto _exit_; 1618 } 1619 1620 _exit_: 1621 1622 if( code < 0 ) 1623 { 1624 ts->set_failed_test_info( code ); 1625 } 1626 return code; 1627 } 1628 1629 #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) 1630 # pragma GCC diagnostic pop 1631 #endif 1632 1633 /****************************************************************************************\ 1634 * ContourMoments Test * 1635 \****************************************************************************************/ 1636 1637 1638 static void 1639 cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes, 1640 double max_r_scale, double angle, CvArr* points, RNG& rng ) 1641 { 1642 int i, total, point_type; 1643 uchar* data = 0; 1644 CvSeqReader reader; 1645 memset( &reader, 0, sizeof(reader) ); 1646 1647 if( CV_IS_SEQ(points) ) 1648 { 1649 CvSeq* ptseq = (CvSeq*)points; 1650 total = ptseq->total; 1651 point_type = CV_SEQ_ELTYPE(ptseq); 1652 cvStartReadSeq( ptseq, &reader ); 1653 } 1654 else 1655 { 1656 CvMat* ptm = (CvMat*)points; 1657 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); 1658 total = ptm->rows + ptm->cols - 1; 1659 point_type = CV_MAT_TYPE(ptm->type); 1660 data = ptm->data.ptr; 1661 } 1662 1663 assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); 1664 1665 for( i = 0; i < total; i++ ) 1666 { 1667 CvPoint* pp; 1668 CvPoint2D32f p; 1669 1670 double phi0 = 2*CV_PI*i/total; 1671 double phi = CV_PI*angle/180.; 1672 double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale); 1673 double ta = axes.height*t; 1674 double tb = axes.width*t; 1675 double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb; 1676 double c = cos(phi), s = sin(phi); 1677 p.x = (float)(c0*c - s0*s + center.x); 1678 p.y = (float)(c0*s + s0*c + center.y); 1679 1680 if( reader.ptr ) 1681 { 1682 pp = (CvPoint*)reader.ptr; 1683 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); 1684 } 1685 else 1686 pp = ((CvPoint*)data) + i; 1687 1688 if( point_type == CV_32SC2 ) 1689 { 1690 pp->x = cvRound(p.x); 1691 pp->y = cvRound(p.y); 1692 } 1693 else 1694 *(CvPoint2D32f*)pp = p; 1695 } 1696 } 1697 1698 1699 class CV_ContourMomentsTest : public CV_BaseShapeDescrTest 1700 { 1701 public: 1702 CV_ContourMomentsTest(); 1703 1704 protected: 1705 int prepare_test_case( int test_case_idx ); 1706 void generate_point_set( void* points ); 1707 void run_func(void); 1708 int validate_test_results( int test_case_idx ); 1709 CvMoments moments0, moments; 1710 double area0, area; 1711 CvSize2D32f axes; 1712 CvPoint2D32f center; 1713 int max_max_r_scale; 1714 double max_r_scale, angle; 1715 CvSize img_size; 1716 }; 1717 1718 1719 CV_ContourMomentsTest::CV_ContourMomentsTest() 1720 { 1721 min_log_size = 3; 1722 max_log_size = 8; 1723 max_max_r_scale = 15; 1724 low_high_range = 200; 1725 enable_flt_points = false; 1726 } 1727 1728 1729 void CV_ContourMomentsTest::generate_point_set( void* pointsSet ) 1730 { 1731 RNG& rng = ts->get_rng(); 1732 float max_sz; 1733 1734 axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); 1735 axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); 1736 max_sz = MAX(axes.width, axes.height); 1737 1738 img_size.width = img_size.height = cvRound(low_high_range*2.2); 1739 1740 center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8); 1741 center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8); 1742 1743 assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width && 1744 0 < center.y - max_sz && center.y + max_sz < img_size.height ); 1745 1746 max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01; 1747 angle = cvtest::randReal(rng)*360; 1748 1749 cvTsGenerateTousledBlob( center, axes, max_r_scale, angle, pointsSet, rng ); 1750 1751 if( points1 ) 1752 points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON; 1753 } 1754 1755 1756 int CV_ContourMomentsTest::prepare_test_case( int test_case_idx ) 1757 { 1758 min_log_size = MAX(min_log_size,3); 1759 max_log_size = MIN(max_log_size,8); 1760 max_log_size = MAX(min_log_size,max_log_size); 1761 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); 1762 return code; 1763 } 1764 1765 1766 void CV_ContourMomentsTest::run_func() 1767 { 1768 if(!test_cpp) 1769 { 1770 cvMoments( points, &moments ); 1771 area = cvContourArea( points ); 1772 } 1773 else 1774 { 1775 moments = (CvMoments)cv::moments(cv::cvarrToMat(points)); 1776 area = cv::contourArea(cv::cvarrToMat(points)); 1777 } 1778 } 1779 1780 1781 int CV_ContourMomentsTest::validate_test_results( int test_case_idx ) 1782 { 1783 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); 1784 int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00)); 1785 CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 ); 1786 CvPoint* pt = (CvPoint*)points2->data.i; 1787 int count = points2->cols + points2->rows - 1; 1788 double max_v0 = 0; 1789 1790 cvZero(img); 1791 cvFillPoly( img, &pt, &count, 1, cvScalarAll(1)); 1792 cvMoments( img, &moments0 ); 1793 1794 for( i = 0; i < n; i++ ) 1795 { 1796 double t = fabs((&moments0.m00)[i]); 1797 max_v0 = MAX(max_v0, t); 1798 } 1799 1800 for( i = 0; i <= n; i++ ) 1801 { 1802 double v = i < n ? (&moments.m00)[i] : area; 1803 double v0 = i < n ? (&moments0.m00)[i] : moments0.m00; 1804 1805 if( cvIsNaN(v) || cvIsInf(v) ) 1806 { 1807 ts->printf( cvtest::TS::LOG, 1808 "The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v ); 1809 code = cvtest::TS::FAIL_INVALID_OUTPUT; 1810 break; 1811 } 1812 1813 if( fabs(v - v0) > 0.1*max_v0 ) 1814 { 1815 ts->printf( cvtest::TS::LOG, 1816 "The computed contour %s is %g, while it should be %g\n", 1817 i < n ? "moment" : "area", v, v0 ); 1818 code = cvtest::TS::FAIL_BAD_ACCURACY; 1819 break; 1820 } 1821 } 1822 1823 if( code < 0 ) 1824 { 1825 #if 0 1826 cvCmpS( img, 0, img, CV_CMP_GT ); 1827 cvNamedWindow( "test", 1 ); 1828 cvShowImage( "test", img ); 1829 cvWaitKey(); 1830 #endif 1831 ts->set_failed_test_info( code ); 1832 } 1833 1834 cvReleaseMat( &img ); 1835 return code; 1836 } 1837 1838 1839 ////////////////////////////////////// Perimeter/Area/Slice test /////////////////////////////////// 1840 1841 class CV_PerimeterAreaSliceTest : public cvtest::BaseTest 1842 { 1843 public: 1844 CV_PerimeterAreaSliceTest(); 1845 ~CV_PerimeterAreaSliceTest(); 1846 protected: 1847 void run(int); 1848 }; 1849 1850 CV_PerimeterAreaSliceTest::CV_PerimeterAreaSliceTest() 1851 { 1852 } 1853 CV_PerimeterAreaSliceTest::~CV_PerimeterAreaSliceTest() {} 1854 1855 void CV_PerimeterAreaSliceTest::run( int ) 1856 { 1857 Ptr<CvMemStorage> storage(cvCreateMemStorage()); 1858 RNG& rng = theRNG(); 1859 const double min_r = 90, max_r = 120; 1860 1861 for( int i = 0; i < 100; i++ ) 1862 { 1863 ts->update_context( this, i, true ); 1864 int n = rng.uniform(3, 30); 1865 cvClearMemStorage(storage); 1866 CvSeq* contour = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(CvPoint), storage); 1867 double dphi = CV_PI*2/n; 1868 CvPoint center; 1869 center.x = rng.uniform(cvCeil(max_r), cvFloor(640-max_r)); 1870 center.y = rng.uniform(cvCeil(max_r), cvFloor(480-max_r)); 1871 1872 for( int j = 0; j < n; j++ ) 1873 { 1874 CvPoint pt; 1875 double r = rng.uniform(min_r, max_r); 1876 double phi = j*dphi; 1877 pt.x = cvRound(center.x + r*cos(phi)); 1878 pt.y = cvRound(center.y - r*sin(phi)); 1879 cvSeqPush(contour, &pt); 1880 } 1881 1882 CvSlice slice; 1883 for(;;) 1884 { 1885 slice.start_index = rng.uniform(-n/2, 3*n/2); 1886 slice.end_index = rng.uniform(-n/2, 3*n/2); 1887 int len = cvSliceLength(slice, contour); 1888 if( len > 2 ) 1889 break; 1890 } 1891 CvSeq *cslice = cvSeqSlice(contour, slice); 1892 /*printf( "%d. (%d, %d) of %d, length = %d, length1 = %d\n", 1893 i, slice.start_index, slice.end_index, 1894 contour->total, cvSliceLength(slice, contour), cslice->total ); 1895 1896 double area0 = cvContourArea(cslice); 1897 double area1 = cvContourArea(contour, slice); 1898 if( area0 != area1 ) 1899 { 1900 ts->printf(cvtest::TS::LOG, 1901 "The contour area slice is computed differently (%g vs %g)\n", area0, area1 ); 1902 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); 1903 return; 1904 }*/ 1905 1906 double len0 = cvArcLength(cslice, CV_WHOLE_SEQ, 1); 1907 double len1 = cvArcLength(contour, slice, 1); 1908 if( len0 != len1 ) 1909 { 1910 ts->printf(cvtest::TS::LOG, 1911 "The contour arc length is computed differently (%g vs %g)\n", len0, len1 ); 1912 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); 1913 return; 1914 } 1915 } 1916 ts->set_failed_test_info(cvtest::TS::OK); 1917 } 1918 1919 1920 TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); } 1921 TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); } 1922 TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); } 1923 TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); } 1924 TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); } 1925 TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); } 1926 TEST(Imgproc_FitEllipse, parallel) { CV_FitEllipseParallelTest test; test.safe_run(); } 1927 TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); } 1928 TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); } 1929 TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); } 1930 TEST(Imgproc_FitEllipse, small) { CV_FitEllipseSmallTest test; test.safe_run(); } 1931 1932 /* End of file. */ 1933