Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/test/test_convhull.cpp
16354 views
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
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
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
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
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
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
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( CvFileStorage* 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
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
241
CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest()
242
{
243
clear();
244
}
245
246
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
257
int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs )
258
{
259
int code = cvtest::BaseTest::read_params( fs );
260
if( code < 0 )
261
return code;
262
263
test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count );
264
min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size );
265
max_log_size = cvReadInt( find_param( fs, "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
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
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
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
410
void CV_BaseShapeDescrTest::run_func(void)
411
{
412
}
413
414
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
446
CV_ConvHullTest::CV_ConvHullTest()
447
{
448
hull1 = 0;
449
hull2 = 0;
450
hull_storage = 0;
451
orientation = return_points = 0;
452
}
453
454
455
CV_ConvHullTest::~CV_ConvHullTest()
456
{
457
clear();
458
}
459
460
461
void CV_ConvHullTest::clear()
462
{
463
CV_BaseShapeDescrTest::clear();
464
cvReleaseMat( &hull2 );
465
hull1 = 0;
466
hull_storage = 0;
467
}
468
469
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
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
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
689
CV_MinAreaRectTest::CV_MinAreaRectTest()
690
{
691
}
692
693
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
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
815
CV_MinTriangleTest::CV_MinTriangleTest()
816
{
817
}
818
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
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
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
953
CV_MinCircleTest::CV_MinCircleTest()
954
{
955
}
956
957
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
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
1046
CV_MinCircleTest2::CV_MinCircleTest2()
1047
{
1048
rng = ts->get_rng();
1049
}
1050
1051
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
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
* Perimeter Test *
1089
\****************************************************************************************/
1090
1091
class CV_PerimeterTest : public CV_BaseShapeDescrTest
1092
{
1093
public:
1094
CV_PerimeterTest();
1095
1096
protected:
1097
int prepare_test_case( int test_case_idx );
1098
void run_func(void);
1099
int validate_test_results( int test_case_idx );
1100
CvSlice slice;
1101
int is_closed;
1102
double result;
1103
};
1104
1105
1106
CV_PerimeterTest::CV_PerimeterTest()
1107
{
1108
}
1109
1110
1111
int CV_PerimeterTest::prepare_test_case( int test_case_idx )
1112
{
1113
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1114
RNG& rng = ts->get_rng();
1115
int total;
1116
1117
if( code < 0 )
1118
return code;
1119
1120
is_closed = cvtest::randInt(rng) % 2;
1121
1122
if( points1 )
1123
{
1124
points1->flags |= CV_SEQ_KIND_CURVE;
1125
if( is_closed )
1126
points1->flags |= CV_SEQ_FLAG_CLOSED;
1127
total = points1->total;
1128
}
1129
else
1130
total = points2->cols + points2->rows - 1;
1131
1132
if( (cvtest::randInt(rng) % 3) && !test_cpp )
1133
{
1134
slice.start_index = cvtest::randInt(rng) % total;
1135
slice.end_index = cvtest::randInt(rng) % total;
1136
}
1137
else
1138
slice = CV_WHOLE_SEQ;
1139
1140
return 1;
1141
}
1142
1143
1144
void CV_PerimeterTest::run_func()
1145
{
1146
if(!test_cpp)
1147
result = cvArcLength( points, slice, points1 ? -1 : is_closed );
1148
else
1149
result = cv::arcLength(cv::cvarrToMat(points),
1150
!points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0);
1151
}
1152
1153
1154
int CV_PerimeterTest::validate_test_results( int test_case_idx )
1155
{
1156
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1157
int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1;
1158
double result0 = 0;
1159
Point2f prev_pt, pt;
1160
CvPoint2D32f *ptr;
1161
1162
if( len < 0 )
1163
len += total;
1164
1165
len = MIN( len, total );
1166
//len -= !is_closed && len == total;
1167
1168
ptr = (CvPoint2D32f*)points2->data.fl;
1169
prev_pt = ptr[(is_closed ? slice.start_index+len-1 : slice.start_index) % total];
1170
1171
for( i = 0; i < len + (len < total && (!is_closed || len==1)); i++ )
1172
{
1173
pt = ptr[(i + slice.start_index) % total];
1174
double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y;
1175
result0 += sqrt(dx*dx + dy*dy);
1176
prev_pt = pt;
1177
}
1178
1179
if( cvIsNaN(result) || cvIsInf(result) )
1180
{
1181
ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result );
1182
code = cvtest::TS::FAIL_INVALID_OUTPUT;
1183
}
1184
else if( fabs(result - result0) > FLT_EPSILON*100*result0 )
1185
{
1186
ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 );
1187
code = cvtest::TS::FAIL_BAD_ACCURACY;
1188
}
1189
1190
if( code < 0 )
1191
ts->set_failed_test_info( code );
1192
return code;
1193
}
1194
1195
1196
/****************************************************************************************\
1197
* FitEllipse Test *
1198
\****************************************************************************************/
1199
1200
class CV_FitEllipseTest : public CV_BaseShapeDescrTest
1201
{
1202
public:
1203
CV_FitEllipseTest();
1204
1205
protected:
1206
int prepare_test_case( int test_case_idx );
1207
void generate_point_set( void* points );
1208
void run_func(void);
1209
int validate_test_results( int test_case_idx );
1210
RotatedRect box0, box;
1211
double min_ellipse_size, max_noise;
1212
};
1213
1214
1215
CV_FitEllipseTest::CV_FitEllipseTest()
1216
{
1217
min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least
1218
max_log_size = 10;
1219
min_ellipse_size = 10;
1220
max_noise = 0.05;
1221
}
1222
1223
1224
void CV_FitEllipseTest::generate_point_set( void* pointsSet )
1225
{
1226
RNG& rng = ts->get_rng();
1227
int i, total, point_type;
1228
CvSeqReader reader;
1229
uchar* data = 0;
1230
double a, b;
1231
1232
box0.center.x = (float)((low.val[0] + high.val[0])*0.5);
1233
box0.center.y = (float)((low.val[1] + high.val[1])*0.5);
1234
box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2);
1235
box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2);
1236
box0.angle = (float)(cvtest::randReal(rng)*180);
1237
a = cos(box0.angle*CV_PI/180.);
1238
b = sin(box0.angle*CV_PI/180.);
1239
1240
if( box0.size.width > box0.size.height )
1241
{
1242
float t;
1243
CV_SWAP( box0.size.width, box0.size.height, t );
1244
}
1245
memset( &reader, 0, sizeof(reader) );
1246
1247
if( CV_IS_SEQ(pointsSet) )
1248
{
1249
CvSeq* ptseq = (CvSeq*)pointsSet;
1250
total = ptseq->total;
1251
point_type = CV_SEQ_ELTYPE(ptseq);
1252
cvStartReadSeq( ptseq, &reader );
1253
}
1254
else
1255
{
1256
CvMat* ptm = (CvMat*)pointsSet;
1257
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1258
total = ptm->rows + ptm->cols - 1;
1259
point_type = CV_MAT_TYPE(ptm->type);
1260
data = ptm->data.ptr;
1261
}
1262
1263
CV_Assert(point_type == CV_32SC2 || point_type == CV_32FC2);
1264
1265
for( i = 0; i < total; i++ )
1266
{
1267
CvPoint* pp;
1268
CvPoint2D32f p = {0, 0};
1269
double angle = cvtest::randReal(rng)*CV_PI*2;
1270
double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);
1271
double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);
1272
p.x = (float)(box0.center.x + a*x + b*y);
1273
p.y = (float)(box0.center.y - b*x + a*y);
1274
1275
if( reader.ptr )
1276
{
1277
pp = (CvPoint*)reader.ptr;
1278
CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1279
}
1280
else
1281
pp = ((CvPoint*)data) + i;
1282
if( point_type == CV_32SC2 )
1283
{
1284
pp->x = cvRound(p.x);
1285
pp->y = cvRound(p.y);
1286
}
1287
else
1288
*(CvPoint2D32f*)pp = p;
1289
}
1290
}
1291
1292
1293
int CV_FitEllipseTest::prepare_test_case( int test_case_idx )
1294
{
1295
min_log_size = MAX(min_log_size,4);
1296
max_log_size = MAX(min_log_size,max_log_size);
1297
return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1298
}
1299
1300
1301
void CV_FitEllipseTest::run_func()
1302
{
1303
if(!test_cpp)
1304
box = cvFitEllipse2( points );
1305
else
1306
box = cv::fitEllipse(cv::cvarrToMat(points));
1307
}
1308
1309
int CV_FitEllipseTest::validate_test_results( int test_case_idx )
1310
{
1311
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1312
double diff_angle;
1313
1314
if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) ||
1315
cvIsNaN(box.center.y) || cvIsInf(box.center.y) ||
1316
cvIsNaN(box.size.width) || cvIsInf(box.size.width) ||
1317
cvIsNaN(box.size.height) || cvIsInf(box.size.height) ||
1318
cvIsNaN(box.angle) || cvIsInf(box.angle) )
1319
{
1320
ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n",
1321
box.center.x, box.center.y, box.size.width, box.size.height, box.angle );
1322
code = cvtest::TS::FAIL_INVALID_OUTPUT;
1323
goto _exit_;
1324
}
1325
1326
box.angle = (float)(90-box.angle);
1327
if( box.angle < 0 )
1328
box.angle += 360;
1329
if( box.angle > 360 )
1330
box.angle -= 360;
1331
1332
if( fabs(box.center.x - box0.center.x) > 3 ||
1333
fabs(box.center.y - box0.center.y) > 3 ||
1334
fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) ||
1335
fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) )
1336
{
1337
ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t"
1338
"(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n",
1339
box.center.x, box.center.y, box.size.width, box.size.height,
1340
box0.center.x, box0.center.y, box0.size.width, box0.size.height );
1341
code = cvtest::TS::FAIL_BAD_ACCURACY;
1342
goto _exit_;
1343
}
1344
1345
diff_angle = fabs(box0.angle - box.angle);
1346
diff_angle = MIN( diff_angle, fabs(diff_angle - 360));
1347
diff_angle = MIN( diff_angle, fabs(diff_angle - 180));
1348
1349
if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 )
1350
{
1351
ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n",
1352
box.angle, box0.angle );
1353
code = cvtest::TS::FAIL_BAD_ACCURACY;
1354
goto _exit_;
1355
}
1356
1357
_exit_:
1358
1359
#if 0
1360
if( code < 0 )
1361
{
1362
cvNamedWindow( "test", 0 );
1363
IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4),
1364
cvRound(low_high_range*4)), 8, 3 );
1365
cvZero( img );
1366
1367
box.center.x += (float)low_high_range*2;
1368
box.center.y += (float)low_high_range*2;
1369
cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 );
1370
1371
for( int i = 0; i < points2->rows + points2->cols - 1; i++ )
1372
{
1373
CvPoint pt;
1374
pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2);
1375
pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2);
1376
cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 );
1377
}
1378
1379
cvShowImage( "test", img );
1380
cvReleaseImage( &img );
1381
cvWaitKey(0);
1382
}
1383
#endif
1384
1385
if( code < 0 )
1386
{
1387
ts->set_failed_test_info( code );
1388
}
1389
return code;
1390
}
1391
1392
1393
class CV_FitEllipseSmallTest : public cvtest::BaseTest
1394
{
1395
public:
1396
CV_FitEllipseSmallTest() {}
1397
~CV_FitEllipseSmallTest() {}
1398
protected:
1399
void run(int)
1400
{
1401
Size sz(50, 50);
1402
vector<vector<Point> > c;
1403
c.push_back(vector<Point>());
1404
int scale = 1;
1405
Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale;
1406
c[0].push_back(Point(2, 0)*scale+ofs);
1407
c[0].push_back(Point(0, 2)*scale+ofs);
1408
c[0].push_back(Point(0, 6)*scale+ofs);
1409
c[0].push_back(Point(2, 8)*scale+ofs);
1410
c[0].push_back(Point(6, 8)*scale+ofs);
1411
c[0].push_back(Point(8, 6)*scale+ofs);
1412
c[0].push_back(Point(8, 2)*scale+ofs);
1413
c[0].push_back(Point(6, 0)*scale+ofs);
1414
1415
RotatedRect e = fitEllipse(c[0]);
1416
CV_Assert( fabs(e.center.x - 4) <= 1. &&
1417
fabs(e.center.y - 4) <= 1. &&
1418
fabs(e.size.width - 9) <= 1. &&
1419
fabs(e.size.height - 9) <= 1. );
1420
}
1421
};
1422
1423
1424
// Regression test for incorrect fitEllipse result reported in Bug #3989
1425
// Check edge cases for rotation angles of ellipse ([-180, 90, 0, 90, 180] degrees)
1426
class CV_FitEllipseParallelTest : public CV_FitEllipseTest
1427
{
1428
public:
1429
CV_FitEllipseParallelTest();
1430
~CV_FitEllipseParallelTest();
1431
protected:
1432
void generate_point_set( void* points );
1433
void run_func(void);
1434
Mat pointsMat;
1435
};
1436
1437
CV_FitEllipseParallelTest::CV_FitEllipseParallelTest()
1438
{
1439
min_ellipse_size = 5;
1440
}
1441
1442
void CV_FitEllipseParallelTest::generate_point_set( void* )
1443
{
1444
RNG& rng = ts->get_rng();
1445
int height = (int)(MAX(high.val[0] - low.val[0], min_ellipse_size));
1446
int width = (int)(MAX(high.val[1] - low.val[1], min_ellipse_size));
1447
const int angle = ( (cvtest::randInt(rng) % 5) - 2 ) * 90;
1448
const int dim = max(height, width);
1449
const Point center = Point(dim*2, dim*2);
1450
1451
if( width > height )
1452
{
1453
int t;
1454
CV_SWAP( width, height, t );
1455
}
1456
1457
Mat image = Mat::zeros(dim*4, dim*4, CV_8UC1);
1458
ellipse(image, center, Size(height, width), angle,
1459
0, 360, Scalar(255, 0, 0), 1, 8);
1460
1461
box0.center.x = (float)center.x;
1462
box0.center.y = (float)center.y;
1463
box0.size.width = (float)width*2;
1464
box0.size.height = (float)height*2;
1465
box0.angle = (float)angle;
1466
1467
vector<vector<Point> > contours;
1468
findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
1469
Mat(contours[0]).convertTo(pointsMat, CV_32F);
1470
}
1471
1472
void CV_FitEllipseParallelTest::run_func()
1473
{
1474
box = cv::fitEllipse(pointsMat);
1475
}
1476
1477
CV_FitEllipseParallelTest::~CV_FitEllipseParallelTest(){
1478
pointsMat.release();
1479
}
1480
1481
/****************************************************************************************\
1482
* FitLine Test *
1483
\****************************************************************************************/
1484
1485
class CV_FitLineTest : public CV_BaseShapeDescrTest
1486
{
1487
public:
1488
CV_FitLineTest();
1489
1490
protected:
1491
int prepare_test_case( int test_case_idx );
1492
void generate_point_set( void* points );
1493
void run_func(void);
1494
int validate_test_results( int test_case_idx );
1495
double max_noise;
1496
AutoBuffer<float> line, line0;
1497
int dist_type;
1498
double reps, aeps;
1499
};
1500
1501
1502
CV_FitLineTest::CV_FitLineTest()
1503
{
1504
min_log_size = 5; // for robust line fitting a dozen of points is needed at least
1505
max_log_size = 10;
1506
max_noise = 0.05;
1507
}
1508
1509
void CV_FitLineTest::generate_point_set( void* pointsSet )
1510
{
1511
RNG& rng = ts->get_rng();
1512
int i, k, n, total, point_type;
1513
CvSeqReader reader;
1514
uchar* data = 0;
1515
double s = 0;
1516
1517
n = dims;
1518
for( k = 0; k < n; k++ )
1519
{
1520
line0[k+n] = (float)((low.val[k] + high.val[k])*0.5);
1521
line0[k] = (float)(high.val[k] - low.val[k]);
1522
if( cvtest::randInt(rng) % 2 )
1523
line0[k] = -line0[k];
1524
s += (double)line0[k]*line0[k];
1525
}
1526
1527
s = 1./sqrt(s);
1528
for( k = 0; k < n; k++ )
1529
line0[k] = (float)(line0[k]*s);
1530
1531
memset( &reader, 0, sizeof(reader) );
1532
1533
if( CV_IS_SEQ(pointsSet) )
1534
{
1535
CvSeq* ptseq = (CvSeq*)pointsSet;
1536
total = ptseq->total;
1537
point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq));
1538
cvStartReadSeq( ptseq, &reader );
1539
}
1540
else
1541
{
1542
CvMat* ptm = (CvMat*)pointsSet;
1543
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1544
total = ptm->rows + ptm->cols - 1;
1545
point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type));
1546
data = ptm->data.ptr;
1547
}
1548
1549
for( i = 0; i < total; i++ )
1550
{
1551
int* pi;
1552
float* pf;
1553
float p[4], t;
1554
if( reader.ptr )
1555
{
1556
pi = (int*)reader.ptr;
1557
pf = (float*)reader.ptr;
1558
CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
1559
}
1560
else
1561
{
1562
pi = (int*)data + i*n;
1563
pf = (float*)data + i*n;
1564
}
1565
1566
t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2);
1567
1568
for( k = 0; k < n; k++ )
1569
{
1570
p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]);
1571
1572
if( point_type == CV_32S )
1573
pi[k] = cvRound(p[k]);
1574
else
1575
pf[k] = p[k];
1576
}
1577
}
1578
}
1579
1580
int CV_FitLineTest::prepare_test_case( int test_case_idx )
1581
{
1582
RNG& rng = ts->get_rng();
1583
dims = cvtest::randInt(rng) % 2 + 2;
1584
line.allocate(dims * 2);
1585
line0.allocate(dims * 2);
1586
min_log_size = MAX(min_log_size,5);
1587
max_log_size = MAX(min_log_size,max_log_size);
1588
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1589
dist_type = cvtest::randInt(rng) % 6 + 1;
1590
dist_type += dist_type == CV_DIST_C;
1591
reps = 0.1; aeps = 0.01;
1592
return code;
1593
}
1594
1595
1596
void CV_FitLineTest::run_func()
1597
{
1598
if(!test_cpp)
1599
cvFitLine( points, dist_type, 0, reps, aeps, line.data());
1600
else if(dims == 2)
1601
cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps);
1602
else
1603
cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps);
1604
}
1605
1606
int CV_FitLineTest::validate_test_results( int test_case_idx )
1607
{
1608
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1609
int k, max_k = 0;
1610
double vec_diff = 0, t;
1611
1612
for( k = 0; k < dims*2; k++ )
1613
{
1614
if( cvIsNaN(line[k]) || cvIsInf(line[k]) )
1615
{
1616
ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n",
1617
k, line[k] );
1618
code = cvtest::TS::FAIL_INVALID_OUTPUT;
1619
goto _exit_;
1620
}
1621
}
1622
1623
if( fabs(line0[1]) > fabs(line0[0]) )
1624
max_k = 1;
1625
if( fabs(line0[dims-1]) > fabs(line0[max_k]) )
1626
max_k = dims-1;
1627
if( line0[max_k] < 0 )
1628
for( k = 0; k < dims; k++ )
1629
line0[k] = -line0[k];
1630
if( line[max_k] < 0 )
1631
for( k = 0; k < dims; k++ )
1632
line[k] = -line[k];
1633
1634
for( k = 0; k < dims; k++ )
1635
{
1636
double dt = line[k] - line0[k];
1637
vec_diff += dt*dt;
1638
}
1639
1640
if( sqrt(vec_diff) > 0.05 )
1641
{
1642
if( dims == 2 )
1643
ts->printf( cvtest::TS::LOG,
1644
"The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n",
1645
line[0], line[1], line0[0], line0[1] );
1646
else
1647
ts->printf( cvtest::TS::LOG,
1648
"The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n",
1649
line[0], line[1], line[2], line0[0], line0[1], line0[2] );
1650
code = cvtest::TS::FAIL_BAD_ACCURACY;
1651
goto _exit_;
1652
}
1653
1654
t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k];
1655
for( k = 0; k < dims; k++ )
1656
{
1657
double p = line0[k+dims] + t*line0[k] - line[k+dims];
1658
vec_diff += p*p;
1659
}
1660
1661
if( sqrt(vec_diff) > 1*MAX(fabs(t),1) )
1662
{
1663
if( dims == 2 )
1664
ts->printf( cvtest::TS::LOG,
1665
"The computed line point (%.2f,%.2f) is too far from the actual line\n",
1666
line[2]+line0[2], line[3]+line0[3] );
1667
else
1668
ts->printf( cvtest::TS::LOG,
1669
"The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n",
1670
line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] );
1671
code = cvtest::TS::FAIL_BAD_ACCURACY;
1672
goto _exit_;
1673
}
1674
1675
_exit_:
1676
1677
if( code < 0 )
1678
{
1679
ts->set_failed_test_info( code );
1680
}
1681
return code;
1682
}
1683
1684
/****************************************************************************************\
1685
* ContourMoments Test *
1686
\****************************************************************************************/
1687
1688
1689
static void
1690
cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes,
1691
double max_r_scale, double angle, CvArr* points, RNG& rng )
1692
{
1693
int i, total, point_type;
1694
uchar* data = 0;
1695
CvSeqReader reader;
1696
memset( &reader, 0, sizeof(reader) );
1697
1698
if( CV_IS_SEQ(points) )
1699
{
1700
CvSeq* ptseq = (CvSeq*)points;
1701
total = ptseq->total;
1702
point_type = CV_SEQ_ELTYPE(ptseq);
1703
cvStartReadSeq( ptseq, &reader );
1704
}
1705
else
1706
{
1707
CvMat* ptm = (CvMat*)points;
1708
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );
1709
total = ptm->rows + ptm->cols - 1;
1710
point_type = CV_MAT_TYPE(ptm->type);
1711
data = ptm->data.ptr;
1712
}
1713
1714
assert( point_type == CV_32SC2 || point_type == CV_32FC2 );
1715
1716
for( i = 0; i < total; i++ )
1717
{
1718
CvPoint* pp;
1719
Point2f p;
1720
1721
double phi0 = 2*CV_PI*i/total;
1722
double phi = CV_PI*angle/180.;
1723
double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale);
1724
double ta = axes.height*t;
1725
double tb = axes.width*t;
1726
double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb;
1727
double c = cos(phi), s = sin(phi);
1728
p.x = (float)(c0*c - s0*s + center.x);
1729
p.y = (float)(c0*s + s0*c + center.y);
1730
1731
if( reader.ptr )
1732
{
1733
pp = (CvPoint*)reader.ptr;
1734
CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );
1735
}
1736
else
1737
pp = ((CvPoint*)data) + i;
1738
1739
if( point_type == CV_32SC2 )
1740
{
1741
pp->x = cvRound(p.x);
1742
pp->y = cvRound(p.y);
1743
}
1744
else
1745
*(CvPoint2D32f*)pp = cvPoint2D32f(p);
1746
}
1747
}
1748
1749
1750
class CV_ContourMomentsTest : public CV_BaseShapeDescrTest
1751
{
1752
public:
1753
CV_ContourMomentsTest();
1754
1755
protected:
1756
int prepare_test_case( int test_case_idx );
1757
void generate_point_set( void* points );
1758
void run_func(void);
1759
int validate_test_results( int test_case_idx );
1760
CvMoments moments0, moments;
1761
double area0, area;
1762
Size2f axes;
1763
Point2f center;
1764
int max_max_r_scale;
1765
double max_r_scale, angle;
1766
Size img_size;
1767
};
1768
1769
1770
CV_ContourMomentsTest::CV_ContourMomentsTest()
1771
{
1772
min_log_size = 3;
1773
max_log_size = 8;
1774
max_max_r_scale = 15;
1775
low_high_range = 200;
1776
enable_flt_points = false;
1777
}
1778
1779
1780
void CV_ContourMomentsTest::generate_point_set( void* pointsSet )
1781
{
1782
RNG& rng = ts->get_rng();
1783
float max_sz;
1784
1785
axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);
1786
axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);
1787
max_sz = MAX(axes.width, axes.height);
1788
1789
img_size.width = img_size.height = cvRound(low_high_range*2.2);
1790
1791
center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8);
1792
center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8);
1793
1794
assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width &&
1795
0 < center.y - max_sz && center.y + max_sz < img_size.height );
1796
1797
max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01;
1798
angle = cvtest::randReal(rng)*360;
1799
1800
cvTsGenerateTousledBlob( cvPoint2D32f(center), cvSize2D32f(axes), max_r_scale, angle, pointsSet, rng );
1801
1802
if( points1 )
1803
points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON;
1804
}
1805
1806
1807
int CV_ContourMomentsTest::prepare_test_case( int test_case_idx )
1808
{
1809
min_log_size = MAX(min_log_size,3);
1810
max_log_size = MIN(max_log_size,8);
1811
max_log_size = MAX(min_log_size,max_log_size);
1812
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );
1813
return code;
1814
}
1815
1816
1817
void CV_ContourMomentsTest::run_func()
1818
{
1819
if(!test_cpp)
1820
{
1821
cvMoments( points, &moments );
1822
area = cvContourArea( points );
1823
}
1824
else
1825
{
1826
moments = cvMoments(cv::moments(cv::cvarrToMat(points)));
1827
area = cv::contourArea(cv::cvarrToMat(points));
1828
}
1829
}
1830
1831
1832
int CV_ContourMomentsTest::validate_test_results( int test_case_idx )
1833
{
1834
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );
1835
int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00));
1836
CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 );
1837
CvPoint* pt = (CvPoint*)points2->data.i;
1838
int count = points2->cols + points2->rows - 1;
1839
double max_v0 = 0;
1840
1841
cvZero(img);
1842
cvFillPoly( img, &pt, &count, 1, cvScalarAll(1));
1843
cvMoments( img, &moments0 );
1844
1845
for( i = 0; i < n; i++ )
1846
{
1847
double t = fabs((&moments0.m00)[i]);
1848
max_v0 = MAX(max_v0, t);
1849
}
1850
1851
for( i = 0; i <= n; i++ )
1852
{
1853
double v = i < n ? (&moments.m00)[i] : area;
1854
double v0 = i < n ? (&moments0.m00)[i] : moments0.m00;
1855
1856
if( cvIsNaN(v) || cvIsInf(v) )
1857
{
1858
ts->printf( cvtest::TS::LOG,
1859
"The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v );
1860
code = cvtest::TS::FAIL_INVALID_OUTPUT;
1861
break;
1862
}
1863
1864
if( fabs(v - v0) > 0.1*max_v0 )
1865
{
1866
ts->printf( cvtest::TS::LOG,
1867
"The computed contour %s is %g, while it should be %g\n",
1868
i < n ? "moment" : "area", v, v0 );
1869
code = cvtest::TS::FAIL_BAD_ACCURACY;
1870
break;
1871
}
1872
}
1873
1874
if( code < 0 )
1875
{
1876
#if 0
1877
cvCmpS( img, 0, img, CV_CMP_GT );
1878
cvNamedWindow( "test", 1 );
1879
cvShowImage( "test", img );
1880
cvWaitKey();
1881
#endif
1882
ts->set_failed_test_info( code );
1883
}
1884
1885
cvReleaseMat( &img );
1886
return code;
1887
}
1888
1889
1890
////////////////////////////////////// Perimeter/Area/Slice test ///////////////////////////////////
1891
1892
class CV_PerimeterAreaSliceTest : public cvtest::BaseTest
1893
{
1894
public:
1895
CV_PerimeterAreaSliceTest();
1896
~CV_PerimeterAreaSliceTest();
1897
protected:
1898
void run(int);
1899
};
1900
1901
CV_PerimeterAreaSliceTest::CV_PerimeterAreaSliceTest()
1902
{
1903
}
1904
CV_PerimeterAreaSliceTest::~CV_PerimeterAreaSliceTest() {}
1905
1906
void CV_PerimeterAreaSliceTest::run( int )
1907
{
1908
Ptr<CvMemStorage> storage(cvCreateMemStorage());
1909
RNG& rng = theRNG();
1910
const double min_r = 90, max_r = 120;
1911
1912
for( int i = 0; i < 100; i++ )
1913
{
1914
ts->update_context( this, i, true );
1915
int n = rng.uniform(3, 30);
1916
cvClearMemStorage(storage);
1917
CvSeq* contour = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(CvPoint), storage);
1918
double dphi = CV_PI*2/n;
1919
Point center;
1920
center.x = rng.uniform(cvCeil(max_r), cvFloor(640-max_r));
1921
center.y = rng.uniform(cvCeil(max_r), cvFloor(480-max_r));
1922
1923
for( int j = 0; j < n; j++ )
1924
{
1925
CvPoint pt = CV_STRUCT_INITIALIZER;
1926
double r = rng.uniform(min_r, max_r);
1927
double phi = j*dphi;
1928
pt.x = cvRound(center.x + r*cos(phi));
1929
pt.y = cvRound(center.y - r*sin(phi));
1930
cvSeqPush(contour, &pt);
1931
}
1932
1933
CvSlice slice = {0, 0};
1934
for(;;)
1935
{
1936
slice.start_index = rng.uniform(-n/2, 3*n/2);
1937
slice.end_index = rng.uniform(-n/2, 3*n/2);
1938
int len = cvSliceLength(slice, contour);
1939
if( len > 2 )
1940
break;
1941
}
1942
CvSeq *cslice = cvSeqSlice(contour, slice);
1943
/*printf( "%d. (%d, %d) of %d, length = %d, length1 = %d\n",
1944
i, slice.start_index, slice.end_index,
1945
contour->total, cvSliceLength(slice, contour), cslice->total );
1946
1947
double area0 = cvContourArea(cslice);
1948
double area1 = cvContourArea(contour, slice);
1949
if( area0 != area1 )
1950
{
1951
ts->printf(cvtest::TS::LOG,
1952
"The contour area slice is computed differently (%g vs %g)\n", area0, area1 );
1953
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
1954
return;
1955
}*/
1956
1957
double len0 = cvArcLength(cslice, CV_WHOLE_SEQ, 1);
1958
double len1 = cvArcLength(contour, slice, 1);
1959
if( len0 != len1 )
1960
{
1961
ts->printf(cvtest::TS::LOG,
1962
"The contour arc length is computed differently (%g vs %g)\n", len0, len1 );
1963
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
1964
return;
1965
}
1966
}
1967
ts->set_failed_test_info(cvtest::TS::OK);
1968
}
1969
1970
1971
TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); }
1972
TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); }
1973
TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); }
1974
TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); }
1975
TEST(Imgproc_MinCircle2, accuracy) { CV_MinCircleTest2 test; test.safe_run(); }
1976
TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); }
1977
TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); }
1978
TEST(Imgproc_FitEllipse, parallel) { CV_FitEllipseParallelTest test; test.safe_run(); }
1979
TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); }
1980
TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); }
1981
TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); }
1982
TEST(Imgproc_FitEllipse, small) { CV_FitEllipseSmallTest test; test.safe_run(); }
1983
1984
1985
1986
PARAM_TEST_CASE(ConvexityDefects_regression_5908, bool, int)
1987
{
1988
public:
1989
int start_index;
1990
bool clockwise;
1991
1992
Mat contour;
1993
1994
virtual void SetUp()
1995
{
1996
clockwise = GET_PARAM(0);
1997
start_index = GET_PARAM(1);
1998
1999
const int N = 11;
2000
const Point2i points[N] = {
2001
Point2i(154, 408),
2002
Point2i(45, 223),
2003
Point2i(115, 275), // inner
2004
Point2i(104, 166),
2005
Point2i(154, 256), // inner
2006
Point2i(169, 144),
2007
Point2i(185, 256), // inner
2008
Point2i(235, 170),
2009
Point2i(240, 320), // inner
2010
Point2i(330, 287),
2011
Point2i(224, 390)
2012
};
2013
2014
contour = Mat(N, 1, CV_32SC2);
2015
for (int i = 0; i < N; i++)
2016
{
2017
contour.at<Point2i>(i) = (!clockwise) // image and convexHull coordinate systems are different
2018
? points[(start_index + i) % N]
2019
: points[N - 1 - ((start_index + i) % N)];
2020
}
2021
}
2022
};
2023
2024
TEST_P(ConvexityDefects_regression_5908, simple)
2025
{
2026
std::vector<int> hull;
2027
cv::convexHull(contour, hull, clockwise, false);
2028
2029
std::vector<Vec4i> result;
2030
cv::convexityDefects(contour, hull, result);
2031
2032
EXPECT_EQ(4, (int)result.size());
2033
}
2034
2035
INSTANTIATE_TEST_CASE_P(Imgproc, ConvexityDefects_regression_5908,
2036
testing::Combine(
2037
testing::Bool(),
2038
testing::Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
2039
));
2040
2041
}} // namespace
2042
/* End of file. */
2043
2044