Path: blob/master/modules/imgproc/test/test_contours.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "test_precomp.hpp"42#include <opencv2/highgui.hpp>4344namespace opencv_test { namespace {4546class CV_FindContourTest : public cvtest::BaseTest47{48public:49enum { NUM_IMG = 4 };5051CV_FindContourTest();52~CV_FindContourTest();53void clear();5455protected:56int read_params( CvFileStorage* fs );57int prepare_test_case( int test_case_idx );58int validate_test_results( int test_case_idx );59void run_func();6061int min_blob_size, max_blob_size;62int blob_count, max_log_blob_count;63int retr_mode, approx_method;6465int min_log_img_width, max_log_img_width;66int min_log_img_height, max_log_img_height;67Size img_size;68int count, count2;6970IplImage* img[NUM_IMG];71CvMemStorage* storage;72CvSeq *contours, *contours2, *chain;7374static const bool useVeryWideImages =75#if SIZE_MAX <= 0xffffffff76// 32-bit: don't even try the very wide images77false78#else79// 64-bit: test with very wide images80true81#endif82;83};848586CV_FindContourTest::CV_FindContourTest()87{88int i;8990test_case_count = useVeryWideImages ? 10 : 300;91min_blob_size = 1;92max_blob_size = 50;93max_log_blob_count = 10;9495min_log_img_width = useVeryWideImages ? 17 : 3;96max_log_img_width = useVeryWideImages ? 17 : 10;9798min_log_img_height = 3;99max_log_img_height = 10;100101for( i = 0; i < NUM_IMG; i++ )102img[i] = 0;103104storage = 0;105}106107108CV_FindContourTest::~CV_FindContourTest()109{110clear();111}112113114void CV_FindContourTest::clear()115{116int i;117118cvtest::BaseTest::clear();119120for( i = 0; i < NUM_IMG; i++ )121cvReleaseImage( &img[i] );122123cvReleaseMemStorage( &storage );124}125126127int CV_FindContourTest::read_params( CvFileStorage* fs )128{129int t;130int code = cvtest::BaseTest::read_params( fs );131132if( code < 0 )133return code;134135min_blob_size = cvReadInt( find_param( fs, "min_blob_size" ), min_blob_size );136max_blob_size = cvReadInt( find_param( fs, "max_blob_size" ), max_blob_size );137max_log_blob_count = cvReadInt( find_param( fs, "max_log_blob_count" ), max_log_blob_count );138min_log_img_width = cvReadInt( find_param( fs, "min_log_img_width" ), min_log_img_width );139max_log_img_width = cvReadInt( find_param( fs, "max_log_img_width" ), max_log_img_width );140min_log_img_height = cvReadInt( find_param( fs, "min_log_img_height"), min_log_img_height );141max_log_img_height = cvReadInt( find_param( fs, "max_log_img_height"), max_log_img_height );142143min_blob_size = cvtest::clipInt( min_blob_size, 1, 100 );144max_blob_size = cvtest::clipInt( max_blob_size, 1, 100 );145146if( min_blob_size > max_blob_size )147CV_SWAP( min_blob_size, max_blob_size, t );148149max_log_blob_count = cvtest::clipInt( max_log_blob_count, 1, 10 );150151min_log_img_width = cvtest::clipInt( min_log_img_width, 1, useVeryWideImages ? 17 : 10 );152min_log_img_width = cvtest::clipInt( max_log_img_width, 1, useVeryWideImages ? 17 : 10 );153min_log_img_height = cvtest::clipInt( min_log_img_height, 1, 10 );154min_log_img_height = cvtest::clipInt( max_log_img_height, 1, 10 );155156if( min_log_img_width > max_log_img_width )157std::swap( min_log_img_width, max_log_img_width );158159if (min_log_img_height > max_log_img_height)160std::swap(min_log_img_height, max_log_img_height);161162return 0;163}164165166static void167cvTsGenerateBlobImage( IplImage* img, int min_blob_size, int max_blob_size,168int blob_count, int min_brightness, int max_brightness,169RNG& rng )170{171int i;172Size size;173174CV_Assert(img->depth == IPL_DEPTH_8U && img->nChannels == 1);175176cvZero( img );177178// keep the border clear179cvSetImageROI( img, cvRect(1,1,img->width-2,img->height-2) );180size = cvGetSize( img );181182for( i = 0; i < blob_count; i++ )183{184Point center;185Size axes;186int angle = cvtest::randInt(rng) % 180;187int brightness = cvtest::randInt(rng) %188(max_brightness - min_brightness) + min_brightness;189center.x = cvtest::randInt(rng) % size.width;190center.y = cvtest::randInt(rng) % size.height;191192axes.width = (cvtest::randInt(rng) %193(max_blob_size - min_blob_size) + min_blob_size + 1)/2;194axes.height = (cvtest::randInt(rng) %195(max_blob_size - min_blob_size) + min_blob_size + 1)/2;196197cvEllipse( img, cvPoint(center), cvSize(axes), angle, 0, 360, cvScalar(brightness), CV_FILLED );198}199200cvResetImageROI( img );201}202203204static void205cvTsMarkContours( IplImage* img, int val )206{207int i, j;208int step = img->widthStep;209210assert( img->depth == IPL_DEPTH_8U && img->nChannels == 1 && (val&1) != 0);211212for( i = 1; i < img->height - 1; i++ )213for( j = 1; j < img->width - 1; j++ )214{215uchar* t = (uchar*)(img->imageData + img->widthStep*i + j);216if( *t == 1 && (t[-step] == 0 || t[-1] == 0 || t[1] == 0 || t[step] == 0))217*t = (uchar)val;218}219220cvThreshold( img, img, val - 2, val, CV_THRESH_BINARY );221}222223224int CV_FindContourTest::prepare_test_case( int test_case_idx )225{226RNG& rng = ts->get_rng();227const int min_brightness = 0, max_brightness = 2;228int i, code = cvtest::BaseTest::prepare_test_case( test_case_idx );229230if( code < 0 )231return code;232233clear();234235blob_count = cvRound(exp(cvtest::randReal(rng)*max_log_blob_count*CV_LOG2));236237img_size.width = cvRound(exp((cvtest::randReal(rng)*238(max_log_img_width - min_log_img_width) + min_log_img_width)*CV_LOG2));239img_size.height = cvRound(exp((cvtest::randReal(rng)*240(max_log_img_height - min_log_img_height) + min_log_img_height)*CV_LOG2));241242approx_method = cvtest::randInt( rng ) % 4 + 1;243retr_mode = cvtest::randInt( rng ) % 4;244245storage = cvCreateMemStorage( 1 << 10 );246247for( i = 0; i < NUM_IMG; i++ )248img[i] = cvCreateImage( cvSize(img_size), 8, 1 );249250cvTsGenerateBlobImage( img[0], min_blob_size, max_blob_size,251blob_count, min_brightness, max_brightness, rng );252253cvCopy( img[0], img[1] );254cvCopy( img[0], img[2] );255256cvTsMarkContours( img[1], 255 );257258return 1;259}260261262void CV_FindContourTest::run_func()263{264contours = contours2 = chain = 0;265count = cvFindContours( img[2], storage, &contours, sizeof(CvContour), retr_mode, approx_method );266267cvZero( img[3] );268269if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )270cvDrawContours( img[3], contours, cvScalar(255), cvScalar(255), INT_MAX, -1 );271272cvCopy( img[0], img[2] );273274count2 = cvFindContours( img[2], storage, &chain, sizeof(CvChain), retr_mode, CV_CHAIN_CODE );275276if( chain )277contours2 = cvApproxChains( chain, storage, approx_method, 0, 0, 1 );278279cvZero( img[2] );280281if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )282cvDrawContours( img[2], contours2, cvScalar(255), cvScalar(255), INT_MAX );283}284285286// the whole testing is done here, run_func() is not utilized in this test287int CV_FindContourTest::validate_test_results( int /*test_case_idx*/ )288{289int code = cvtest::TS::OK;290291cvCmpS( img[0], 0, img[0], CV_CMP_GT );292293if( count != count2 )294{295ts->printf( cvtest::TS::LOG, "The number of contours retrieved with different "296"approximation methods is not the same\n"297"(%d contour(s) for method %d vs %d contour(s) for method %d)\n",298count, approx_method, count2, CV_CHAIN_CODE );299code = cvtest::TS::FAIL_INVALID_OUTPUT;300}301302if( retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )303{304Mat _img[4];305for( int i = 0; i < 4; i++ )306_img[i] = cvarrToMat(img[i]);307308code = cvtest::cmpEps2(ts, _img[0], _img[3], 0, true, "Comparing original image with the map of filled contours" );309310if( code < 0 )311goto _exit_;312313code = cvtest::cmpEps2( ts, _img[1], _img[2], 0, true,314"Comparing contour outline vs manually produced edge map" );315316if( code < 0 )317goto _exit_;318}319320if( contours )321{322CvTreeNodeIterator iterator1;323CvTreeNodeIterator iterator2;324int count3;325326for(int i = 0; i < 2; i++ )327{328CvTreeNodeIterator iterator;329cvInitTreeNodeIterator( &iterator, i == 0 ? contours : contours2, INT_MAX );330331for( count3 = 0; cvNextTreeNode( &iterator ) != 0; count3++ )332;333334if( count3 != count )335{336ts->printf( cvtest::TS::LOG,337"The returned number of retrieved contours (using the approx_method = %d) does not match\n"338"to the actual number of contours in the tree/list (returned %d, actual %d)\n",339i == 0 ? approx_method : 0, count, count3 );340code = cvtest::TS::FAIL_INVALID_OUTPUT;341goto _exit_;342}343}344345cvInitTreeNodeIterator( &iterator1, contours, INT_MAX );346cvInitTreeNodeIterator( &iterator2, contours2, INT_MAX );347348for( count3 = 0; count3 < count; count3++ )349{350CvSeq* seq1 = (CvSeq*)cvNextTreeNode( &iterator1 );351CvSeq* seq2 = (CvSeq*)cvNextTreeNode( &iterator2 );352CvSeqReader reader1;353CvSeqReader reader2;354355if( !seq1 || !seq2 )356{357ts->printf( cvtest::TS::LOG,358"There are NULL pointers in the original contour tree or the "359"tree produced by cvApproxChains\n" );360code = cvtest::TS::FAIL_INVALID_OUTPUT;361goto _exit_;362}363364cvStartReadSeq( seq1, &reader1 );365cvStartReadSeq( seq2, &reader2 );366367if( seq1->total != seq2->total )368{369ts->printf( cvtest::TS::LOG,370"The original contour #%d has %d points, while the corresponding contour has %d point\n",371count3, seq1->total, seq2->total );372code = cvtest::TS::FAIL_INVALID_OUTPUT;373goto _exit_;374}375376for(int i = 0; i < seq1->total; i++ )377{378CvPoint pt1 = {0, 0};379CvPoint pt2 = {0, 0};380381CV_READ_SEQ_ELEM( pt1, reader1 );382CV_READ_SEQ_ELEM( pt2, reader2 );383384if( pt1.x != pt2.x || pt1.y != pt2.y )385{386ts->printf( cvtest::TS::LOG,387"The point #%d in the contour #%d is different from the corresponding point "388"in the approximated chain ((%d,%d) vs (%d,%d)", count3, i, pt1.x, pt1.y, pt2.x, pt2.y );389code = cvtest::TS::FAIL_INVALID_OUTPUT;390goto _exit_;391}392}393}394}395396_exit_:397if( code < 0 )398{399#if 0400cvNamedWindow( "test", 0 );401cvShowImage( "test", img[0] );402cvWaitKey();403#endif404ts->set_failed_test_info( code );405}406407return code;408}409410TEST(Imgproc_FindContours, accuracy) { CV_FindContourTest test; test.safe_run(); }411412//rotate/flip a quadrant appropriately413static void rot(int n, int *x, int *y, int rx, int ry)414{415if (ry == 0) {416if (rx == 1) {417*x = n-1 - *x;418*y = n-1 - *y;419}420421//Swap x and y422int t = *x;423*x = *y;424*y = t;425}426}427428static void d2xy(int n, int d, int *x, int *y)429{430int rx, ry, s, t=d;431*x = *y = 0;432for (s=1; s<n; s*=2)433{434rx = 1 & (t/2);435ry = 1 & (t ^ rx);436rot(s, x, y, rx, ry);437*x += s * rx;438*y += s * ry;439t /= 4;440}441}442443TEST(Imgproc_FindContours, hilbert)444{445int n = 64, n2 = n*n, scale = 10, w = (n + 2)*scale;446Point ofs(scale, scale);447Mat img(w, w, CV_8U);448img.setTo(Scalar::all(0));449450Point p(0,0);451for( int i = 0; i < n2; i++ )452{453Point q(0,0);454d2xy(n2, i, &q.x, &q.y);455line(img, p*scale + ofs, q*scale + ofs, Scalar::all(255));456p = q;457}458dilate(img, img, Mat());459vector<vector<Point> > contours;460findContours(img, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);461printf("ncontours = %d, contour[0].npoints=%d\n", (int)contours.size(), (int)contours[0].size());462img.setTo(Scalar::all(0));463464drawContours(img, contours, 0, Scalar::all(255), 1);465466ASSERT_EQ(1, (int)contours.size());467ASSERT_EQ(9832, (int)contours[0].size());468}469470TEST(Imgproc_FindContours, border)471{472Mat img;473cv::copyMakeBorder(Mat::zeros(8, 10, CV_8U), img, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(1));474475std::vector<std::vector<cv::Point> > contours;476findContours(img, contours, RETR_LIST, CHAIN_APPROX_NONE);477478Mat img_draw_contours = Mat::zeros(img.size(), CV_8U);479for (size_t cpt = 0; cpt < contours.size(); cpt++)480{481drawContours(img_draw_contours, contours, static_cast<int>(cpt), cv::Scalar(1));482}483484ASSERT_EQ(0, cvtest::norm(img, img_draw_contours, NORM_INF));485}486487TEST(Imgproc_PointPolygonTest, regression_10222)488{489vector<Point> contour;490contour.push_back(Point(0, 0));491contour.push_back(Point(0, 100000));492contour.push_back(Point(100000, 100000));493contour.push_back(Point(100000, 50000));494contour.push_back(Point(100000, 0));495496const Point2f point(40000, 40000);497const double result = cv::pointPolygonTest(contour, point, false);498EXPECT_GT(result, 0) << "Desired result: point is inside polygon - actual result: point is not inside polygon";499}500501}} // namespace502/* End of file. */503504505