本文参考了《OpenCV图像处理编程实例》
图像形态学中两种最基本的操作就是对图形的腐蚀和膨胀,可以说,形态学中的中高级操作都是建立在这两种操作之上。
关于图像腐蚀和膨胀的概念,不严谨的简单理解是:经过腐蚀和膨胀操作后最终的效果是对图像中物体的边界进行一层一层的腐蚀和膨胀操作,这个边界包括内边界和外边界。当然这是精枝大叶的理解,如果只知道这个,在实际的工作中是不够的。严谨的理解是这样的:图像的腐蚀和膨胀实际上是用某种形状的窗去遍历图像中的每一个元素,并用这个区域中的最大值或最小值去代替窗口中的某个元素值(一般设为中心元素),若取得是最大值,则是膨胀操作,若取得是最小值,则是腐蚀操作。这里某种形状可以是十字形、菱形、矩形、X形等。这里我要强调一点,其实所有的窗都是矩形的,那么怎么样区分不同的形状呢?是这样操作的:通过把矩形中的元素置为0或1去区分。在窗内求最大值或最小值时,只有元素值不为0的位置才参与求最大值或最小值。具体的不同形状的构造方法可以参见我写的下篇博文“用形态学的方法实现图像的角点检测”。
下面我结合OpenCV的代码介绍一下标题中提到的相关概念。
首先说腐蚀和膨胀操作:
在OpenCV中,使用erode()和dilate()函数分别用于图像形态学的腐蚀与膨胀操作,各自的函数原型如下。
void erode( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
void dilate( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
可见,两个函数的参数个数和名称是一模一样的,不同的只是函数功能,所以就一起介绍了哈。
src:为输入图像,要求是二值图像或灰度图像。
dst:为输出图像,参数类型与输入图像一致。
kernel:erode()使用某种形状的窗侵蚀一个图像,dilate()也使用某种形状的窗对图像进行膨胀。参数kernel就表示这个某种形状的窗。其实和卷积核/窗口的概念类似。结合我上面提到的腐蚀和膨胀的概念可以知道,这个窗越大,显然每次对图像的腐蚀或膨胀越多,反之亦然。有下面两种构造这个窗的方法。
①使用函数getStructuringElement()来获取,原型如下:
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));来构造,参数意思如下:
shape:设置窗的形状,MORPH_RECT为矩形,矩形中的每一个元素的值为1;MORPH_ELLIPSE为椭圆,椭圆中的每一个元素的值也为1,椭圆内切于ksize参数定义的矩形大小;MORPH_CROSS代表十字形结构,十字形的长和宽由ksize定,十字形上的元素值也为1。
ksize:表示shape设置的形状的长和宽。
anchor表示形状中每个窗求出最大或最小值后用这个最大值或最小值要替代的元素,如果高为Point(-1,-1)即表示中心元素。
②直接用MAT类的构造函数生成,这个不好说的,具体可以参考我写得下篇博文“用形态学的方法实现图像的角点检测”的代码。
anchor:表示结构化元素的中心,如果取默认参数(-1,-1),程序会自动将其设置为结构元素的中心。这个概念其实和卷积核/窗口中的类似概念相同。
iterations:表示迭代次数,即对图像做几次腐蚀和膨胀。
borderType:边界处理方法,这个不多讲了,和卷积核/窗口中的类似概念相同,详情可见我写的博文 http://blog.csdn.net/wenhao_ir/article/details/51699064
borderValue:如果边界处理方法选为BORDER_CONSTANT,那么就需要设置这个CONSTANT值,这个参数就表示CONSTANT值
下面上使用这两个函数进行腐蚀和膨胀处理的源码:
源码中用到的图像下载链接:https://pan.baidu.com/s/1hsmAiiw
//OpenCV版本3.0.0 //交流QQ2487872782 2016-8-4注:很报歉,此代码目前不能公开发表在博客上,已经删除,希望大家能理解!
代码运行结果如下图所示:
再来说形态学开操作和闭操作:
形态学开运算操作的定义是先对图像进行腐蚀操作,然后再对图像进行膨胀操作。形态学闭运算则刚好相反,先对图像进行膨胀操作,再对图像进行腐蚀操作。
形态学开运算能排除小区域物体、消除孤立点、去噪、平滑物体的轮廓。
形态学闭运算能填充目标区域内的离散小空洞和分散部分。
PS:理解上面开运算和闭运算为什么有这些作用的核心是知道腐蚀和膨胀的概念哦!
OpenCV提供了morphologyEx()用于形态学开闭运算操作,其原型和参数意义如下:
void morphologyEx( InputArray src, OutputArray dst, int op, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
可见,其实这个函数的参数与上面介绍的腐蚀膨胀操作是很相似的,只多了op这个参数,所以这里就只介绍下op这个参数吧!
op为形态学操作类型选择参数,具体的可选值有以下这些:
MORPH_OPEN 形态学开操作
MORPH_CLOSE 形态学闭操作
MORPH_GRADIENT 形态学梯度操作(不急,这篇博文后面会介绍)
MORPH_TOPHAT 形态学顶帽操作(不急,这篇博文后面会介绍)
MORPH_BLACKHAT 形态学黑帽操作(不急,这篇博文后面会介绍)
使用这个函数进行形态学开、闭操作的代码如下:
源码中用到的图像下载链接:https://pan.baidu.com/s/1hsmAiiw
//OpenCV版本3.0.0 //交流QQ2487872782 2016-8-4注:很报歉,此代码目前不能公开发表在博客上,已经删除,希望大家能理解!
运行结果如下图所示:
形态学梯度(形态学边缘提取)
对于图像来说,求得了梯度很大程度上就求得了边缘,前面博文介绍的各种边缘检测算子实际上都是在求梯度嘛,所以我在括号里写了形态学梯度实际上就是进行边缘提取。形态学梯度的原理是根据膨胀或腐蚀与原图作差来检测梯度(边缘)
上面已经介绍了,形态学梯度也是使用morphologyEx()来作操作,上面已经介绍了,这里就不介绍了。
代码如下(代码中用到的图像下载链接:https://pan.baidu.com/s/1bQ2izc):
//OpenCV版本3.0.0 //交流QQ2487872782 2016-8-4注:很报歉,此代码目前不能公开发表在博客上,已经删除,希望大家能理解!
运行结果如下图所示:
大家可以把这个边缘检测结果和前面博文中提到的各种边缘检测算法作个对比,详情http://blog.csdn.net/wenhao_ir/article/details/51768214,实际上形态学也是基于窗(卷积核)来实现的,因为你腐蚀和膨胀是基于窗的操作嘛。
形态学顶帽操作和黑帽操作:
形态学顶帽操作是计算原图像与开运算结果之差,形态学黑帽操作是计算闭运算结果图与原图像之差。各自有什么作用呢?那你首先要知道开运算和闭运算分别给图像减少和增加了什么东西,它们与原图像的差就是这些增加和减少的东西吧,我引用这篇博文中上面介绍的开运算和闭运算的作用如下吧:
形态学开运算能排除小区域物体、消除孤立点、去噪、平滑物体的轮廓。
形态学闭运算能填充目标区域内的离散小空洞和分散部分。
由于时间有限,所以这里我就不给具体的代码了,反正还是使用morphologyEx函数实现,具体怎么用,上面已经介绍过了,这里就不多说了!
-------------------------------------------
欢迎大家加入图像识别技术交流群:271891601,另外,特别欢迎成都从事图像识别工作的朋友交流,我的QQ号2487872782