图像识别
在一般的图像识别中,我们要将图片按照 模糊处理->灰度->二极化->腐蚀/膨胀->边缘检测->框住物体 来识别物体
一.灰度
cvtColor() 函数,这是一个颜色空间转换,在图像识别中,有很大的作用,可以将通道数变少,后期易于图像处理
以下是常用的操作
cvtColor(srcImage ,dstImage ,COLOR_GRAY2BGR); // 转换原始图为灰度图
cvtColor(srcImage ,dstImage ,COLOR_BGR2HSV); // 转换原始图为hsv图
原图
转化为灰度后
转化为HSV后
二.模糊处理
主要分为均值滤波,高斯滤波。
均值滤波:
blur(Mat src,Mat dst,Size(x,y),Point(-1,-1))
高斯滤波:
GaussianBlur(Mat src,Mat dst,Size(x,y),0,0,Point(-1,-1))
其中,x,y一般都是奇数,为了让结构元素有一个中心点,其中Point(-1,-1)表示的就是在中心点。
接下来解释一下它们的原理
均值滤波,顾名思义,是用像素点周围的邻域像素(Size)的平均值来代替point像素的值,缺点是遇到临近点像素差距大,可能会丢失重要信息,在简单识别中容易使用。
高斯滤波,是类似于正态分布函数,中心点按权值分配,可以规避均值滤波的缺点,使图像更平滑,适合干扰项较多的识别。
三.二极化
核心函数,threshold(InputArray src ,OutputArray dst, double thresh , double maxva l , int type)
补充
0代表黑色,255代表白色,中间值则为深浅不一的灰色
这是一种固定阈值的操作,常用的type是THRESH_BINARY,本质就是,在灰度处理后的图像,若低于阈值都为0,高于阈值都为1
以下是其他type下的操作方式
在二极化后,图像会变得有理可循,需要识别的特征物也会如约而至,如果说二极化前,是乱花渐欲迷人眼,那么,二极化后,就是柳暗花明又一村。
四.腐蚀/膨胀
一、腐蚀
1.概念
腐蚀是一种消除边界点,使边界向内部收缩的过程。可以用来消除小且无意义的目标物。如果两目标物间有细小的连通,可以选取足够大的结构元素,将细小连通腐蚀掉。
简单点说就是,就是将白色区域缩小
2.算法的具体步骤
1.搭建好size大小的结构元素
2.从上到下,从左到右找到第一个像素值为1的点。
3.将结构元素的原点移动到该点
4.判断该结构元素覆盖范围内的图像的像素值是否全部为1,如果是,则腐蚀后图像相同位置上的像素值为1,如果至少有一个像素的值为0,则腐蚀后图像的相同位置上的像素值为0
5.对原图中其余像素值为1的点重复进行步骤3和步骤4
原图
第一次变化后
以此类推…
原图(左)腐蚀后(右)
3.代码
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
erode(src,dst, kernel);
其中,MORPH_ELLIPSE 表示 椭圆(包括圆形)
于此类似的还有 MORPH_RECT 矩形(包括线形) MORPH_CROSS 十字形(一定要设置好锚点)
Size(2,2)即是2x2的预先设定好的形状
二、膨胀
1.概念
膨胀是将目标区域接触的背景点合并到该目标物中,使目标边界向外扩张的处理。膨胀可以用来填补目标区域存在的某些空洞,也可以用来消除包含在目标区域中的小颗粒噪声。膨胀处理是腐蚀处理的对偶。
简单来说就是,填满空隙,使物体形象更丰满,更容易理解就是让白色区域扩大。
2.原理
和腐蚀差不多,就是判断该结构元素覆盖范围内的图像的像素值是否全部为0,如果是,则腐蚀后图像相同位置上的像素值为0,如果至少有一个像素的值为1,则腐蚀后图像的相同位置上的像素值为1.
原图 (左) 膨胀后(右)
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
dilate(src,dst, kernel);
其余亦和腐蚀相似
由此,我们可以引申出开闭运算以及顶帽黑帽等,他们本质上是腐蚀和膨胀的组合。
五.边缘检测
这里介绍最好的一种:Canny算子
代码
Canny(srcImage, dsIImage , 3 (阈值1), 9(阈值2), 3 (孔径大小,默认为3))
两个阈值中较小的值用于边缘连接,而较大的值用来控制强边缘的初始段,简单来说就是,超过大阈值的为边缘,在大阈值和小阈值之间的若与超过大阈值部分连接,也看成边缘。
为什么在众多算子中,我只介绍Canny呢?
因为Canny使用了变分法,一个最优的边缘检测算法,这是很多人推崇的算子。
六.框住物体
1.寻找轮廓
vector<vector<Point>> contours;
findContours(InputOutputArray image , OutputArrayOfArrays contours, OutputArray hierarchy, int mode , int method , Point offset=Point () );
第一个参数 Mat类的对象即可,且需为8位单通道图像
第二个参数 检测到的轮廓、函数调用后的运算结果存在这里
第三个参数 hierarchy,作为数组,来存储轮廓之间的关系(里外关系)
第四个参数 轮廓检索模式,比如RETR_EXTERNAL 表示只检测外层轮廓
第五个参数 轮廓的近似办法 一般取CHAIN APPROX SlMPLE,只保留方向终点坐标
第六个参数 每个轮廓点的可选偏移量
vector可以看成一个容器,与数组类似,区别在于vector可以自己扩展空间。
其中contours可以看出一个二位数组,用来存储相同点集的集合,实际上每一个连续的轮廓,是由一个个独立的点拼成。
like:
contours[0][];
contours[1][];
…
不同的第一个下标储存不同形状。
2.绘制轮廓
在储存好点集后,需要去确定要检测轮廓的中心,即用规则的图形来框住不规则轮廓。
函数名 | 作用 |
---|---|
minAreaRect | 寻找最小包围矩形 |
boundingRect | 返回外部矩形边界 |
minEnclosingCircle | 寻找最小包围圆形 |
代码
drawContours ( InputOutputArrayimage , InputArrayOfArrays contours, int contourldx, const Scalar& color,
int thickness=l , int lineType=8 , InputArray hierarchy=noArray () , int maxLeve l =INT MAX , Point offset=Point () )
这一代码的效果是会有设定好颜色的线,将找到的轮廓,描绘出来。
当然你也可以根据自己想要的结果用合适的轮廓框住物体,比如最小外接矩形、最小外接圆、多边形(凸点)…
总结
上述介绍的内容,只是opencv中的冰山一角,路漫漫其修远兮,笔者尽力省去了书上复杂的原理,但是带来了省略了一些重
要信息的结果,以及更多精密识别的操作,以上讲述方法,并不能解决类似于识别颜色,识别复杂背景下的物体,这些需要
其他函数来实现。本人只是让识别更容易上手,有一个基础的认识。
参考资料
OpenCV3编程入门_毛星云编著_电子工业出版