现在的位置: 首页 > 综合 > 正文

第八章 – 轮廓 – 查找轮廓、表示轮廓、绘制轮廓

2013年10月14日 ⁄ 综合 ⁄ 共 5198字 ⁄ 字号 评论关闭

1、查找轮廓

轮廓(contour)到底是什么?

      一个轮廓对应一系列的店,也就是图像中的一条曲线,OpenCV中一般用序列来存储轮廓信息,序列中的每一个元素是曲线中一个点的位置。

轮廓树?

      OpenCV将得到的轮廓聚合成一个轮廓树,把轮廓的包含关系编码到树结构中,保护的轮廓在树中体现为节点。轮廓树有4种拓扑结构,CV_RETR_EXTERNAL、CV_RETR_CCOMP、CV_RETR_TREE、CV_RETR_LIST。

2、表示轮廓

轮廓只是序列所能表示的一种,轮廓类型CvContour是从CvSeq扩展而来,它还含有几个其他的成员,例如颜色、外包矩形区域等。

轮廓时点的序列,可以用来表示图像空间中的曲线,涉及到的几个处理函数如下:

    cvFindContours();  //查找到所有轮廓后统一返回轮廓个数   

    cvStartFindContours();  //每次查找并返回一个轮廓

    cvFindNextContour();  //查找剩余的轮廓

    cvSubstituteContour();  //替换scannner指向的轮廓

    cvEndFindContour();  //结束轮廓查找

    cvApproxChains();  //将Freeman链转换为多边形表示,近似拟合。

Freeman链码

      在链码中,多边形被表示为一系列的位移,每一个位移有8个方向。

通过以下函数读出每个点:

    cvStartReadChainPoints();  //初始化Freeman链CvChainPtReader结构

    cvReadChainPoint();  //读取每个点

3、绘制轮廓

    在屏幕上绘制检测到的轮廓,用cvDrawContours函数。

例程:根据滑动条参数设置阈值检测轮廓。

--------------------------------------------------------------------------------------------------

cvCreateTrackbar
创建trackbar并将它添加到指定的窗口。

CV_EXTERN_C_FUNCPTR( void (*CvTrackbarCallback)(int pos) );

int cvCreateTrackbar( const char* trackbar_name, const char* window_name,
int* value, int count, CvTrackbarCallback on_change );
trackbar_name
被创建的trackbar名字。
window_name
窗口名字,这个窗口将为被创建trackbar的父对象。
value
整数指针,它的值将反映滑块的位置。这个变量指定创建时的滑块位置。
count
滑块位置的最大值。最小值一直是0。
on_change
每次滑块位置被改变的时候,被调用函数的指针。这个函数应该被声明为void Foo(int); 如果没有回调函数,这个值可以设为NULL。
函数cvCreateTrackbar用指定的名字和范围来创建trackbar(滑块或者范围控制),指定与trackbar位置同步的变量,并且指定当trackbar位置被改变的时候调用的回调函数。被创建的trackbar显示在指定窗口的顶端。 

FindContours
在二值图像中寻找轮廓

int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,
int header_size=sizeof(CvContour), int mode=CV_RETR_LIST,
int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );
image
输入的 8-比特、单通道图像. 非零元素被当成 1, 0 象素值保留为 0 - 从而图像被看成二值的。为了从灰度图像中得到这样的二值图像,可以使用 cvThreshold, cvAdaptiveThreshold 或 cvCanny. 本函数改变输入图像内容。
storage
得到的轮廓的存储容器
first_contour
输出参数:包含第一个输出轮廓的指针
header_size
如果 method=CV_CHAIN_CODE,则序列头的大小 >=sizeof(CvChain),否则 >=sizeof(CvContour) .
mode
提取模式.
CV_RETR_EXTERNAL - 只提取最外层的轮廓
CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中
CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。
CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy
method
逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS).
CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列).
CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译(转化)为点序列形式
CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素点;
CV_CHAIN_APPROX_TC89_L1,
CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法. CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用.
offset
每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析.
函数 cvFindContours 从二值图像中提取轮廓,并且返回提取轮廓的数目。指针 first_contour 的内容由函数填写。它包含第一个最外层轮廓的指针,如果指针为 NULL,则没有检测到轮廓(比如图像是全黑的)。其它轮廓可以从 first_contour 利用 h_next 和 v_next 链接访问到。 在 cvDrawContours 的样例显示如何使用轮廓来进行连通域的检测。轮廓也可以用来做形状分析和对象识别 - 见CVPR2001 教程中的 squares 样例。该教程可以在 SourceForge
网站上找到。 
--------------------------------------------------------------------------------------------------
/*code*/

