图像分割是图像处理中算是比较核心的部分了,就图像分割方法很多但也很灵活,切忌拘泥于一种算法而套用。图像分割的主要应用是利用一些特性从复杂环境中把需要的目标分割出来处理。比如说交通监制系统把视野中的车辆分割提取出来,从而实现对车辆的监控的。
这里假设已经通过基本的分割方法,得到了图像分割结果的Mask图像。那么,如果从Mask图像中提取各个连通区域并提取相关属性呢。幸运的是opencv 中给我们提供了这些需要的环境。这里要介绍的是cvFindContours()函数的应用。也是查找轮廓,应用比较广主要是因为它的参数容易出彩。
在二值图像中寻找轮廓
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) );
函数 cvFindContours 从二值图像中提取轮廓,并且返回提取轮廓的数目。指针 first_contour 的内容由函数填写。它包含第一个最外层轮廓的指针,如果指针为 NULL,则没有检测到轮廓(比如图像是全黑的)。其它轮廓可以从 first_contour 利用 h_next 和 v_next 链接访问到。 在 cvDrawContours 的样例显示如何使用轮廓来进行连通域的检测。轮廓也可以用来做形状分析和对象识别 - 见CVPR2001 教程中的 squares 样例。该教程可以在 SourceForge 网站上找到。
// findcontours //writen @ polly_yang CvMemStorage * p_storage = cvCreateMemStorage(0); CvSeq *p_first_contour = 0; int region_num = cvFindContours(img,p_storage,&p_first_contour,sizeof(CvContour),CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0)); printf("the region number is %d...\n",region_num); double area; int flag = 0 ; for (;p_first_contour;p_first_contour = p_first_contour->h_next) //访问cvseq { if (p_first_contour) { area = fabs(cvContourArea(p_first_contour,CV_WHOLE_SEQ)); printf(" no. %d area == %lf ..\n",flag,area); flag++; if (area > 100) { CvRect area_Rect = cvBoundingRect(p_first_contour,0); cvRectangleR(m_pNowImg,area_Rect,CV_RGB(255,0,0)); } } } CString out_temp_region = m_OutTempStr + CString(_T("_2_region.bmp")); cvSaveImage(CT2A(out_temp_region),m_pNowImg);
这段代码实现了检测Mask图像中的所有连通域并将所有连通域的外接矩形绘制到原始图像上去,连通域的结果序列存放在CvSeq序列中。通过cvContourArea可以得到一个连通域的面积;通过cvBoundingRect可以得到连通域的外接矩形。剩下的就是对ROI操作了。
函数原型 | 说明 |
CvSeq* cvCreateSeq(int seq_flags,int header_size,int elem_size,CvMemStorage* storage) | 功能:创建一序列 参数:seq_flags为序列的符号标志。如果序列不会被传递给任何使用特定序列的函数,那么将它设为0,否则从预定义的序列类型中选择一合适的类型。 Header_size为序列头部的大小;必须大于或等于sizeof(CvSeq)。如果制定了类型或它的扩展名,则此类型必须适合基类的头部大小。 Elem_size为元素的大小,以字节计。这个大小必须与序列类型(由seq_flags指定)相一致。例如,对于一个点的序列,元素类型 CV_SEQ_ELTYPE_POINT应当被指定,参数elem_size必须等同于sizeof(CvPoint)。Storage为指向前面定义的 内存存储器 |
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) |
功能:初始化序列中的读取过程 |