![7c76c786e2f9664f61e3f512ce544c1c.png](https://i-blog.csdnimg.cn/blog_migrate/c679f85ea224b136fe06e56b1eed409f.png)
opencv形态学运算
形态学是生物学中研究动物和植物结构的一个学科分支。数学形态学是以形态为基础对图像进行分析的数学工具。基本思想是用具有一定形态的结构元素去度量和提取图像中的对应形状以达到对图像分析和识别的目的。形态学图像处理的数学基础是集合论。
1964,Matheron指导下的Serra做岩相学分析。1966年命名Mathematical Morphology。1968年成立枫丹白露数学形态学研究中心。70年代纹理分析器商业应用。理论方面有Mathron的《随机集和积分几何》,很多年来,数学形态学的核心内容并未引起信号图像处理方面重视,更多的是为自然科学家所用。
Serra 1982完成《图像分析与数学形态学》,形态学走向美国及世界。在格论框架上建立的数学形态学基础。
90年代至今,在模式识别,编码,运动分析,运动景物描述等方面取得进展。用于数值函数的形态学算子开始开发。Serra说 “如果证明,在某些时候,形态学方法比其他方法在模式识别方面更有效,那是因为它更好地把握了景物的几何特点,仅此而已”。形态学运算以集合论为基础,如图1所示交并补集。
![456cf5e64652db90219967fa81bdc755.png](https://i-blog.csdnimg.cn/blog_migrate/899ddafa6b01ecea5511bdc51410bb76.png)
二值形态学中的运算对象是集合。设A为图像集合,S为结构元素,数学形态学运算是用S对A进行操作。结构元素本身也是一个图像集合。对每个结构元素可以指定一个原点,它是结构元素参与形态学运算的参考点。二值形态学中两个最基本的运算是腐蚀与膨胀。
腐蚀运算是把结构元素S看作为一个卷积模板,每当结构元素平移到其原点位置与目标图像A中那些像素值为“1”的位置重合时,就判断被结构元素覆盖的子图像的其他像素的值是否都与结构元素相应位置的像素值相同;只有当其都相同时,就将结果图像中的那个与原点位置对应的像素位置的值置为“1”,否则置为“0”。
腐蚀可以把小于结构元素的物体(毛刺、小凸起)去除,这样选取不同大小的结构元素,就可以在原图像中去掉不同大小的物体。如果两个物体之间有细小的连通,那么当结构元素足够大时,通过腐蚀运算可以将两个物体分开。
膨胀运算与腐蚀运算相反,每当结构元素S在目标图像A上平移后,结构元素S与其覆盖的子图像中至少有一个元素相交时,就将目标图像中与结构元素S的原点对应的那个位置的像素值置为“1”,否则置为“0”。
膨胀和腐蚀运算是形态学中的两个基本运算,由此衍生出其他运算,如开运算和闭运算等,开运算是先膨胀后腐蚀,闭运算是先腐蚀后膨胀。膨胀和腐蚀并不是一对逆运算。因此,开运算和闭运算得到的图像结果是完全不一样的。
形态学运算的理论非常简单,就是根据两个区域的交并补等情况决定位置要素的值为“0”或“1”。刚开始接触形态学运算的人,可能很快就会学会怎么调用opencv的形态学函数,可是,用了几次之后,发现可能效果并不理想。其实不然,形态学运算可以说几乎在所有的图像处理过程中都会用到,而且效果非常好。如果说效果不好,那只是因为你还没有掌握形态学运算的技巧。与均值滤波类似,很多人在选择结构元素的时候,形状一般是矩形,大小一般是3X3或5X5。而不愿意改变一下结构元素的 形状和大小,当然看不到效果了。
图2是一幅音乐符号图像。现在,我们要在这张图像上把直线部分分割出来,同时,也要把音乐符号部分分割出来。刚开始很多人拿到这种图像的时候不知所措。其实,非常简单,就用几个形态学算法就可以实现。下面以具体的代码来看下怎么实现的。代码在vs2015+opencv4.01上实现,如果配置好了opencv的环境,直接拷贝过去就可以运行,当然,需要有对应的图像。在代码里面有具体的注释。
#include <iostream>
#include <opencv2opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
//读取图像
Mat src = imread("e:/1.png", 1);
imshow("src", src);
Mat gray;
//如果是三通道图像,转成灰度图
if (src.channels() == 3)
{
cvtColor(src, gray, COLOR_BGR2GRAY);
}
else
{
gray = src;
}
imshow("gray", gray);
Mat bw;
//对图像进行二值化,因为这里演示的是二值图像的形态学,
//灰度图一样可以做形态学运算,只不过运算的方式稍微有一点变化
threshold(gray, bw, 100, 255, 0);
imshow("binary", bw);
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
//定义水平方向进行形态学运算的结构元素,注意一下大小,很关键
int horizontal_size = horizontal.cols / 30;
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1));
//腐蚀运算
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
//膨胀运算
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
//可以看到提取出的直线
imshow("horizontal", horizontal);
//定义垂直方向进行形态学运算的结构元素,注意一下大小,很关键
int vertical_size = vertical.rows / 30;
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size));
//腐蚀运算
erode(vertical, vertical, verticalStructure, Point(-1, -1));
//膨胀运算
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
//可以看到提取出的音乐符号
imshow("vertical", vertical);
waitKey(0);
return 0;
}
图3和图4是水平方向和垂直方向提取出来的效果。
![8612f7e34ea9f83a4c8ef14844baccd0.png](https://i-blog.csdnimg.cn/blog_migrate/876fe084a3301612ddf6120049db4a2e.png)
![333726ac81c607849168fe909ee7ff95.png](https://i-blog.csdnimg.cn/blog_migrate/b344cd5a1d82ac1186edb488ddd3e8eb.png)
![602e4e021f12f5c424090ceee97f4a7e.png](https://i-blog.csdnimg.cn/blog_migrate/f3538c8f2af7bd6c7f027dc76fe7092d.png)
可以说,几乎没有利用形态学提取不了的特征,如果一次不够,那就多用几次。如果还提取不出来,那就是使用的方法有问题,注意一下结构元素的大小和形状,同时要深刻理解膨胀、腐蚀、开运算、闭运算等怎么结合使用。形态学的结构元素形状主要有矩形、十字形、圆或椭圆。此外,还有最关键的是,要学会自己定义不同的结构形状,不要局限于固定的几种。比如,要在一对数字中提取出数字“7”,那么,可以定义一个“7”这样的结构形状,自然就可以提取出来了。当然,在实际使用中还有很多需要注意的地方,这需要自己慢慢摸索。通过自己定义的结构元素,可以实现很多意想不到的效果。