#include <cv.h>
#include <highgui.h>

IplImage* g_image = NULL;
IplImage* g_gray = NULL;
int g_thresh = 100;  //阈值
CvMemStorage* g_storage = NULL;

void on_trackbar( int a );

int main( int argc, char** argv )
{
	if( ( argc != 2 ) || !( g_image = cvLoadImage( argv[1] ) ) )
		return -1;
	cvNamedWindow( "Contours", 1 );
	cvCreateTrackbar( "Threshold", "Contours", &g_thresh, 255, on_trackbar );  //滚动条设置阈值,得到的值存储到g_thresh中
	on_trackbar( 0 );
	cvWaitKey(0);

	return 0;
}

void on_trackbar( int a )
{
	if( g_storage == NULL )
	{
		g_gray = cvCreateImage( cvGetSize( g_image ), 8, 1 );
		g_storage = cvCreateMemStorage(0);
	}
	else
	{
		cvClearMemStorage( g_storage );
	}
	CvSeq* contours = 0;
	cvCvtColor( g_image, g_gray, CV_BGR2GRAY );  //转换色彩空间,转为灰度图
	cvThreshold( g_gray, g_gray, g_thresh, 255, CV_THRESH_BINARY );  //阈值变换
	cvFindContours( g_gray, g_storage, &contours );
	cvZero( g_gray );
	if( contours )
	{
		cvDrawContours( g_gray, contours, cvScalarAll( 255 ), cvScalarAll( 255 ), 1 );  //绘制轮廓
	}
	cvShowImage( "Contours", g_gray );
}

--------------------------------------------------------------------------------------------------

/*result*/

依旧是水果图

Threshold == 120

Threshold == 150

------------------------------------------------华丽的分割线-------------------------------------------------

/*在输入图像上寻找并绘制轮廓*/

/*code*/

#include <highgui.h>
#include <cv.h>
#include <stdio.h>

#define CVX_RED CV_RGB( 0xff, 0x00, 0x00 )  //宏定义
#define CVX_GREEN CV_RGB( 0x00, 0xff, 0x00 )
#define CVX_BLUE CV_RGB( 0x00, 0x00, 0xff )

int main(int argc, char** argv)
{
	cvNamedWindow( argv[0], 1 );
	IplImage* img_8uc1 = cvLoadImage( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
	IplImage* img_edge = cvCreateImage( cvGetSize( img_8uc1 ), 8, 1 );  //单通道图
	IplImage* img_8uc3 = cvCreateImage( cvGetSize( img_8uc1 ), 8, 3 );
	cvThreshold( img_8uc1, img_edge, 128, 255, CV_THRESH_BINARY );
	CvMemStorage* storage = cvCreateMemStorage();
	CvSeq* first_contour = NULL;

	int Nc = cvFindContours( img_edge, storage, &first_contour, sizeof( CvContour ), CV_RETR_LIST );  //返回找到的轮廓数,并将第一个轮廓的序列返回到first_contour变量中
	int n = 0;
	printf( "Total Contours Detected: %d\n", Nc );
	for( CvSeq* c = first_contour; c != NULL; c = c -> h_next )
	{
		cvCvtColor( img_8uc1, img_8uc3, CV_GRAY2BGR );
		cvDrawContours( img_8uc3, c, CVX_RED, CVX_BLUE, 0, 2, 8 ); //绘制,CVX_RED是宏定义函数
		printf( "Contour #%d\n", n );
		cvShowImage( argv[0], img_8uc3 );
		printf( " %d elements:\n", c -> total );
		for( int i = 0; i < c -> total; ++i )
		{
			CvPoint* p = CV_GET_SEQ_ELEM( CvPoint, c, i );
			printf( "     (%d,%d)\n", p -> x, p -> y );  //打印出序列的所有元素
		}
		cvWaitKey(0);
		n++;
	}
	
	printf( "Finished all contours.\n" );
	cvCvtColor( img_8uc1, img_8uc3, CV_GRAY2BGR );
	cvShowImage( argv[0], img_8uc3 );
	cvWaitKey(0);
	cvDestroyWindow( argv[0] );
	cvReleaseImage( &img_8uc1 );
	cvReleaseImage( &img_8uc3 );
	cvReleaseImage( &img_edge );

	return 0;
}

/*result*/

source image

contour 1

contour 2

8 contours

抱歉!评论已关闭.