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