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 namespace opencv_test { namespace {
45
46 /*static int
47 cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n )
48 {
49 CvPoint2D32f v0 = v[n-1];
50 int i, sign = 0;
51
52 for( i = 0; i < n; i++ )
53 {
54 CvPoint2D32f v1 = v[i];
55 float dx = pt.x - v0.x, dy = pt.y - v0.y;
56 float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y;
57 double t = (double)dx*dy1 - (double)dx1*dy;
58 if( fabs(t) > DBL_EPSILON )
59 {
60 if( t*sign < 0 )
61 break;
62 if( sign == 0 )
63 sign = t < 0 ? -1 : 1;
64 }
65 else if( fabs(dx) + fabs(dy) < DBL_EPSILON )
66 return i+1;
67 v0 = v1;
68 }
69
70 return i < n ? -1 : 0;
71 }*/
72
73 CV_INLINE double
cvTsDist(CvPoint2D32f a,CvPoint2D32f b)74 cvTsDist( CvPoint2D32f a, CvPoint2D32f b )
75 {
76 double dx = a.x - b.x;
77 double dy = a.y - b.y;
78 return sqrt(dx*dx + dy*dy);
79 }
80 CV_INLINE double
cvTsDist(const Point2f & a,const Point2f & b)81 cvTsDist( const Point2f& a, const Point2f& b )
82 {
83 double dx = a.x - b.x;
84 double dy = a.y - b.y;
85 return sqrt(dx*dx + dy*dy);
86 }
87
88 CV_INLINE double
cvTsPtLineDist(CvPoint2D32f pt,CvPoint2D32f a,CvPoint2D32f b)89 cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b )
90 {
91 double d0 = cvTsDist( pt, a ), d1;
92 double dd = cvTsDist( a, b );
93 if( dd < FLT_EPSILON )
94 return d0;
95 d1 = cvTsDist( pt, b );
96 dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd;
97 d0 = MIN( d0, d1 );
98 return MIN( d0, dd );
99 }
100
101 static double
cvTsPointPolygonTest(CvPoint2D32f pt,const CvPoint2D32f * vv,int n,int * _idx=0,int * _on_edge=0)102 cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 )
103 {
104 int i;
105 Point2f v = vv[n-1], v0;
106 double min_dist_num = FLT_MAX, min_dist_denom = 1;
107 int min_dist_idx = -1, min_on_edge = 0;
108 int counter = 0;
109 double result;
110
111 for( i = 0; i < n; i++ )
112 {
113 double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1;
114 int on_edge = 0, idx = i;
115
116 v0 = v; v = vv[i];
117 dx = v.x - v0.x; dy = v.y - v0.y;
118 dx1 = pt.x - v0.x; dy1 = pt.y - v0.y;
119 dx2 = pt.x - v.x; dy2 = pt.y - v.y;
120
121 if( dx2*dx + dy2*dy >= 0 )
122 dist_num = dx2*dx2 + dy2*dy2;
123 else if( dx1*dx + dy1*dy <= 0 )
124 {
125 dist_num = dx1*dx1 + dy1*dy1;
126 idx = i - 1;
127 if( idx < 0 ) idx = n-1;
128 }
129 else
130 {
131 dist_num = (dy1*dx - dx1*dy);
132 dist_num *= dist_num;
133 dist_denom = dx*dx + dy*dy;
134 on_edge = 1;
135 }
136
137 if( dist_num*min_dist_denom < min_dist_num*dist_denom )
138 {
139 min_dist_num = dist_num;
140 min_dist_denom = dist_denom;
141 min_dist_idx = idx;
142 min_on_edge = on_edge;
143 if( min_dist_num == 0 )
144 break;
145 }
146
147 if( (v0.y <= pt.y && v.y <= pt.y) ||
148 (v0.y > pt.y && v.y > pt.y) ||
149 (v0.x < pt.x && v.x < pt.x) )
150 continue;
151
152 dist_num = dy1*dx - dx1*dy;
153 if( dy < 0 )
154 dist_num = -dist_num;
155 counter += dist_num > 0;
156 }
157
158 result = sqrt(min_dist_num/min_dist_denom);
159 if( counter % 2 == 0 )
160 result = -result;
161
162 if( _idx )
163 *_idx = min_dist_idx;
164 if( _on_edge )
165 *_on_edge = min_on_edge;
166
167 return result;
168 }
169
170 static cv::Point2f
cvTsMiddlePoint(const cv::Point2f & a,const cv::Point2f & b)171 cvTsMiddlePoint(const cv::Point2f &a, const cv::Point2f &b)
172 {
173 return cv::Point2f((a.x + b.x) / 2, (a.y + b.y) / 2);
174 }
175
176 static bool
cvTsIsPointOnLineSegment(const cv::Point2f & x,const cv::Point2f & a,const cv::Point2f & b)177 cvTsIsPointOnLineSegment(const cv::Point2f &x, const cv::Point2f &a, const cv::Point2f &b)
178 {
179 double d1 = cvTsDist(cvPoint2D32f(x.x, x.y), cvPoint2D32f(a.x, a.y));
180 double d2 = cvTsDist(cvPoint2D32f(x.x, x.y), cvPoint2D32f(b.x, b.y));
181 double d3 = cvTsDist(cvPoint2D32f(a.x, a.y), cvPoint2D32f(b.x, b.y));
182
183 return (abs(d1 + d2 - d3) <= (1E-5));
184 }
185
186
187 /****************************************************************************************\
188 * Base class for shape descriptor tests *
189 \****************************************************************************************/
190
191 class CV_BaseShapeDescrTest : public cvtest::BaseTest
192 {
193 public:
194 CV_BaseShapeDescrTest();
195 virtual ~CV_BaseShapeDescrTest();
196 void clear();
197
198 protected:
199 int read_params( const cv::FileStorage& fs );
200 void run_func(void);
201 int prepare_test_case( int test_case_idx );
202 int validate_test_results( int test_case_idx );
203 virtual void generate_point_set( void* points );
204 virtual void extract_points();
205
206 int min_log_size;
207 int max_log_size;
208 int dims;
209 bool enable_flt_points;
210
211 CvMemStorage* storage;
212 CvSeq* points1;
213 CvMat* points2;
214 void* points;
215 void* result;
216 double low_high_range;
217 Scalar low, high;
218
219 bool test_cpp;
220 };
221
222
CV_BaseShapeDescrTest()223 CV_BaseShapeDescrTest::CV_BaseShapeDescrTest()
224 {
225 points1 = 0;
226 points2 = 0;
227 points = 0;
228 storage = 0;
229 test_case_count = 500;
230 min_log_size = 0;
231 max_log_size = 10;
232 low = high = cvScalarAll(0);
233 low_high_range = 50;
234 dims = 2;
235 enable_flt_points = true;
236
237 test_cpp = false;
238 }
239
240
~CV_BaseShapeDescrTest()241 CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest()
242 {
243 clear();
244 }
245
246
clear()247 void CV_BaseShapeDescrTest::clear()
248 {
249 cvtest::BaseTest::clear();
250 cvReleaseMemStorage( &storage );
251 cvReleaseMat( &points2 );
252 points1 = 0;
253 points = 0;
254 }
255
256
read_params(const cv::FileStorage & fs)257 int CV_BaseShapeDescrTest::read_params( const cv::FileStorage& fs )
258 {
259 int code = cvtest::BaseTest::read_params( fs );
260 if( code < 0 )
261 return code;
262
263 read( find_param( fs, "struct_count" ), test_case_count, test_case_count );
264 read( find_param( fs, "min_log_size" ), min_log_size, min_log_size );
265 read( find_param( fs, "max_log_size" ), max_log_size, max_log_size );
266
267 min_log_size = cvtest::clipInt( min_log_size, 0, 8 );
268 max_log_size = cvtest::clipInt( max_log_size, 0, 10 );
269 if( min_log_size > max_log_size )
270 {
271 int t;
272 CV_SWAP( min_log_size, max_log_size, t );
273 }
274
275 return 0;
276 }
277
278
generate_point_set(void * pointsSet)279 void CV_BaseShapeDescrTest::generate_point_set( void* pointsSet )
280 {
281 RNG& rng = ts->get_rng();
282 int i, k, n, total, point_type;
283 CvSeqReader reader;
284 uchar* data = 0;
285 double a[4], b[4];
286
287 for( k = 0; k < 4; k++ )
288 {
289 a[k] = high.val[k] - low.val[k];
290 b[k] = low.val[k];
291 }
292 memset( &reader, 0, sizeof(reader) );
293
294 if( CV_IS_SEQ(pointsSet) )
295 {
296 CvSeq* ptseq = (CvSeq*)pointsSet;
297 total = ptseq->total;
298 point_type = CV_SEQ_ELTYPE(ptseq);
299 cvStartReadSeq( ptseq, &reader );
300 }
301 else
302 {
303 CvMat* ptm = (CvMat*)pointsSet;
304 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
305 total = ptm->rows + ptm->cols - 1;
306 point_type = CV_MAT_TYPE(ptm->type);
307 data = ptm->data.ptr;
308 }
309
310 n = CV_MAT_CN(point_type);
311 point_type = CV_MAT_DEPTH(point_type);
312
313 assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 );
314
315 for( i = 0; i < total; i++ )
316 {
317 int* pi;
318 float* pf;
319 if( reader.ptr )
320 {
321 pi = (int*)reader.ptr;
322 pf = (float*)reader.ptr;
323 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
324 }
325 else
326 {
327 pi = (int*)data + i*n;
328 pf = (float*)data + i*n;
329 }
330 if( point_type == CV_32S )
331 for( k = 0; k < n; k++ )
332 pi[k] = cvRound(cvtest::randReal(rng)*a[k] + b[k]);
333 else
334 for( k = 0; k < n; k++ )
335 pf[k] = (float)(cvtest::randReal(rng)*a[k] + b[k]);
336 }
337 }
338
339
prepare_test_case(int test_case_idx)340 int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx )
341 {
342 int size;
343 int use_storage = 0;
344 int point_type;
345 int i;
346 RNG& rng = ts->get_rng();
347
348 cvtest::BaseTest::prepare_test_case( test_case_idx );
349
350 clear();
351 size = cvRound( exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) );
352 use_storage = cvtest::randInt(rng) % 2;
353 point_type = CV_MAKETYPE(cvtest::randInt(rng) %
354 (enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims);
355
356 if( use_storage )
357 {
358 storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 );
359 points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage );
360 cvSeqPushMulti( points1, 0, size );
361 points = points1;
362 }
363 else
364 {
365 int rows = 1, cols = size;
366 if( cvtest::randInt(rng) % 2 )
367 rows = size, cols = 1;
368
369 points2 = cvCreateMat( rows, cols, point_type );
370 points = points2;
371 }
372
373 for( i = 0; i < 4; i++ )
374 {
375 low.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2;
376 high.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2;
377 if( low.val[i] > high.val[i] )
378 {
379 double t;
380 CV_SWAP( low.val[i], high.val[i], t );
381 }
382 if( high.val[i] < low.val[i] + 1 )
383 high.val[i] += 1;
384 }
385
386 generate_point_set( points );
387
388 test_cpp = (cvtest::randInt(rng) & 16) == 0;
389 return 1;
390 }
391
392
extract_points()393 void CV_BaseShapeDescrTest::extract_points()
394 {
395 if( points1 )
396 {
397 points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) );
398 cvCvtSeqToArray( points1, points2->data.ptr );
399 }
400
401 if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points )
402 {
403 CvMat tmp = cvMat( points2->rows, points2->cols,
404 (points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr );
405 cvConvert( points2, &tmp );
406 }
407 }
408
409
run_func(void)410 void CV_BaseShapeDescrTest::run_func(void)
411 {
412 }
413
414
validate_test_results(int)415 int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ )
416 {
417 extract_points();
418 return 0;
419 }
420
421
422 /****************************************************************************************\
423 * Convex Hull Test *
424 \****************************************************************************************/
425
426 class CV_ConvHullTest : public CV_BaseShapeDescrTest
427 {
428 public:
429 CV_ConvHullTest();
430 virtual ~CV_ConvHullTest();
431 void clear();
432
433 protected:
434 void run_func(void);
435 int prepare_test_case( int test_case_idx );
436 int validate_test_results( int test_case_idx );
437
438 CvSeq* hull1;
439 CvMat* hull2;
440 void* hull_storage;
441 int orientation;
442 int return_points;
443 };
444
445
CV_ConvHullTest()446 CV_ConvHullTest::CV_ConvHullTest()
447 {
448 hull1 = 0;
449 hull2 = 0;
450 hull_storage = 0;
451 orientation = return_points = 0;
452 }
453
454
~CV_ConvHullTest()455 CV_ConvHullTest::~CV_ConvHullTest()
456 {
457 clear();
458 }
459
460
clear()461 void CV_ConvHullTest::clear()
462 {
463 CV_BaseShapeDescrTest::clear();
464 cvReleaseMat( &hull2 );
465 hull1 = 0;
466 hull_storage = 0;
467 }
468
469
prepare_test_case(int test_case_idx)470 int CV_ConvHullTest::prepare_test_case( int test_case_idx )
471 {
472 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
473 int use_storage_for_hull = 0;
474 RNG& rng = ts->get_rng();
475
476 if( code <= 0 )
477 return code;
478
479 orientation = cvtest::randInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE;
480 return_points = cvtest::randInt(rng) % 2;
481
482 use_storage_for_hull = (cvtest::randInt(rng) % 2) && !test_cpp;
483 if( use_storage_for_hull )
484 {
485 if( !storage )
486 storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 );
487 hull_storage = storage;
488 }
489 else
490 {
491 int rows, cols;
492 int sz = points1 ? points1->total : points2->cols + points2->rows - 1;
493 int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type);
494
495 if( cvtest::randInt(rng) % 2 )
496 rows = sz, cols = 1;
497 else
498 rows = 1, cols = sz;
499
500 hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 );
501 hull_storage = hull2;
502 }
503
504 return code;
505 }
506
507
run_func()508 void CV_ConvHullTest::run_func()
509 {
510 if(!test_cpp)
511 hull1 = cvConvexHull2( points, hull_storage, orientation, return_points );
512 else
513 {
514 cv::Mat _points = cv::cvarrToMat(points);
515 bool clockwise = orientation == CV_CLOCKWISE;
516 size_t n = 0;
517 if( !return_points )
518 {
519 std::vector<int> _hull;
520 cv::convexHull(_points, _hull, clockwise);
521 n = _hull.size();
522 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));
523 }
524 else if(_points.type() == CV_32SC2)
525 {
526 std::vector<cv::Point> _hull;
527 cv::convexHull(_points, _hull, clockwise);
528 n = _hull.size();
529 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));
530 }
531 else if(_points.type() == CV_32FC2)
532 {
533 std::vector<cv::Point2f> _hull;
534 cv::convexHull(_points, _hull, clockwise);
535 n = _hull.size();
536 memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));
537 }
538 if(hull2->rows > hull2->cols)
539 hull2->rows = (int)n;
540 else
541 hull2->cols = (int)n;
542 }
543 }
544
545
validate_test_results(int test_case_idx)546 int CV_ConvHullTest::validate_test_results( int test_case_idx )
547 {
548 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
549 CvMat* hull = 0;
550 CvMat* mask = 0;
551 int i, point_count, hull_count;
552 CvPoint2D32f *p, *h;
553 CvSeq header, hheader, *ptseq, *hseq;
554 CvSeqBlock block, hblock;
555
556 if( points1 )
557 ptseq = points1;
558 else
559 ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type),
560 sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr,
561 points2->rows + points2->cols - 1, &header, &block );
562 point_count = ptseq->total;
563 p = (CvPoint2D32f*)(points2->data.ptr);
564
565 if( hull1 )
566 hseq = hull1;
567 else
568 hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type),
569 sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr,
570 hull2->rows + hull2->cols - 1, &hheader, &hblock );
571 hull_count = hseq->total;
572 hull = cvCreateMat( 1, hull_count, CV_32FC2 );
573 mask = cvCreateMat( 1, hull_count, CV_8UC1 );
574 cvZero( mask );
575 Mat _mask = cvarrToMat(mask);
576
577 h = (CvPoint2D32f*)(hull->data.ptr);
578
579 // extract convex hull points
580 if( return_points )
581 {
582 cvCvtSeqToArray( hseq, hull->data.ptr );
583 if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 )
584 {
585 CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr );
586 cvConvert( &tmp, hull );
587 }
588 }
589 else
590 {
591 CvSeqReader reader;
592 cvStartReadSeq( hseq, &reader );
593
594 for( i = 0; i < hull_count; i++ )
595 {
596 schar* ptr = reader.ptr;
597 int idx;
598 CV_NEXT_SEQ_ELEM( hseq->elem_size, reader );
599
600 if( hull1 )
601 idx = cvSeqElemIdx( ptseq, *(uchar**)ptr );
602 else
603 idx = *(int*)ptr;
604
605 if( idx < 0 || idx >= point_count )
606 {
607 ts->printf( cvtest::TS::LOG, "Invalid convex hull point #%d\n", i );
608 code = cvtest::TS::FAIL_INVALID_OUTPUT;
609 goto _exit_;
610 }
611 h[i] = p[idx];
612 }
613 }
614
615 // check that the convex hull is a convex polygon
616 if( hull_count >= 3 )
617 {
618 CvPoint2D32f pt0 = h[hull_count-1];
619 for( i = 0; i < hull_count; i++ )
620 {
621 int j = i+1;
622 CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0];
623 float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y;
624 float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y;
625 double t = (double)dx0*dy1 - (double)dx1*dy0;
626 if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) )
627 {
628 ts->printf( cvtest::TS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i );
629 code = cvtest::TS::FAIL_INVALID_OUTPUT;
630 goto _exit_;
631 }
632 pt0 = pt1;
633 }
634 }
635
636 // check that all the points are inside the hull or on the hull edge
637 // and at least hull_point points are at the hull vertices
638 for( i = 0; i < point_count; i++ )
639 {
640 int idx = 0, on_edge = 0;
641 double pptresult = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge );
642
643 if( pptresult < 0 )
644 {
645 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the convex hull\n", i );
646 code = cvtest::TS::FAIL_BAD_ACCURACY;
647 goto _exit_;
648 }
649
650 if( pptresult < FLT_EPSILON && !on_edge )
651 mask->data.ptr[idx] = (uchar)1;
652 }
653
654 if( cvtest::norm( _mask, Mat::zeros(_mask.dims, _mask.size, _mask.type()), NORM_L1 ) != hull_count )
655 {
656 ts->printf( cvtest::TS::LOG, "Not every convex hull vertex coincides with some input point\n" );
657 code = cvtest::TS::FAIL_BAD_ACCURACY;
658 goto _exit_;
659 }
660
661 _exit_:
662
663 cvReleaseMat( &hull );
664 cvReleaseMat( &mask );
665 if( code < 0 )
666 ts->set_failed_test_info( code );
667 return code;
668 }
669
670
671 /****************************************************************************************\
672 * MinAreaRect Test *
673 \****************************************************************************************/
674
675 class CV_MinAreaRectTest : public CV_BaseShapeDescrTest
676 {
677 public:
678 CV_MinAreaRectTest();
679
680 protected:
681 void run_func(void);
682 int validate_test_results( int test_case_idx );
683
684 CvBox2D box;
685 CvPoint2D32f box_pt[4];
686 };
687
688
CV_MinAreaRectTest()689 CV_MinAreaRectTest::CV_MinAreaRectTest()
690 {
691 }
692
693
run_func()694 void CV_MinAreaRectTest::run_func()
695 {
696 if(!test_cpp)
697 {
698 box = cvMinAreaRect2( points, storage );
699 cvBoxPoints( box, box_pt );
700 }
701 else
702 {
703 cv::RotatedRect r = cv::minAreaRect(cv::cvarrToMat(points));
704 box = cvBox2D(r);
705 r.points((cv::Point2f*)box_pt);
706 }
707 }
708
709
validate_test_results(int test_case_idx)710 int CV_MinAreaRectTest::validate_test_results( int test_case_idx )
711 {
712 double eps = 1e-1;
713 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
714 int i, j, point_count = points2->rows + points2->cols - 1;
715 CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr);
716 int mask[] = {0,0,0,0};
717
718 // check that the bounding box is a rotated rectangle:
719 // 1. diagonals should be equal
720 // 2. they must intersect in their middle points
721 {
722 double d0 = cvTsDist( box_pt[0], box_pt[2] );
723 double d1 = cvTsDist( box_pt[1], box_pt[3] );
724
725 double x0 = (box_pt[0].x + box_pt[2].x)*0.5;
726 double y0 = (box_pt[0].y + box_pt[2].y)*0.5;
727 double x1 = (box_pt[1].x + box_pt[3].x)*0.5;
728 double y1 = (box_pt[1].y + box_pt[3].y)*0.5;
729
730 if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) )
731 {
732 ts->printf( cvtest::TS::LOG, "The bounding box is not a rectangle\n" );
733 code = cvtest::TS::FAIL_INVALID_OUTPUT;
734 goto _exit_;
735 }
736 }
737
738 #if 0
739 {
740 int n = 4;
741 double a = 8, c = 8, b = 100, d = 150;
742 CvPoint bp[4], *bpp = bp;
743 cvNamedWindow( "test", 1 );
744 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
745 cvZero(img);
746 for( i = 0; i < point_count; i++ )
747 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 );
748 for( i = 0; i < n; i++ )
749 bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d));
750 cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 );
751 cvShowImage( "test", img );
752 cvWaitKey();
753 cvReleaseImage(&img);
754 }
755 #endif
756
757 // check that the box includes all the points
758 // and there is at least one point at (or very close to) every box side
759 for( i = 0; i < point_count; i++ )
760 {
761 int idx = 0, on_edge = 0;
762 double pptresult = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge );
763 if( pptresult < -eps )
764 {
765 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the box\n", i );
766 code = cvtest::TS::FAIL_BAD_ACCURACY;
767 goto _exit_;
768 }
769
770 if( pptresult < eps )
771 {
772 for( j = 0; j < 4; j++ )
773 {
774 double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] );
775 if( d < eps )
776 mask[j] = (uchar)1;
777 }
778 }
779 }
780
781 if( mask[0] + mask[1] + mask[2] + mask[3] != 4 )
782 {
783 ts->printf( cvtest::TS::LOG, "Not every box side has a point nearby\n" );
784 code = cvtest::TS::FAIL_BAD_ACCURACY;
785 goto _exit_;
786 }
787
788 _exit_:
789
790 if( code < 0 )
791 ts->set_failed_test_info( code );
792 return code;
793 }
794
795
796 /****************************************************************************************\
797 * MinEnclosingTriangle Test *
798 \****************************************************************************************/
799
800 class CV_MinTriangleTest : public CV_BaseShapeDescrTest
801 {
802 public:
803 CV_MinTriangleTest();
804
805 protected:
806 void run_func(void);
807 int validate_test_results( int test_case_idx );
808 std::vector<cv::Point2f> getTriangleMiddlePoints();
809
810 std::vector<cv::Point2f> convexPolygon;
811 std::vector<cv::Point2f> triangle;
812 };
813
814
CV_MinTriangleTest()815 CV_MinTriangleTest::CV_MinTriangleTest()
816 {
817 }
818
getTriangleMiddlePoints()819 std::vector<cv::Point2f> CV_MinTriangleTest::getTriangleMiddlePoints()
820 {
821 std::vector<cv::Point2f> triangleMiddlePoints;
822
823 for (int i = 0; i < 3; i++) {
824 triangleMiddlePoints.push_back(cvTsMiddlePoint(triangle[i], triangle[(i + 1) % 3]));
825 }
826
827 return triangleMiddlePoints;
828 }
829
830
run_func()831 void CV_MinTriangleTest::run_func()
832 {
833 std::vector<cv::Point2f> pointsAsVector;
834
835 cv::cvarrToMat(points).convertTo(pointsAsVector, CV_32F);
836
837 cv::minEnclosingTriangle(pointsAsVector, triangle);
838 cv::convexHull(pointsAsVector, convexPolygon, true, true);
839 }
840
841
validate_test_results(int test_case_idx)842 int CV_MinTriangleTest::validate_test_results( int test_case_idx )
843 {
844 bool errorEnclosed = false, errorMiddlePoints = false, errorFlush = true;
845 double eps = 1e-4;
846 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
847
848 #if 0
849 {
850 int n = 3;
851 double a = 8, c = 8, b = 100, d = 150;
852 CvPoint bp[4], *bpp = bp;
853 cvNamedWindow( "test", 1 );
854 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
855 cvZero(img);
856 for( i = 0; i < point_count; i++ )
857 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 );
858 for( i = 0; i < n; i++ )
859 bp[i] = cvPoint(cvRound(triangle[i].x*a+b),cvRound(triangle[i].y*c+d));
860 cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 );
861 cvShowImage( "test", img );
862 cvWaitKey();
863 cvReleaseImage(&img);
864 }
865 #endif
866
867 int polygonVertices = (int) convexPolygon.size();
868
869 if (polygonVertices > 2) {
870 // Check if all points are enclosed by the triangle
871 for (int i = 0; (i < polygonVertices) && (!errorEnclosed); i++)
872 {
873 if (cv::pointPolygonTest(triangle, cv::Point2f(convexPolygon[i].x, convexPolygon[i].y), true) < (-eps))
874 errorEnclosed = true;
875 }
876
877 // Check if triangle edges middle points touch the polygon
878 std::vector<cv::Point2f> middlePoints = getTriangleMiddlePoints();
879
880 for (int i = 0; (i < 3) && (!errorMiddlePoints); i++)
881 {
882 bool isTouching = false;
883
884 for (int j = 0; (j < polygonVertices) && (!isTouching); j++)
885 {
886 if (cvTsIsPointOnLineSegment(middlePoints[i], convexPolygon[j],
887 convexPolygon[(j + 1) % polygonVertices]))
888 isTouching = true;
889 }
890
891 errorMiddlePoints = (isTouching) ? false : true;
892 }
893
894 // Check if at least one of the edges is flush
895 for (int i = 0; (i < 3) && (errorFlush); i++)
896 {
897 for (int j = 0; (j < polygonVertices) && (errorFlush); j++)
898 {
899 if ((cvTsIsPointOnLineSegment(convexPolygon[j], triangle[i],
900 triangle[(i + 1) % 3])) &&
901 (cvTsIsPointOnLineSegment(convexPolygon[(j + 1) % polygonVertices], triangle[i],
902 triangle[(i + 1) % 3])))
903 errorFlush = false;
904 }
905 }
906
907 // Report any found errors
908 if (errorEnclosed)
909 {
910 ts->printf( cvtest::TS::LOG,
911 "All points should be enclosed by the triangle.\n" );
912 code = cvtest::TS::FAIL_BAD_ACCURACY;
913 }
914 else if (errorMiddlePoints)
915 {
916 ts->printf( cvtest::TS::LOG,
917 "All triangle edges middle points should touch the convex hull of the points.\n" );
918 code = cvtest::TS::FAIL_INVALID_OUTPUT;
919 }
920 else if (errorFlush)
921 {
922 ts->printf( cvtest::TS::LOG,
923 "At least one edge of the enclosing triangle should be flush with one edge of the polygon.\n" );
924 code = cvtest::TS::FAIL_INVALID_OUTPUT;
925 }
926 }
927
928 if ( code < 0 )
929 ts->set_failed_test_info( code );
930
931 return code;
932 }
933
934
935 /****************************************************************************************\
936 * MinEnclosingCircle Test *
937 \****************************************************************************************/
938
939 class CV_MinCircleTest : public CV_BaseShapeDescrTest
940 {
941 public:
942 CV_MinCircleTest();
943
944 protected:
945 void run_func(void);
946 int validate_test_results( int test_case_idx );
947
948 Point2f center;
949 float radius;
950 };
951
952
CV_MinCircleTest()953 CV_MinCircleTest::CV_MinCircleTest()
954 {
955 }
956
957
run_func()958 void CV_MinCircleTest::run_func()
959 {
960 if(!test_cpp)
961 {
962 CvPoint2D32f c_center = cvPoint2D32f(center);
963 cvMinEnclosingCircle( points, &c_center, &radius );
964 center = c_center;
965 }
966 else
967 {
968 cv::Point2f tmpcenter;
969 cv::minEnclosingCircle(cv::cvarrToMat(points), tmpcenter, radius);
970 center = tmpcenter;
971 }
972 }
973
974
validate_test_results(int test_case_idx)975 int CV_MinCircleTest::validate_test_results( int test_case_idx )
976 {
977 double eps = 1.03;
978 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
979 int i, j = 0, point_count = points2->rows + points2->cols - 1;
980 Point2f *p = (Point2f*)(points2->data.ptr);
981 Point2f v[3];
982
983 #if 0
984 {
985 double a = 2, b = 200, d = 400;
986 cvNamedWindow( "test", 1 );
987 IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
988 cvZero(img);
989 for( i = 0; i < point_count; i++ )
990 cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 );
991 cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)),
992 cvRound(radius*a), CV_RGB(255,255,0), 1 );
993 cvShowImage( "test", img );
994 cvWaitKey();
995 cvReleaseImage(&img);
996 }
997 #endif
998
999 // check that the circle contains all the points inside and
1000 // remember at most 3 points that are close to the boundary
1001 for( i = 0; i < point_count; i++ )
1002 {
1003 double d = cvTsDist(p[i], center);
1004 if( d > radius )
1005 {
1006 ts->printf( cvtest::TS::LOG, "The point #%d is outside of the circle\n", i );
1007 code = cvtest::TS::FAIL_BAD_ACCURACY;
1008 goto _exit_;
1009 }
1010
1011 if( radius - d < eps*radius && j < 3 )
1012 v[j++] = p[i];
1013 }
1014
1015 if( point_count >= 2 && (j < 2 || (j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps)) )
1016 {
1017 ts->printf( cvtest::TS::LOG,
1018 "There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" );
1019 code = cvtest::TS::FAIL_BAD_ACCURACY;
1020 goto _exit_;
1021 }
1022
1023 _exit_:
1024
1025 if( code < 0 )
1026 ts->set_failed_test_info( code );
1027 return code;
1028 }
1029
1030 /****************************************************************************************\
1031 * MinEnclosingCircle Test 2 *
1032 \****************************************************************************************/
1033
1034 class CV_MinCircleTest2 : public CV_BaseShapeDescrTest
1035 {
1036 public:
1037 CV_MinCircleTest2();
1038 protected:
1039 RNG rng;
1040 void run_func(void);
1041 int validate_test_results( int test_case_idx );
1042 float delta;
1043 };
1044
1045
CV_MinCircleTest2()1046 CV_MinCircleTest2::CV_MinCircleTest2()
1047 {
1048 rng = ts->get_rng();
1049 }
1050
1051
run_func()1052 void CV_MinCircleTest2::run_func()
1053 {
1054 Point2f center = Point2f(rng.uniform(0.0f, 1000.0f), rng.uniform(0.0f, 1000.0f));;
1055 float radius = rng.uniform(0.0f, 500.0f);
1056 float angle = (float)rng.uniform(0.0f, (float)(CV_2PI));
1057 vector<Point2f> pts;
1058 pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));
1059 angle += (float)CV_PI;
1060 pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));
1061 float radius2 = radius * radius;
1062 float x = rng.uniform(center.x - radius, center.x + radius);
1063 float deltaX = x - center.x;
1064 float upperBoundY = sqrt(radius2 - deltaX * deltaX);
1065 float y = rng.uniform(center.y - upperBoundY, center.y + upperBoundY);
1066 pts.push_back(Point2f(x, y));
1067 // Find the minimum area enclosing circle
1068 Point2f calcCenter;
1069 float calcRadius;
1070 minEnclosingCircle(pts, calcCenter, calcRadius);
1071 delta = (float)cv::norm(calcCenter - center) + abs(calcRadius - radius);
1072 }
1073
validate_test_results(int test_case_idx)1074 int CV_MinCircleTest2::validate_test_results( int test_case_idx )
1075 {
1076 float eps = 1.0F;
1077 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1078 if (delta > eps)
1079 {
1080 ts->printf( cvtest::TS::LOG, "Delta center and calcCenter > %f\n", eps );
1081 code = cvtest::TS::FAIL_BAD_ACCURACY;
1082 ts->set_failed_test_info( code );
1083 }
1084 return code;
1085 }
1086
1087 /****************************************************************************************\
1088 * minEnclosingCircle Test 3 *
1089 \****************************************************************************************/
1090
TEST(Imgproc_minEnclosingCircle,basic_test)1091 TEST(Imgproc_minEnclosingCircle, basic_test)
1092 {
1093 vector<Point2f> pts;
1094 pts.push_back(Point2f(0, 0));
1095 pts.push_back(Point2f(10, 0));
1096 pts.push_back(Point2f(5, 1));
1097 const float EPS = 1.0e-3f;
1098 Point2f center;
1099 float radius;
1100
1101 // pts[2] is within the circle with diameter pts[0] - pts[1].
1102 // 2
1103 // 0 1
1104 // NB: The triangle is obtuse, so the only pts[0] and pts[1] are on the circle.
1105 minEnclosingCircle(pts, center, radius);
1106 EXPECT_NEAR(center.x, 5, EPS);
1107 EXPECT_NEAR(center.y, 0, EPS);
1108 EXPECT_NEAR(5, radius, EPS);
1109
1110 // pts[2] is on the circle with diameter pts[0] - pts[1].
1111 // 2
1112 // 0 1
1113 pts[2] = Point2f(5, 5);
1114 minEnclosingCircle(pts, center, radius);
1115 EXPECT_NEAR(center.x, 5, EPS);
1116 EXPECT_NEAR(center.y, 0, EPS);
1117 EXPECT_NEAR(5, radius, EPS);
1118
1119 // pts[2] is outside the circle with diameter pts[0] - pts[1].
1120 // 2
1121 //
1122 //
1123 // 0 1
1124 // NB: The triangle is acute, so all 3 points are on the circle.
1125 pts[2] = Point2f(5, 10);
1126 minEnclosingCircle(pts, center, radius);
1127 EXPECT_NEAR(center.x, 5, EPS);
1128 EXPECT_NEAR(center.y, 3.75, EPS);
1129 EXPECT_NEAR(6.25f, radius, EPS);
1130
1131 // The 3 points are colinear.
1132 pts[2] = Point2f(3, 0);
1133 minEnclosingCircle(pts, center, radius);
1134 EXPECT_NEAR(center.x, 5, EPS);
1135 EXPECT_NEAR(center.y, 0, EPS);
1136 EXPECT_NEAR(5, radius, EPS);
1137
1138 // 2 points are the same.
1139 pts[2] = pts[1];
1140 minEnclosingCircle(pts, center, radius);
1141 EXPECT_NEAR(center.x, 5, EPS);
1142 EXPECT_NEAR(center.y, 0, EPS);
1143 EXPECT_NEAR(5, radius, EPS);
1144
1145 // 3 points are the same.
1146 pts[0] = pts[1];
1147 minEnclosingCircle(pts, center, radius);
1148 EXPECT_NEAR(center.x, 10, EPS);
1149 EXPECT_NEAR(center.y, 0, EPS);
1150 EXPECT_NEAR(0, radius, EPS);
1151 }
1152
TEST(Imgproc_minEnclosingCircle,regression_16051)1153 TEST(Imgproc_minEnclosingCircle, regression_16051) {
1154 vector<Point2f> pts;
1155 pts.push_back(Point2f(85, 1415));
1156 pts.push_back(Point2f(87, 1415));
1157 pts.push_back(Point2f(89, 1414));
1158 pts.push_back(Point2f(89, 1414));
1159 pts.push_back(Point2f(87, 1412));
1160 Point2f center;
1161 float radius;
1162 minEnclosingCircle(pts, center, radius);
1163 EXPECT_NEAR(center.x, 86.9f, 1e-3);
1164 EXPECT_NEAR(center.y, 1414.1f, 1e-3);
1165 EXPECT_NEAR(2.1024551f, radius, 1e-3);
1166 }
1167
1168 /****************************************************************************************\
1169 * Perimeter Test *
1170 \****************************************************************************************/
1171
1172 class CV_PerimeterTest : public CV_BaseShapeDescrTest
1173 {
1174 public:
1175 CV_PerimeterTest();
1176
1177 protected:
1178 int prepare_test_case( int test_case_idx );
1179 void run_func(void);
1180 int validate_test_results( int test_case_idx );
1181 CvSlice slice;
1182 int is_closed;
1183 double result;
1184 };
1185
1186
CV_PerimeterTest()1187 CV_PerimeterTest::CV_PerimeterTest()
1188 {
1189 }
1190
1191
prepare_test_case(int test_case_idx)1192 int CV_PerimeterTest::prepare_test_case( int test_case_idx )
1193 {
1194 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1195 RNG& rng = ts->get_rng();
1196 int total;
1197
1198 if( code < 0 )
1199 return code;
1200
1201 is_closed = cvtest::randInt(rng) % 2;
1202
1203 if( points1 )
1204 {
1205 points1->flags |= CV_SEQ_KIND_CURVE;
1206 if( is_closed )
1207 points1->flags |= CV_SEQ_FLAG_CLOSED;
1208 total = points1->total;
1209 }
1210 else
1211 total = points2->cols + points2->rows - 1;
1212
1213 if( (cvtest::randInt(rng) % 3) && !test_cpp )
1214 {
1215 slice.start_index = cvtest::randInt(rng) % total;
1216 slice.end_index = cvtest::randInt(rng) % total;
1217 }
1218 else
1219 slice = CV_WHOLE_SEQ;
1220
1221 return 1;
1222 }
1223
1224
run_func()1225 void CV_PerimeterTest::run_func()
1226 {
1227 if(!test_cpp)
1228 result = cvArcLength( points, slice, points1 ? -1 : is_closed );
1229 else
1230 result = cv::arcLength(cv::cvarrToMat(points),
1231 !points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0);
1232 }
1233
1234
validate_test_results(int test_case_idx)1235 int CV_PerimeterTest::validate_test_results( int test_case_idx )
1236 {
1237 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1238 int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1;
1239 double result0 = 0;
1240 Point2f prev_pt, pt;
1241 CvPoint2D32f *ptr;
1242
1243 if( len < 0 )
1244 len += total;
1245
1246 len = MIN( len, total );
1247 //len -= !is_closed && len == total;
1248
1249 ptr = (CvPoint2D32f*)points2->data.fl;
1250 prev_pt = ptr[(is_closed ? slice.start_index+len-1 : slice.start_index) % total];
1251
1252 for( i = 0; i < len + (len < total && (!is_closed || len==1)); i++ )
1253 {
1254 pt = ptr[(i + slice.start_index) % total];
1255 double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y;
1256 result0 += sqrt(dx*dx + dy*dy);
1257 prev_pt = pt;
1258 }
1259
1260 if( cvIsNaN(result) || cvIsInf(result) )
1261 {
1262 ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result );
1263 code = cvtest::TS::FAIL_INVALID_OUTPUT;
1264 }
1265 else if( fabs(result - result0) > FLT_EPSILON*100*result0 )
1266 {
1267 ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 );
1268 code = cvtest::TS::FAIL_BAD_ACCURACY;
1269 }
1270
1271 if( code < 0 )
1272 ts->set_failed_test_info( code );
1273 return code;
1274 }
1275
1276
1277 /****************************************************************************************\
1278 * FitEllipse Test *
1279 \****************************************************************************************/
1280
1281 class CV_FitEllipseTest : public CV_BaseShapeDescrTest
1282 {
1283 public:
1284 CV_FitEllipseTest();
1285
1286 protected:
1287 int prepare_test_case( int test_case_idx );
1288 void generate_point_set( void* points );
1289 void run_func(void);
1290 int validate_test_results( int test_case_idx );
1291 RotatedRect box0, box;
1292 double min_ellipse_size, max_noise;
1293 };
1294
1295
CV_FitEllipseTest()1296 CV_FitEllipseTest::CV_FitEllipseTest()
1297 {
1298 min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least
1299 max_log_size = 10;
1300 min_ellipse_size = 10;
1301 max_noise = 0.05;
1302 }
1303
1304
generate_point_set(void * pointsSet)1305 void CV_FitEllipseTest::generate_point_set( void* pointsSet )
1306 {
1307 RNG& rng = ts->get_rng();
1308 int i, total, point_type;
1309 CvSeqReader reader;
1310 uchar* data = 0;
1311 double a, b;
1312
1313 box0.center.x = (float)((low.val[0] + high.val[0])*0.5);
1314 box0.center.y = (float)((low.val[1] + high.val[1])*0.5);
1315 box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2);
1316 box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2);
1317 box0.angle = (float)(cvtest::randReal(rng)*180);
1318 a = cos(box0.angle*CV_PI/180.);
1319 b = sin(box0.angle*CV_PI/180.);
1320
1321 if( box0.size.width > box0.size.height )
1322 {
1323 float t;
1324 CV_SWAP( box0.size.width, box0.size.height, t );
1325 }
1326 memset( &reader, 0, sizeof(reader) );
1327
1328 if( CV_IS_SEQ(pointsSet) )
1329 {
1330 CvSeq* ptseq = (CvSeq*)pointsSet;
1331 total = ptseq->total;
1332 point_type = CV_SEQ_ELTYPE(ptseq);
1333 cvStartReadSeq( ptseq, &reader );
1334 }
1335 else
1336 {
1337 CvMat* ptm = (CvMat*)pointsSet;
1338 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1339 total = ptm->rows + ptm->cols - 1;
1340 point_type = CV_MAT_TYPE(ptm->type);
1341 data = ptm->data.ptr;
1342 }
1343
1344 CV_Assert(point_type == CV_32SC2 || point_type == CV_32FC2);
1345
1346 for( i = 0; i < total; i++ )
1347 {
1348 CvPoint* pp;
1349 CvPoint2D32f p = {0, 0};
1350 double angle = cvtest::randReal(rng)*CV_PI*2;
1351 double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);
1352 double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);
1353 p.x = (float)(box0.center.x + a*x + b*y);
1354 p.y = (float)(box0.center.y - b*x + a*y);
1355
1356 if( reader.ptr )
1357 {
1358 pp = (CvPoint*)reader.ptr;
1359 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1360 }
1361 else
1362 pp = ((CvPoint*)data) + i;
1363 if( point_type == CV_32SC2 )
1364 {
1365 pp->x = cvRound(p.x);
1366 pp->y = cvRound(p.y);
1367 }
1368 else
1369 *(CvPoint2D32f*)pp = p;
1370 }
1371 }
1372
1373
prepare_test_case(int test_case_idx)1374 int CV_FitEllipseTest::prepare_test_case( int test_case_idx )
1375 {
1376 min_log_size = MAX(min_log_size,4);
1377 max_log_size = MAX(min_log_size,max_log_size);
1378 return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1379 }
1380
1381
run_func()1382 void CV_FitEllipseTest::run_func()
1383 {
1384 if(!test_cpp)
1385 box = cvFitEllipse2( points );
1386 else
1387 box = cv::fitEllipse(cv::cvarrToMat(points));
1388 }
1389
validate_test_results(int test_case_idx)1390 int CV_FitEllipseTest::validate_test_results( int test_case_idx )
1391 {
1392 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1393 double diff_angle;
1394
1395 if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) ||
1396 cvIsNaN(box.center.y) || cvIsInf(box.center.y) ||
1397 cvIsNaN(box.size.width) || cvIsInf(box.size.width) ||
1398 cvIsNaN(box.size.height) || cvIsInf(box.size.height) ||
1399 cvIsNaN(box.angle) || cvIsInf(box.angle) )
1400 {
1401 ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n",
1402 box.center.x, box.center.y, box.size.width, box.size.height, box.angle );
1403 code = cvtest::TS::FAIL_INVALID_OUTPUT;
1404 goto _exit_;
1405 }
1406
1407 box.angle = (float)(90-box.angle);
1408 if( box.angle < 0 )
1409 box.angle += 360;
1410 if( box.angle > 360 )
1411 box.angle -= 360;
1412
1413 if( fabs(box.center.x - box0.center.x) > 3 ||
1414 fabs(box.center.y - box0.center.y) > 3 ||
1415 fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) ||
1416 fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) )
1417 {
1418 ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t"
1419 "(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n",
1420 box.center.x, box.center.y, box.size.width, box.size.height,
1421 box0.center.x, box0.center.y, box0.size.width, box0.size.height );
1422 code = cvtest::TS::FAIL_BAD_ACCURACY;
1423 goto _exit_;
1424 }
1425
1426 diff_angle = fabs(box0.angle - box.angle);
1427 diff_angle = MIN( diff_angle, fabs(diff_angle - 360));
1428 diff_angle = MIN( diff_angle, fabs(diff_angle - 180));
1429
1430 if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 )
1431 {
1432 ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n",
1433 box.angle, box0.angle );
1434 code = cvtest::TS::FAIL_BAD_ACCURACY;
1435 goto _exit_;
1436 }
1437
1438 _exit_:
1439
1440 #if 0
1441 if( code < 0 )
1442 {
1443 cvNamedWindow( "test", 0 );
1444 IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4),
1445 cvRound(low_high_range*4)), 8, 3 );
1446 cvZero( img );
1447
1448 box.center.x += (float)low_high_range*2;
1449 box.center.y += (float)low_high_range*2;
1450 cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 );
1451
1452 for( int i = 0; i < points2->rows + points2->cols - 1; i++ )
1453 {
1454 CvPoint pt;
1455 pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2);
1456 pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2);
1457 cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 );
1458 }
1459
1460 cvShowImage( "test", img );
1461 cvReleaseImage( &img );
1462 cvWaitKey(0);
1463 }
1464 #endif
1465
1466 if( code < 0 )
1467 {
1468 ts->set_failed_test_info( code );
1469 }
1470 return code;
1471 }
1472
1473
1474 class CV_FitEllipseSmallTest : public cvtest::BaseTest
1475 {
1476 public:
CV_FitEllipseSmallTest()1477 CV_FitEllipseSmallTest() {}
~CV_FitEllipseSmallTest()1478 ~CV_FitEllipseSmallTest() {}
1479 protected:
run(int)1480 void run(int)
1481 {
1482 Size sz(50, 50);
1483 vector<vector<Point> > c;
1484 c.push_back(vector<Point>());
1485 int scale = 1;
1486 Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale;
1487 c[0].push_back(Point(2, 0)*scale+ofs);
1488 c[0].push_back(Point(0, 2)*scale+ofs);
1489 c[0].push_back(Point(0, 6)*scale+ofs);
1490 c[0].push_back(Point(2, 8)*scale+ofs);
1491 c[0].push_back(Point(6, 8)*scale+ofs);
1492 c[0].push_back(Point(8, 6)*scale+ofs);
1493 c[0].push_back(Point(8, 2)*scale+ofs);
1494 c[0].push_back(Point(6, 0)*scale+ofs);
1495
1496 RotatedRect e = fitEllipse(c[0]);
1497 CV_Assert( fabs(e.center.x - 4) <= 1. &&
1498 fabs(e.center.y - 4) <= 1. &&
1499 fabs(e.size.width - 9) <= 1. &&
1500 fabs(e.size.height - 9) <= 1. );
1501 }
1502 };
1503
1504
1505 // Regression test for incorrect fitEllipse result reported in Bug #3989
1506 // Check edge cases for rotation angles of ellipse ([-180, 90, 0, 90, 180] degrees)
1507 class CV_FitEllipseParallelTest : public CV_FitEllipseTest
1508 {
1509 public:
1510 CV_FitEllipseParallelTest();
1511 ~CV_FitEllipseParallelTest();
1512 protected:
1513 void generate_point_set( void* points );
1514 void run_func(void);
1515 Mat pointsMat;
1516 };
1517
CV_FitEllipseParallelTest()1518 CV_FitEllipseParallelTest::CV_FitEllipseParallelTest()
1519 {
1520 min_ellipse_size = 5;
1521 }
1522
generate_point_set(void *)1523 void CV_FitEllipseParallelTest::generate_point_set( void* )
1524 {
1525 RNG& rng = ts->get_rng();
1526 int height = (int)(MAX(high.val[0] - low.val[0], min_ellipse_size));
1527 int width = (int)(MAX(high.val[1] - low.val[1], min_ellipse_size));
1528 const int angle = ( (cvtest::randInt(rng) % 5) - 2 ) * 90;
1529 const int dim = max(height, width);
1530 const Point center = Point(dim*2, dim*2);
1531
1532 if( width > height )
1533 {
1534 int t;
1535 CV_SWAP( width, height, t );
1536 }
1537
1538 Mat image = Mat::zeros(dim*4, dim*4, CV_8UC1);
1539 ellipse(image, center, Size(height, width), angle,
1540 0, 360, Scalar(255, 0, 0), 1, 8);
1541
1542 box0.center.x = (float)center.x;
1543 box0.center.y = (float)center.y;
1544 box0.size.width = (float)width*2;
1545 box0.size.height = (float)height*2;
1546 box0.angle = (float)angle;
1547
1548 vector<vector<Point> > contours;
1549 findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
1550 Mat(contours[0]).convertTo(pointsMat, CV_32F);
1551 }
1552
run_func()1553 void CV_FitEllipseParallelTest::run_func()
1554 {
1555 box = cv::fitEllipse(pointsMat);
1556 }
1557
~CV_FitEllipseParallelTest()1558 CV_FitEllipseParallelTest::~CV_FitEllipseParallelTest(){
1559 pointsMat.release();
1560 }
1561
1562 /****************************************************************************************\
1563 * FitLine Test *
1564 \****************************************************************************************/
1565
1566 class CV_FitLineTest : public CV_BaseShapeDescrTest
1567 {
1568 public:
1569 CV_FitLineTest();
1570
1571 protected:
1572 int prepare_test_case( int test_case_idx );
1573 void generate_point_set( void* points );
1574 void run_func(void);
1575 int validate_test_results( int test_case_idx );
1576 double max_noise;
1577 AutoBuffer<float> line, line0;
1578 int dist_type;
1579 double reps, aeps;
1580 };
1581
1582
CV_FitLineTest()1583 CV_FitLineTest::CV_FitLineTest()
1584 {
1585 min_log_size = 5; // for robust line fitting a dozen of points is needed at least
1586 max_log_size = 10;
1587 max_noise = 0.05;
1588 }
1589
generate_point_set(void * pointsSet)1590 void CV_FitLineTest::generate_point_set( void* pointsSet )
1591 {
1592 RNG& rng = ts->get_rng();
1593 int i, k, n, total, point_type;
1594 CvSeqReader reader;
1595 uchar* data = 0;
1596 double s = 0;
1597
1598 n = dims;
1599 for( k = 0; k < n; k++ )
1600 {
1601 line0[k+n] = (float)((low.val[k] + high.val[k])*0.5);
1602 line0[k] = (float)(high.val[k] - low.val[k]);
1603 if( cvtest::randInt(rng) % 2 )
1604 line0[k] = -line0[k];
1605 s += (double)line0[k]*line0[k];
1606 }
1607
1608 s = 1./sqrt(s);
1609 for( k = 0; k < n; k++ )
1610 line0[k] = (float)(line0[k]*s);
1611
1612 memset( &reader, 0, sizeof(reader) );
1613
1614 if( CV_IS_SEQ(pointsSet) )
1615 {
1616 CvSeq* ptseq = (CvSeq*)pointsSet;
1617 total = ptseq->total;
1618 point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq));
1619 cvStartReadSeq( ptseq, &reader );
1620 }
1621 else
1622 {
1623 CvMat* ptm = (CvMat*)pointsSet;
1624 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1625 total = ptm->rows + ptm->cols - 1;
1626 point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type));
1627 data = ptm->data.ptr;
1628 }
1629
1630 for( i = 0; i < total; i++ )
1631 {
1632 int* pi;
1633 float* pf;
1634 float p[4], t;
1635 if( reader.ptr )
1636 {
1637 pi = (int*)reader.ptr;
1638 pf = (float*)reader.ptr;
1639 CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
1640 }
1641 else
1642 {
1643 pi = (int*)data + i*n;
1644 pf = (float*)data + i*n;
1645 }
1646
1647 t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2);
1648
1649 for( k = 0; k < n; k++ )
1650 {
1651 p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]);
1652
1653 if( point_type == CV_32S )
1654 pi[k] = cvRound(p[k]);
1655 else
1656 pf[k] = p[k];
1657 }
1658 }
1659 }
1660
prepare_test_case(int test_case_idx)1661 int CV_FitLineTest::prepare_test_case( int test_case_idx )
1662 {
1663 RNG& rng = ts->get_rng();
1664 dims = cvtest::randInt(rng) % 2 + 2;
1665 line.allocate(dims * 2);
1666 line0.allocate(dims * 2);
1667 min_log_size = MAX(min_log_size,5);
1668 max_log_size = MAX(min_log_size,max_log_size);
1669 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1670 dist_type = cvtest::randInt(rng) % 6 + 1;
1671 dist_type += dist_type == CV_DIST_C;
1672 reps = 0.1; aeps = 0.01;
1673 return code;
1674 }
1675
1676
run_func()1677 void CV_FitLineTest::run_func()
1678 {
1679 if(!test_cpp)
1680 cvFitLine( points, dist_type, 0, reps, aeps, line.data());
1681 else if(dims == 2)
1682 cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps);
1683 else
1684 cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps);
1685 }
1686
validate_test_results(int test_case_idx)1687 int CV_FitLineTest::validate_test_results( int test_case_idx )
1688 {
1689 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1690 int k, max_k = 0;
1691 double vec_diff = 0, t;
1692
1693 //std::cout << dims << " " << Mat(1, dims*2, CV_32FC1, line.data()) << " " << Mat(1, dims, CV_32FC1, line0.data()) << std::endl;
1694
1695 for( k = 0; k < dims*2; k++ )
1696 {
1697 if( cvIsNaN(line[k]) || cvIsInf(line[k]) )
1698 {
1699 ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n",
1700 k, line[k] );
1701 code = cvtest::TS::FAIL_INVALID_OUTPUT;
1702 goto _exit_;
1703 }
1704 }
1705
1706 if( fabs(line0[1]) > fabs(line0[0]) )
1707 max_k = 1;
1708 if( fabs(line0[dims-1]) > fabs(line0[max_k]) )
1709 max_k = dims-1;
1710 if( line0[max_k] < 0 )
1711 for( k = 0; k < dims; k++ )
1712 line0[k] = -line0[k];
1713 if( line[max_k] < 0 )
1714 for( k = 0; k < dims; k++ )
1715 line[k] = -line[k];
1716
1717 for( k = 0; k < dims; k++ )
1718 {
1719 double dt = line[k] - line0[k];
1720 vec_diff += dt*dt;
1721 }
1722
1723 if( sqrt(vec_diff) > 0.05 )
1724 {
1725 if( dims == 2 )
1726 ts->printf( cvtest::TS::LOG,
1727 "The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n",
1728 line[0], line[1], line0[0], line0[1] );
1729 else
1730 ts->printf( cvtest::TS::LOG,
1731 "The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n",
1732 line[0], line[1], line[2], line0[0], line0[1], line0[2] );
1733 code = cvtest::TS::FAIL_BAD_ACCURACY;
1734 goto _exit_;
1735 }
1736
1737 t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k];
1738 for( k = 0; k < dims; k++ )
1739 {
1740 double p = line0[k+dims] + t*line0[k] - line[k+dims];
1741 vec_diff += p*p;
1742 }
1743
1744 if( sqrt(vec_diff) > 1*MAX(fabs(t),1) )
1745 {
1746 if( dims == 2 )
1747 ts->printf( cvtest::TS::LOG,
1748 "The computed line point (%.2f,%.2f) is too far from the actual line\n",
1749 line[2]+line0[2], line[3]+line0[3] );
1750 else
1751 ts->printf( cvtest::TS::LOG,
1752 "The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n",
1753 line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] );
1754 code = cvtest::TS::FAIL_BAD_ACCURACY;
1755 goto _exit_;
1756 }
1757
1758 _exit_:
1759
1760 if( code < 0 )
1761 {
1762 ts->set_failed_test_info( code );
1763 }
1764 return code;
1765 }
1766
1767 /****************************************************************************************\
1768 * ContourMoments Test *
1769 \****************************************************************************************/
1770
1771
1772 static void
cvTsGenerateTousledBlob(CvPoint2D32f center,CvSize2D32f axes,double max_r_scale,double angle,CvArr * points,RNG & rng)1773 cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes,
1774 double max_r_scale, double angle, CvArr* points, RNG& rng )
1775 {
1776 int i, total, point_type;
1777 uchar* data = 0;
1778 CvSeqReader reader;
1779 memset( &reader, 0, sizeof(reader) );
1780
1781 if( CV_IS_SEQ(points) )
1782 {
1783 CvSeq* ptseq = (CvSeq*)points;
1784 total = ptseq->total;
1785 point_type = CV_SEQ_ELTYPE(ptseq);
1786 cvStartReadSeq( ptseq, &reader );
1787 }
1788 else
1789 {
1790 CvMat* ptm = (CvMat*)points;
1791 assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1792 total = ptm->rows + ptm->cols - 1;
1793 point_type = CV_MAT_TYPE(ptm->type);
1794 data = ptm->data.ptr;
1795 }
1796
1797 assert( point_type == CV_32SC2 || point_type == CV_32FC2 );
1798
1799 for( i = 0; i < total; i++ )
1800 {
1801 CvPoint* pp;
1802 Point2f p;
1803
1804 double phi0 = 2*CV_PI*i/total;
1805 double phi = CV_PI*angle/180.;
1806 double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale);
1807 double ta = axes.height*t;
1808 double tb = axes.width*t;
1809 double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb;
1810 double c = cos(phi), s = sin(phi);
1811 p.x = (float)(c0*c - s0*s + center.x);
1812 p.y = (float)(c0*s + s0*c + center.y);
1813
1814 if( reader.ptr )
1815 {
1816 pp = (CvPoint*)reader.ptr;
1817 CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1818 }
1819 else
1820 pp = ((CvPoint*)data) + i;
1821
1822 if( point_type == CV_32SC2 )
1823 {
1824 pp->x = cvRound(p.x);
1825 pp->y = cvRound(p.y);
1826 }
1827 else
1828 *(CvPoint2D32f*)pp = cvPoint2D32f(p);
1829 }
1830 }
1831
1832
1833 class CV_ContourMomentsTest : public CV_BaseShapeDescrTest
1834 {
1835 public:
1836 CV_ContourMomentsTest();
1837
1838 protected:
1839 int prepare_test_case( int test_case_idx );
1840 void generate_point_set( void* points );
1841 void run_func(void);
1842 int validate_test_results( int test_case_idx );
1843 CvMoments moments0, moments;
1844 double area0, area;
1845 Size2f axes;
1846 Point2f center;
1847 int max_max_r_scale;
1848 double max_r_scale, angle;
1849 Size img_size;
1850 };
1851
1852
CV_ContourMomentsTest()1853 CV_ContourMomentsTest::CV_ContourMomentsTest()
1854 {
1855 min_log_size = 3;
1856 max_log_size = 8;
1857 max_max_r_scale = 15;
1858 low_high_range = 200;
1859 enable_flt_points = false;
1860 }
1861
1862
generate_point_set(void * pointsSet)1863 void CV_ContourMomentsTest::generate_point_set( void* pointsSet )
1864 {
1865 RNG& rng = ts->get_rng();
1866 float max_sz;
1867
1868 axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);
1869 axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);
1870 max_sz = MAX(axes.width, axes.height);
1871
1872 img_size.width = img_size.height = cvRound(low_high_range*2.2);
1873
1874 center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8);
1875 center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8);
1876
1877 assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width &&
1878 0 < center.y - max_sz && center.y + max_sz < img_size.height );
1879
1880 max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01;
1881 angle = cvtest::randReal(rng)*360;
1882
1883 cvTsGenerateTousledBlob( cvPoint2D32f(center), cvSize2D32f(axes), max_r_scale, angle, pointsSet, rng );
1884
1885 if( points1 )
1886 points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON;
1887 }
1888
1889
prepare_test_case(int test_case_idx)1890 int CV_ContourMomentsTest::prepare_test_case( int test_case_idx )
1891 {
1892 min_log_size = MAX(min_log_size,3);
1893 max_log_size = MIN(max_log_size,8);
1894 max_log_size = MAX(min_log_size,max_log_size);
1895 int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1896 return code;
1897 }
1898
1899
run_func()1900 void CV_ContourMomentsTest::run_func()
1901 {
1902 if(!test_cpp)
1903 {
1904 cvMoments( points, &moments );
1905 area = cvContourArea( points );
1906 }
1907 else
1908 {
1909 moments = cvMoments(cv::moments(cv::cvarrToMat(points)));
1910 area = cv::contourArea(cv::cvarrToMat(points));
1911 }
1912 }
1913
1914
validate_test_results(int test_case_idx)1915 int CV_ContourMomentsTest::validate_test_results( int test_case_idx )
1916 {
1917 int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1918 int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00));
1919 CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 );
1920 CvPoint* pt = (CvPoint*)points2->data.i;
1921 int count = points2->cols + points2->rows - 1;
1922 double max_v0 = 0;
1923
1924 cvZero(img);
1925 cvFillPoly( img, &pt, &count, 1, cvScalarAll(1));
1926 cvMoments( img, &moments0 );
1927
1928 for( i = 0; i < n; i++ )
1929 {
1930 double t = fabs((&moments0.m00)[i]);
1931 max_v0 = MAX(max_v0, t);
1932 }
1933
1934 for( i = 0; i <= n; i++ )
1935 {
1936 double v = i < n ? (&moments.m00)[i] : area;
1937 double v0 = i < n ? (&moments0.m00)[i] : moments0.m00;
1938
1939 if( cvIsNaN(v) || cvIsInf(v) )
1940 {
1941 ts->printf( cvtest::TS::LOG,
1942 "The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v );
1943 code = cvtest::TS::FAIL_INVALID_OUTPUT;
1944 break;
1945 }
1946
1947 if( fabs(v - v0) > 0.1*max_v0 )
1948 {
1949 ts->printf( cvtest::TS::LOG,
1950 "The computed contour %s is %g, while it should be %g\n",
1951 i < n ? "moment" : "area", v, v0 );
1952 code = cvtest::TS::FAIL_BAD_ACCURACY;
1953 break;
1954 }
1955 }
1956
1957 if( code < 0 )
1958 {
1959 #if 0
1960 cvCmpS( img, 0, img, CV_CMP_GT );
1961 cvNamedWindow( "test", 1 );
1962 cvShowImage( "test", img );
1963 cvWaitKey();
1964 #endif
1965 ts->set_failed_test_info( code );
1966 }
1967
1968 cvReleaseMat( &img );
1969 return code;
1970 }
1971
1972
1973 ////////////////////////////////////// Perimeter/Area/Slice test ///////////////////////////////////
1974
1975 class CV_PerimeterAreaSliceTest : public cvtest::BaseTest
1976 {
1977 public:
1978 CV_PerimeterAreaSliceTest();
1979 ~CV_PerimeterAreaSliceTest();
1980 protected:
1981 void run(int);
1982 };
1983
CV_PerimeterAreaSliceTest()1984 CV_PerimeterAreaSliceTest::CV_PerimeterAreaSliceTest()
1985 {
1986 }
~CV_PerimeterAreaSliceTest()1987 CV_PerimeterAreaSliceTest::~CV_PerimeterAreaSliceTest() {}
1988
run(int)1989 void CV_PerimeterAreaSliceTest::run( int )
1990 {
1991 Ptr<CvMemStorage> storage(cvCreateMemStorage());
1992 RNG& rng = theRNG();
1993 const double min_r = 90, max_r = 120;
1994
1995 for( int i = 0; i < 100; i++ )
1996 {
1997 ts->update_context( this, i, true );
1998 int n = rng.uniform(3, 30);
1999 cvClearMemStorage(storage);
2000 CvSeq* contour = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(CvPoint), storage);
2001 double dphi = CV_PI*2/n;
2002 Point center;
2003 center.x = rng.uniform(cvCeil(max_r), cvFloor(640-max_r));
2004 center.y = rng.uniform(cvCeil(max_r), cvFloor(480-max_r));
2005
2006 for( int j = 0; j < n; j++ )
2007 {
2008 CvPoint pt = CV_STRUCT_INITIALIZER;
2009 double r = rng.uniform(min_r, max_r);
2010 double phi = j*dphi;
2011 pt.x = cvRound(center.x + r*cos(phi));
2012 pt.y = cvRound(center.y - r*sin(phi));
2013 cvSeqPush(contour, &pt);
2014 }
2015
2016 CvSlice slice = {0, 0};
2017 for(;;)
2018 {
2019 slice.start_index = rng.uniform(-n/2, 3*n/2);
2020 slice.end_index = rng.uniform(-n/2, 3*n/2);
2021 int len = cvSliceLength(slice, contour);
2022 if( len > 2 )
2023 break;
2024 }
2025 CvSeq *cslice = cvSeqSlice(contour, slice);
2026 /*printf( "%d. (%d, %d) of %d, length = %d, length1 = %d\n",
2027 i, slice.start_index, slice.end_index,
2028 contour->total, cvSliceLength(slice, contour), cslice->total );
2029
2030 double area0 = cvContourArea(cslice);
2031 double area1 = cvContourArea(contour, slice);
2032 if( area0 != area1 )
2033 {
2034 ts->printf(cvtest::TS::LOG,
2035 "The contour area slice is computed differently (%g vs %g)\n", area0, area1 );
2036 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
2037 return;
2038 }*/
2039
2040 double len0 = cvArcLength(cslice, CV_WHOLE_SEQ, 1);
2041 double len1 = cvArcLength(contour, slice, 1);
2042 if( len0 != len1 )
2043 {
2044 ts->printf(cvtest::TS::LOG,
2045 "The contour arc length is computed differently (%g vs %g)\n", len0, len1 );
2046 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
2047 return;
2048 }
2049 }
2050 ts->set_failed_test_info(cvtest::TS::OK);
2051 }
2052
2053
TEST(Imgproc_ConvexHull,accuracy)2054 TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); }
TEST(Imgproc_MinAreaRect,accuracy)2055 TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); }
TEST(Imgproc_MinTriangle,accuracy)2056 TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); }
TEST(Imgproc_MinCircle,accuracy)2057 TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); }
TEST(Imgproc_MinCircle2,accuracy)2058 TEST(Imgproc_MinCircle2, accuracy) { CV_MinCircleTest2 test; test.safe_run(); }
TEST(Imgproc_ContourPerimeter,accuracy)2059 TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); }
TEST(Imgproc_FitEllipse,accuracy)2060 TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); }
TEST(Imgproc_FitEllipse,parallel)2061 TEST(Imgproc_FitEllipse, parallel) { CV_FitEllipseParallelTest test; test.safe_run(); }
TEST(Imgproc_FitLine,accuracy)2062 TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); }
TEST(Imgproc_ContourMoments,accuracy)2063 TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); }
TEST(Imgproc_ContourPerimeterSlice,accuracy)2064 TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); }
TEST(Imgproc_FitEllipse,small)2065 TEST(Imgproc_FitEllipse, small) { CV_FitEllipseSmallTest test; test.safe_run(); }
2066
2067
2068
PARAM_TEST_CASE(ConvexityDefects_regression_5908,bool,int)2069 PARAM_TEST_CASE(ConvexityDefects_regression_5908, bool, int)
2070 {
2071 public:
2072 int start_index;
2073 bool clockwise;
2074
2075 Mat contour;
2076
2077 virtual void SetUp()
2078 {
2079 clockwise = GET_PARAM(0);
2080 start_index = GET_PARAM(1);
2081
2082 const int N = 11;
2083 const Point2i points[N] = {
2084 Point2i(154, 408),
2085 Point2i(45, 223),
2086 Point2i(115, 275), // inner
2087 Point2i(104, 166),
2088 Point2i(154, 256), // inner
2089 Point2i(169, 144),
2090 Point2i(185, 256), // inner
2091 Point2i(235, 170),
2092 Point2i(240, 320), // inner
2093 Point2i(330, 287),
2094 Point2i(224, 390)
2095 };
2096
2097 contour = Mat(N, 1, CV_32SC2);
2098 for (int i = 0; i < N; i++)
2099 {
2100 contour.at<Point2i>(i) = (!clockwise) // image and convexHull coordinate systems are different
2101 ? points[(start_index + i) % N]
2102 : points[N - 1 - ((start_index + i) % N)];
2103 }
2104 }
2105 };
2106
2107 TEST_P(ConvexityDefects_regression_5908, simple)
2108 {
2109 std::vector<int> hull;
2110 cv::convexHull(contour, hull, clockwise, false);
2111
2112 std::vector<Vec4i> result;
2113 cv::convexityDefects(contour, hull, result);
2114
2115 EXPECT_EQ(4, (int)result.size());
2116 }
2117
2118 INSTANTIATE_TEST_CASE_P(Imgproc, ConvexityDefects_regression_5908,
2119 testing::Combine(
2120 testing::Bool(),
2121 testing::Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
2122 ));
2123
2124 TEST(Imgproc_FitLine, regression_15083)
2125 {
2126 int points2i_[] = {
2127 432, 654,
2128 370, 656,
2129 390, 656,
2130 410, 656,
2131 348, 658
2132 };
2133 Mat points(5, 1, CV_32SC2, points2i_);
2134
2135 Vec4f lineParam;
2136 fitLine(points, lineParam, DIST_L1, 0, 0.01, 0.01);
2137 EXPECT_GE(fabs(lineParam[0]), fabs(lineParam[1]) * 4) << lineParam;
2138 }
2139
2140 TEST(Imgproc_FitLine, regression_4903)
2141 {
2142 float points2f_[] = {
2143 1224.0, 576.0,
2144 1234.0, 683.0,
2145 1215.0, 471.0,
2146 1184.0, 137.0,
2147 1079.0, 377.0,
2148 1239.0, 788.0,
2149 };
2150 Mat points(6, 1, CV_32FC2, points2f_);
2151
2152 Vec4f lineParam;
2153 fitLine(points, lineParam, DIST_WELSCH, 0, 0.01, 0.01);
2154 EXPECT_GE(fabs(lineParam[1]), fabs(lineParam[0]) * 4) << lineParam;
2155 }
2156
2157 #if 0
2158 #define DRAW(x) x
2159 #else
2160 #define DRAW(x)
2161 #endif
2162
2163 // the Python test by @hannarud is converted to C++; see the issue #4539
2164 TEST(Imgproc_ConvexityDefects, ordering_4539)
2165 {
2166 int contour[][2] =
2167 {
2168 {26, 9}, {25, 10}, {24, 10}, {23, 10}, {22, 10}, {21, 10}, {20, 11}, {19, 11}, {18, 11}, {17, 12},
2169 {17, 13}, {18, 14}, {18, 15}, {18, 16}, {18, 17}, {19, 18}, {19, 19}, {20, 20}, {21, 21}, {21, 22},
2170 {22, 23}, {22, 24}, {23, 25}, {23, 26}, {24, 27}, {25, 28}, {26, 29}, {27, 30}, {27, 31}, {28, 32},
2171 {29, 32}, {30, 33}, {31, 34}, {30, 35}, {29, 35}, {30, 35}, {31, 34}, {32, 34}, {33, 34}, {34, 33},
2172 {35, 32}, {35, 31}, {35, 30}, {36, 29}, {37, 28}, {37, 27}, {38, 26}, {39, 25}, {40, 24}, {40, 23},
2173 {41, 22}, {42, 21}, {42, 20}, {42, 19}, {43, 18}, {43, 17}, {44, 16}, {45, 15}, {45, 14}, {46, 13},
2174 {46, 12}, {45, 11}, {44, 11}, {43, 11}, {42, 10}, {41, 10}, {40, 9}, {39, 9}, {38, 9}, {37, 9},
2175 {36, 9}, {35, 9}, {34, 9}, {33, 9}, {32, 9}, {31, 9}, {30, 9}, {29, 9}, {28, 9}, {27, 9}
2176 };
2177 int npoints = (int)(sizeof(contour)/sizeof(contour[0][0])/2);
2178 Mat contour_(1, npoints, CV_32SC2, contour);
2179 vector<Point> hull;
2180 vector<int> hull_ind;
2181 vector<Vec4i> defects;
2182
2183 // first, check the original contour as-is, without intermediate fillPoly/drawContours.
2184 convexHull(contour_, hull_ind, false, false);
2185 EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception );
2186
2187 int scale = 20;
2188 contour_ *= (double)scale;
2189
2190 Mat canvas_gray(Size(60*scale, 45*scale), CV_8U, Scalar::all(0));
2191 const Point* ptptr = contour_.ptr<Point>();
2192 fillPoly(canvas_gray, &ptptr, &npoints, 1, Scalar(255, 255, 255));
2193
2194 vector<vector<Point> > contours;
2195 findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
2196 convexHull(contours[0], hull_ind, false, false);
2197
2198 // the original contour contains self-intersections,
2199 // therefore convexHull does not return a monotonous sequence of points
2200 // and therefore convexityDefects throws an exception
2201 EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception );
2202
2203 #if 1
2204 // one way to eliminate the contour self-intersection in this particular case is to apply dilate(),
2205 // so that the self-repeating points are not self-repeating anymore
2206 dilate(canvas_gray, canvas_gray, Mat());
2207 #else
2208 // another popular technique to eliminate such thin "hair" is to use morphological "close" operation,
2209 // which is erode() + dilate()
2210 erode(canvas_gray, canvas_gray, Mat());
2211 dilate(canvas_gray, canvas_gray, Mat());
2212 #endif
2213
2214 // after the "fix", the newly retrieved contour should not have self-intersections,
2215 // and everything should work well
2216 findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
2217 convexHull(contours[0], hull, false, true);
2218 convexHull(contours[0], hull_ind, false, false);
2219
2220 DRAW(Mat canvas(Size(60*scale, 45*scale), CV_8UC3, Scalar::all(0));
2221 drawContours(canvas, contours, -1, Scalar(255, 255, 255), -1));
2222
2223 size_t nhull = hull.size();
2224 ASSERT_EQ( nhull, hull_ind.size() );
2225
2226 if( nhull > 2 )
2227 {
2228 bool initial_lt = hull_ind[0] < hull_ind[1];
2229 for( size_t i = 0; i < nhull; i++ )
2230 {
2231 int ind = hull_ind[i];
2232 Point pt = contours[0][ind];
2233
2234 ASSERT_EQ(pt, hull[i]);
2235 if( i > 0 )
2236 {
2237 // check that the convex hull indices are monotone
2238 if( initial_lt )
2239 {
2240 ASSERT_LT(hull_ind[i-1], hull_ind[i]);
2241 }
2242 else
2243 {
2244 ASSERT_GT(hull_ind[i-1], hull_ind[i]);
2245 }
2246 }
2247 DRAW(circle(canvas, pt, 7, Scalar(180, 0, 180), -1, LINE_AA);
2248 putText(canvas, format("%d (%d)", (int)i, ind), pt+Point(15, 0), FONT_HERSHEY_SIMPLEX, 0.4, Scalar(200, 0, 200), 1, LINE_AA));
2249 //printf("%d. ind=%d, pt=(%d, %d)\n", (int)i, ind, pt.x, pt.y);
2250 }
2251 }
2252
2253 convexityDefects(contours[0], hull_ind, defects);
2254
2255 for(size_t i = 0; i < defects.size(); i++ )
2256 {
2257 Vec4i d = defects[i];
2258 //printf("defect %d. start=%d, end=%d, farthest=%d, depth=%d\n", (int)i, d[0], d[1], d[2], d[3]);
2259 EXPECT_LT(d[0], d[1]);
2260 EXPECT_LE(d[0], d[2]);
2261 EXPECT_LE(d[2], d[1]);
2262
2263 DRAW(Point start = contours[0][d[0]];
2264 Point end = contours[0][d[1]];
2265 Point far = contours[0][d[2]];
2266 line(canvas, start, end, Scalar(255, 255, 128), 3, LINE_AA);
2267 line(canvas, start, far, Scalar(255, 150, 255), 3, LINE_AA);
2268 line(canvas, end, far, Scalar(255, 150, 255), 3, LINE_AA);
2269 circle(canvas, start, 7, Scalar(0, 0, 255), -1, LINE_AA);
2270 circle(canvas, end, 7, Scalar(0, 0, 255), -1, LINE_AA);
2271 circle(canvas, far, 7, Scalar(255, 0, 0), -1, LINE_AA));
2272 }
2273
2274 DRAW(imshow("defects", canvas);
2275 waitKey());
2276 }
2277
2278 #undef DRAW
2279
2280 TEST(Imgproc_ConvexHull, overflow)
2281 {
2282 std::vector<Point> points;
2283 std::vector<Point2f> pointsf;
2284
2285 points.push_back(Point(14763, 2890));
2286 points.push_back(Point(14388, 72088));
2287 points.push_back(Point(62810, 72274));
2288 points.push_back(Point(63166, 3945));
2289 points.push_back(Point(56782, 3945));
2290 points.push_back(Point(56763, 3077));
2291 points.push_back(Point(34666, 2965));
2292 points.push_back(Point(34547, 2953));
2293 points.push_back(Point(34508, 2866));
2294 points.push_back(Point(34429, 2965));
2295
2296 size_t i, n = points.size();
2297 for( i = 0; i < n; i++ )
2298 pointsf.push_back(Point2f(points[i]));
2299
2300 std::vector<int> hull;
2301 std::vector<int> hullf;
2302
2303 convexHull(points, hull, false, false);
2304 convexHull(pointsf, hullf, false, false);
2305
2306 ASSERT_EQ(hull, hullf);
2307 }
2308
2309 static
2310 bool checkMinAreaRect(const RotatedRect& rr, const Mat& c, double eps = 0.5f)
2311 {
2312 int N = c.rows;
2313
2314 Mat rr_pts;
2315 boxPoints(rr, rr_pts);
2316
2317 double maxError = 0.0;
2318 int nfailed = 0;
2319 for (int i = 0; i < N; i++)
2320 {
2321 double d = pointPolygonTest(rr_pts, c.at<Point2f>(i), true);
2322 maxError = std::max(-d, maxError);
2323 if (d < -eps)
2324 nfailed++;
2325 }
2326
2327 if (nfailed)
2328 std::cout << "nfailed=" << nfailed << " (total=" << N << ") maxError=" << maxError << std::endl;
2329 return nfailed == 0;
2330 }
2331
2332 TEST(Imgproc_minAreaRect, reproducer_18157)
2333 {
2334 const int N = 168;
2335 float pts_[N][2] = {
2336 { 1903, 266 }, { 1897, 267 }, { 1893, 268 }, { 1890, 269 },
2337 { 1878, 275 }, { 1875, 277 }, { 1872, 279 }, { 1868, 282 },
2338 { 1862, 287 }, { 1750, 400 }, { 1748, 402 }, { 1742, 407 },
2339 { 1742, 408 }, { 1740, 410 }, { 1738, 412 }, { 1593, 558 },
2340 { 1590, 560 }, { 1588, 562 }, { 1586, 564 }, { 1580, 570 },
2341 { 1443, 709 }, { 1437, 714 }, { 1435, 716 }, { 1304, 848 },
2342 { 1302, 850 }, { 1292, 860 }, { 1175, 979 }, { 1172, 981 },
2343 { 1049, 1105 }, { 936, 1220 }, { 933, 1222 }, { 931, 1224 },
2344 { 830, 1326 }, { 774, 1383 }, { 769, 1389 }, { 766, 1393 },
2345 { 764, 1396 }, { 762, 1399 }, { 760, 1402 }, { 757, 1408 },
2346 { 757, 1410 }, { 755, 1413 }, { 754, 1416 }, { 753, 1420 },
2347 { 752, 1424 }, { 752, 1442 }, { 753, 1447 }, { 754, 1451 },
2348 { 755, 1454 }, { 757, 1457 }, { 757, 1459 }, { 761, 1467 },
2349 { 763, 1470 }, { 765, 1473 }, { 767, 1476 }, { 771, 1481 },
2350 { 779, 1490 }, { 798, 1510 }, { 843, 1556 }, { 847, 1560 },
2351 { 851, 1564 }, { 863, 1575 }, { 907, 1620 }, { 909, 1622 },
2352 { 913, 1626 }, { 1154, 1866 }, { 1156, 1868 }, { 1158, 1870 },
2353 { 1207, 1918 }, { 1238, 1948 }, { 1252, 1961 }, { 1260, 1968 },
2354 { 1264, 1971 }, { 1268, 1974 }, { 1271, 1975 }, { 1273, 1977 },
2355 { 1283, 1982 }, { 1286, 1983 }, { 1289, 1984 }, { 1294, 1985 },
2356 { 1300, 1986 }, { 1310, 1986 }, { 1316, 1985 }, { 1320, 1984 },
2357 { 1323, 1983 }, { 1326, 1982 }, { 1338, 1976 }, { 1341, 1974 },
2358 { 1344, 1972 }, { 1349, 1968 }, { 1358, 1960 }, { 1406, 1911 },
2359 { 1421, 1897 }, { 1624, 1693 }, { 1788, 1528 }, { 1790, 1526 },
2360 { 1792, 1524 }, { 1794, 1522 }, { 1796, 1520 }, { 1798, 1518 },
2361 { 1800, 1516 }, { 1919, 1396 }, { 1921, 1394 }, { 2038, 1275 },
2362 { 2047, 1267 }, { 2048, 1265 }, { 2145, 1168 }, { 2148, 1165 },
2363 { 2260, 1052 }, { 2359, 952 }, { 2434, 876 }, { 2446, 863 },
2364 { 2450, 858 }, { 2453, 854 }, { 2455, 851 }, { 2457, 846 },
2365 { 2459, 844 }, { 2460, 842 }, { 2460, 840 }, { 2462, 837 },
2366 { 2463, 834 }, { 2464, 830 }, { 2465, 825 }, { 2465, 809 },
2367 { 2464, 804 }, { 2463, 800 }, { 2462, 797 }, { 2461, 794 },
2368 { 2456, 784 }, { 2454, 781 }, { 2452, 778 }, { 2450, 775 },
2369 { 2446, 770 }, { 2437, 760 }, { 2412, 734 }, { 2410, 732 },
2370 { 2408, 730 }, { 2382, 704 }, { 2380, 702 }, { 2378, 700 },
2371 { 2376, 698 }, { 2372, 694 }, { 2370, 692 }, { 2368, 690 },
2372 { 2366, 688 }, { 2362, 684 }, { 2360, 682 }, { 2252, 576 },
2373 { 2250, 573 }, { 2168, 492 }, { 2166, 490 }, { 2085, 410 },
2374 { 2026, 352 }, { 1988, 315 }, { 1968, 296 }, { 1958, 287 },
2375 { 1953, 283 }, { 1949, 280 }, { 1946, 278 }, { 1943, 276 },
2376 { 1940, 274 }, { 1936, 272 }, { 1934, 272 }, { 1931, 270 },
2377 { 1928, 269 }, { 1925, 268 }, { 1921, 267 }, { 1915, 266 }
2378 };
2379
2380 Mat contour(N, 1, CV_32FC2, (void*)pts_);
2381
2382 RotatedRect rr = cv::minAreaRect(contour);
2383
2384 EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
2385 }
2386
2387 }} // namespace
2388 /* End of file. */
2389