Path: blob/master/modules/imgproc/test/test_convhull.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "test_precomp.hpp"4243namespace opencv_test { namespace {4445/*static int46cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n )47{48CvPoint2D32f v0 = v[n-1];49int i, sign = 0;5051for( i = 0; i < n; i++ )52{53CvPoint2D32f v1 = v[i];54float dx = pt.x - v0.x, dy = pt.y - v0.y;55float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y;56double t = (double)dx*dy1 - (double)dx1*dy;57if( fabs(t) > DBL_EPSILON )58{59if( t*sign < 0 )60break;61if( sign == 0 )62sign = t < 0 ? -1 : 1;63}64else if( fabs(dx) + fabs(dy) < DBL_EPSILON )65return i+1;66v0 = v1;67}6869return i < n ? -1 : 0;70}*/7172CV_INLINE double73cvTsDist( CvPoint2D32f a, CvPoint2D32f b )74{75double dx = a.x - b.x;76double dy = a.y - b.y;77return sqrt(dx*dx + dy*dy);78}79CV_INLINE double80cvTsDist( const Point2f& a, const Point2f& b )81{82double dx = a.x - b.x;83double dy = a.y - b.y;84return sqrt(dx*dx + dy*dy);85}8687CV_INLINE double88cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b )89{90double d0 = cvTsDist( pt, a ), d1;91double dd = cvTsDist( a, b );92if( dd < FLT_EPSILON )93return d0;94d1 = cvTsDist( pt, b );95dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd;96d0 = MIN( d0, d1 );97return MIN( d0, dd );98}99100static double101cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 )102{103int i;104Point2f v = vv[n-1], v0;105double min_dist_num = FLT_MAX, min_dist_denom = 1;106int min_dist_idx = -1, min_on_edge = 0;107int counter = 0;108double result;109110for( i = 0; i < n; i++ )111{112double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1;113int on_edge = 0, idx = i;114115v0 = v; v = vv[i];116dx = v.x - v0.x; dy = v.y - v0.y;117dx1 = pt.x - v0.x; dy1 = pt.y - v0.y;118dx2 = pt.x - v.x; dy2 = pt.y - v.y;119120if( dx2*dx + dy2*dy >= 0 )121dist_num = dx2*dx2 + dy2*dy2;122else if( dx1*dx + dy1*dy <= 0 )123{124dist_num = dx1*dx1 + dy1*dy1;125idx = i - 1;126if( idx < 0 ) idx = n-1;127}128else129{130dist_num = (dy1*dx - dx1*dy);131dist_num *= dist_num;132dist_denom = dx*dx + dy*dy;133on_edge = 1;134}135136if( dist_num*min_dist_denom < min_dist_num*dist_denom )137{138min_dist_num = dist_num;139min_dist_denom = dist_denom;140min_dist_idx = idx;141min_on_edge = on_edge;142if( min_dist_num == 0 )143break;144}145146if( (v0.y <= pt.y && v.y <= pt.y) ||147(v0.y > pt.y && v.y > pt.y) ||148(v0.x < pt.x && v.x < pt.x) )149continue;150151dist_num = dy1*dx - dx1*dy;152if( dy < 0 )153dist_num = -dist_num;154counter += dist_num > 0;155}156157result = sqrt(min_dist_num/min_dist_denom);158if( counter % 2 == 0 )159result = -result;160161if( _idx )162*_idx = min_dist_idx;163if( _on_edge )164*_on_edge = min_on_edge;165166return result;167}168169static cv::Point2f170cvTsMiddlePoint(const cv::Point2f &a, const cv::Point2f &b)171{172return cv::Point2f((a.x + b.x) / 2, (a.y + b.y) / 2);173}174175static bool176cvTsIsPointOnLineSegment(const cv::Point2f &x, const cv::Point2f &a, const cv::Point2f &b)177{178double d1 = cvTsDist(cvPoint2D32f(x.x, x.y), cvPoint2D32f(a.x, a.y));179double d2 = cvTsDist(cvPoint2D32f(x.x, x.y), cvPoint2D32f(b.x, b.y));180double d3 = cvTsDist(cvPoint2D32f(a.x, a.y), cvPoint2D32f(b.x, b.y));181182return (abs(d1 + d2 - d3) <= (1E-5));183}184185186/****************************************************************************************\187* Base class for shape descriptor tests *188\****************************************************************************************/189190class CV_BaseShapeDescrTest : public cvtest::BaseTest191{192public:193CV_BaseShapeDescrTest();194virtual ~CV_BaseShapeDescrTest();195void clear();196197protected:198int read_params( CvFileStorage* fs );199void run_func(void);200int prepare_test_case( int test_case_idx );201int validate_test_results( int test_case_idx );202virtual void generate_point_set( void* points );203virtual void extract_points();204205int min_log_size;206int max_log_size;207int dims;208bool enable_flt_points;209210CvMemStorage* storage;211CvSeq* points1;212CvMat* points2;213void* points;214void* result;215double low_high_range;216Scalar low, high;217218bool test_cpp;219};220221222CV_BaseShapeDescrTest::CV_BaseShapeDescrTest()223{224points1 = 0;225points2 = 0;226points = 0;227storage = 0;228test_case_count = 500;229min_log_size = 0;230max_log_size = 10;231low = high = cvScalarAll(0);232low_high_range = 50;233dims = 2;234enable_flt_points = true;235236test_cpp = false;237}238239240CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest()241{242clear();243}244245246void CV_BaseShapeDescrTest::clear()247{248cvtest::BaseTest::clear();249cvReleaseMemStorage( &storage );250cvReleaseMat( &points2 );251points1 = 0;252points = 0;253}254255256int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs )257{258int code = cvtest::BaseTest::read_params( fs );259if( code < 0 )260return code;261262test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count );263min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size );264max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size );265266min_log_size = cvtest::clipInt( min_log_size, 0, 8 );267max_log_size = cvtest::clipInt( max_log_size, 0, 10 );268if( min_log_size > max_log_size )269{270int t;271CV_SWAP( min_log_size, max_log_size, t );272}273274return 0;275}276277278void CV_BaseShapeDescrTest::generate_point_set( void* pointsSet )279{280RNG& rng = ts->get_rng();281int i, k, n, total, point_type;282CvSeqReader reader;283uchar* data = 0;284double a[4], b[4];285286for( k = 0; k < 4; k++ )287{288a[k] = high.val[k] - low.val[k];289b[k] = low.val[k];290}291memset( &reader, 0, sizeof(reader) );292293if( CV_IS_SEQ(pointsSet) )294{295CvSeq* ptseq = (CvSeq*)pointsSet;296total = ptseq->total;297point_type = CV_SEQ_ELTYPE(ptseq);298cvStartReadSeq( ptseq, &reader );299}300else301{302CvMat* ptm = (CvMat*)pointsSet;303assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );304total = ptm->rows + ptm->cols - 1;305point_type = CV_MAT_TYPE(ptm->type);306data = ptm->data.ptr;307}308309n = CV_MAT_CN(point_type);310point_type = CV_MAT_DEPTH(point_type);311312assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 );313314for( i = 0; i < total; i++ )315{316int* pi;317float* pf;318if( reader.ptr )319{320pi = (int*)reader.ptr;321pf = (float*)reader.ptr;322CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );323}324else325{326pi = (int*)data + i*n;327pf = (float*)data + i*n;328}329if( point_type == CV_32S )330for( k = 0; k < n; k++ )331pi[k] = cvRound(cvtest::randReal(rng)*a[k] + b[k]);332else333for( k = 0; k < n; k++ )334pf[k] = (float)(cvtest::randReal(rng)*a[k] + b[k]);335}336}337338339int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx )340{341int size;342int use_storage = 0;343int point_type;344int i;345RNG& rng = ts->get_rng();346347cvtest::BaseTest::prepare_test_case( test_case_idx );348349clear();350size = cvRound( exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) );351use_storage = cvtest::randInt(rng) % 2;352point_type = CV_MAKETYPE(cvtest::randInt(rng) %353(enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims);354355if( use_storage )356{357storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 );358points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage );359cvSeqPushMulti( points1, 0, size );360points = points1;361}362else363{364int rows = 1, cols = size;365if( cvtest::randInt(rng) % 2 )366rows = size, cols = 1;367368points2 = cvCreateMat( rows, cols, point_type );369points = points2;370}371372for( i = 0; i < 4; i++ )373{374low.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2;375high.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2;376if( low.val[i] > high.val[i] )377{378double t;379CV_SWAP( low.val[i], high.val[i], t );380}381if( high.val[i] < low.val[i] + 1 )382high.val[i] += 1;383}384385generate_point_set( points );386387test_cpp = (cvtest::randInt(rng) & 16) == 0;388return 1;389}390391392void CV_BaseShapeDescrTest::extract_points()393{394if( points1 )395{396points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) );397cvCvtSeqToArray( points1, points2->data.ptr );398}399400if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points )401{402CvMat tmp = cvMat( points2->rows, points2->cols,403(points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr );404cvConvert( points2, &tmp );405}406}407408409void CV_BaseShapeDescrTest::run_func(void)410{411}412413414int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ )415{416extract_points();417return 0;418}419420421/****************************************************************************************\422* Convex Hull Test *423\****************************************************************************************/424425class CV_ConvHullTest : public CV_BaseShapeDescrTest426{427public:428CV_ConvHullTest();429virtual ~CV_ConvHullTest();430void clear();431432protected:433void run_func(void);434int prepare_test_case( int test_case_idx );435int validate_test_results( int test_case_idx );436437CvSeq* hull1;438CvMat* hull2;439void* hull_storage;440int orientation;441int return_points;442};443444445CV_ConvHullTest::CV_ConvHullTest()446{447hull1 = 0;448hull2 = 0;449hull_storage = 0;450orientation = return_points = 0;451}452453454CV_ConvHullTest::~CV_ConvHullTest()455{456clear();457}458459460void CV_ConvHullTest::clear()461{462CV_BaseShapeDescrTest::clear();463cvReleaseMat( &hull2 );464hull1 = 0;465hull_storage = 0;466}467468469int CV_ConvHullTest::prepare_test_case( int test_case_idx )470{471int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );472int use_storage_for_hull = 0;473RNG& rng = ts->get_rng();474475if( code <= 0 )476return code;477478orientation = cvtest::randInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE;479return_points = cvtest::randInt(rng) % 2;480481use_storage_for_hull = (cvtest::randInt(rng) % 2) && !test_cpp;482if( use_storage_for_hull )483{484if( !storage )485storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 );486hull_storage = storage;487}488else489{490int rows, cols;491int sz = points1 ? points1->total : points2->cols + points2->rows - 1;492int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type);493494if( cvtest::randInt(rng) % 2 )495rows = sz, cols = 1;496else497rows = 1, cols = sz;498499hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 );500hull_storage = hull2;501}502503return code;504}505506507void CV_ConvHullTest::run_func()508{509if(!test_cpp)510hull1 = cvConvexHull2( points, hull_storage, orientation, return_points );511else512{513cv::Mat _points = cv::cvarrToMat(points);514bool clockwise = orientation == CV_CLOCKWISE;515size_t n = 0;516if( !return_points )517{518std::vector<int> _hull;519cv::convexHull(_points, _hull, clockwise);520n = _hull.size();521memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));522}523else if(_points.type() == CV_32SC2)524{525std::vector<cv::Point> _hull;526cv::convexHull(_points, _hull, clockwise);527n = _hull.size();528memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));529}530else if(_points.type() == CV_32FC2)531{532std::vector<cv::Point2f> _hull;533cv::convexHull(_points, _hull, clockwise);534n = _hull.size();535memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0]));536}537if(hull2->rows > hull2->cols)538hull2->rows = (int)n;539else540hull2->cols = (int)n;541}542}543544545int CV_ConvHullTest::validate_test_results( int test_case_idx )546{547int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );548CvMat* hull = 0;549CvMat* mask = 0;550int i, point_count, hull_count;551CvPoint2D32f *p, *h;552CvSeq header, hheader, *ptseq, *hseq;553CvSeqBlock block, hblock;554555if( points1 )556ptseq = points1;557else558ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type),559sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr,560points2->rows + points2->cols - 1, &header, &block );561point_count = ptseq->total;562p = (CvPoint2D32f*)(points2->data.ptr);563564if( hull1 )565hseq = hull1;566else567hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type),568sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr,569hull2->rows + hull2->cols - 1, &hheader, &hblock );570hull_count = hseq->total;571hull = cvCreateMat( 1, hull_count, CV_32FC2 );572mask = cvCreateMat( 1, hull_count, CV_8UC1 );573cvZero( mask );574Mat _mask = cvarrToMat(mask);575576h = (CvPoint2D32f*)(hull->data.ptr);577578// extract convex hull points579if( return_points )580{581cvCvtSeqToArray( hseq, hull->data.ptr );582if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 )583{584CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr );585cvConvert( &tmp, hull );586}587}588else589{590CvSeqReader reader;591cvStartReadSeq( hseq, &reader );592593for( i = 0; i < hull_count; i++ )594{595schar* ptr = reader.ptr;596int idx;597CV_NEXT_SEQ_ELEM( hseq->elem_size, reader );598599if( hull1 )600idx = cvSeqElemIdx( ptseq, *(uchar**)ptr );601else602idx = *(int*)ptr;603604if( idx < 0 || idx >= point_count )605{606ts->printf( cvtest::TS::LOG, "Invalid convex hull point #%d\n", i );607code = cvtest::TS::FAIL_INVALID_OUTPUT;608goto _exit_;609}610h[i] = p[idx];611}612}613614// check that the convex hull is a convex polygon615if( hull_count >= 3 )616{617CvPoint2D32f pt0 = h[hull_count-1];618for( i = 0; i < hull_count; i++ )619{620int j = i+1;621CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0];622float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y;623float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y;624double t = (double)dx0*dy1 - (double)dx1*dy0;625if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) )626{627ts->printf( cvtest::TS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i );628code = cvtest::TS::FAIL_INVALID_OUTPUT;629goto _exit_;630}631pt0 = pt1;632}633}634635// check that all the points are inside the hull or on the hull edge636// and at least hull_point points are at the hull vertices637for( i = 0; i < point_count; i++ )638{639int idx = 0, on_edge = 0;640double pptresult = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge );641642if( pptresult < 0 )643{644ts->printf( cvtest::TS::LOG, "The point #%d is outside of the convex hull\n", i );645code = cvtest::TS::FAIL_BAD_ACCURACY;646goto _exit_;647}648649if( pptresult < FLT_EPSILON && !on_edge )650mask->data.ptr[idx] = (uchar)1;651}652653if( cvtest::norm( _mask, Mat::zeros(_mask.dims, _mask.size, _mask.type()), NORM_L1 ) != hull_count )654{655ts->printf( cvtest::TS::LOG, "Not every convex hull vertex coincides with some input point\n" );656code = cvtest::TS::FAIL_BAD_ACCURACY;657goto _exit_;658}659660_exit_:661662cvReleaseMat( &hull );663cvReleaseMat( &mask );664if( code < 0 )665ts->set_failed_test_info( code );666return code;667}668669670/****************************************************************************************\671* MinAreaRect Test *672\****************************************************************************************/673674class CV_MinAreaRectTest : public CV_BaseShapeDescrTest675{676public:677CV_MinAreaRectTest();678679protected:680void run_func(void);681int validate_test_results( int test_case_idx );682683CvBox2D box;684CvPoint2D32f box_pt[4];685};686687688CV_MinAreaRectTest::CV_MinAreaRectTest()689{690}691692693void CV_MinAreaRectTest::run_func()694{695if(!test_cpp)696{697box = cvMinAreaRect2( points, storage );698cvBoxPoints( box, box_pt );699}700else701{702cv::RotatedRect r = cv::minAreaRect(cv::cvarrToMat(points));703box = cvBox2D(r);704r.points((cv::Point2f*)box_pt);705}706}707708709int CV_MinAreaRectTest::validate_test_results( int test_case_idx )710{711double eps = 1e-1;712int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );713int i, j, point_count = points2->rows + points2->cols - 1;714CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr);715int mask[] = {0,0,0,0};716717// check that the bounding box is a rotated rectangle:718// 1. diagonals should be equal719// 2. they must intersect in their middle points720{721double d0 = cvTsDist( box_pt[0], box_pt[2] );722double d1 = cvTsDist( box_pt[1], box_pt[3] );723724double x0 = (box_pt[0].x + box_pt[2].x)*0.5;725double y0 = (box_pt[0].y + box_pt[2].y)*0.5;726double x1 = (box_pt[1].x + box_pt[3].x)*0.5;727double y1 = (box_pt[1].y + box_pt[3].y)*0.5;728729if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) )730{731ts->printf( cvtest::TS::LOG, "The bounding box is not a rectangle\n" );732code = cvtest::TS::FAIL_INVALID_OUTPUT;733goto _exit_;734}735}736737#if 0738{739int n = 4;740double a = 8, c = 8, b = 100, d = 150;741CvPoint bp[4], *bpp = bp;742cvNamedWindow( "test", 1 );743IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );744cvZero(img);745for( i = 0; i < point_count; i++ )746cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 );747for( i = 0; i < n; i++ )748bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d));749cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 );750cvShowImage( "test", img );751cvWaitKey();752cvReleaseImage(&img);753}754#endif755756// check that the box includes all the points757// and there is at least one point at (or very close to) every box side758for( i = 0; i < point_count; i++ )759{760int idx = 0, on_edge = 0;761double pptresult = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge );762if( pptresult < -eps )763{764ts->printf( cvtest::TS::LOG, "The point #%d is outside of the box\n", i );765code = cvtest::TS::FAIL_BAD_ACCURACY;766goto _exit_;767}768769if( pptresult < eps )770{771for( j = 0; j < 4; j++ )772{773double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] );774if( d < eps )775mask[j] = (uchar)1;776}777}778}779780if( mask[0] + mask[1] + mask[2] + mask[3] != 4 )781{782ts->printf( cvtest::TS::LOG, "Not every box side has a point nearby\n" );783code = cvtest::TS::FAIL_BAD_ACCURACY;784goto _exit_;785}786787_exit_:788789if( code < 0 )790ts->set_failed_test_info( code );791return code;792}793794795/****************************************************************************************\796* MinEnclosingTriangle Test *797\****************************************************************************************/798799class CV_MinTriangleTest : public CV_BaseShapeDescrTest800{801public:802CV_MinTriangleTest();803804protected:805void run_func(void);806int validate_test_results( int test_case_idx );807std::vector<cv::Point2f> getTriangleMiddlePoints();808809std::vector<cv::Point2f> convexPolygon;810std::vector<cv::Point2f> triangle;811};812813814CV_MinTriangleTest::CV_MinTriangleTest()815{816}817818std::vector<cv::Point2f> CV_MinTriangleTest::getTriangleMiddlePoints()819{820std::vector<cv::Point2f> triangleMiddlePoints;821822for (int i = 0; i < 3; i++) {823triangleMiddlePoints.push_back(cvTsMiddlePoint(triangle[i], triangle[(i + 1) % 3]));824}825826return triangleMiddlePoints;827}828829830void CV_MinTriangleTest::run_func()831{832std::vector<cv::Point2f> pointsAsVector;833834cv::cvarrToMat(points).convertTo(pointsAsVector, CV_32F);835836cv::minEnclosingTriangle(pointsAsVector, triangle);837cv::convexHull(pointsAsVector, convexPolygon, true, true);838}839840841int CV_MinTriangleTest::validate_test_results( int test_case_idx )842{843bool errorEnclosed = false, errorMiddlePoints = false, errorFlush = true;844double eps = 1e-4;845int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );846847#if 0848{849int n = 3;850double a = 8, c = 8, b = 100, d = 150;851CvPoint bp[4], *bpp = bp;852cvNamedWindow( "test", 1 );853IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );854cvZero(img);855for( i = 0; i < point_count; i++ )856cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 );857for( i = 0; i < n; i++ )858bp[i] = cvPoint(cvRound(triangle[i].x*a+b),cvRound(triangle[i].y*c+d));859cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 );860cvShowImage( "test", img );861cvWaitKey();862cvReleaseImage(&img);863}864#endif865866int polygonVertices = (int) convexPolygon.size();867868if (polygonVertices > 2) {869// Check if all points are enclosed by the triangle870for (int i = 0; (i < polygonVertices) && (!errorEnclosed); i++)871{872if (cv::pointPolygonTest(triangle, cv::Point2f(convexPolygon[i].x, convexPolygon[i].y), true) < (-eps))873errorEnclosed = true;874}875876// Check if triangle edges middle points touch the polygon877std::vector<cv::Point2f> middlePoints = getTriangleMiddlePoints();878879for (int i = 0; (i < 3) && (!errorMiddlePoints); i++)880{881bool isTouching = false;882883for (int j = 0; (j < polygonVertices) && (!isTouching); j++)884{885if (cvTsIsPointOnLineSegment(middlePoints[i], convexPolygon[j],886convexPolygon[(j + 1) % polygonVertices]))887isTouching = true;888}889890errorMiddlePoints = (isTouching) ? false : true;891}892893// Check if at least one of the edges is flush894for (int i = 0; (i < 3) && (errorFlush); i++)895{896for (int j = 0; (j < polygonVertices) && (errorFlush); j++)897{898if ((cvTsIsPointOnLineSegment(convexPolygon[j], triangle[i],899triangle[(i + 1) % 3])) &&900(cvTsIsPointOnLineSegment(convexPolygon[(j + 1) % polygonVertices], triangle[i],901triangle[(i + 1) % 3])))902errorFlush = false;903}904}905906// Report any found errors907if (errorEnclosed)908{909ts->printf( cvtest::TS::LOG,910"All points should be enclosed by the triangle.\n" );911code = cvtest::TS::FAIL_BAD_ACCURACY;912}913else if (errorMiddlePoints)914{915ts->printf( cvtest::TS::LOG,916"All triangle edges middle points should touch the convex hull of the points.\n" );917code = cvtest::TS::FAIL_INVALID_OUTPUT;918}919else if (errorFlush)920{921ts->printf( cvtest::TS::LOG,922"At least one edge of the enclosing triangle should be flush with one edge of the polygon.\n" );923code = cvtest::TS::FAIL_INVALID_OUTPUT;924}925}926927if ( code < 0 )928ts->set_failed_test_info( code );929930return code;931}932933934/****************************************************************************************\935* MinEnclosingCircle Test *936\****************************************************************************************/937938class CV_MinCircleTest : public CV_BaseShapeDescrTest939{940public:941CV_MinCircleTest();942943protected:944void run_func(void);945int validate_test_results( int test_case_idx );946947Point2f center;948float radius;949};950951952CV_MinCircleTest::CV_MinCircleTest()953{954}955956957void CV_MinCircleTest::run_func()958{959if(!test_cpp)960{961CvPoint2D32f c_center = cvPoint2D32f(center);962cvMinEnclosingCircle( points, &c_center, &radius );963center = c_center;964}965else966{967cv::Point2f tmpcenter;968cv::minEnclosingCircle(cv::cvarrToMat(points), tmpcenter, radius);969center = tmpcenter;970}971}972973974int CV_MinCircleTest::validate_test_results( int test_case_idx )975{976double eps = 1.03;977int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );978int i, j = 0, point_count = points2->rows + points2->cols - 1;979Point2f *p = (Point2f*)(points2->data.ptr);980Point2f v[3];981982#if 0983{984double a = 2, b = 200, d = 400;985cvNamedWindow( "test", 1 );986IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );987cvZero(img);988for( i = 0; i < point_count; i++ )989cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 );990cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)),991cvRound(radius*a), CV_RGB(255,255,0), 1 );992cvShowImage( "test", img );993cvWaitKey();994cvReleaseImage(&img);995}996#endif997998// check that the circle contains all the points inside and999// remember at most 3 points that are close to the boundary1000for( i = 0; i < point_count; i++ )1001{1002double d = cvTsDist(p[i], center);1003if( d > radius )1004{1005ts->printf( cvtest::TS::LOG, "The point #%d is outside of the circle\n", i );1006code = cvtest::TS::FAIL_BAD_ACCURACY;1007goto _exit_;1008}10091010if( radius - d < eps*radius && j < 3 )1011v[j++] = p[i];1012}10131014if( point_count >= 2 && (j < 2 || (j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps)) )1015{1016ts->printf( cvtest::TS::LOG,1017"There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" );1018code = cvtest::TS::FAIL_BAD_ACCURACY;1019goto _exit_;1020}10211022_exit_:10231024if( code < 0 )1025ts->set_failed_test_info( code );1026return code;1027}10281029/****************************************************************************************\1030* MinEnclosingCircle Test 2 *1031\****************************************************************************************/10321033class CV_MinCircleTest2 : public CV_BaseShapeDescrTest1034{1035public:1036CV_MinCircleTest2();1037protected:1038RNG rng;1039void run_func(void);1040int validate_test_results( int test_case_idx );1041float delta;1042};104310441045CV_MinCircleTest2::CV_MinCircleTest2()1046{1047rng = ts->get_rng();1048}104910501051void CV_MinCircleTest2::run_func()1052{1053Point2f center = Point2f(rng.uniform(0.0f, 1000.0f), rng.uniform(0.0f, 1000.0f));;1054float radius = rng.uniform(0.0f, 500.0f);1055float angle = (float)rng.uniform(0.0f, (float)(CV_2PI));1056vector<Point2f> pts;1057pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));1058angle += (float)CV_PI;1059pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));1060float radius2 = radius * radius;1061float x = rng.uniform(center.x - radius, center.x + radius);1062float deltaX = x - center.x;1063float upperBoundY = sqrt(radius2 - deltaX * deltaX);1064float y = rng.uniform(center.y - upperBoundY, center.y + upperBoundY);1065pts.push_back(Point2f(x, y));1066// Find the minimum area enclosing circle1067Point2f calcCenter;1068float calcRadius;1069minEnclosingCircle(pts, calcCenter, calcRadius);1070delta = (float)cv::norm(calcCenter - center) + abs(calcRadius - radius);1071}10721073int CV_MinCircleTest2::validate_test_results( int test_case_idx )1074{1075float eps = 1.0F;1076int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );1077if (delta > eps)1078{1079ts->printf( cvtest::TS::LOG, "Delta center and calcCenter > %f\n", eps );1080code = cvtest::TS::FAIL_BAD_ACCURACY;1081ts->set_failed_test_info( code );1082}1083return code;1084}10851086/****************************************************************************************\1087* Perimeter Test *1088\****************************************************************************************/10891090class CV_PerimeterTest : public CV_BaseShapeDescrTest1091{1092public:1093CV_PerimeterTest();10941095protected:1096int prepare_test_case( int test_case_idx );1097void run_func(void);1098int validate_test_results( int test_case_idx );1099CvSlice slice;1100int is_closed;1101double result;1102};110311041105CV_PerimeterTest::CV_PerimeterTest()1106{1107}110811091110int CV_PerimeterTest::prepare_test_case( int test_case_idx )1111{1112int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );1113RNG& rng = ts->get_rng();1114int total;11151116if( code < 0 )1117return code;11181119is_closed = cvtest::randInt(rng) % 2;11201121if( points1 )1122{1123points1->flags |= CV_SEQ_KIND_CURVE;1124if( is_closed )1125points1->flags |= CV_SEQ_FLAG_CLOSED;1126total = points1->total;1127}1128else1129total = points2->cols + points2->rows - 1;11301131if( (cvtest::randInt(rng) % 3) && !test_cpp )1132{1133slice.start_index = cvtest::randInt(rng) % total;1134slice.end_index = cvtest::randInt(rng) % total;1135}1136else1137slice = CV_WHOLE_SEQ;11381139return 1;1140}114111421143void CV_PerimeterTest::run_func()1144{1145if(!test_cpp)1146result = cvArcLength( points, slice, points1 ? -1 : is_closed );1147else1148result = cv::arcLength(cv::cvarrToMat(points),1149!points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0);1150}115111521153int CV_PerimeterTest::validate_test_results( int test_case_idx )1154{1155int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );1156int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1;1157double result0 = 0;1158Point2f prev_pt, pt;1159CvPoint2D32f *ptr;11601161if( len < 0 )1162len += total;11631164len = MIN( len, total );1165//len -= !is_closed && len == total;11661167ptr = (CvPoint2D32f*)points2->data.fl;1168prev_pt = ptr[(is_closed ? slice.start_index+len-1 : slice.start_index) % total];11691170for( i = 0; i < len + (len < total && (!is_closed || len==1)); i++ )1171{1172pt = ptr[(i + slice.start_index) % total];1173double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y;1174result0 += sqrt(dx*dx + dy*dy);1175prev_pt = pt;1176}11771178if( cvIsNaN(result) || cvIsInf(result) )1179{1180ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result );1181code = cvtest::TS::FAIL_INVALID_OUTPUT;1182}1183else if( fabs(result - result0) > FLT_EPSILON*100*result0 )1184{1185ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 );1186code = cvtest::TS::FAIL_BAD_ACCURACY;1187}11881189if( code < 0 )1190ts->set_failed_test_info( code );1191return code;1192}119311941195/****************************************************************************************\1196* FitEllipse Test *1197\****************************************************************************************/11981199class CV_FitEllipseTest : public CV_BaseShapeDescrTest1200{1201public:1202CV_FitEllipseTest();12031204protected:1205int prepare_test_case( int test_case_idx );1206void generate_point_set( void* points );1207void run_func(void);1208int validate_test_results( int test_case_idx );1209RotatedRect box0, box;1210double min_ellipse_size, max_noise;1211};121212131214CV_FitEllipseTest::CV_FitEllipseTest()1215{1216min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least1217max_log_size = 10;1218min_ellipse_size = 10;1219max_noise = 0.05;1220}122112221223void CV_FitEllipseTest::generate_point_set( void* pointsSet )1224{1225RNG& rng = ts->get_rng();1226int i, total, point_type;1227CvSeqReader reader;1228uchar* data = 0;1229double a, b;12301231box0.center.x = (float)((low.val[0] + high.val[0])*0.5);1232box0.center.y = (float)((low.val[1] + high.val[1])*0.5);1233box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2);1234box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2);1235box0.angle = (float)(cvtest::randReal(rng)*180);1236a = cos(box0.angle*CV_PI/180.);1237b = sin(box0.angle*CV_PI/180.);12381239if( box0.size.width > box0.size.height )1240{1241float t;1242CV_SWAP( box0.size.width, box0.size.height, t );1243}1244memset( &reader, 0, sizeof(reader) );12451246if( CV_IS_SEQ(pointsSet) )1247{1248CvSeq* ptseq = (CvSeq*)pointsSet;1249total = ptseq->total;1250point_type = CV_SEQ_ELTYPE(ptseq);1251cvStartReadSeq( ptseq, &reader );1252}1253else1254{1255CvMat* ptm = (CvMat*)pointsSet;1256assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );1257total = ptm->rows + ptm->cols - 1;1258point_type = CV_MAT_TYPE(ptm->type);1259data = ptm->data.ptr;1260}12611262CV_Assert(point_type == CV_32SC2 || point_type == CV_32FC2);12631264for( i = 0; i < total; i++ )1265{1266CvPoint* pp;1267CvPoint2D32f p = {0, 0};1268double angle = cvtest::randReal(rng)*CV_PI*2;1269double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);1270double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise);1271p.x = (float)(box0.center.x + a*x + b*y);1272p.y = (float)(box0.center.y - b*x + a*y);12731274if( reader.ptr )1275{1276pp = (CvPoint*)reader.ptr;1277CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );1278}1279else1280pp = ((CvPoint*)data) + i;1281if( point_type == CV_32SC2 )1282{1283pp->x = cvRound(p.x);1284pp->y = cvRound(p.y);1285}1286else1287*(CvPoint2D32f*)pp = p;1288}1289}129012911292int CV_FitEllipseTest::prepare_test_case( int test_case_idx )1293{1294min_log_size = MAX(min_log_size,4);1295max_log_size = MAX(min_log_size,max_log_size);1296return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );1297}129812991300void CV_FitEllipseTest::run_func()1301{1302if(!test_cpp)1303box = cvFitEllipse2( points );1304else1305box = cv::fitEllipse(cv::cvarrToMat(points));1306}13071308int CV_FitEllipseTest::validate_test_results( int test_case_idx )1309{1310int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );1311double diff_angle;13121313if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) ||1314cvIsNaN(box.center.y) || cvIsInf(box.center.y) ||1315cvIsNaN(box.size.width) || cvIsInf(box.size.width) ||1316cvIsNaN(box.size.height) || cvIsInf(box.size.height) ||1317cvIsNaN(box.angle) || cvIsInf(box.angle) )1318{1319ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n",1320box.center.x, box.center.y, box.size.width, box.size.height, box.angle );1321code = cvtest::TS::FAIL_INVALID_OUTPUT;1322goto _exit_;1323}13241325box.angle = (float)(90-box.angle);1326if( box.angle < 0 )1327box.angle += 360;1328if( box.angle > 360 )1329box.angle -= 360;13301331if( fabs(box.center.x - box0.center.x) > 3 ||1332fabs(box.center.y - box0.center.y) > 3 ||1333fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) ||1334fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) )1335{1336ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t"1337"(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n",1338box.center.x, box.center.y, box.size.width, box.size.height,1339box0.center.x, box0.center.y, box0.size.width, box0.size.height );1340code = cvtest::TS::FAIL_BAD_ACCURACY;1341goto _exit_;1342}13431344diff_angle = fabs(box0.angle - box.angle);1345diff_angle = MIN( diff_angle, fabs(diff_angle - 360));1346diff_angle = MIN( diff_angle, fabs(diff_angle - 180));13471348if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 )1349{1350ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n",1351box.angle, box0.angle );1352code = cvtest::TS::FAIL_BAD_ACCURACY;1353goto _exit_;1354}13551356_exit_:13571358#if 01359if( code < 0 )1360{1361cvNamedWindow( "test", 0 );1362IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4),1363cvRound(low_high_range*4)), 8, 3 );1364cvZero( img );13651366box.center.x += (float)low_high_range*2;1367box.center.y += (float)low_high_range*2;1368cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 );13691370for( int i = 0; i < points2->rows + points2->cols - 1; i++ )1371{1372CvPoint pt;1373pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2);1374pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2);1375cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 );1376}13771378cvShowImage( "test", img );1379cvReleaseImage( &img );1380cvWaitKey(0);1381}1382#endif13831384if( code < 0 )1385{1386ts->set_failed_test_info( code );1387}1388return code;1389}139013911392class CV_FitEllipseSmallTest : public cvtest::BaseTest1393{1394public:1395CV_FitEllipseSmallTest() {}1396~CV_FitEllipseSmallTest() {}1397protected:1398void run(int)1399{1400Size sz(50, 50);1401vector<vector<Point> > c;1402c.push_back(vector<Point>());1403int scale = 1;1404Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale;1405c[0].push_back(Point(2, 0)*scale+ofs);1406c[0].push_back(Point(0, 2)*scale+ofs);1407c[0].push_back(Point(0, 6)*scale+ofs);1408c[0].push_back(Point(2, 8)*scale+ofs);1409c[0].push_back(Point(6, 8)*scale+ofs);1410c[0].push_back(Point(8, 6)*scale+ofs);1411c[0].push_back(Point(8, 2)*scale+ofs);1412c[0].push_back(Point(6, 0)*scale+ofs);14131414RotatedRect e = fitEllipse(c[0]);1415CV_Assert( fabs(e.center.x - 4) <= 1. &&1416fabs(e.center.y - 4) <= 1. &&1417fabs(e.size.width - 9) <= 1. &&1418fabs(e.size.height - 9) <= 1. );1419}1420};142114221423// Regression test for incorrect fitEllipse result reported in Bug #39891424// Check edge cases for rotation angles of ellipse ([-180, 90, 0, 90, 180] degrees)1425class CV_FitEllipseParallelTest : public CV_FitEllipseTest1426{1427public:1428CV_FitEllipseParallelTest();1429~CV_FitEllipseParallelTest();1430protected:1431void generate_point_set( void* points );1432void run_func(void);1433Mat pointsMat;1434};14351436CV_FitEllipseParallelTest::CV_FitEllipseParallelTest()1437{1438min_ellipse_size = 5;1439}14401441void CV_FitEllipseParallelTest::generate_point_set( void* )1442{1443RNG& rng = ts->get_rng();1444int height = (int)(MAX(high.val[0] - low.val[0], min_ellipse_size));1445int width = (int)(MAX(high.val[1] - low.val[1], min_ellipse_size));1446const int angle = ( (cvtest::randInt(rng) % 5) - 2 ) * 90;1447const int dim = max(height, width);1448const Point center = Point(dim*2, dim*2);14491450if( width > height )1451{1452int t;1453CV_SWAP( width, height, t );1454}14551456Mat image = Mat::zeros(dim*4, dim*4, CV_8UC1);1457ellipse(image, center, Size(height, width), angle,14580, 360, Scalar(255, 0, 0), 1, 8);14591460box0.center.x = (float)center.x;1461box0.center.y = (float)center.y;1462box0.size.width = (float)width*2;1463box0.size.height = (float)height*2;1464box0.angle = (float)angle;14651466vector<vector<Point> > contours;1467findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);1468Mat(contours[0]).convertTo(pointsMat, CV_32F);1469}14701471void CV_FitEllipseParallelTest::run_func()1472{1473box = cv::fitEllipse(pointsMat);1474}14751476CV_FitEllipseParallelTest::~CV_FitEllipseParallelTest(){1477pointsMat.release();1478}14791480/****************************************************************************************\1481* FitLine Test *1482\****************************************************************************************/14831484class CV_FitLineTest : public CV_BaseShapeDescrTest1485{1486public:1487CV_FitLineTest();14881489protected:1490int prepare_test_case( int test_case_idx );1491void generate_point_set( void* points );1492void run_func(void);1493int validate_test_results( int test_case_idx );1494double max_noise;1495AutoBuffer<float> line, line0;1496int dist_type;1497double reps, aeps;1498};149915001501CV_FitLineTest::CV_FitLineTest()1502{1503min_log_size = 5; // for robust line fitting a dozen of points is needed at least1504max_log_size = 10;1505max_noise = 0.05;1506}15071508void CV_FitLineTest::generate_point_set( void* pointsSet )1509{1510RNG& rng = ts->get_rng();1511int i, k, n, total, point_type;1512CvSeqReader reader;1513uchar* data = 0;1514double s = 0;15151516n = dims;1517for( k = 0; k < n; k++ )1518{1519line0[k+n] = (float)((low.val[k] + high.val[k])*0.5);1520line0[k] = (float)(high.val[k] - low.val[k]);1521if( cvtest::randInt(rng) % 2 )1522line0[k] = -line0[k];1523s += (double)line0[k]*line0[k];1524}15251526s = 1./sqrt(s);1527for( k = 0; k < n; k++ )1528line0[k] = (float)(line0[k]*s);15291530memset( &reader, 0, sizeof(reader) );15311532if( CV_IS_SEQ(pointsSet) )1533{1534CvSeq* ptseq = (CvSeq*)pointsSet;1535total = ptseq->total;1536point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq));1537cvStartReadSeq( ptseq, &reader );1538}1539else1540{1541CvMat* ptm = (CvMat*)pointsSet;1542assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );1543total = ptm->rows + ptm->cols - 1;1544point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type));1545data = ptm->data.ptr;1546}15471548for( i = 0; i < total; i++ )1549{1550int* pi;1551float* pf;1552float p[4], t;1553if( reader.ptr )1554{1555pi = (int*)reader.ptr;1556pf = (float*)reader.ptr;1557CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );1558}1559else1560{1561pi = (int*)data + i*n;1562pf = (float*)data + i*n;1563}15641565t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2);15661567for( k = 0; k < n; k++ )1568{1569p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]);15701571if( point_type == CV_32S )1572pi[k] = cvRound(p[k]);1573else1574pf[k] = p[k];1575}1576}1577}15781579int CV_FitLineTest::prepare_test_case( int test_case_idx )1580{1581RNG& rng = ts->get_rng();1582dims = cvtest::randInt(rng) % 2 + 2;1583line.allocate(dims * 2);1584line0.allocate(dims * 2);1585min_log_size = MAX(min_log_size,5);1586max_log_size = MAX(min_log_size,max_log_size);1587int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );1588dist_type = cvtest::randInt(rng) % 6 + 1;1589dist_type += dist_type == CV_DIST_C;1590reps = 0.1; aeps = 0.01;1591return code;1592}159315941595void CV_FitLineTest::run_func()1596{1597if(!test_cpp)1598cvFitLine( points, dist_type, 0, reps, aeps, line.data());1599else if(dims == 2)1600cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps);1601else1602cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps);1603}16041605int CV_FitLineTest::validate_test_results( int test_case_idx )1606{1607int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );1608int k, max_k = 0;1609double vec_diff = 0, t;16101611for( k = 0; k < dims*2; k++ )1612{1613if( cvIsNaN(line[k]) || cvIsInf(line[k]) )1614{1615ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n",1616k, line[k] );1617code = cvtest::TS::FAIL_INVALID_OUTPUT;1618goto _exit_;1619}1620}16211622if( fabs(line0[1]) > fabs(line0[0]) )1623max_k = 1;1624if( fabs(line0[dims-1]) > fabs(line0[max_k]) )1625max_k = dims-1;1626if( line0[max_k] < 0 )1627for( k = 0; k < dims; k++ )1628line0[k] = -line0[k];1629if( line[max_k] < 0 )1630for( k = 0; k < dims; k++ )1631line[k] = -line[k];16321633for( k = 0; k < dims; k++ )1634{1635double dt = line[k] - line0[k];1636vec_diff += dt*dt;1637}16381639if( sqrt(vec_diff) > 0.05 )1640{1641if( dims == 2 )1642ts->printf( cvtest::TS::LOG,1643"The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n",1644line[0], line[1], line0[0], line0[1] );1645else1646ts->printf( cvtest::TS::LOG,1647"The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n",1648line[0], line[1], line[2], line0[0], line0[1], line0[2] );1649code = cvtest::TS::FAIL_BAD_ACCURACY;1650goto _exit_;1651}16521653t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k];1654for( k = 0; k < dims; k++ )1655{1656double p = line0[k+dims] + t*line0[k] - line[k+dims];1657vec_diff += p*p;1658}16591660if( sqrt(vec_diff) > 1*MAX(fabs(t),1) )1661{1662if( dims == 2 )1663ts->printf( cvtest::TS::LOG,1664"The computed line point (%.2f,%.2f) is too far from the actual line\n",1665line[2]+line0[2], line[3]+line0[3] );1666else1667ts->printf( cvtest::TS::LOG,1668"The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n",1669line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] );1670code = cvtest::TS::FAIL_BAD_ACCURACY;1671goto _exit_;1672}16731674_exit_:16751676if( code < 0 )1677{1678ts->set_failed_test_info( code );1679}1680return code;1681}16821683/****************************************************************************************\1684* ContourMoments Test *1685\****************************************************************************************/168616871688static void1689cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes,1690double max_r_scale, double angle, CvArr* points, RNG& rng )1691{1692int i, total, point_type;1693uchar* data = 0;1694CvSeqReader reader;1695memset( &reader, 0, sizeof(reader) );16961697if( CV_IS_SEQ(points) )1698{1699CvSeq* ptseq = (CvSeq*)points;1700total = ptseq->total;1701point_type = CV_SEQ_ELTYPE(ptseq);1702cvStartReadSeq( ptseq, &reader );1703}1704else1705{1706CvMat* ptm = (CvMat*)points;1707assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) );1708total = ptm->rows + ptm->cols - 1;1709point_type = CV_MAT_TYPE(ptm->type);1710data = ptm->data.ptr;1711}17121713assert( point_type == CV_32SC2 || point_type == CV_32FC2 );17141715for( i = 0; i < total; i++ )1716{1717CvPoint* pp;1718Point2f p;17191720double phi0 = 2*CV_PI*i/total;1721double phi = CV_PI*angle/180.;1722double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale);1723double ta = axes.height*t;1724double tb = axes.width*t;1725double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb;1726double c = cos(phi), s = sin(phi);1727p.x = (float)(c0*c - s0*s + center.x);1728p.y = (float)(c0*s + s0*c + center.y);17291730if( reader.ptr )1731{1732pp = (CvPoint*)reader.ptr;1733CV_NEXT_SEQ_ELEM( sizeof(*pp), reader );1734}1735else1736pp = ((CvPoint*)data) + i;17371738if( point_type == CV_32SC2 )1739{1740pp->x = cvRound(p.x);1741pp->y = cvRound(p.y);1742}1743else1744*(CvPoint2D32f*)pp = cvPoint2D32f(p);1745}1746}174717481749class CV_ContourMomentsTest : public CV_BaseShapeDescrTest1750{1751public:1752CV_ContourMomentsTest();17531754protected:1755int prepare_test_case( int test_case_idx );1756void generate_point_set( void* points );1757void run_func(void);1758int validate_test_results( int test_case_idx );1759CvMoments moments0, moments;1760double area0, area;1761Size2f axes;1762Point2f center;1763int max_max_r_scale;1764double max_r_scale, angle;1765Size img_size;1766};176717681769CV_ContourMomentsTest::CV_ContourMomentsTest()1770{1771min_log_size = 3;1772max_log_size = 8;1773max_max_r_scale = 15;1774low_high_range = 200;1775enable_flt_points = false;1776}177717781779void CV_ContourMomentsTest::generate_point_set( void* pointsSet )1780{1781RNG& rng = ts->get_rng();1782float max_sz;17831784axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);1785axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range);1786max_sz = MAX(axes.width, axes.height);17871788img_size.width = img_size.height = cvRound(low_high_range*2.2);17891790center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8);1791center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8);17921793assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width &&17940 < center.y - max_sz && center.y + max_sz < img_size.height );17951796max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01;1797angle = cvtest::randReal(rng)*360;17981799cvTsGenerateTousledBlob( cvPoint2D32f(center), cvSize2D32f(axes), max_r_scale, angle, pointsSet, rng );18001801if( points1 )1802points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON;1803}180418051806int CV_ContourMomentsTest::prepare_test_case( int test_case_idx )1807{1808min_log_size = MAX(min_log_size,3);1809max_log_size = MIN(max_log_size,8);1810max_log_size = MAX(min_log_size,max_log_size);1811int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx );1812return code;1813}181418151816void CV_ContourMomentsTest::run_func()1817{1818if(!test_cpp)1819{1820cvMoments( points, &moments );1821area = cvContourArea( points );1822}1823else1824{1825moments = cvMoments(cv::moments(cv::cvarrToMat(points)));1826area = cv::contourArea(cv::cvarrToMat(points));1827}1828}182918301831int CV_ContourMomentsTest::validate_test_results( int test_case_idx )1832{1833int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx );1834int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00));1835CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 );1836CvPoint* pt = (CvPoint*)points2->data.i;1837int count = points2->cols + points2->rows - 1;1838double max_v0 = 0;18391840cvZero(img);1841cvFillPoly( img, &pt, &count, 1, cvScalarAll(1));1842cvMoments( img, &moments0 );18431844for( i = 0; i < n; i++ )1845{1846double t = fabs((&moments0.m00)[i]);1847max_v0 = MAX(max_v0, t);1848}18491850for( i = 0; i <= n; i++ )1851{1852double v = i < n ? (&moments.m00)[i] : area;1853double v0 = i < n ? (&moments0.m00)[i] : moments0.m00;18541855if( cvIsNaN(v) || cvIsInf(v) )1856{1857ts->printf( cvtest::TS::LOG,1858"The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v );1859code = cvtest::TS::FAIL_INVALID_OUTPUT;1860break;1861}18621863if( fabs(v - v0) > 0.1*max_v0 )1864{1865ts->printf( cvtest::TS::LOG,1866"The computed contour %s is %g, while it should be %g\n",1867i < n ? "moment" : "area", v, v0 );1868code = cvtest::TS::FAIL_BAD_ACCURACY;1869break;1870}1871}18721873if( code < 0 )1874{1875#if 01876cvCmpS( img, 0, img, CV_CMP_GT );1877cvNamedWindow( "test", 1 );1878cvShowImage( "test", img );1879cvWaitKey();1880#endif1881ts->set_failed_test_info( code );1882}18831884cvReleaseMat( &img );1885return code;1886}188718881889////////////////////////////////////// Perimeter/Area/Slice test ///////////////////////////////////18901891class CV_PerimeterAreaSliceTest : public cvtest::BaseTest1892{1893public:1894CV_PerimeterAreaSliceTest();1895~CV_PerimeterAreaSliceTest();1896protected:1897void run(int);1898};18991900CV_PerimeterAreaSliceTest::CV_PerimeterAreaSliceTest()1901{1902}1903CV_PerimeterAreaSliceTest::~CV_PerimeterAreaSliceTest() {}19041905void CV_PerimeterAreaSliceTest::run( int )1906{1907Ptr<CvMemStorage> storage(cvCreateMemStorage());1908RNG& rng = theRNG();1909const double min_r = 90, max_r = 120;19101911for( int i = 0; i < 100; i++ )1912{1913ts->update_context( this, i, true );1914int n = rng.uniform(3, 30);1915cvClearMemStorage(storage);1916CvSeq* contour = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(CvPoint), storage);1917double dphi = CV_PI*2/n;1918Point center;1919center.x = rng.uniform(cvCeil(max_r), cvFloor(640-max_r));1920center.y = rng.uniform(cvCeil(max_r), cvFloor(480-max_r));19211922for( int j = 0; j < n; j++ )1923{1924CvPoint pt = CV_STRUCT_INITIALIZER;1925double r = rng.uniform(min_r, max_r);1926double phi = j*dphi;1927pt.x = cvRound(center.x + r*cos(phi));1928pt.y = cvRound(center.y - r*sin(phi));1929cvSeqPush(contour, &pt);1930}19311932CvSlice slice = {0, 0};1933for(;;)1934{1935slice.start_index = rng.uniform(-n/2, 3*n/2);1936slice.end_index = rng.uniform(-n/2, 3*n/2);1937int len = cvSliceLength(slice, contour);1938if( len > 2 )1939break;1940}1941CvSeq *cslice = cvSeqSlice(contour, slice);1942/*printf( "%d. (%d, %d) of %d, length = %d, length1 = %d\n",1943i, slice.start_index, slice.end_index,1944contour->total, cvSliceLength(slice, contour), cslice->total );19451946double area0 = cvContourArea(cslice);1947double area1 = cvContourArea(contour, slice);1948if( area0 != area1 )1949{1950ts->printf(cvtest::TS::LOG,1951"The contour area slice is computed differently (%g vs %g)\n", area0, area1 );1952ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );1953return;1954}*/19551956double len0 = cvArcLength(cslice, CV_WHOLE_SEQ, 1);1957double len1 = cvArcLength(contour, slice, 1);1958if( len0 != len1 )1959{1960ts->printf(cvtest::TS::LOG,1961"The contour arc length is computed differently (%g vs %g)\n", len0, len1 );1962ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );1963return;1964}1965}1966ts->set_failed_test_info(cvtest::TS::OK);1967}196819691970TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); }1971TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); }1972TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); }1973TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); }1974TEST(Imgproc_MinCircle2, accuracy) { CV_MinCircleTest2 test; test.safe_run(); }1975TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); }1976TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); }1977TEST(Imgproc_FitEllipse, parallel) { CV_FitEllipseParallelTest test; test.safe_run(); }1978TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); }1979TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); }1980TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); }1981TEST(Imgproc_FitEllipse, small) { CV_FitEllipseSmallTest test; test.safe_run(); }1982198319841985PARAM_TEST_CASE(ConvexityDefects_regression_5908, bool, int)1986{1987public:1988int start_index;1989bool clockwise;19901991Mat contour;19921993virtual void SetUp()1994{1995clockwise = GET_PARAM(0);1996start_index = GET_PARAM(1);19971998const int N = 11;1999const Point2i points[N] = {2000Point2i(154, 408),2001Point2i(45, 223),2002Point2i(115, 275), // inner2003Point2i(104, 166),2004Point2i(154, 256), // inner2005Point2i(169, 144),2006Point2i(185, 256), // inner2007Point2i(235, 170),2008Point2i(240, 320), // inner2009Point2i(330, 287),2010Point2i(224, 390)2011};20122013contour = Mat(N, 1, CV_32SC2);2014for (int i = 0; i < N; i++)2015{2016contour.at<Point2i>(i) = (!clockwise) // image and convexHull coordinate systems are different2017? points[(start_index + i) % N]2018: points[N - 1 - ((start_index + i) % N)];2019}2020}2021};20222023TEST_P(ConvexityDefects_regression_5908, simple)2024{2025std::vector<int> hull;2026cv::convexHull(contour, hull, clockwise, false);20272028std::vector<Vec4i> result;2029cv::convexityDefects(contour, hull, result);20302031EXPECT_EQ(4, (int)result.size());2032}20332034INSTANTIATE_TEST_CASE_P(Imgproc, ConvexityDefects_regression_5908,2035testing::Combine(2036testing::Bool(),2037testing::Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)2038));20392040}} // namespace2041/* End of file. */204220432044