您的位置:首页 > 运维架构

opencv序列结构CvSeq和轮廓提取cvFindContours的简单运用

2015-06-13 22:21 621 查看
1)OpenCV CvSeq的内部结构探讨

  对于CvSeq这一结构体,又称为可动态增长元素序列(OpenCV_1.0已发生改变,详见cxtypes.h) Growable sequence of elements。

  CvSeq定义复杂,首先,定义CV_SEQUENCE_FIELDS()。

  #define CV_SEQUENCE_FIELDS() \

typedef struct CvSeq

{

  int flags; /* micsellaneous flags */

  int header_size; /* size of sequence header */


  struct CvSeq* h_prev; /* previous sequence */

  struct CvSeq* h_next; /* next sequence */


  struct CvSeq* v_prev; /* 2nd previous sequence */

  struct CvSeq* v_next; /* 2nd next sequence */

  int total; /* total number of elements */

  int elem_size; /* size of sequence element in bytes */

  char* block_max; /* maximal bound of the last block */

  char* ptr; /* current write pointer */ \

  int delta_elems; /* how many elements allocated when the


sequence grows (sequence granularity) */

  CvMemStorage* storage; /* where the seq is stored */

  CvSeqBlock* free_blocks; /* free blocks list */

  CvSeqBlock* first; /* pointer to the first sequence block */

}


而CvSeq可以表达成:

  typedef struct CvSeq

  {

  CV_SEQUENCE_FIELDS()

  } CvSeq


2)cvCreateSeq:创建一序列

CvSeq* cvCreateSeq(int seq_flags,int header_size,int elem_size,CvMemStorage* storage)

  说明:CvSeq本身就是一个可增长的序列,不是固定的序列

  参数:seq_flags为序列的符号标志。如果序列不会被传递给任何使用特定序列的函数,那么将它设为0,否则从预定义的序列类型中选择一合适的类型。 Header_size为序列头部的大小;必须大于或等于sizeof(CvSeq)。如果制定了类型或它的扩展名,则此类型必须适合基类的头部大小。 Elem_size为元素的大小,以 字节计。这个大小必须与序列类型(由seq_flags指定)相一致。例如,对于一个点的序列,元素类型
CV_SEQ_ELTYPE_POINT应当被指定,参数elem_size必须等同于sizeof(CvPoint)。Storage为指向前面定义的内存存储器。


3)一些序列函数集合

CvSeq* cvCloneSeq(const CvSeq* seq,CvMemStorage* storage=NULL) 创建序列的一份拷贝



Void cvSeqInvert(CvSeq* seq) 将序列中的元素进行逆序操作




Void cvSeqSort(CvSeq* seq,CvCmpFunc func,


void *userdata=NULL) 使用特定的比较函数对序列中的元素进行排序

Char* cvSeqSearch(CvSeq* seq,const void* elem,CvCmpFunc func,int is_sorted,


int *elem_idx,void *userdata=NULL) 查询序列中的元素



Void cvClearSeq(CvSeq* seq); 清空序列




Char* cvSeqPush(CvSeq* seq,void* element=NULL) 添加元素到序列的尾部




void cvSeqPop(CvSeq* seq,void* element=NULL)删除序列尾部元素

Char* cvSeqPushFront(CvSeq* seq,void* element=NULL) 在序列头部添加元素

Void cvSeqPopFront(CvSeq* seq,void* element=NULL) 删除在序列的头部的元素

Void cvSeqPushMulti(CvSeq* seq,void* elements,


int count,int in_front=0);添加多个元素到序列尾部或头部

Void cvSeqPopMulti(CvSeq* seq,void* elements,


int count,int in_front=0) 删除多个序列头部或尾部元素

Char* cvSeqInsert(CvSeq* seq,int before_index,


void* element=NULL)在序列中的指定位置添加元素

Void cvSeqRemove(CvSeq* seq,int index) 删除序列中的指定位置的元素

Char* cvGetSeqElem(const CvSeq* seq,int index) 返回索引所指定的元素指针

Int cvSeqElemIdx(const CvSeq* seq,const void* element,


CvSeqBlock** block=NULL)返回序列中元素的索引

Void cvStartAppendToSeq(CvSeq* seq,CvSeqWriter* writer) 将数据写入序列中,并初始化该过程

Void cvStartWriteSeq(int seq_flags,int header_size,int elem_size,


CvMemStorage* storage,CvSeqWriter* writer)创建新序列,并初始化写入部分

CvSeq* cvEndWriteSeq(CvSeqWriter* writer) 完成写入操作

Void cvStartReadSeq(const CvSeq* seq,CvSeqReader* reader,


int reverse=0)初始化序列中的读取过程



4)cvFindContours:返回检测到的轮廓的个数

函数cvFindContours从二值图像中检索轮廓,并返回检测到的轮廓的个数。first_contour的值由函数填充返回,它的值将为第一个外轮廓的指针,当没有轮廓被检测到时为NULL。其它轮廓可以使用h_next和v_next连接,从first_contour到达。

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像素保存不变。从一个灰度图像得到二值图像的函数有:cvThreshold,cvAdaptiveThreshold和cvCanny。

storage

返回轮廓的容器。

first_contour

输出参数,用于存储指向第一个外接轮廓。

header_size

header序列的尺寸。就两种情况:

如果选择method = CV_CHAIN_CODE, 则header_size >= sizeof(CvChain);

其他,则header_size >=sizeof(CvContour)。

mode

