opencv 轮廓判别法(find contour)使用详解

时间:2024-03-22 10:48:44

本周学习了手写数字识别

////////////////////////////////////////////////////////////////////////////////////////////////////

我先尝试了第一种方法:基于轮廓查找的识别方法

这种方法主要运用了两个函数:findcontourdrawcontour

其中,第一个函数可以实现对于数字图像的轮廓查找,将查找到的点通过点向量的形式保存在了contour(为findcontour的第二个函数参数)中,然后通过drawcontour将查找到的轮廓描出来!

NOTE:需要注意的是,这两个函数是共同使用的,即有find则有draw。最重要的一点是他们共同使用了contour中的数据,换句话说,findcontour查找到了二值图像中的点向量信息(必须是二值图像,而且还必须是黑底白字貌似才能查找),然后在drawcontour函数中,就回会得到contour的数据。

好处在于,灰度图得到的数据,可以在原彩色图中被利用,从而实现了彩色图像数据的检测。

如图所示:

opencv 轮廓判别法(find contour)使用详解 

opencv 轮廓判别法(find contour)使用详解 

通过上图可以看出,我们获得了contour是(函数的第二个参数)后,将org彩色图给了lunkuo,在原图上就可以检测出数字。

NOTE:这里有一个问题一定要注意!!!!

(1)函数findcontours从二值图像中提取轮廓,并且返回提取轮廓的数目。指针First_contour的内容由函数填写。它包含第一个最外层轮廓的指针,如果指针为NULL,则没有检测到轮廓(比如图像全部为黑)。

 

(2)在做阈值处理时,一定要注意最终二值图像必须为黑底白字,如果是白底黑字,那么我们就需要用:

opencv 轮廓判别法(find contour)使用详解 

如果此处我们改为白底黑字,即CV_THRESH_BINARY,则会出现了误检情况,如图所示,最外层红色大框为误检测!!

opencv 轮廓判别法(find contour)使用详解 

而正常情况下黑底白字,能够正常识别,这点需要注意!

opencv 轮廓判别法(find contour)使用详解 

 

3注意:findContours()运行的时候,这个图像会被直接涂改,因此如果是将来还有用的图像,应该复制之后再传给findContours()

即不能将经过findcontour处理运行后的图像在显示或者拿来用,因为该图片已经被处理过了!!这就需要重新引用原图!!

此处我将findcontour运行过的out图直接拿来进行轮廓检测,结果出现了如下情况:

opencv 轮廓判别法(find contour)使用详解 

opencv 轮廓判别法(find contour)使用详解 

图像变成了黑色,但是我设置断点运行,并且查看打了contour中确实有返回参数,即轮廓参数时正常的,这就验证了上面所说的观点。即indContours()运行的时候,这个图像会被直接涂改!!

因此,不能将处理过后的out拿来运行。

所以正确的操作应该是:

opencv 轮廓判别法(find contour)使用详解 

//////////////////////////////////////////////////////////////////////////////////////////////

EXAMPLE在二值图像中,进行检测:实测:

如图所示:

(1)我们输入一个经过处理后的图像:  该图像我们定义为org(原图)

opencv 轮廓判别法(find contour)使用详解 

 

(2) 我们将原图org经过处理反二值变化滤波后得到了图像out(输出)

 

opencv 轮廓判别法(find contour)使用详解 

findContours(out, contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);//查找所有外轮廓,输入图像必须为二值图

使用find contour查找轮廓(NOTE:1)必须二值图像2)必须为黑底白字)

我们用蓝色描绘出字体,处理流程结果如上图所示。

(3)进行检测

 opencv 轮廓判别法(find contour)使用详解

 

此处在findContours(out, contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE)

我们需要知道:

1. MODE 4种类型:

CV_RETR_EXTERNAL 只检测出最外轮廓即c0。图2中第一个轮廓指向最外的序列,除此之外没有别的连接。

    CV_RETR_LIST 检测出所有的轮廓并将他们保存到表(list)中,图2中描绘了这个表,被找到的9条轮廓相互之间由h_prev和h_next连接。这里并没有表达出纵向的连接关系,没有使用v_prev和v_next.

    CV_RETR_COMP 检测出所有的轮廓并将他们组织成双层的结构,第一层是外部轮廓边界,第二层边界是孔的边界。从图2可以看到5个轮廓的边界,其中3个包含孔。最外层边界c0有两个孔,c0之间的所有孔相互间由h_prev和h_next指针连接。

CV_RETR_TREE 检测出所有轮廓并且重新建立网状的轮廓结构。图2中,根节点是最外层的边界c0,c0之下是孔h00,在同一层中与另一个孔h01相连接。同理,每个孔都有子节点(相对于c000和c010),这些子节点和父节点被垂直连接起来。这个步骤一直持续到图像最内层的轮廓,这些轮廓会成为树叶节点。

2. METHOD 5个值

CV_CHAIN_CODE 用freeman链码输出轮廓,其他方法输出多边形(顶点的序列)。

    CV_CHAIN_APPROX_NONE将链码编码中的所有点转换为点序列形式(差不多就是把所有点都描出来)

    CV_CHAIN_APPROX_SIMPLE压缩水平,垂直或斜的部分,只保存最后一个点。

    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_QPPROX_TC89_KCOS使用Teh-Chin链逼近算法中的一个。

    CV_LINK_RUNS与上述的算法完全不同,连接所有的水平层次的轮廓。

 

此处:我们选择CHAIN_APPROX_SIMPLE可以成功识别所有数字和字母。

如果使用CHAIN_APPROX_NONE, 字幕E无法检测出来。

//////////////////////////////////////////////////////////////////////////////////////

 

 

//////////////////////////////////////////////////////////////////////////////////////

 

以上所描述的方法,仍存在以下问题:

(1)输入的图像是经过二次截图后的二值图像,最开始无法单独检测,再次截图后,加载截图后的图像,才能分别检测出三个字符。

这个问题可以从修改程序,定义一个有返回值return的函数,让它把二次处理后的图片直接return给主函数。

(2)是否可以使用canny 边缘检测处理。

预处理思路为:灰度→二值(自适应or大津阈值(参考车道线检测))→腐蚀→canny检测

(3)如何去除图像边缘的白色干扰??

S1 : 运用车牌号检测程序的方法,自动截取上下左右边界,从而去除外边界框的干扰

S2 : 设置感兴趣ROI区域?

S3 : 结合实际,我们发现第一次检测往往会出现将三个数字都框起来的误检情况,能够将第一次误检框出的图片自动截取保存,然后在次基础上,进行第二次轮廓检测,从而实现分别检测。(具体过程实际和上文方法一样,上文程序的实现,就是手动截取了第一次三个数都框起来的图片,然后重新输入后,得到的检测结果)

该方法可以验证!!

(4)可以采用垂直投影方法检测

混合方法:用轮廓检测/或者车牌号 得到理想的区域,使用垂直投影检测分割。

NOTE:

分割程序:

//************************************* 切割出定位的矩*********************************///

char filenamew[255];

sprintf_s(filenamew,"定位矩形");

char rectnum[255];

char file[255];

Mat pic;

for (int i = 0; i<contours.size(); i++)

{

sprintf_s(file,"%s%d", filenamew, i);

sprintf_s(rectnum,"%s%d.bmp", filenamew, i);

Rect r = boundingRect(Mat(contours[i]));

pic= yuchuli(r);//选定roi

 // resize(pic,pic,Size(128,128));//把切割的图片缩放成1128*1128

imwrite(rectnum, pic);

imshow(file, pic);

}