1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
45 cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n )
47 CvPoint2D32f v0 = v[n-1];
50 for( i = 0; i < n; i++ )
52 CvPoint2D32f v1 = v[i];
53 float dx = pt.x - v0.x, dy = pt.y - v0.y;
54 float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y;
55 double t = (double)dx*dy1 - (double)dx1*dy;
56 if( fabs(t) > DBL_EPSILON )
61 sign = t < 0 ? -1 : 1;
63 else if( fabs(dx) + fabs(dy) < DBL_EPSILON )
68 return i < n ? -1 : 0;
72 cvTsDist( CvPoint2D32f a, CvPoint2D32f b )
74 double dx = a.x - b.x;
75 double dy = a.y - b.y;
76 return sqrt(dx*dx + dy*dy);
80 cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b )
82 double d0 = cvTsDist( pt, a ), d1;
83 double dd = cvTsDist( a, b );
84 if( dd < FLT_EPSILON )
86 d1 = cvTsDist( pt, b );
87 dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd;
93 cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 )
96 CvPoint2D32f v = vv[n-1], v0;
97 double min_dist_num = FLT_MAX, min_dist_denom = 1;
98 int min_dist_idx = -1, min_on_edge = 0;
102 for( i = 0; i < n; i++ )
104 double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1;
105 int on_edge = 0, idx = i;
108 dx = v.x - v0.x; dy = v.y - v0.y;
109 dx1 = pt.x - v0.x; dy1 = pt.y - v0.y;
110 dx2 = pt.x - v.x; dy2 = pt.y - v.y;
112 if( dx2*dx + dy2*dy >= 0 )
113 dist_num = dx2*dx2 + dy2*dy2;
114 else if( dx1*dx + dy1*dy <= 0 )
116 dist_num = dx1*dx1 + dy1*dy1;
118 if( idx < 0 ) idx = n-1;
122 dist_num = (dy1*dx - dx1*dy);
123 dist_num *= dist_num;
124 dist_denom = dx*dx + dy*dy;
128 if( dist_num*min_dist_denom < min_dist_num*dist_denom )
130 min_dist_num = dist_num;
131 min_dist_denom = dist_denom;
133 min_on_edge = on_edge;
134 if( min_dist_num == 0 )
138 if( v0.y <= pt.y && v.y <= pt.y ||
139 v0.y > pt.y && v.y > pt.y ||
140 v0.x < pt.x && v.x < pt.x )
143 dist_num = dy1*dx - dx1*dy;
145 dist_num = -dist_num;
146 counter += dist_num > 0;
149 result = sqrt(min_dist_num/min_dist_denom);
150 if( counter % 2 == 0 )
154 *_idx = min_dist_idx;
156 *_on_edge = min_on_edge;
162 /****************************************************************************************\
163 * Base class for shape descriptor tests *
164 \****************************************************************************************/
166 class CV_BaseShapeDescrTest : public CvTest
169 CV_BaseShapeDescrTest( const char* test_name, const char* test_funcs );
170 virtual ~CV_BaseShapeDescrTest();
172 int write_default_params(CvFileStorage* fs);
175 int read_params( CvFileStorage* fs );
177 int prepare_test_case( int test_case_idx );
178 int validate_test_results( int test_case_idx );
179 virtual void generate_point_set( void* points );
180 virtual void extract_points();
185 bool enable_flt_points;
187 CvMemStorage* storage;
192 double low_high_range;
197 CV_BaseShapeDescrTest::CV_BaseShapeDescrTest( const char* test_name, const char* test_funcs ):
198 CvTest( test_name, test_funcs )
204 test_case_count = 500;
207 low = high = cvScalarAll(0);
210 enable_flt_points = true;
212 support_testing_modes = CvTS::CORRECTNESS_CHECK_MODE;
216 CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest()
222 void CV_BaseShapeDescrTest::clear()
225 cvReleaseMemStorage( &storage );
226 cvReleaseMat( &points2 );
232 int CV_BaseShapeDescrTest::write_default_params( CvFileStorage* fs )
234 CvTest::write_default_params( fs );
235 if( ts->get_testing_mode() != CvTS::TIMING_MODE )
237 write_param( fs, "test_case_count", test_case_count );
238 write_param( fs, "min_log_size", min_log_size );
239 write_param( fs, "max_log_size", max_log_size );
245 int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs )
247 int code = CvTest::read_params( fs );
251 test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count );
252 min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size );
253 max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size );
255 min_log_size = cvTsClipInt( min_log_size, 0, 8 );
256 max_log_size = cvTsClipInt( max_log_size, 0, 10 );
257 if( min_log_size > max_log_size )
260 CV_SWAP( min_log_size, max_log_size, t );
267 void CV_BaseShapeDescrTest::generate_point_set( void* points )
269 CvRNG* rng = ts->get_rng();
270 int i, k, n, total, point_type;
275 for( k = 0; k < 4; k++ )
277 a[k] = high.val[k] - low.val[k];
280 memset( &reader, 0, sizeof(reader) );
282 if( CV_IS_SEQ(points) )
284 CvSeq* ptseq = (CvSeq*)points;
285 total = ptseq->total;
286 point_type = CV_SEQ_ELTYPE(ptseq);
287 cvStartReadSeq( ptseq, &reader );
291 CvMat* ptm = (CvMat*)points;
292 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
293 total = ptm->rows + ptm->cols - 1;
294 point_type = CV_MAT_TYPE(ptm->type);
295 data = ptm->data.ptr;
298 n = CV_MAT_CN(point_type);
299 point_type = CV_MAT_DEPTH(point_type);
301 assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 );
303 for( i = 0; i < total; i++ )
309 pi = (int*)reader.ptr;
310 pf = (float*)reader.ptr;
311 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
315 pi = (int*)data + i*n;
316 pf = (float*)data + i*n;
318 if( point_type == CV_32S )
319 for( k = 0; k < n; k++ )
320 pi[k] = cvRound(cvTsRandReal(rng)*a[k] + b[k]);
322 for( k = 0; k < n; k++ )
323 pf[k] = (float)(cvTsRandReal(rng)*a[k] + b[k]);
328 int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx )
334 CvRNG* rng = ts->get_rng();
336 CvTest::prepare_test_case( test_case_idx );
339 size = cvRound( exp((cvTsRandReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) );
340 use_storage = cvTsRandInt(rng) % 2;
341 point_type = CV_MAKETYPE(cvTsRandInt(rng) %
342 (enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims);
346 storage = cvCreateMemStorage( (cvTsRandInt(rng)%10 + 1)*1024 );
347 points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage );
348 cvSeqPushMulti( points1, 0, size );
353 int rows = 1, cols = size;
354 if( cvTsRandInt(rng) % 2 )
355 rows = size, cols = 1;
357 points2 = cvCreateMat( rows, cols, point_type );
361 for( i = 0; i < 4; i++ )
363 low.val[i] = (cvTsRandReal(rng)-0.5)*low_high_range*2;
364 high.val[i] = (cvTsRandReal(rng)-0.5)*low_high_range*2;
365 if( low.val[i] > high.val[i] )
368 CV_SWAP( low.val[i], high.val[i], t );
372 generate_point_set( points );
377 void CV_BaseShapeDescrTest::extract_points()
381 points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) );
382 cvCvtSeqToArray( points1, points2->data.ptr );
385 if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points )
387 CvMat tmp = cvMat( points2->rows, points2->cols,
388 (points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr );
389 cvConvert( points2, &tmp );
394 void CV_BaseShapeDescrTest::run_func(void)
399 int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ )
406 CV_BaseShapeDescrTest shape_basetest( "shape", "" );
409 /****************************************************************************************\
411 \****************************************************************************************/
413 class CV_ConvHullTest : public CV_BaseShapeDescrTest
417 virtual ~CV_ConvHullTest();
422 int prepare_test_case( int test_case_idx );
423 int validate_test_results( int test_case_idx );
433 CV_ConvHullTest::CV_ConvHullTest():
434 CV_BaseShapeDescrTest( "shape-convhull", "cvConvexHull2" )
439 orientation = return_points = 0;
443 CV_ConvHullTest::~CV_ConvHullTest()
449 void CV_ConvHullTest::clear()
451 CV_BaseShapeDescrTest::clear();
452 cvReleaseMat( &hull2 );
458 int CV_ConvHullTest::prepare_test_case( int test_case_idx )
460 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
461 int use_storage_for_hull = 0;
462 CvRNG* rng = ts->get_rng();
467 orientation = cvTsRandInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE;
468 return_points = cvTsRandInt(rng) % 2;
470 use_storage_for_hull = cvTsRandInt(rng) % 2;
471 if( use_storage_for_hull )
474 storage = cvCreateMemStorage( (cvTsRandInt(rng)%10 + 1)*1024 );
475 hull_storage = storage;
480 int sz = points1 ? points1->total : points2->cols + points2->rows - 1;
481 int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type);
483 if( cvTsRandInt(rng) % 2 )
488 hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 );
489 hull_storage = hull2;
496 void CV_ConvHullTest::run_func()
498 hull1 = cvConvexHull2( points, hull_storage, orientation, return_points );
502 int CV_ConvHullTest::validate_test_results( int test_case_idx )
504 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
507 int i, point_count, hull_count;
509 CvSeq header, hheader, *ptseq, *hseq;
510 CvSeqBlock block, hblock;
515 ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type),
516 sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr,
517 points2->rows + points2->cols - 1, &header, &block );
518 point_count = ptseq->total;
519 p = (CvPoint2D32f*)(points2->data.ptr);
524 hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type),
525 sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr,
526 hull2->rows + hull2->cols - 1, &hheader, &hblock );
527 hull_count = hseq->total;
528 hull = cvCreateMat( 1, hull_count, CV_32FC2 );
529 mask = cvCreateMat( 1, hull_count, CV_8UC1 );
531 h = (CvPoint2D32f*)(hull->data.ptr);
533 // extract convex hull points
536 cvCvtSeqToArray( hseq, hull->data.ptr );
537 if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 )
539 CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr );
540 cvConvert( &tmp, hull );
546 cvStartReadSeq( hseq, &reader );
548 for( i = 0; i < hull_count; i++ )
550 schar* ptr = reader.ptr;
552 CV_NEXT_SEQ_ELEM( hseq->elem_size, reader );
555 idx = cvSeqElemIdx( ptseq, *(uchar**)ptr );
559 if( idx < 0 || idx >= point_count )
561 ts->printf( CvTS::LOG, "Invalid convex hull point #%d\n", i );
562 code = CvTS::FAIL_INVALID_OUTPUT;
569 // check that the convex hull is a convex polygon
570 if( hull_count >= 3 )
572 CvPoint2D32f pt0 = h[hull_count-1];
573 for( i = 0; i < hull_count; i++ )
576 CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0];
577 float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y;
578 float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y;
579 double t = (double)dx0*dy1 - (double)dx1*dy0;
580 if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) )
582 ts->printf( CvTS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i );
583 code = CvTS::FAIL_INVALID_OUTPUT;
590 // check that all the points are inside the hull or on the hull edge
591 // and at least hull_point points are at the hull vertices
592 for( i = 0; i < point_count; i++ )
594 int idx = 0, on_edge = 0;
595 double result = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge );
599 ts->printf( CvTS::LOG, "The point #%d is outside of the convex hull\n", i );
600 code = CvTS::FAIL_BAD_ACCURACY;
604 if( result < FLT_EPSILON && !on_edge )
605 mask->data.ptr[idx] = (uchar)1;
608 if( cvNorm( mask, 0, CV_L1 ) != hull_count )
610 ts->printf( CvTS::LOG, "Not every convex hull vertex coincides with some input point\n" );
611 code = CvTS::FAIL_BAD_ACCURACY;
617 cvReleaseMat( &hull );
618 cvReleaseMat( &mask );
620 ts->set_failed_test_info( code );
625 CV_ConvHullTest shape_convhull_test;
628 /****************************************************************************************\
630 \****************************************************************************************/
632 class CV_MinAreaRectTest : public CV_BaseShapeDescrTest
635 CV_MinAreaRectTest();
639 int validate_test_results( int test_case_idx );
642 CvPoint2D32f box_pt[4];
646 CV_MinAreaRectTest::CV_MinAreaRectTest():
647 CV_BaseShapeDescrTest( "shape-minarearect", "cvMinAreaRect2, cvBoxPoints" )
652 void CV_MinAreaRectTest::run_func()
654 box = cvMinAreaRect2( points, storage );
655 cvBoxPoints( box, box_pt );
659 int CV_MinAreaRectTest::validate_test_results( int test_case_idx )
662 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
663 int i, j, point_count = points2->rows + points2->cols - 1;
664 CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr);
665 int mask[] = {0,0,0,0};
667 // check that the bounding box is a rotated rectangle:
668 // 1. diagonals should be equal
669 // 2. they must intersect in their middle points
671 double d0 = cvTsDist( box_pt[0], box_pt[2] );
672 double d1 = cvTsDist( box_pt[1], box_pt[3] );
674 double x0 = (box_pt[0].x + box_pt[2].x)*0.5;
675 double y0 = (box_pt[0].y + box_pt[2].y)*0.5;
676 double x1 = (box_pt[1].x + box_pt[3].x)*0.5;
677 double y1 = (box_pt[1].y + box_pt[3].y)*0.5;
679 if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) )
681 ts->printf( CvTS::LOG, "The bounding box is not a rectangle\n" );
682 code = CvTS::FAIL_INVALID_OUTPUT;
690 double a = 8, c = 8, b = 100, d = 150;
691 CvPoint bp[4], *bpp = bp;
692 cvNamedWindow( "test", 1 );
693 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
695 for( i = 0; i < point_count; i++ )
696 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 );
697 for( i = 0; i < n; i++ )
698 bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d));
699 cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 );
700 cvShowImage( "test", img );
702 cvReleaseImage(&img);
706 // check that the box includes all the points
707 // and there is at least one point at (or very close to) every box side
708 for( i = 0; i < point_count; i++ )
710 int idx = 0, on_edge = 0;
711 double result = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge );
714 ts->printf( CvTS::LOG, "The point #%d is outside of the box\n", i );
715 code = CvTS::FAIL_BAD_ACCURACY;
721 for( j = 0; j < 4; j++ )
723 double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] );
730 if( mask[0] + mask[1] + mask[2] + mask[3] != 4 )
732 ts->printf( CvTS::LOG, "Not every box side has a point nearby\n" );
733 code = CvTS::FAIL_BAD_ACCURACY;
740 ts->set_failed_test_info( code );
745 CV_MinAreaRectTest shape_minarearect_test;
748 /****************************************************************************************\
749 * MinEnclosingCircle Test *
750 \****************************************************************************************/
752 class CV_MinCircleTest : public CV_BaseShapeDescrTest
759 int validate_test_results( int test_case_idx );
766 CV_MinCircleTest::CV_MinCircleTest():
767 CV_BaseShapeDescrTest( "shape-mincircle", "cvMinEnclosingCircle" )
772 void CV_MinCircleTest::run_func()
774 cvMinEnclosingCircle( points, ¢er, &radius );
778 int CV_MinCircleTest::validate_test_results( int test_case_idx )
781 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
782 int i, j = 0, point_count = points2->rows + points2->cols - 1;
783 CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr);
788 double a = 2, b = 200, d = 400;
789 cvNamedWindow( "test", 1 );
790 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
792 for( i = 0; i < point_count; i++ )
793 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 );
794 cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)),
795 cvRound(radius*a), CV_RGB(255,255,0), 1 );
796 cvShowImage( "test", img );
798 cvReleaseImage(&img);
802 // check that the circle contains all the points inside and
803 // remember at most 3 points that are close to the boundary
804 for( i = 0; i < point_count; i++ )
806 double d = cvTsDist( p[i], center );
809 ts->printf( CvTS::LOG, "The point #%d is outside of the circle\n", i );
810 code = CvTS::FAIL_BAD_ACCURACY;
814 if( radius - d < eps*radius && j < 3 )
818 if( point_count >= 2 && (j < 2 || j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps) )
820 ts->printf( CvTS::LOG,
821 "There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" );
822 code = CvTS::FAIL_BAD_ACCURACY;
829 ts->set_failed_test_info( code );
834 CV_MinCircleTest shape_mincircle_test;
837 /****************************************************************************************\
839 \****************************************************************************************/
841 class CV_PerimeterTest : public CV_BaseShapeDescrTest
847 int prepare_test_case( int test_case_idx );
849 int validate_test_results( int test_case_idx );
856 CV_PerimeterTest::CV_PerimeterTest():
857 CV_BaseShapeDescrTest( "shape-perimeter", "cvArcLength" )
862 int CV_PerimeterTest::prepare_test_case( int test_case_idx )
864 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
865 CvRNG* rng = ts->get_rng();
871 is_closed = cvTsRandInt(rng) % 2;
875 points1->flags |= CV_SEQ_KIND_CURVE;
877 points1->flags |= CV_SEQ_FLAG_CLOSED;
878 total = points1->total;
881 total = points2->cols + points2->rows - 1;
883 if( cvTsRandInt(rng) % 3 )
885 slice.start_index = cvTsRandInt(rng) % total;
886 slice.end_index = cvTsRandInt(rng) % total;
889 slice = CV_WHOLE_SEQ;
895 void CV_PerimeterTest::run_func()
897 result = cvArcLength( points, slice, points1 ? -1 : is_closed );
901 int CV_PerimeterTest::validate_test_results( int test_case_idx )
903 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
904 int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1;
906 CvPoint2D32f prev_pt, pt, *ptr;
911 len = MIN( len, total );
912 len -= !is_closed && len == total;
914 ptr = (CvPoint2D32f*)points2->data.fl;
915 prev_pt = ptr[slice.start_index % total];
917 for( i = 1; i <= len; i++ )
919 pt = ptr[(i + slice.start_index) % total];
920 double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y;
921 result0 += sqrt(dx*dx + dy*dy);
925 if( cvIsNaN(result) || cvIsInf(result) )
927 ts->printf( CvTS::LOG, "cvArcLength() returned invalid value (%g)\n", result );
928 code = CvTS::FAIL_INVALID_OUTPUT;
930 else if( fabs(result - result0) > FLT_EPSILON*100*result0 )
932 ts->printf( CvTS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 );
933 code = CvTS::FAIL_BAD_ACCURACY;
937 ts->set_failed_test_info( code );
942 CV_PerimeterTest shape_perimeter_test;
946 /****************************************************************************************\
948 \****************************************************************************************/
950 class CV_FitEllipseTest : public CV_BaseShapeDescrTest
956 int prepare_test_case( int test_case_idx );
957 void generate_point_set( void* points );
959 int validate_test_results( int test_case_idx );
961 double min_ellipse_size, max_noise;
965 CV_FitEllipseTest::CV_FitEllipseTest():
966 CV_BaseShapeDescrTest( "shape-fit-ellipse", "cvFitEllipse" )
968 min_log_size = 4; // for robust ellipse fitting a dozen of points is needed at least
970 min_ellipse_size = 10;
975 void CV_FitEllipseTest::generate_point_set( void* points )
977 CvRNG* rng = ts->get_rng();
978 int i, total, point_type;
983 box0.center.x = (float)((low.val[0] + high.val[0])*0.5);
984 box0.center.y = (float)((low.val[1] + high.val[1])*0.5);
985 box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2);
986 box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2);
987 box0.angle = (float)(cvTsRandReal(rng)*180);
988 a = cos(box0.angle*CV_PI/180.);
989 b = sin(box0.angle*CV_PI/180.);
991 if( box0.size.width > box0.size.height )
994 CV_SWAP( box0.size.width, box0.size.height, t );
996 memset( &reader, 0, sizeof(reader) );
998 if( CV_IS_SEQ(points) )
1000 CvSeq* ptseq = (CvSeq*)points;
1001 total = ptseq->total;
1002 point_type = CV_SEQ_ELTYPE(ptseq);
1003 cvStartReadSeq( ptseq, &reader );
1007 CvMat* ptm = (CvMat*)points;
1008 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1009 total = ptm->rows + ptm->cols - 1;
1010 point_type = CV_MAT_TYPE(ptm->type);
1011 data = ptm->data.ptr;
1014 assert( point_type == CV_32SC2 || point_type == CV_32FC2 );
1016 for( i = 0; i < total; i++ )
1020 double angle = cvTsRandReal(rng)*CV_PI*2;
1021 double x = box0.size.height*0.5*(cos(angle) + (cvTsRandReal(rng)-0.5)*2*max_noise);
1022 double y = box0.size.width*0.5*(sin(angle) + (cvTsRandReal(rng)-0.5)*2*max_noise);
1023 p.x = (float)(box0.center.x + a*x + b*y);
1024 p.y = (float)(box0.center.y - b*x + a*y);
1028 pp = (CvPoint*)reader.ptr;
1029 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1032 pp = ((CvPoint*)data) + i;
1033 if( point_type == CV_32SC2 )
1035 pp->x = cvRound(p.x);
1036 pp->y = cvRound(p.y);
1039 *(CvPoint2D32f*)pp = p;
1044 int CV_FitEllipseTest::prepare_test_case( int test_case_idx )
1046 min_log_size = MAX(min_log_size,4);
1047 max_log_size = MAX(min_log_size,max_log_size);
1048 return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1052 void CV_FitEllipseTest::run_func()
1054 box = cvFitEllipse2( points );
1058 int CV_FitEllipseTest::validate_test_results( int test_case_idx )
1060 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1063 if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) ||
1064 cvIsNaN(box.center.y) || cvIsInf(box.center.y) ||
1065 cvIsNaN(box.size.width) || cvIsInf(box.size.width) ||
1066 cvIsNaN(box.size.height) || cvIsInf(box.size.height) ||
1067 cvIsNaN(box.angle) || cvIsInf(box.angle) )
1069 ts->printf( CvTS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n",
1070 box.center.x, box.center.y, box.size.width, box.size.height, box.angle );
1071 code = CvTS::FAIL_INVALID_OUTPUT;
1075 box.angle = (float)(90-box.angle);
1078 if( box.angle > 360 )
1081 if( fabs(box.center.x - box0.center.x) > 3 ||
1082 fabs(box.center.y - box0.center.y) > 3 ||
1083 fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) ||
1084 fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) )
1086 ts->printf( CvTS::LOG, "The computed ellipse center and/or size are incorrect:\n\t"
1087 "(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n",
1088 box.center.x, box.center.y, box.size.width, box.size.height,
1089 box0.center.x, box0.center.y, box0.size.width, box0.size.height );
1090 code = CvTS::FAIL_BAD_ACCURACY;
1094 diff_angle = fabs(box0.angle - box.angle);
1095 diff_angle = MIN( diff_angle, fabs(diff_angle - 360));
1096 diff_angle = MIN( diff_angle, fabs(diff_angle - 180));
1098 if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 )
1100 ts->printf( CvTS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n",
1101 box.angle, box0.angle );
1102 code = CvTS::FAIL_BAD_ACCURACY;
1109 cvNamedWindow( "test", 0 );
1110 IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4),
1111 cvRound(low_high_range*4)), 8, 3 );
1114 box.center.x += (float)low_high_range*2;
1115 box.center.y += (float)low_high_range*2;
1116 cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 );
1118 for( int i = 0; i < points2->rows + points2->cols - 1; i++ )
1121 pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2);
1122 pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2);
1123 cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 );
1126 cvShowImage( "test", img );
1127 cvReleaseImage( &img );
1133 ts->set_failed_test_info( code );
1139 CV_FitEllipseTest fit_ellipse_test;
1143 /****************************************************************************************\
1145 \****************************************************************************************/
1147 class CV_FitLineTest : public CV_BaseShapeDescrTest
1153 int prepare_test_case( int test_case_idx );
1154 void generate_point_set( void* points );
1155 void run_func(void);
1156 int validate_test_results( int test_case_idx );
1158 float line[6], line0[6];
1164 CV_FitLineTest::CV_FitLineTest():
1165 CV_BaseShapeDescrTest( "shape-fit-line", "cvFitLine" )
1167 min_log_size = 4; // for robust ellipse fitting a dozen of points is needed at least
1173 void CV_FitLineTest::generate_point_set( void* points )
1175 CvRNG* rng = ts->get_rng();
1176 int i, k, n, total, point_type;
1182 for( k = 0; k < n; k++ )
1184 line0[k+n] = (float)((low.val[k] + high.val[k])*0.5);
1185 line0[k] = (float)(high.val[k] - low.val[k]);
1186 if( cvTsRandInt(rng) % 2 )
1187 line0[k] = -line0[k];
1188 s += (double)line0[k]*line0[k];
1192 for( k = 0; k < n; k++ )
1193 line0[k] = (float)(line0[k]*s);
1195 memset( &reader, 0, sizeof(reader) );
1197 if( CV_IS_SEQ(points) )
1199 CvSeq* ptseq = (CvSeq*)points;
1200 total = ptseq->total;
1201 point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq));
1202 cvStartReadSeq( ptseq, &reader );
1206 CvMat* ptm = (CvMat*)points;
1207 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1208 total = ptm->rows + ptm->cols - 1;
1209 point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type));
1210 data = ptm->data.ptr;
1213 for( i = 0; i < total; i++ )
1220 pi = (int*)reader.ptr;
1221 pf = (float*)reader.ptr;
1222 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
1226 pi = (int*)data + i*n;
1227 pf = (float*)data + i*n;
1230 t = (float)((cvTsRandReal(rng)-0.5)*low_high_range*2);
1232 for( k = 0; k < n; k++ )
1233 p[k] = (float)((cvTsRandReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]);
1235 if( point_type == CV_32S )
1236 for( k = 0; k < n; k++ )
1237 pi[k] = cvRound(p[k]);
1239 for( k = 0; k < n; k++ )
1245 int CV_FitLineTest::prepare_test_case( int test_case_idx )
1247 CvRNG* rng = ts->get_rng();
1248 dims = cvTsRandInt(rng) % 2 + 2;
1249 min_log_size = MAX(min_log_size,5);
1250 max_log_size = MAX(min_log_size,max_log_size);
1251 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1252 dist_type = cvTsRandInt(rng) % 6 + 1;
1253 dist_type += dist_type == CV_DIST_C;
1254 reps = 0.1; aeps = 0.01;
1259 void CV_FitLineTest::run_func()
1261 cvFitLine( points, dist_type, 0, reps, aeps, line );
1265 int CV_FitLineTest::validate_test_results( int test_case_idx )
1267 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1269 double vec_diff = 0, t;
1271 for( k = 0; k < dims*2; k++ )
1273 if( cvIsNaN(line[k]) || cvIsInf(line[k]) )
1275 ts->printf( CvTS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n",
1277 code = CvTS::FAIL_INVALID_OUTPUT;
1282 if( fabs(line0[1]) > fabs(line0[0]) )
1284 if( fabs(line0[dims-1]) > fabs(line0[max_k]) )
1286 if( line0[max_k] < 0 )
1287 for( k = 0; k < dims; k++ )
1288 line0[k] = -line0[k];
1289 if( line[max_k] < 0 )
1290 for( k = 0; k < dims; k++ )
1293 for( k = 0; k < dims; k++ )
1295 double dt = line[k] - line0[k];
1299 if( sqrt(vec_diff) > 0.05 )
1302 ts->printf( CvTS::LOG,
1303 "The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n",
1304 line[0], line[1], line0[0], line0[1] );
1306 ts->printf( CvTS::LOG,
1307 "The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n",
1308 line[0], line[1], line[2], line0[0], line0[1], line0[2] );
1309 code = CvTS::FAIL_BAD_ACCURACY;
1313 t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k];
1314 for( k = 0; k < dims; k++ )
1316 double p = line0[k+dims] + t*line0[k] - line[k+dims];
1320 if( sqrt(vec_diff) > 1*MAX(fabs(t),1) )
1323 ts->printf( CvTS::LOG,
1324 "The computed line point (%.2f,%.2f) is too far from the actual line\n",
1325 line[2]+line0[2], line[3]+line0[3] );
1327 ts->printf( CvTS::LOG,
1328 "The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n",
1329 line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] );
1330 code = CvTS::FAIL_BAD_ACCURACY;
1338 ts->set_failed_test_info( code );
1344 CV_FitLineTest fit_line_test;
1347 /****************************************************************************************\
1348 * ContourMoments Test *
1349 \****************************************************************************************/
1353 cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes,
1354 double max_r_scale, double angle, CvArr* points, CvRNG* rng )
1356 int i, total, point_type;
1359 memset( &reader, 0, sizeof(reader) );
1361 if( CV_IS_SEQ(points) )
1363 CvSeq* ptseq = (CvSeq*)points;
1364 total = ptseq->total;
1365 point_type = CV_SEQ_ELTYPE(ptseq);
1366 cvStartReadSeq( ptseq, &reader );
1370 CvMat* ptm = (CvMat*)points;
1371 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1372 total = ptm->rows + ptm->cols - 1;
1373 point_type = CV_MAT_TYPE(ptm->type);
1374 data = ptm->data.ptr;
1377 assert( point_type == CV_32SC2 || point_type == CV_32FC2 );
1379 for( i = 0; i < total; i++ )
1384 double phi0 = 2*CV_PI*i/total;
1385 double phi = CV_PI*angle/180.;
1386 double t = cvTsRandReal(rng)*max_r_scale + (1 - max_r_scale);
1387 double ta = axes.height*t;
1388 double tb = axes.width*t;
1389 double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb;
1390 double c = cos(phi), s = sin(phi);
1391 p.x = (float)(c0*c - s0*s + center.x);
1392 p.y = (float)(c0*s + s0*c + center.y);
1396 pp = (CvPoint*)reader.ptr;
1397 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1400 pp = ((CvPoint*)data) + i;
1402 if( point_type == CV_32SC2 )
1404 pp->x = cvRound(p.x);
1405 pp->y = cvRound(p.y);
1408 *(CvPoint2D32f*)pp = p;
1413 class CV_ContourMomentsTest : public CV_BaseShapeDescrTest
1416 CV_ContourMomentsTest();
1419 int prepare_test_case( int test_case_idx );
1420 void generate_point_set( void* points );
1421 void run_func(void);
1422 int validate_test_results( int test_case_idx );
1423 CvMoments moments0, moments;
1426 CvPoint2D32f center;
1427 int max_max_r_scale;
1428 double max_r_scale, angle;
1433 CV_ContourMomentsTest::CV_ContourMomentsTest():
1434 CV_BaseShapeDescrTest( "shape-moments", "cvMoments, cvContourArea" )
1438 max_max_r_scale = 25;
1439 low_high_range = 200;
1440 enable_flt_points = false;
1444 void CV_ContourMomentsTest::generate_point_set( void* points )
1446 CvRNG* rng = ts->get_rng();
1449 axes.width = (float)((cvTsRandReal(rng)*0.9 + 0.1)*low_high_range);
1450 axes.height = (float)((cvTsRandReal(rng)*0.9 + 0.1)*low_high_range);
1451 max_sz = MAX(axes.width, axes.height);
1453 img_size.width = img_size.height = cvRound(low_high_range*2.2);
1455 center.x = (float)(img_size.width*0.5 + (cvTsRandReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8);
1456 center.y = (float)(img_size.height*0.5 + (cvTsRandReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8);
1458 assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width &&
1459 0 < center.y - max_sz && center.y + max_sz < img_size.height );
1461 max_r_scale = cvTsRandReal(rng)*max_max_r_scale*0.01;
1462 angle = cvTsRandReal(rng)*360;
1464 cvTsGenerateTousledBlob( center, axes, max_r_scale, angle, points, rng );
1467 points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON;
1471 int CV_ContourMomentsTest::prepare_test_case( int test_case_idx )
1473 min_log_size = MAX(min_log_size,3);
1474 max_log_size = MIN(max_log_size,8);
1475 max_log_size = MAX(min_log_size,max_log_size);
1476 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1481 void CV_ContourMomentsTest::run_func()
1483 cvMoments( points, &moments );
1484 area = cvContourArea( points );
1488 int CV_ContourMomentsTest::validate_test_results( int test_case_idx )
1490 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1491 int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00));
1492 CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 );
1493 CvPoint* pt = (CvPoint*)points2->data.i;
1494 int count = points2->cols + points2->rows - 1;
1498 cvFillPoly( img, &pt, &count, 1, cvScalarAll(1));
1499 cvMoments( img, &moments0 );
1501 for( i = 0; i < n; i++ )
1503 double t = fabs((&moments0.m00)[i]);
1504 max_v0 = MAX(max_v0, t);
1507 for( i = 0; i <= n; i++ )
1509 double v = i < n ? (&moments.m00)[i] : area;
1510 double v0 = i < n ? (&moments0.m00)[i] : moments0.m00;
1512 if( cvIsNaN(v) || cvIsInf(v) )
1514 ts->printf( CvTS::LOG,
1515 "The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v );
1516 code = CvTS::FAIL_INVALID_OUTPUT;
1520 if( fabs(v - v0) > 0.1*max_v0 )
1522 ts->printf( CvTS::LOG,
1523 "The computed contour %s is %g, while it should be %g\n",
1524 i < n ? "moment" : "area", v, v0 );
1525 code = CvTS::FAIL_BAD_ACCURACY;
1533 cvCmpS( img, 0, img, CV_CMP_GT );
1534 cvNamedWindow( "test", 1 );
1535 cvShowImage( "test", img );
1538 ts->set_failed_test_info( code );
1541 cvReleaseMat( &img );
1546 CV_ContourMomentsTest contour_moments_test;