检索模式,可取值如下:

CV_RETR_EXTERNAL:只检索最外面的轮廓;

CV_RETR_LIST:检索所有的轮廓,并将其放入list中;

CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;

CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。

蓝色表示v_next,绿色表示h_next

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:使用the flavors of Teh-Chin chain近似算法的一种。

CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。

offset

偏移量,用于移动所有轮廓点。当轮廓是从图像的ROI提取的,并且需要在整个图像中分析时,这个参数将很有用。

讨论部分cvDrawContours中的案例显示了任何使用轮廓检测连通区域。轮廓可以用于形状分析和目标识别——可以参考文件夹OpenCV sample中的squares.c

5)cvDrawContours:在图像上绘制外部和内部轮廓

函数cvDrawContours用于在图像上绘制外部和内部轮廓。当thickness >= 0 时,绘制轮廓线;否则填充由轮廓包围的部分。

void cvDrawContours( CvArr *img, CvSeq* contour,CvScalar external_color, CvScalar hole_color,

int max_level, int thickness=1,int line_type=8, CvPoint offset=cvPoint(0,0) );

img

要在其上绘制轮廓的图像。和在其他绘图函数里一样,轮廓是ROI的修剪结果。

contour

指向第一个轮廓的指针。

external_color

外轮廓的颜色。

hole_color

内轮廓的颜色。

max_level

画轮廓的最大层数。如果是0,只绘制contour;如果是1,将绘制contour后和contour同层的所有轮廓;如果是2,绘制contour后所有同层和低一层的轮廓,以此类推;如果值是负值,则函数并不绘制contour后的轮廓,但是将画出其子轮廓,一直到abs(max_level) - 1层。

thickness

绘制轮廓线的宽度。如果为负值(例如,等于CV_FILLED),则contour内部将被绘制。

line_type

轮廓线段的类型,具体查看cvLine的描述。

offset

按给定值移动所有点的坐标。

6)cvBoundingRect:返回二维点集的最外面 (up-right)矩形边界

CvRect cvBoundingRect( CvArr* points, int update=0 );

points:

二维点集,点的序列或向量 (CvMat)

update:

更新标识。

下面是轮廓类型和标识的一些可能组合:

update=0, contour ~ CvContour*: 不计算矩形边界,但直接由轮廓头的 rect 域得到。

update=1, contour ~ CvContour*: 计算矩形边界,而且将结果写入到轮廓头的 rect 域中 header。

update=0, contour ~ CvSeq* or CvMat*: 计算并返回边界矩形。

update=1, contour ~ CvSeq* or CvMat*: 产生运行错误 (runtime error is raised)。

函数 cvBoundingRect 返回二维点集的最外面 (up-right)矩形边界。

7) cvRectangle:通过对角线上的两个顶点绘制简单、指定粗细或者带填充的矩形,是个绘图函数

函数原型:void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color,int thickness=1, int line_type=8, int shift=0 );

参数介绍:

img -- 图像.

pt1 -- 矩形的一个顶点。

pt2 -- 矩形对角线上的另一个顶点

color -- 线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。

thickness -- 组成矩形的线条的粗细程度。取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。

line_type -- 线条的类型。见cvLine的描述

shift -- 坐标点的小数点位数。

代码:
#include <stdio.h>

#include <cv.h>

#include <highgui.h>

#include <math.h>

int main()

{

IplImage *src = cvLoadImage(".\\a4.jpg", 0);

IplImage *dsw = cvCreateImage(cvGetSize(src), 8, 1);

IplImage *dst = cvCreateImage(cvGetSize(src), 8, 3);

CvMemStorage *storage = cvCreateMemStorage(0);

CvSeq *first_contour = NULL;

//turn the src image to a binary image

//cvThreshold(src, dsw, 125, 255, CV_THRESH_BINARY_INV);

//二值化源图像

cvThreshold(src, dsw, 100, 255, CV_THRESH_BINARY);

cvFindContours(dsw, storage, &first_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

cvZero(dst);

int cnt = 0;

for(; first_contour != 0; first_contour = first_contour->h_next)

{

cnt++;

//外内轮廓的颜色

CvScalar color = CV_RGB(rand()&255, rand()&255, rand()&255); //rand()&255, rand()&255, rand()&255???

//#define CV_FILLED -1 如果为负值(例如,等于CV_FILLED),则contour内部将被绘制。

cvDrawContours(dst, first_contour, color, color, 0, 2, CV_FILLED, cvPoint(0, 0));

//返回二维点集的最外面 (up-right)矩形边界

CvRect rect = cvBoundingRect(first_contour,0);

//cvRectangle是绘图函数,绘制矩形

cvRectangle(dst, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y + rect.height),CV_RGB(255, 0, 0), 1, 8, 0);

}

printf("the num of contours : %d\n", cnt);

cvNamedWindow( "Source", 1 );

cvShowImage( "Source", src );

cvSaveImage("source.jpg",src);

cvNamedWindow( "dsw", 1 );

cvShowImage( "dsw", dsw );

cvSaveImage("dsw.jpg",dsw);

cvNamedWindow( "Components", 1 );

cvShowImage( "Components", dst );

cvSaveImage("components.jpg",dst);

cvReleaseMemStorage(&storage);

cvWaitKey(-1);

return 0;

}
结果:

输入:


二值化:


轮廓:


二值化图像的效果不是很好,这个rand()&255, rand()&255, rand()&255???还不知道是什么意思,详细有待学习后再更新。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: