mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1874 lines
54 KiB
1874 lines
54 KiB
/*M/////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
|
// |
|
// By downloading, copying, installing or using the software you agree to this license. |
|
// If you do not agree to this license, do not download, install, |
|
// copy or use the software. |
|
// |
|
// |
|
// Intel License Agreement |
|
// For Open Source Computer Vision Library |
|
// |
|
// Copyright (C) 2000, Intel Corporation, all rights reserved. |
|
// Third party copyrights are property of their respective owners. |
|
// |
|
// Redistribution and use in source and binary forms, with or without modification, |
|
// are permitted provided that the following conditions are met: |
|
// |
|
// * Redistribution's of source code must retain the above copyright notice, |
|
// this list of conditions and the following disclaimer. |
|
// |
|
// * Redistribution's in binary form must reproduce the above copyright notice, |
|
// this list of conditions and the following disclaimer in the documentation |
|
// and/or other materials provided with the distribution. |
|
// |
|
// * The name of Intel Corporation may not be used to endorse or promote products |
|
// derived from this software without specific prior written permission. |
|
// |
|
// This software is provided by the copyright holders and contributors "as is" and |
|
// any express or implied warranties, including, but not limited to, the implied |
|
// warranties of merchantability and fitness for a particular purpose are disclaimed. |
|
// In no event shall the Intel Corporation or contributors be liable for any direct, |
|
// indirect, incidental, special, exemplary, or consequential damages |
|
// (including, but not limited to, procurement of substitute goods or services; |
|
// loss of use, data, or profits; or business interruption) however caused |
|
// and on any theory of liability, whether in contract, strict liability, |
|
// or tort (including negligence or otherwise) arising in any way out of |
|
// the use of this software, even if advised of the possibility of such damage. |
|
// |
|
//M*/ |
|
|
|
#include "test_precomp.hpp" |
|
|
|
using namespace cv; |
|
using namespace std; |
|
|
|
/*static int |
|
cvTsPointConvexPolygon( CvPoint2D32f pt, CvPoint2D32f* v, int n ) |
|
{ |
|
CvPoint2D32f v0 = v[n-1]; |
|
int i, sign = 0; |
|
|
|
for( i = 0; i < n; i++ ) |
|
{ |
|
CvPoint2D32f v1 = v[i]; |
|
float dx = pt.x - v0.x, dy = pt.y - v0.y; |
|
float dx1 = v1.x - v0.x, dy1 = v1.y - v0.y; |
|
double t = (double)dx*dy1 - (double)dx1*dy; |
|
if( fabs(t) > DBL_EPSILON ) |
|
{ |
|
if( t*sign < 0 ) |
|
break; |
|
if( sign == 0 ) |
|
sign = t < 0 ? -1 : 1; |
|
} |
|
else if( fabs(dx) + fabs(dy) < DBL_EPSILON ) |
|
return i+1; |
|
v0 = v1; |
|
} |
|
|
|
return i < n ? -1 : 0; |
|
}*/ |
|
|
|
CV_INLINE double |
|
cvTsDist( CvPoint2D32f a, CvPoint2D32f b ) |
|
{ |
|
double dx = a.x - b.x; |
|
double dy = a.y - b.y; |
|
return sqrt(dx*dx + dy*dy); |
|
} |
|
|
|
CV_INLINE double |
|
cvTsPtLineDist( CvPoint2D32f pt, CvPoint2D32f a, CvPoint2D32f b ) |
|
{ |
|
double d0 = cvTsDist( pt, a ), d1; |
|
double dd = cvTsDist( a, b ); |
|
if( dd < FLT_EPSILON ) |
|
return d0; |
|
d1 = cvTsDist( pt, b ); |
|
dd = fabs((double)(pt.x - a.x)*(b.y - a.y) - (double)(pt.y - a.y)*(b.x - a.x))/dd; |
|
d0 = MIN( d0, d1 ); |
|
return MIN( d0, dd ); |
|
} |
|
|
|
static double |
|
cvTsPointPolygonTest( CvPoint2D32f pt, const CvPoint2D32f* vv, int n, int* _idx=0, int* _on_edge=0 ) |
|
{ |
|
int i; |
|
CvPoint2D32f v = vv[n-1], v0; |
|
double min_dist_num = FLT_MAX, min_dist_denom = 1; |
|
int min_dist_idx = -1, min_on_edge = 0; |
|
int counter = 0; |
|
double result; |
|
|
|
for( i = 0; i < n; i++ ) |
|
{ |
|
double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1; |
|
int on_edge = 0, idx = i; |
|
|
|
v0 = v; v = vv[i]; |
|
dx = v.x - v0.x; dy = v.y - v0.y; |
|
dx1 = pt.x - v0.x; dy1 = pt.y - v0.y; |
|
dx2 = pt.x - v.x; dy2 = pt.y - v.y; |
|
|
|
if( dx2*dx + dy2*dy >= 0 ) |
|
dist_num = dx2*dx2 + dy2*dy2; |
|
else if( dx1*dx + dy1*dy <= 0 ) |
|
{ |
|
dist_num = dx1*dx1 + dy1*dy1; |
|
idx = i - 1; |
|
if( idx < 0 ) idx = n-1; |
|
} |
|
else |
|
{ |
|
dist_num = (dy1*dx - dx1*dy); |
|
dist_num *= dist_num; |
|
dist_denom = dx*dx + dy*dy; |
|
on_edge = 1; |
|
} |
|
|
|
if( dist_num*min_dist_denom < min_dist_num*dist_denom ) |
|
{ |
|
min_dist_num = dist_num; |
|
min_dist_denom = dist_denom; |
|
min_dist_idx = idx; |
|
min_on_edge = on_edge; |
|
if( min_dist_num == 0 ) |
|
break; |
|
} |
|
|
|
if( (v0.y <= pt.y && v.y <= pt.y) || |
|
(v0.y > pt.y && v.y > pt.y) || |
|
(v0.x < pt.x && v.x < pt.x) ) |
|
continue; |
|
|
|
dist_num = dy1*dx - dx1*dy; |
|
if( dy < 0 ) |
|
dist_num = -dist_num; |
|
counter += dist_num > 0; |
|
} |
|
|
|
result = sqrt(min_dist_num/min_dist_denom); |
|
if( counter % 2 == 0 ) |
|
result = -result; |
|
|
|
if( _idx ) |
|
*_idx = min_dist_idx; |
|
if( _on_edge ) |
|
*_on_edge = min_on_edge; |
|
|
|
return result; |
|
} |
|
|
|
static cv::Point2f |
|
cvTsMiddlePoint(const cv::Point2f &a, const cv::Point2f &b) |
|
{ |
|
return cv::Point2f((a.x + b.x) / 2, (a.y + b.y) / 2); |
|
} |
|
|
|
static bool |
|
cvTsIsPointOnLineSegment(const cv::Point2f &x, const cv::Point2f &a, const cv::Point2f &b) |
|
{ |
|
double d1 = cvTsDist(CvPoint2D32f(x.x, x.y), CvPoint2D32f(a.x, a.y)); |
|
double d2 = cvTsDist(CvPoint2D32f(x.x, x.y), CvPoint2D32f(b.x, b.y)); |
|
double d3 = cvTsDist(CvPoint2D32f(a.x, a.y), CvPoint2D32f(b.x, b.y)); |
|
|
|
return (abs(d1 + d2 - d3) <= (1E-5)); |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* Base class for shape descriptor tests * |
|
\****************************************************************************************/ |
|
|
|
class CV_BaseShapeDescrTest : public cvtest::BaseTest |
|
{ |
|
public: |
|
CV_BaseShapeDescrTest(); |
|
virtual ~CV_BaseShapeDescrTest(); |
|
void clear(); |
|
|
|
protected: |
|
int read_params( CvFileStorage* fs ); |
|
void run_func(void); |
|
int prepare_test_case( int test_case_idx ); |
|
int validate_test_results( int test_case_idx ); |
|
virtual void generate_point_set( void* points ); |
|
virtual void extract_points(); |
|
|
|
int min_log_size; |
|
int max_log_size; |
|
int dims; |
|
bool enable_flt_points; |
|
|
|
CvMemStorage* storage; |
|
CvSeq* points1; |
|
CvMat* points2; |
|
void* points; |
|
void* result; |
|
double low_high_range; |
|
CvScalar low, high; |
|
|
|
bool test_cpp; |
|
}; |
|
|
|
|
|
CV_BaseShapeDescrTest::CV_BaseShapeDescrTest() |
|
{ |
|
points1 = 0; |
|
points2 = 0; |
|
points = 0; |
|
storage = 0; |
|
test_case_count = 500; |
|
min_log_size = 0; |
|
max_log_size = 10; |
|
low = high = cvScalarAll(0); |
|
low_high_range = 50; |
|
dims = 2; |
|
enable_flt_points = true; |
|
|
|
test_cpp = false; |
|
} |
|
|
|
|
|
CV_BaseShapeDescrTest::~CV_BaseShapeDescrTest() |
|
{ |
|
clear(); |
|
} |
|
|
|
|
|
void CV_BaseShapeDescrTest::clear() |
|
{ |
|
cvtest::BaseTest::clear(); |
|
cvReleaseMemStorage( &storage ); |
|
cvReleaseMat( &points2 ); |
|
points1 = 0; |
|
points = 0; |
|
} |
|
|
|
|
|
int CV_BaseShapeDescrTest::read_params( CvFileStorage* fs ) |
|
{ |
|
int code = cvtest::BaseTest::read_params( fs ); |
|
if( code < 0 ) |
|
return code; |
|
|
|
test_case_count = cvReadInt( find_param( fs, "struct_count" ), test_case_count ); |
|
min_log_size = cvReadInt( find_param( fs, "min_log_size" ), min_log_size ); |
|
max_log_size = cvReadInt( find_param( fs, "max_log_size" ), max_log_size ); |
|
|
|
min_log_size = cvtest::clipInt( min_log_size, 0, 8 ); |
|
max_log_size = cvtest::clipInt( max_log_size, 0, 10 ); |
|
if( min_log_size > max_log_size ) |
|
{ |
|
int t; |
|
CV_SWAP( min_log_size, max_log_size, t ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
void CV_BaseShapeDescrTest::generate_point_set( void* pointsSet ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
int i, k, n, total, point_type; |
|
CvSeqReader reader; |
|
uchar* data = 0; |
|
double a[4], b[4]; |
|
|
|
for( k = 0; k < 4; k++ ) |
|
{ |
|
a[k] = high.val[k] - low.val[k]; |
|
b[k] = low.val[k]; |
|
} |
|
memset( &reader, 0, sizeof(reader) ); |
|
|
|
if( CV_IS_SEQ(pointsSet) ) |
|
{ |
|
CvSeq* ptseq = (CvSeq*)pointsSet; |
|
total = ptseq->total; |
|
point_type = CV_SEQ_ELTYPE(ptseq); |
|
cvStartReadSeq( ptseq, &reader ); |
|
} |
|
else |
|
{ |
|
CvMat* ptm = (CvMat*)pointsSet; |
|
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); |
|
total = ptm->rows + ptm->cols - 1; |
|
point_type = CV_MAT_TYPE(ptm->type); |
|
data = ptm->data.ptr; |
|
} |
|
|
|
n = CV_MAT_CN(point_type); |
|
point_type = CV_MAT_DEPTH(point_type); |
|
|
|
assert( (point_type == CV_32S || point_type == CV_32F) && n <= 4 ); |
|
|
|
for( i = 0; i < total; i++ ) |
|
{ |
|
int* pi; |
|
float* pf; |
|
if( reader.ptr ) |
|
{ |
|
pi = (int*)reader.ptr; |
|
pf = (float*)reader.ptr; |
|
CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); |
|
} |
|
else |
|
{ |
|
pi = (int*)data + i*n; |
|
pf = (float*)data + i*n; |
|
} |
|
if( point_type == CV_32S ) |
|
for( k = 0; k < n; k++ ) |
|
pi[k] = cvRound(cvtest::randReal(rng)*a[k] + b[k]); |
|
else |
|
for( k = 0; k < n; k++ ) |
|
pf[k] = (float)(cvtest::randReal(rng)*a[k] + b[k]); |
|
} |
|
} |
|
|
|
|
|
int CV_BaseShapeDescrTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
int size; |
|
int use_storage = 0; |
|
int point_type; |
|
int i; |
|
RNG& rng = ts->get_rng(); |
|
|
|
cvtest::BaseTest::prepare_test_case( test_case_idx ); |
|
|
|
clear(); |
|
size = cvRound( exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2) ); |
|
use_storage = cvtest::randInt(rng) % 2; |
|
point_type = CV_MAKETYPE(cvtest::randInt(rng) % |
|
(enable_flt_points ? 2 : 1) ? CV_32F : CV_32S, dims); |
|
|
|
if( use_storage ) |
|
{ |
|
storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); |
|
points1 = cvCreateSeq( point_type, sizeof(CvSeq), CV_ELEM_SIZE(point_type), storage ); |
|
cvSeqPushMulti( points1, 0, size ); |
|
points = points1; |
|
} |
|
else |
|
{ |
|
int rows = 1, cols = size; |
|
if( cvtest::randInt(rng) % 2 ) |
|
rows = size, cols = 1; |
|
|
|
points2 = cvCreateMat( rows, cols, point_type ); |
|
points = points2; |
|
} |
|
|
|
for( i = 0; i < 4; i++ ) |
|
{ |
|
low.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; |
|
high.val[i] = (cvtest::randReal(rng)-0.5)*low_high_range*2; |
|
if( low.val[i] > high.val[i] ) |
|
{ |
|
double t; |
|
CV_SWAP( low.val[i], high.val[i], t ); |
|
} |
|
if( high.val[i] < low.val[i] + 1 ) |
|
high.val[i] += 1; |
|
} |
|
|
|
generate_point_set( points ); |
|
|
|
test_cpp = (cvtest::randInt(rng) & 16) == 0; |
|
return 1; |
|
} |
|
|
|
|
|
void CV_BaseShapeDescrTest::extract_points() |
|
{ |
|
if( points1 ) |
|
{ |
|
points2 = cvCreateMat( 1, points1->total, CV_SEQ_ELTYPE(points1) ); |
|
cvCvtSeqToArray( points1, points2->data.ptr ); |
|
} |
|
|
|
if( CV_MAT_DEPTH(points2->type) != CV_32F && enable_flt_points ) |
|
{ |
|
CvMat tmp = cvMat( points2->rows, points2->cols, |
|
(points2->type & ~CV_MAT_DEPTH_MASK) | CV_32F, points2->data.ptr ); |
|
cvConvert( points2, &tmp ); |
|
} |
|
} |
|
|
|
|
|
void CV_BaseShapeDescrTest::run_func(void) |
|
{ |
|
} |
|
|
|
|
|
int CV_BaseShapeDescrTest::validate_test_results( int /*test_case_idx*/ ) |
|
{ |
|
extract_points(); |
|
return 0; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* Convex Hull Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_ConvHullTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_ConvHullTest(); |
|
virtual ~CV_ConvHullTest(); |
|
void clear(); |
|
|
|
protected: |
|
void run_func(void); |
|
int prepare_test_case( int test_case_idx ); |
|
int validate_test_results( int test_case_idx ); |
|
|
|
CvSeq* hull1; |
|
CvMat* hull2; |
|
void* hull_storage; |
|
int orientation; |
|
int return_points; |
|
}; |
|
|
|
|
|
CV_ConvHullTest::CV_ConvHullTest() |
|
{ |
|
hull1 = 0; |
|
hull2 = 0; |
|
hull_storage = 0; |
|
orientation = return_points = 0; |
|
} |
|
|
|
|
|
CV_ConvHullTest::~CV_ConvHullTest() |
|
{ |
|
clear(); |
|
} |
|
|
|
|
|
void CV_ConvHullTest::clear() |
|
{ |
|
CV_BaseShapeDescrTest::clear(); |
|
cvReleaseMat( &hull2 ); |
|
hull1 = 0; |
|
hull_storage = 0; |
|
} |
|
|
|
|
|
int CV_ConvHullTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); |
|
int use_storage_for_hull = 0; |
|
RNG& rng = ts->get_rng(); |
|
|
|
if( code <= 0 ) |
|
return code; |
|
|
|
orientation = cvtest::randInt(rng) % 2 ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE; |
|
return_points = cvtest::randInt(rng) % 2; |
|
|
|
use_storage_for_hull = (cvtest::randInt(rng) % 2) && !test_cpp; |
|
if( use_storage_for_hull ) |
|
{ |
|
if( !storage ) |
|
storage = cvCreateMemStorage( (cvtest::randInt(rng)%10 + 1)*1024 ); |
|
hull_storage = storage; |
|
} |
|
else |
|
{ |
|
int rows, cols; |
|
int sz = points1 ? points1->total : points2->cols + points2->rows - 1; |
|
int point_type = points1 ? CV_SEQ_ELTYPE(points1) : CV_MAT_TYPE(points2->type); |
|
|
|
if( cvtest::randInt(rng) % 2 ) |
|
rows = sz, cols = 1; |
|
else |
|
rows = 1, cols = sz; |
|
|
|
hull2 = cvCreateMat( rows, cols, return_points ? point_type : CV_32SC1 ); |
|
hull_storage = hull2; |
|
} |
|
|
|
return code; |
|
} |
|
|
|
|
|
void CV_ConvHullTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
hull1 = cvConvexHull2( points, hull_storage, orientation, return_points ); |
|
else |
|
{ |
|
cv::Mat _points = cv::cvarrToMat(points); |
|
bool clockwise = orientation == CV_CLOCKWISE; |
|
size_t n = 0; |
|
if( !return_points ) |
|
{ |
|
std::vector<int> _hull; |
|
cv::convexHull(_points, _hull, clockwise); |
|
n = _hull.size(); |
|
memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); |
|
} |
|
else if(_points.type() == CV_32SC2) |
|
{ |
|
std::vector<cv::Point> _hull; |
|
cv::convexHull(_points, _hull, clockwise); |
|
n = _hull.size(); |
|
memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); |
|
} |
|
else if(_points.type() == CV_32FC2) |
|
{ |
|
std::vector<cv::Point2f> _hull; |
|
cv::convexHull(_points, _hull, clockwise); |
|
n = _hull.size(); |
|
memcpy(hull2->data.ptr, &_hull[0], n*sizeof(_hull[0])); |
|
} |
|
if(hull2->rows > hull2->cols) |
|
hull2->rows = (int)n; |
|
else |
|
hull2->cols = (int)n; |
|
} |
|
} |
|
|
|
|
|
int CV_ConvHullTest::validate_test_results( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
CvMat* hull = 0; |
|
CvMat* mask = 0; |
|
int i, point_count, hull_count; |
|
CvPoint2D32f *p, *h; |
|
CvSeq header, hheader, *ptseq, *hseq; |
|
CvSeqBlock block, hblock; |
|
|
|
if( points1 ) |
|
ptseq = points1; |
|
else |
|
ptseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(points2->type), |
|
sizeof(CvSeq), CV_ELEM_SIZE(points2->type), points2->data.ptr, |
|
points2->rows + points2->cols - 1, &header, &block ); |
|
point_count = ptseq->total; |
|
p = (CvPoint2D32f*)(points2->data.ptr); |
|
|
|
if( hull1 ) |
|
hseq = hull1; |
|
else |
|
hseq = cvMakeSeqHeaderForArray( CV_MAT_TYPE(hull2->type), |
|
sizeof(CvSeq), CV_ELEM_SIZE(hull2->type), hull2->data.ptr, |
|
hull2->rows + hull2->cols - 1, &hheader, &hblock ); |
|
hull_count = hseq->total; |
|
hull = cvCreateMat( 1, hull_count, CV_32FC2 ); |
|
mask = cvCreateMat( 1, hull_count, CV_8UC1 ); |
|
cvZero( mask ); |
|
Mat _mask = cvarrToMat(mask); |
|
|
|
h = (CvPoint2D32f*)(hull->data.ptr); |
|
|
|
// extract convex hull points |
|
if( return_points ) |
|
{ |
|
cvCvtSeqToArray( hseq, hull->data.ptr ); |
|
if( CV_SEQ_ELTYPE(hseq) != CV_32FC2 ) |
|
{ |
|
CvMat tmp = cvMat( hull->rows, hull->cols, CV_32SC2, hull->data.ptr ); |
|
cvConvert( &tmp, hull ); |
|
} |
|
} |
|
else |
|
{ |
|
CvSeqReader reader; |
|
cvStartReadSeq( hseq, &reader ); |
|
|
|
for( i = 0; i < hull_count; i++ ) |
|
{ |
|
schar* ptr = reader.ptr; |
|
int idx; |
|
CV_NEXT_SEQ_ELEM( hseq->elem_size, reader ); |
|
|
|
if( hull1 ) |
|
idx = cvSeqElemIdx( ptseq, *(uchar**)ptr ); |
|
else |
|
idx = *(int*)ptr; |
|
|
|
if( idx < 0 || idx >= point_count ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Invalid convex hull point #%d\n", i ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
goto _exit_; |
|
} |
|
h[i] = p[idx]; |
|
} |
|
} |
|
|
|
// check that the convex hull is a convex polygon |
|
if( hull_count >= 3 ) |
|
{ |
|
CvPoint2D32f pt0 = h[hull_count-1]; |
|
for( i = 0; i < hull_count; i++ ) |
|
{ |
|
int j = i+1; |
|
CvPoint2D32f pt1 = h[i], pt2 = h[j < hull_count ? j : 0]; |
|
float dx0 = pt1.x - pt0.x, dy0 = pt1.y - pt0.y; |
|
float dx1 = pt2.x - pt1.x, dy1 = pt2.y - pt1.y; |
|
double t = (double)dx0*dy1 - (double)dx1*dy0; |
|
if( (t < 0) ^ (orientation != CV_COUNTER_CLOCKWISE) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The convex hull is not convex or has a wrong orientation (vtx %d)\n", i ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
goto _exit_; |
|
} |
|
pt0 = pt1; |
|
} |
|
} |
|
|
|
// check that all the points are inside the hull or on the hull edge |
|
// and at least hull_point points are at the hull vertices |
|
for( i = 0; i < point_count; i++ ) |
|
{ |
|
int idx = 0, on_edge = 0; |
|
double pptresult = cvTsPointPolygonTest( p[i], h, hull_count, &idx, &on_edge ); |
|
|
|
if( pptresult < 0 ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The point #%d is outside of the convex hull\n", i ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
if( pptresult < FLT_EPSILON && !on_edge ) |
|
mask->data.ptr[idx] = (uchar)1; |
|
} |
|
|
|
if( cvtest::norm( _mask, Mat::zeros(_mask.dims, _mask.size, _mask.type()), NORM_L1 ) != hull_count ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Not every convex hull vertex coincides with some input point\n" ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
_exit_: |
|
|
|
cvReleaseMat( &hull ); |
|
cvReleaseMat( &mask ); |
|
if( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
return code; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* MinAreaRect Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_MinAreaRectTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_MinAreaRectTest(); |
|
|
|
protected: |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
|
|
CvBox2D box; |
|
CvPoint2D32f box_pt[4]; |
|
}; |
|
|
|
|
|
CV_MinAreaRectTest::CV_MinAreaRectTest() |
|
{ |
|
} |
|
|
|
|
|
void CV_MinAreaRectTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
{ |
|
box = cvMinAreaRect2( points, storage ); |
|
cvBoxPoints( box, box_pt ); |
|
} |
|
else |
|
{ |
|
cv::RotatedRect r = cv::minAreaRect(cv::cvarrToMat(points)); |
|
box = (CvBox2D)r; |
|
r.points((cv::Point2f*)box_pt); |
|
} |
|
} |
|
|
|
|
|
int CV_MinAreaRectTest::validate_test_results( int test_case_idx ) |
|
{ |
|
double eps = 1e-1; |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
int i, j, point_count = points2->rows + points2->cols - 1; |
|
CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); |
|
int mask[] = {0,0,0,0}; |
|
|
|
// check that the bounding box is a rotated rectangle: |
|
// 1. diagonals should be equal |
|
// 2. they must intersect in their middle points |
|
{ |
|
double d0 = cvTsDist( box_pt[0], box_pt[2] ); |
|
double d1 = cvTsDist( box_pt[1], box_pt[3] ); |
|
|
|
double x0 = (box_pt[0].x + box_pt[2].x)*0.5; |
|
double y0 = (box_pt[0].y + box_pt[2].y)*0.5; |
|
double x1 = (box_pt[1].x + box_pt[3].x)*0.5; |
|
double y1 = (box_pt[1].y + box_pt[3].y)*0.5; |
|
|
|
if( fabs(d0 - d1) + fabs(x0 - x1) + fabs(y0 - y1) > eps*MAX(d0,d1) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The bounding box is not a rectangle\n" ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
goto _exit_; |
|
} |
|
} |
|
|
|
#if 0 |
|
{ |
|
int n = 4; |
|
double a = 8, c = 8, b = 100, d = 150; |
|
CvPoint bp[4], *bpp = bp; |
|
cvNamedWindow( "test", 1 ); |
|
IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); |
|
cvZero(img); |
|
for( i = 0; i < point_count; i++ ) |
|
cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 ); |
|
for( i = 0; i < n; i++ ) |
|
bp[i] = cvPoint(cvRound(box_pt[i].x*a+b),cvRound(box_pt[i].y*c+d)); |
|
cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 ); |
|
cvShowImage( "test", img ); |
|
cvWaitKey(); |
|
cvReleaseImage(&img); |
|
} |
|
#endif |
|
|
|
// check that the box includes all the points |
|
// and there is at least one point at (or very close to) every box side |
|
for( i = 0; i < point_count; i++ ) |
|
{ |
|
int idx = 0, on_edge = 0; |
|
double pptresult = cvTsPointPolygonTest( p[i], box_pt, 4, &idx, &on_edge ); |
|
if( pptresult < -eps ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The point #%d is outside of the box\n", i ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
if( pptresult < eps ) |
|
{ |
|
for( j = 0; j < 4; j++ ) |
|
{ |
|
double d = cvTsPtLineDist( p[i], box_pt[(j-1)&3], box_pt[j] ); |
|
if( d < eps ) |
|
mask[j] = (uchar)1; |
|
} |
|
} |
|
} |
|
|
|
if( mask[0] + mask[1] + mask[2] + mask[3] != 4 ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Not every box side has a point nearby\n" ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
_exit_: |
|
|
|
if( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
return code; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* MinEnclosingTriangle Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_MinTriangleTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_MinTriangleTest(); |
|
|
|
protected: |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
std::vector<cv::Point2f> getTriangleMiddlePoints(); |
|
|
|
std::vector<cv::Point2f> convexPolygon; |
|
std::vector<cv::Point2f> triangle; |
|
}; |
|
|
|
|
|
CV_MinTriangleTest::CV_MinTriangleTest() |
|
{ |
|
} |
|
|
|
std::vector<cv::Point2f> CV_MinTriangleTest::getTriangleMiddlePoints() |
|
{ |
|
std::vector<cv::Point2f> triangleMiddlePoints; |
|
|
|
for (int i = 0; i < 3; i++) { |
|
triangleMiddlePoints.push_back(cvTsMiddlePoint(triangle[i], triangle[(i + 1) % 3])); |
|
} |
|
|
|
return triangleMiddlePoints; |
|
} |
|
|
|
|
|
void CV_MinTriangleTest::run_func() |
|
{ |
|
std::vector<cv::Point2f> pointsAsVector; |
|
|
|
cv::cvarrToMat(points).convertTo(pointsAsVector, CV_32F); |
|
|
|
cv::minEnclosingTriangle(pointsAsVector, triangle); |
|
cv::convexHull(pointsAsVector, convexPolygon, true, true); |
|
} |
|
|
|
|
|
int CV_MinTriangleTest::validate_test_results( int test_case_idx ) |
|
{ |
|
bool errorEnclosed = false, errorMiddlePoints = false, errorFlush = true; |
|
double eps = 1e-4; |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
|
|
#if 0 |
|
{ |
|
int n = 3; |
|
double a = 8, c = 8, b = 100, d = 150; |
|
CvPoint bp[4], *bpp = bp; |
|
cvNamedWindow( "test", 1 ); |
|
IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); |
|
cvZero(img); |
|
for( i = 0; i < point_count; i++ ) |
|
cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*c+d)), 3, CV_RGB(0,255,0), -1 ); |
|
for( i = 0; i < n; i++ ) |
|
bp[i] = cvPoint(cvRound(triangle[i].x*a+b),cvRound(triangle[i].y*c+d)); |
|
cvPolyLine( img, &bpp, &n, 1, 1, CV_RGB(255,255,0), 1, CV_AA, 0 ); |
|
cvShowImage( "test", img ); |
|
cvWaitKey(); |
|
cvReleaseImage(&img); |
|
} |
|
#endif |
|
|
|
int polygonVertices = (int) convexPolygon.size(); |
|
|
|
if (polygonVertices > 2) { |
|
// Check if all points are enclosed by the triangle |
|
for (int i = 0; (i < polygonVertices) && (!errorEnclosed); i++) |
|
{ |
|
if (cv::pointPolygonTest(triangle, cv::Point2f(convexPolygon[i].x, convexPolygon[i].y), true) < (-eps)) |
|
errorEnclosed = true; |
|
} |
|
|
|
// Check if triangle edges middle points touch the polygon |
|
std::vector<cv::Point2f> middlePoints = getTriangleMiddlePoints(); |
|
|
|
for (int i = 0; (i < 3) && (!errorMiddlePoints); i++) |
|
{ |
|
bool isTouching = false; |
|
|
|
for (int j = 0; (j < polygonVertices) && (!isTouching); j++) |
|
{ |
|
if (cvTsIsPointOnLineSegment(middlePoints[i], convexPolygon[j], |
|
convexPolygon[(j + 1) % polygonVertices])) |
|
isTouching = true; |
|
} |
|
|
|
errorMiddlePoints = (isTouching) ? false : true; |
|
} |
|
|
|
// Check if at least one of the edges is flush |
|
for (int i = 0; (i < 3) && (errorFlush); i++) |
|
{ |
|
for (int j = 0; (j < polygonVertices) && (errorFlush); j++) |
|
{ |
|
if ((cvTsIsPointOnLineSegment(convexPolygon[j], triangle[i], |
|
triangle[(i + 1) % 3])) && |
|
(cvTsIsPointOnLineSegment(convexPolygon[(j + 1) % polygonVertices], triangle[i], |
|
triangle[(i + 1) % 3]))) |
|
errorFlush = false; |
|
} |
|
} |
|
|
|
// Report any found errors |
|
if (errorEnclosed) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"All points should be enclosed by the triangle.\n" ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
} |
|
else if (errorMiddlePoints) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"All triangle edges middle points should touch the convex hull of the points.\n" ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
} |
|
else if (errorFlush) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"At least one edge of the enclosing triangle should be flush with one edge of the polygon.\n" ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
} |
|
} |
|
|
|
if ( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
|
|
return code; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* MinEnclosingCircle Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_MinCircleTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_MinCircleTest(); |
|
|
|
protected: |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
|
|
CvPoint2D32f center; |
|
float radius; |
|
}; |
|
|
|
|
|
CV_MinCircleTest::CV_MinCircleTest() |
|
{ |
|
} |
|
|
|
|
|
void CV_MinCircleTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
cvMinEnclosingCircle( points, ¢er, &radius ); |
|
else |
|
{ |
|
cv::Point2f tmpcenter; |
|
cv::minEnclosingCircle(cv::cvarrToMat(points), tmpcenter, radius); |
|
center = tmpcenter; |
|
} |
|
} |
|
|
|
|
|
int CV_MinCircleTest::validate_test_results( int test_case_idx ) |
|
{ |
|
double eps = 1.03; |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
int i, j = 0, point_count = points2->rows + points2->cols - 1; |
|
CvPoint2D32f *p = (CvPoint2D32f*)(points2->data.ptr); |
|
CvPoint2D32f v[3]; |
|
|
|
#if 0 |
|
{ |
|
double a = 2, b = 200, d = 400; |
|
cvNamedWindow( "test", 1 ); |
|
IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 ); |
|
cvZero(img); |
|
for( i = 0; i < point_count; i++ ) |
|
cvCircle(img,cvPoint(cvRound(p[i].x*a+b),cvRound(p[i].y*a+d)), 3, CV_RGB(0,255,0), -1 ); |
|
cvCircle( img, cvPoint(cvRound(center.x*a+b),cvRound(center.y*a+d)), |
|
cvRound(radius*a), CV_RGB(255,255,0), 1 ); |
|
cvShowImage( "test", img ); |
|
cvWaitKey(); |
|
cvReleaseImage(&img); |
|
} |
|
#endif |
|
|
|
// check that the circle contains all the points inside and |
|
// remember at most 3 points that are close to the boundary |
|
for( i = 0; i < point_count; i++ ) |
|
{ |
|
double d = cvTsDist( p[i], center ); |
|
if( d > radius ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The point #%d is outside of the circle\n", i ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
if( radius - d < eps*radius && j < 3 ) |
|
v[j++] = p[i]; |
|
} |
|
|
|
if( point_count >= 2 && (j < 2 || (j == 2 && cvTsDist(v[0],v[1]) < (radius-1)*2/eps)) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"There should be at at least 3 points near the circle boundary or 2 points on the diameter\n" ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
_exit_: |
|
|
|
if( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
return code; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* Perimeter Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_PerimeterTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_PerimeterTest(); |
|
|
|
protected: |
|
int prepare_test_case( int test_case_idx ); |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
CvSlice slice; |
|
int is_closed; |
|
double result; |
|
}; |
|
|
|
|
|
CV_PerimeterTest::CV_PerimeterTest() |
|
{ |
|
} |
|
|
|
|
|
int CV_PerimeterTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); |
|
RNG& rng = ts->get_rng(); |
|
int total; |
|
|
|
if( code < 0 ) |
|
return code; |
|
|
|
is_closed = cvtest::randInt(rng) % 2; |
|
|
|
if( points1 ) |
|
{ |
|
points1->flags |= CV_SEQ_KIND_CURVE; |
|
if( is_closed ) |
|
points1->flags |= CV_SEQ_FLAG_CLOSED; |
|
total = points1->total; |
|
} |
|
else |
|
total = points2->cols + points2->rows - 1; |
|
|
|
if( (cvtest::randInt(rng) % 3) && !test_cpp ) |
|
{ |
|
slice.start_index = cvtest::randInt(rng) % total; |
|
slice.end_index = cvtest::randInt(rng) % total; |
|
} |
|
else |
|
slice = CV_WHOLE_SEQ; |
|
|
|
return 1; |
|
} |
|
|
|
|
|
void CV_PerimeterTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
result = cvArcLength( points, slice, points1 ? -1 : is_closed ); |
|
else |
|
result = cv::arcLength(cv::cvarrToMat(points), |
|
!points1 ? is_closed != 0 : (points1->flags & CV_SEQ_FLAG_CLOSED) != 0); |
|
} |
|
|
|
|
|
int CV_PerimeterTest::validate_test_results( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
int i, len = slice.end_index - slice.start_index, total = points2->cols + points2->rows - 1; |
|
double result0 = 0; |
|
CvPoint2D32f prev_pt, pt, *ptr; |
|
|
|
if( len < 0 ) |
|
len += total; |
|
|
|
len = MIN( len, total ); |
|
//len -= !is_closed && len == total; |
|
|
|
ptr = (CvPoint2D32f*)points2->data.fl; |
|
prev_pt = ptr[(is_closed ? slice.start_index+len-1 : slice.start_index) % total]; |
|
|
|
for( i = 0; i < len + (len < total && (!is_closed || len==1)); i++ ) |
|
{ |
|
pt = ptr[(i + slice.start_index) % total]; |
|
double dx = pt.x - prev_pt.x, dy = pt.y - prev_pt.y; |
|
result0 += sqrt(dx*dx + dy*dy); |
|
prev_pt = pt; |
|
} |
|
|
|
if( cvIsNaN(result) || cvIsInf(result) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "cvArcLength() returned invalid value (%g)\n", result ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
} |
|
else if( fabs(result - result0) > FLT_EPSILON*100*result0 ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The function returned %g, while the correct result is %g\n", result, result0 ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
} |
|
|
|
if( code < 0 ) |
|
ts->set_failed_test_info( code ); |
|
return code; |
|
} |
|
|
|
|
|
/****************************************************************************************\ |
|
* FitEllipse Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_FitEllipseTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_FitEllipseTest(); |
|
|
|
protected: |
|
int prepare_test_case( int test_case_idx ); |
|
void generate_point_set( void* points ); |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
CvBox2D box0, box; |
|
double min_ellipse_size, max_noise; |
|
}; |
|
|
|
|
|
CV_FitEllipseTest::CV_FitEllipseTest() |
|
{ |
|
min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least |
|
max_log_size = 10; |
|
min_ellipse_size = 10; |
|
max_noise = 0.05; |
|
} |
|
|
|
|
|
void CV_FitEllipseTest::generate_point_set( void* pointsSet ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
int i, total, point_type; |
|
CvSeqReader reader; |
|
uchar* data = 0; |
|
double a, b; |
|
|
|
box0.center.x = (float)((low.val[0] + high.val[0])*0.5); |
|
box0.center.y = (float)((low.val[1] + high.val[1])*0.5); |
|
box0.size.width = (float)(MAX(high.val[0] - low.val[0], min_ellipse_size)*2); |
|
box0.size.height = (float)(MAX(high.val[1] - low.val[1], min_ellipse_size)*2); |
|
box0.angle = (float)(cvtest::randReal(rng)*180); |
|
a = cos(box0.angle*CV_PI/180.); |
|
b = sin(box0.angle*CV_PI/180.); |
|
|
|
if( box0.size.width > box0.size.height ) |
|
{ |
|
float t; |
|
CV_SWAP( box0.size.width, box0.size.height, t ); |
|
} |
|
memset( &reader, 0, sizeof(reader) ); |
|
|
|
if( CV_IS_SEQ(pointsSet) ) |
|
{ |
|
CvSeq* ptseq = (CvSeq*)pointsSet; |
|
total = ptseq->total; |
|
point_type = CV_SEQ_ELTYPE(ptseq); |
|
cvStartReadSeq( ptseq, &reader ); |
|
} |
|
else |
|
{ |
|
CvMat* ptm = (CvMat*)pointsSet; |
|
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); |
|
total = ptm->rows + ptm->cols - 1; |
|
point_type = CV_MAT_TYPE(ptm->type); |
|
data = ptm->data.ptr; |
|
} |
|
|
|
assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); |
|
|
|
for( i = 0; i < total; i++ ) |
|
{ |
|
CvPoint* pp; |
|
CvPoint2D32f p; |
|
double angle = cvtest::randReal(rng)*CV_PI*2; |
|
double x = box0.size.height*0.5*(cos(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); |
|
double y = box0.size.width*0.5*(sin(angle) + (cvtest::randReal(rng)-0.5)*2*max_noise); |
|
p.x = (float)(box0.center.x + a*x + b*y); |
|
p.y = (float)(box0.center.y - b*x + a*y); |
|
|
|
if( reader.ptr ) |
|
{ |
|
pp = (CvPoint*)reader.ptr; |
|
CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); |
|
} |
|
else |
|
pp = ((CvPoint*)data) + i; |
|
if( point_type == CV_32SC2 ) |
|
{ |
|
pp->x = cvRound(p.x); |
|
pp->y = cvRound(p.y); |
|
} |
|
else |
|
*(CvPoint2D32f*)pp = p; |
|
} |
|
} |
|
|
|
|
|
int CV_FitEllipseTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
min_log_size = MAX(min_log_size,4); |
|
max_log_size = MAX(min_log_size,max_log_size); |
|
return CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); |
|
} |
|
|
|
|
|
void CV_FitEllipseTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
box = cvFitEllipse2( points ); |
|
else |
|
box = (CvBox2D)cv::fitEllipse(cv::cvarrToMat(points)); |
|
} |
|
|
|
|
|
int CV_FitEllipseTest::validate_test_results( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
double diff_angle; |
|
|
|
if( cvIsNaN(box.center.x) || cvIsInf(box.center.x) || |
|
cvIsNaN(box.center.y) || cvIsInf(box.center.y) || |
|
cvIsNaN(box.size.width) || cvIsInf(box.size.width) || |
|
cvIsNaN(box.size.height) || cvIsInf(box.size.height) || |
|
cvIsNaN(box.angle) || cvIsInf(box.angle) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Some of the computed ellipse parameters are invalid (x=%g,y=%g,w=%g,h=%g,angle=%g)\n", |
|
box.center.x, box.center.y, box.size.width, box.size.height, box.angle ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
goto _exit_; |
|
} |
|
|
|
box.angle = (float)(90-box.angle); |
|
if( box.angle < 0 ) |
|
box.angle += 360; |
|
if( box.angle > 360 ) |
|
box.angle -= 360; |
|
|
|
if( fabs(box.center.x - box0.center.x) > 3 || |
|
fabs(box.center.y - box0.center.y) > 3 || |
|
fabs(box.size.width - box0.size.width) > 0.1*fabs(box0.size.width) || |
|
fabs(box.size.height - box0.size.height) > 0.1*fabs(box0.size.height) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "The computed ellipse center and/or size are incorrect:\n\t" |
|
"(x=%.1f,y=%.1f,w=%.1f,h=%.1f), while it should be (x=%.1f,y=%.1f,w=%.1f,h=%.1f)\n", |
|
box.center.x, box.center.y, box.size.width, box.size.height, |
|
box0.center.x, box0.center.y, box0.size.width, box0.size.height ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
diff_angle = fabs(box0.angle - box.angle); |
|
diff_angle = MIN( diff_angle, fabs(diff_angle - 360)); |
|
diff_angle = MIN( diff_angle, fabs(diff_angle - 180)); |
|
|
|
if( box0.size.height >= 1.3*box0.size.width && diff_angle > 30 ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Incorrect ellipse angle (=%1.f, should be %1.f)\n", |
|
box.angle, box0.angle ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
_exit_: |
|
|
|
#if 0 |
|
if( code < 0 ) |
|
{ |
|
cvNamedWindow( "test", 0 ); |
|
IplImage* img = cvCreateImage( cvSize(cvRound(low_high_range*4), |
|
cvRound(low_high_range*4)), 8, 3 ); |
|
cvZero( img ); |
|
|
|
box.center.x += (float)low_high_range*2; |
|
box.center.y += (float)low_high_range*2; |
|
cvEllipseBox( img, box, CV_RGB(255,0,0), 3, 8 ); |
|
|
|
for( int i = 0; i < points2->rows + points2->cols - 1; i++ ) |
|
{ |
|
CvPoint pt; |
|
pt.x = cvRound(points2->data.fl[i*2] + low_high_range*2); |
|
pt.y = cvRound(points2->data.fl[i*2+1] + low_high_range*2); |
|
cvCircle( img, pt, 1, CV_RGB(255,255,255), -1, 8 ); |
|
} |
|
|
|
cvShowImage( "test", img ); |
|
cvReleaseImage( &img ); |
|
cvWaitKey(0); |
|
} |
|
#endif |
|
|
|
if( code < 0 ) |
|
{ |
|
ts->set_failed_test_info( code ); |
|
} |
|
return code; |
|
} |
|
|
|
|
|
class CV_FitEllipseSmallTest : public cvtest::BaseTest |
|
{ |
|
public: |
|
CV_FitEllipseSmallTest() {} |
|
~CV_FitEllipseSmallTest() {} |
|
protected: |
|
void run(int) |
|
{ |
|
Size sz(50, 50); |
|
vector<vector<Point> > c; |
|
c.push_back(vector<Point>()); |
|
int scale = 1; |
|
Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale; |
|
c[0].push_back(Point(2, 0)*scale+ofs); |
|
c[0].push_back(Point(0, 2)*scale+ofs); |
|
c[0].push_back(Point(0, 6)*scale+ofs); |
|
c[0].push_back(Point(2, 8)*scale+ofs); |
|
c[0].push_back(Point(6, 8)*scale+ofs); |
|
c[0].push_back(Point(8, 6)*scale+ofs); |
|
c[0].push_back(Point(8, 2)*scale+ofs); |
|
c[0].push_back(Point(6, 0)*scale+ofs); |
|
|
|
RotatedRect e = fitEllipse(c[0]); |
|
CV_Assert( fabs(e.center.x - 4) <= 1. && |
|
fabs(e.center.y - 4) <= 1. && |
|
fabs(e.size.width - 9) <= 1. && |
|
fabs(e.size.height - 9) <= 1. ); |
|
} |
|
}; |
|
|
|
/****************************************************************************************\ |
|
* FitLine Test * |
|
\****************************************************************************************/ |
|
|
|
class CV_FitLineTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_FitLineTest(); |
|
|
|
protected: |
|
int prepare_test_case( int test_case_idx ); |
|
void generate_point_set( void* points ); |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
double max_noise; |
|
float line[6], line0[6]; |
|
int dist_type; |
|
double reps, aeps; |
|
}; |
|
|
|
|
|
CV_FitLineTest::CV_FitLineTest() |
|
{ |
|
min_log_size = 5; // for robust ellipse fitting a dozen of points is needed at least |
|
max_log_size = 10; |
|
max_noise = 0.05; |
|
} |
|
|
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) |
|
# pragma GCC diagnostic push |
|
# pragma GCC diagnostic ignored "-Warray-bounds" |
|
#endif |
|
|
|
void CV_FitLineTest::generate_point_set( void* pointsSet ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
int i, k, n, total, point_type; |
|
CvSeqReader reader; |
|
uchar* data = 0; |
|
double s = 0; |
|
|
|
n = dims; |
|
for( k = 0; k < n; k++ ) |
|
{ |
|
line0[k+n] = (float)((low.val[k] + high.val[k])*0.5); |
|
line0[k] = (float)(high.val[k] - low.val[k]); |
|
if( cvtest::randInt(rng) % 2 ) |
|
line0[k] = -line0[k]; |
|
s += (double)line0[k]*line0[k]; |
|
} |
|
|
|
s = 1./sqrt(s); |
|
for( k = 0; k < n; k++ ) |
|
line0[k] = (float)(line0[k]*s); |
|
|
|
memset( &reader, 0, sizeof(reader) ); |
|
|
|
if( CV_IS_SEQ(pointsSet) ) |
|
{ |
|
CvSeq* ptseq = (CvSeq*)pointsSet; |
|
total = ptseq->total; |
|
point_type = CV_MAT_DEPTH(CV_SEQ_ELTYPE(ptseq)); |
|
cvStartReadSeq( ptseq, &reader ); |
|
} |
|
else |
|
{ |
|
CvMat* ptm = (CvMat*)pointsSet; |
|
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); |
|
total = ptm->rows + ptm->cols - 1; |
|
point_type = CV_MAT_DEPTH(CV_MAT_TYPE(ptm->type)); |
|
data = ptm->data.ptr; |
|
} |
|
|
|
for( i = 0; i < total; i++ ) |
|
{ |
|
int* pi; |
|
float* pf; |
|
float p[4], t; |
|
if( reader.ptr ) |
|
{ |
|
pi = (int*)reader.ptr; |
|
pf = (float*)reader.ptr; |
|
CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); |
|
} |
|
else |
|
{ |
|
pi = (int*)data + i*n; |
|
pf = (float*)data + i*n; |
|
} |
|
|
|
t = (float)((cvtest::randReal(rng)-0.5)*low_high_range*2); |
|
|
|
for( k = 0; k < n; k++ ) |
|
{ |
|
p[k] = (float)((cvtest::randReal(rng)-0.5)*max_noise*2 + t*line0[k] + line0[k+n]); |
|
|
|
if( point_type == CV_32S ) |
|
pi[k] = cvRound(p[k]); |
|
else |
|
pf[k] = p[k]; |
|
} |
|
} |
|
} |
|
|
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) |
|
# pragma GCC diagnostic pop |
|
#endif |
|
|
|
int CV_FitLineTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
dims = cvtest::randInt(rng) % 2 + 2; |
|
min_log_size = MAX(min_log_size,5); |
|
max_log_size = MAX(min_log_size,max_log_size); |
|
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); |
|
dist_type = cvtest::randInt(rng) % 6 + 1; |
|
dist_type += dist_type == CV_DIST_C; |
|
reps = 0.1; aeps = 0.01; |
|
return code; |
|
} |
|
|
|
|
|
void CV_FitLineTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
cvFitLine( points, dist_type, 0, reps, aeps, line ); |
|
else if(dims == 2) |
|
cv::fitLine(cv::cvarrToMat(points), (cv::Vec4f&)line[0], dist_type, 0, reps, aeps); |
|
else |
|
cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps); |
|
} |
|
|
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) |
|
# pragma GCC diagnostic push |
|
# pragma GCC diagnostic ignored "-Warray-bounds" |
|
#endif |
|
|
|
int CV_FitLineTest::validate_test_results( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
int k, max_k = 0; |
|
double vec_diff = 0, t; |
|
|
|
for( k = 0; k < dims*2; k++ ) |
|
{ |
|
if( cvIsNaN(line[k]) || cvIsInf(line[k]) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, "Some of the computed line parameters are invalid (line[%d]=%g)\n", |
|
k, line[k] ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
goto _exit_; |
|
} |
|
} |
|
|
|
if( fabs(line0[1]) > fabs(line0[0]) ) |
|
max_k = 1; |
|
if( fabs(line0[dims-1]) > fabs(line0[max_k]) ) |
|
max_k = dims-1; |
|
if( line0[max_k] < 0 ) |
|
for( k = 0; k < dims; k++ ) |
|
line0[k] = -line0[k]; |
|
if( line[max_k] < 0 ) |
|
for( k = 0; k < dims; k++ ) |
|
line[k] = -line[k]; |
|
|
|
for( k = 0; k < dims; k++ ) |
|
{ |
|
double dt = line[k] - line0[k]; |
|
vec_diff += dt*dt; |
|
} |
|
|
|
if( sqrt(vec_diff) > 0.05 ) |
|
{ |
|
if( dims == 2 ) |
|
ts->printf( cvtest::TS::LOG, |
|
"The computed line vector (%.2f,%.2f) is different from the actual (%.2f,%.2f)\n", |
|
line[0], line[1], line0[0], line0[1] ); |
|
else |
|
ts->printf( cvtest::TS::LOG, |
|
"The computed line vector (%.2f,%.2f,%.2f) is different from the actual (%.2f,%.2f,%.2f)\n", |
|
line[0], line[1], line[2], line0[0], line0[1], line0[2] ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
t = (line[max_k+dims] - line0[max_k+dims])/line0[max_k]; |
|
for( k = 0; k < dims; k++ ) |
|
{ |
|
double p = line0[k+dims] + t*line0[k] - line[k+dims]; |
|
vec_diff += p*p; |
|
} |
|
|
|
if( sqrt(vec_diff) > 1*MAX(fabs(t),1) ) |
|
{ |
|
if( dims == 2 ) |
|
ts->printf( cvtest::TS::LOG, |
|
"The computed line point (%.2f,%.2f) is too far from the actual line\n", |
|
line[2]+line0[2], line[3]+line0[3] ); |
|
else |
|
ts->printf( cvtest::TS::LOG, |
|
"The computed line point (%.2f,%.2f,%.2f) is too far from the actual line\n", |
|
line[3]+line0[3], line[4]+line0[4], line[5]+line0[5] ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
goto _exit_; |
|
} |
|
|
|
_exit_: |
|
|
|
if( code < 0 ) |
|
{ |
|
ts->set_failed_test_info( code ); |
|
} |
|
return code; |
|
} |
|
|
|
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) |
|
# pragma GCC diagnostic pop |
|
#endif |
|
|
|
/****************************************************************************************\ |
|
* ContourMoments Test * |
|
\****************************************************************************************/ |
|
|
|
|
|
static void |
|
cvTsGenerateTousledBlob( CvPoint2D32f center, CvSize2D32f axes, |
|
double max_r_scale, double angle, CvArr* points, RNG& rng ) |
|
{ |
|
int i, total, point_type; |
|
uchar* data = 0; |
|
CvSeqReader reader; |
|
memset( &reader, 0, sizeof(reader) ); |
|
|
|
if( CV_IS_SEQ(points) ) |
|
{ |
|
CvSeq* ptseq = (CvSeq*)points; |
|
total = ptseq->total; |
|
point_type = CV_SEQ_ELTYPE(ptseq); |
|
cvStartReadSeq( ptseq, &reader ); |
|
} |
|
else |
|
{ |
|
CvMat* ptm = (CvMat*)points; |
|
assert( CV_IS_MAT(ptm) && CV_IS_MAT_CONT(ptm->type) ); |
|
total = ptm->rows + ptm->cols - 1; |
|
point_type = CV_MAT_TYPE(ptm->type); |
|
data = ptm->data.ptr; |
|
} |
|
|
|
assert( point_type == CV_32SC2 || point_type == CV_32FC2 ); |
|
|
|
for( i = 0; i < total; i++ ) |
|
{ |
|
CvPoint* pp; |
|
CvPoint2D32f p; |
|
|
|
double phi0 = 2*CV_PI*i/total; |
|
double phi = CV_PI*angle/180.; |
|
double t = cvtest::randReal(rng)*max_r_scale + (1 - max_r_scale); |
|
double ta = axes.height*t; |
|
double tb = axes.width*t; |
|
double c0 = cos(phi0)*ta, s0 = sin(phi0)*tb; |
|
double c = cos(phi), s = sin(phi); |
|
p.x = (float)(c0*c - s0*s + center.x); |
|
p.y = (float)(c0*s + s0*c + center.y); |
|
|
|
if( reader.ptr ) |
|
{ |
|
pp = (CvPoint*)reader.ptr; |
|
CV_NEXT_SEQ_ELEM( sizeof(*pp), reader ); |
|
} |
|
else |
|
pp = ((CvPoint*)data) + i; |
|
|
|
if( point_type == CV_32SC2 ) |
|
{ |
|
pp->x = cvRound(p.x); |
|
pp->y = cvRound(p.y); |
|
} |
|
else |
|
*(CvPoint2D32f*)pp = p; |
|
} |
|
} |
|
|
|
|
|
class CV_ContourMomentsTest : public CV_BaseShapeDescrTest |
|
{ |
|
public: |
|
CV_ContourMomentsTest(); |
|
|
|
protected: |
|
int prepare_test_case( int test_case_idx ); |
|
void generate_point_set( void* points ); |
|
void run_func(void); |
|
int validate_test_results( int test_case_idx ); |
|
CvMoments moments0, moments; |
|
double area0, area; |
|
CvSize2D32f axes; |
|
CvPoint2D32f center; |
|
int max_max_r_scale; |
|
double max_r_scale, angle; |
|
CvSize img_size; |
|
}; |
|
|
|
|
|
CV_ContourMomentsTest::CV_ContourMomentsTest() |
|
{ |
|
min_log_size = 3; |
|
max_log_size = 8; |
|
max_max_r_scale = 15; |
|
low_high_range = 200; |
|
enable_flt_points = false; |
|
} |
|
|
|
|
|
void CV_ContourMomentsTest::generate_point_set( void* pointsSet ) |
|
{ |
|
RNG& rng = ts->get_rng(); |
|
float max_sz; |
|
|
|
axes.width = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); |
|
axes.height = (float)((cvtest::randReal(rng)*0.9 + 0.1)*low_high_range); |
|
max_sz = MAX(axes.width, axes.height); |
|
|
|
img_size.width = img_size.height = cvRound(low_high_range*2.2); |
|
|
|
center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - max_sz*2)*0.8); |
|
center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - max_sz*2)*0.8); |
|
|
|
assert( 0 < center.x - max_sz && center.x + max_sz < img_size.width && |
|
0 < center.y - max_sz && center.y + max_sz < img_size.height ); |
|
|
|
max_r_scale = cvtest::randReal(rng)*max_max_r_scale*0.01; |
|
angle = cvtest::randReal(rng)*360; |
|
|
|
cvTsGenerateTousledBlob( center, axes, max_r_scale, angle, pointsSet, rng ); |
|
|
|
if( points1 ) |
|
points1->flags = CV_SEQ_MAGIC_VAL + CV_SEQ_POLYGON; |
|
} |
|
|
|
|
|
int CV_ContourMomentsTest::prepare_test_case( int test_case_idx ) |
|
{ |
|
min_log_size = MAX(min_log_size,3); |
|
max_log_size = MIN(max_log_size,8); |
|
max_log_size = MAX(min_log_size,max_log_size); |
|
int code = CV_BaseShapeDescrTest::prepare_test_case( test_case_idx ); |
|
return code; |
|
} |
|
|
|
|
|
void CV_ContourMomentsTest::run_func() |
|
{ |
|
if(!test_cpp) |
|
{ |
|
cvMoments( points, &moments ); |
|
area = cvContourArea( points ); |
|
} |
|
else |
|
{ |
|
moments = (CvMoments)cv::moments(cv::cvarrToMat(points)); |
|
area = cv::contourArea(cv::cvarrToMat(points)); |
|
} |
|
} |
|
|
|
|
|
int CV_ContourMomentsTest::validate_test_results( int test_case_idx ) |
|
{ |
|
int code = CV_BaseShapeDescrTest::validate_test_results( test_case_idx ); |
|
int i, n = (int)(sizeof(moments)/sizeof(moments.inv_sqrt_m00)); |
|
CvMat* img = cvCreateMat( img_size.height, img_size.width, CV_8UC1 ); |
|
CvPoint* pt = (CvPoint*)points2->data.i; |
|
int count = points2->cols + points2->rows - 1; |
|
double max_v0 = 0; |
|
|
|
cvZero(img); |
|
cvFillPoly( img, &pt, &count, 1, cvScalarAll(1)); |
|
cvMoments( img, &moments0 ); |
|
|
|
for( i = 0; i < n; i++ ) |
|
{ |
|
double t = fabs((&moments0.m00)[i]); |
|
max_v0 = MAX(max_v0, t); |
|
} |
|
|
|
for( i = 0; i <= n; i++ ) |
|
{ |
|
double v = i < n ? (&moments.m00)[i] : area; |
|
double v0 = i < n ? (&moments0.m00)[i] : moments0.m00; |
|
|
|
if( cvIsNaN(v) || cvIsInf(v) ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"The contour %s is invalid (=%g)\n", i < n ? "moment" : "area", v ); |
|
code = cvtest::TS::FAIL_INVALID_OUTPUT; |
|
break; |
|
} |
|
|
|
if( fabs(v - v0) > 0.1*max_v0 ) |
|
{ |
|
ts->printf( cvtest::TS::LOG, |
|
"The computed contour %s is %g, while it should be %g\n", |
|
i < n ? "moment" : "area", v, v0 ); |
|
code = cvtest::TS::FAIL_BAD_ACCURACY; |
|
break; |
|
} |
|
} |
|
|
|
if( code < 0 ) |
|
{ |
|
#if 0 |
|
cvCmpS( img, 0, img, CV_CMP_GT ); |
|
cvNamedWindow( "test", 1 ); |
|
cvShowImage( "test", img ); |
|
cvWaitKey(); |
|
#endif |
|
ts->set_failed_test_info( code ); |
|
} |
|
|
|
cvReleaseMat( &img ); |
|
return code; |
|
} |
|
|
|
|
|
////////////////////////////////////// Perimeter/Area/Slice test /////////////////////////////////// |
|
|
|
class CV_PerimeterAreaSliceTest : public cvtest::BaseTest |
|
{ |
|
public: |
|
CV_PerimeterAreaSliceTest(); |
|
~CV_PerimeterAreaSliceTest(); |
|
protected: |
|
void run(int); |
|
}; |
|
|
|
CV_PerimeterAreaSliceTest::CV_PerimeterAreaSliceTest() |
|
{ |
|
} |
|
CV_PerimeterAreaSliceTest::~CV_PerimeterAreaSliceTest() {} |
|
|
|
void CV_PerimeterAreaSliceTest::run( int ) |
|
{ |
|
Ptr<CvMemStorage> storage(cvCreateMemStorage()); |
|
RNG& rng = theRNG(); |
|
const double min_r = 90, max_r = 120; |
|
|
|
for( int i = 0; i < 100; i++ ) |
|
{ |
|
ts->update_context( this, i, true ); |
|
int n = rng.uniform(3, 30); |
|
cvClearMemStorage(storage); |
|
CvSeq* contour = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(CvPoint), storage); |
|
double dphi = CV_PI*2/n; |
|
CvPoint center; |
|
center.x = rng.uniform(cvCeil(max_r), cvFloor(640-max_r)); |
|
center.y = rng.uniform(cvCeil(max_r), cvFloor(480-max_r)); |
|
|
|
for( int j = 0; j < n; j++ ) |
|
{ |
|
CvPoint pt; |
|
double r = rng.uniform(min_r, max_r); |
|
double phi = j*dphi; |
|
pt.x = cvRound(center.x + r*cos(phi)); |
|
pt.y = cvRound(center.y - r*sin(phi)); |
|
cvSeqPush(contour, &pt); |
|
} |
|
|
|
CvSlice slice; |
|
for(;;) |
|
{ |
|
slice.start_index = rng.uniform(-n/2, 3*n/2); |
|
slice.end_index = rng.uniform(-n/2, 3*n/2); |
|
int len = cvSliceLength(slice, contour); |
|
if( len > 2 ) |
|
break; |
|
} |
|
CvSeq *cslice = cvSeqSlice(contour, slice); |
|
/*printf( "%d. (%d, %d) of %d, length = %d, length1 = %d\n", |
|
i, slice.start_index, slice.end_index, |
|
contour->total, cvSliceLength(slice, contour), cslice->total ); |
|
|
|
double area0 = cvContourArea(cslice); |
|
double area1 = cvContourArea(contour, slice); |
|
if( area0 != area1 ) |
|
{ |
|
ts->printf(cvtest::TS::LOG, |
|
"The contour area slice is computed differently (%g vs %g)\n", area0, area1 ); |
|
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); |
|
return; |
|
}*/ |
|
|
|
double len0 = cvArcLength(cslice, CV_WHOLE_SEQ, 1); |
|
double len1 = cvArcLength(contour, slice, 1); |
|
if( len0 != len1 ) |
|
{ |
|
ts->printf(cvtest::TS::LOG, |
|
"The contour arc length is computed differently (%g vs %g)\n", len0, len1 ); |
|
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); |
|
return; |
|
} |
|
} |
|
ts->set_failed_test_info(cvtest::TS::OK); |
|
} |
|
|
|
|
|
TEST(Imgproc_ConvexHull, accuracy) { CV_ConvHullTest test; test.safe_run(); } |
|
TEST(Imgproc_MinAreaRect, accuracy) { CV_MinAreaRectTest test; test.safe_run(); } |
|
TEST(Imgproc_MinTriangle, accuracy) { CV_MinTriangleTest test; test.safe_run(); } |
|
TEST(Imgproc_MinCircle, accuracy) { CV_MinCircleTest test; test.safe_run(); } |
|
TEST(Imgproc_ContourPerimeter, accuracy) { CV_PerimeterTest test; test.safe_run(); } |
|
TEST(Imgproc_FitEllipse, accuracy) { CV_FitEllipseTest test; test.safe_run(); } |
|
TEST(Imgproc_FitLine, accuracy) { CV_FitLineTest test; test.safe_run(); } |
|
TEST(Imgproc_ContourMoments, accuracy) { CV_ContourMomentsTest test; test.safe_run(); } |
|
TEST(Imgproc_ContourPerimeterSlice, accuracy) { CV_PerimeterAreaSliceTest test; test.safe_run(); } |
|
TEST(Imgproc_FitEllipse, small) { CV_FitEllipseSmallTest test; test.safe_run(); } |
|
|
|
/* End of file. */
|
|
|