test_convhull.cpp 76 KB

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