图像处理中的膨胀与腐蚀

2020-03-02 14:49:04 qq_41503660 阅读数 67
  • 图像形态学操作-腐蚀与膨胀

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析测量编码开发技巧

    24863课时 0分钟 55人学习 贾志刚
    免费试看

OpenCV 膨胀与腐蚀


1、什么是膨胀与腐蚀

  膨胀与腐蚀属于形态学范围,具体的含义根据字面意思来理解即可。但是更形象的话就是“增肥”与“减肥”。

  它们的用途就是用来处理图形问题上。总结性的来说:

  • 膨胀用来处理缺陷问题;
  • 腐蚀用来处理毛刺问题。

  膨胀就是把缺陷给填补了,腐蚀就是把毛刺给腐蚀掉了。这里其实说的并不严谨,也是为了大家理解方便。下面我们就用实例来进行演示。

2、形态学处理——膨胀

  我们先引入一张图片进行分析。
程序实现:

img = cv2.imread('Pic/corrode.png')
def cv_show(img):
    cv2.imshow('', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
cv_show(img)

  图中我们可以看到,这张图片是一个二值图片(只有黑白),而且还增加了一些毛刺。而且还包含字体中还包含一些小的间隙(缺陷)。

  下面我们对这张图片进行膨胀处理。膨胀是如何处理的呢?对于一个像素点,我们需要先指定对每个像素点膨胀的范围。

  这里我们指定范围为33的矩阵,kernel(卷积核核)指定为全为1的33矩阵,卷积计算后,该像素点的值等于以该像素点为中心的3*3范围内的最大值。由于我们是二值图像,所以只要包含周围白的部分,就变为白的。

总结: 只要原图片3 * 3范围内有白的,该像素点就是白的。
程序实现:

kernel = np.ones((3, 3), dtype=np.uint8)
dilate = cv2.dilate(img, kernel, 1) # 1:迭代次数,也就是执行几次膨胀操作
cv_show(dilate)

分析: 上图我们可以看出毛刺部分变粗,与此同时字体中的间隙也变小,补了缺陷部分。

2.1 更改卷积核大小

  如果我们更改核的大小(4 * 4),也就改变了膨胀的程度。 只要4 * 4范围内有白的就变成白的。

kernel_2 = np.ones((4, 4), dtype=np.uint8) # 卷积核变为4*4
dilate = cv2.dilate(img, kernel_2, 1)
cv_show(dilate)

2.2、更改迭代次数

kernel = np.ones((3, 3), dtype=np.uint8)
dilate = cv2.dilate(img, kernel, 2) # 更改迭代次数为2
ss = np.hstack((img, dilate))
cv_show(ss)

分析: 更改迭代次数将为2,将对图片进行2次的膨胀操作

3、形态学处理——腐蚀

  腐蚀操作和膨胀操作相反,也就是将毛刺消除,判断方法为:在卷积核大小中对图片进行卷积。取图像中(3 * 3)区域内的最小值。由于我们是二值图像,也就是取0(黑色)。
总结: 只要原图片3 * 3范围内有黑的,该像素点就是黑的。

程序实现:

kernel = np.ones((3, 3), dtype=np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
ss = np.hstack((img, erosion))
cv_show(ss)

分析: 可以看出来,毛刺部分被清除掉,但与此同时,字体边缘部分也向里凹陷了一部分。

3.1、更改卷积核大小

kernel_2 = np.ones((4, 4), dtype=np.uint8)
erosion = cv2.erode(img, kernel_2, iterations=1)
ss = np.hstack((img, erosion))
cv_show(ss)

分析: 卷积核变大后,我们发现他已经腐蚀的部分有点多了,字体原来的部分也被清除。

3.2、更改迭代次数

kernel = np.ones((3, 3), dtype=np.uint8)
erosion = cv2.erode(img, kernel, iterations=2)
ss = np.hstack((img, erosion))
cv_show(ss)

分析: 增加迭代次数后,腐蚀变得更加厉害,所以应该选择合适的迭代次数。

4、开运算和闭运算

开运算:先腐蚀,在膨胀
闭运算:先膨胀,在腐蚀

  我们在上面的膨胀和腐蚀的图片中可以看到,图片大小程度上都受到了损失,字体信息缺失或者变粗等等。如果我们不想更改原有信息,即字体粗细。那么我们可以使用上面的两种运算。例如开运算,先对字体进行变细,在对字体进行变粗,整体上字体粗细不会发生变化。毛刺信息在腐蚀的时候就已经消除了,膨胀也不会膨胀出多余信息。

4.1、开运算

opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, 1)
ss = np.hstack((img, opening))
cv_show(ss)

分析: 我们发现大部分毛刺已经消除,而且字体信息也没有发生变化,这也就是我们想要的效果。虽然仍然有一部信息没有被清除,我们只需要调整卷积核的大小就可以实现。

4.2、闭运算

closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)  ## 有缺陷,填补缺陷
ss = np.hstack((img, closing))
cv_show(ss)

分析: 字体不改变的前提下,我们把字体缺陷信息补全。

5、梯度计算

  梯度计算主要显示的是边缘信息。计算的方法:

膨胀的图像 - 腐蚀的图像

  我们明显的看出,用大一圈的图像减去小一圈的图像正好就是边缘的信息。

gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
cv_show(gradient)

分析: 我们可以看出来,我们形成了一个空心的字体样式

6、高帽和黑帽

高帽计算:原始图像 - 开运算结果
黑帽计算:闭运算结果 - 原始图像

6.1、高帽计算

  我们知道开运算的结果就是去除毛刺,我们原始图像减去开运算结果就是我们要消除的毛刺信息。

top_hat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
ss = np.hstack((img, top_hat))
cv_show(ss)

分析: 可以看出来,所有的毛刺信息我们全部提取了出来。

6.2、黑帽计算

  高帽操作显示毛刺,那么黑帽就是显示缺陷

black_hat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
ss = np.hstack((img, black_hat))
cv_show(ss)

分析: 这里我们看的不是很明显,我们只需要只要黑帽所处理的问题是什么。针对不同的场景应用不用的方法。

最后

更多精彩内容,大家可以转到我的主页:曲怪曲怪的主页
或者关注我的微信公众号:TeaUrn

2019-01-09 20:22:10 qq_42887760 阅读数 1578
  • 图像形态学操作-腐蚀与膨胀

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析测量编码开发技巧

    24863课时 0分钟 55人学习 贾志刚
    免费试看

形态学操作(morphology operators)-膨胀与腐蚀(Dilation与Erosion)。

  • 图像形态学操作
    • 图像形态学操作 – 基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学
    • 形态学有四个基本操作:腐蚀、膨胀、开、闭
    • 膨胀与腐蚀是图像处理中最常用的形态学操作手段
    • 腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
  • 膨胀与腐蚀能实现多种多样的功能,主要如下:
    • 消除噪声
    • 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素。
    • 寻找图像中的明显的极大值区域或极小值区域
    • 求出图像的梯度
相关的 API

getStructuringElement(int shape, Size ksize, Point anchor)

  • 形状 (MORPH_RECT \MORPH_CROSS \MORPH_ELLIPSE)
  • 大小
  • 锚点 默认是Point(-1, -1)意思就是中心像素

1. 形态学操作-膨胀

  • 相关的API:dilate(src, dst, kernel);
    在这里插入图片描述
  • 跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点,计算B覆盖下A的最大像素值用来替换锚点的像素,其中B作为结构体可以是任意形状
  • 形态学操作-膨胀(感官上图像变细,变白了)
    在这里插入图片描述

2. 形态学操作-腐蚀

  • 相关的API:erode(src, dst, kernel)
    在这里插入图片描述
  • 腐蚀跟膨胀操作的过程类似,唯一不同的是以最小值替换锚点重叠下图像的像素值
  • 形态学操作-腐蚀(感官上图像变粗,变黑了)

在这里插入图片描述

动态调整结构元素大小

  • TrackBar –
    createTrackbar(const String & trackbarname, const String winName, int* value, int count, Trackbarcallback func, void* userdata=0)
    其中最中要的是 callback 函数功能。如果设置为NULL就是说只有值update,但是不会调用callback的函数。

代码示例

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;

Mat src,erode_dst,dilate_dst;
char input_Win[]="input windows",dilate_Win[]="Dilation windows",Erode_Win[]="Erosion windows";
int dilate_elem=0,erode_elem=0;
int dilate_size=0,erode_size=0;
int const max_elem=2;
int const max_kernel_size=21;

void Dilation(int,void*);//膨胀
void Erosion(int,void*);//腐蚀

int main(int argc,char** argv){
	//1、加载图像,可以是BGR或灰度
	src = imread("E:/Experiment/OpenCV/Pictures/lenanoise.jpg");
	if(src.empty()){
		printf("Could not load Image ...");
		return -1;
	}

	namedWindow(input_Win,CV_WINDOW_AUTOSIZE);
	imshow(input_Win,src);
	
	//2、创建两个窗口(一个用于膨胀Dilation,另一个用于侵蚀Erosion)
	//每次移动任何滑块时,都会调用用户的Erosion或Dilation函数,它将根据当前的trackbar值更新输出图像。
	namedWindow(dilate_Win,CV_WINDOW_AUTOSIZE);
	namedWindow(Erode_Win,CV_WINDOW_AUTOSIZE);
	
	//3、为每个操作创建两组轨道栏:
	
	//3.1、第一个轨道栏“Element”返回erosion_elem或dilation_elem
	createTrackbar("卷积核类型",dilate_Win,&dilate_elem,max_elem,Dilation);
	//3.2、第二个轨道栏“内核大小”返回相应操作的erosion_size或dilation_size。
	createTrackbar("卷积核大小",dilate_Win,&dilate_size,max_kernel_size,Dilation);
	
	
	createTrackbar("卷积核类型",Erode_Win,&erode_elem,max_elem,Erosion);
	createTrackbar("卷积核大小",Erode_Win,&erode_size,max_kernel_size,Erosion);

	Dilation( 0, 0 );
	Erosion( 0, 0 );

	waitKey(0);
}

//膨胀
void Dilation(int,void*){
	int dilate_type;//内核选择三种形状中的任何一种
	if(dilate_elem == 0){ dilate_type = MORPH_RECT; }//矩形内核:MORPH_RECT
	else if(dilate_elem == 1){ dilate_type = MORPH_CROSS; }//十字架内核:MORPH_CROSS
	else if(dilate_elem == 2){ dilate_type = MORPH_ELLIPSE; }//椭圆内核:MORPH_ELLIPSE

	Mat kernel = getStructuringElement(dilate_type,Size(2*dilate_size+1,2*dilate_size+1),Point(dilate_size,dilate_size));
	dilate(src,dilate_dst,kernel);
	imshow( dilate_Win, dilate_dst );
}

//腐蚀
void Erosion(int,void*){
	int erosion_type;//内核选择三种形状中的任何一种
	if(erode_elem == 0){ erosion_type = MORPH_RECT; }//矩形内核:MORPH_RECT
	else if(erode_elem == 1){ erosion_type = MORPH_CROSS; }//十字架内核:MORPH_CROSS
	else if(erode_elem == 2){ erosion_type = MORPH_ELLIPSE; }//椭圆内核:MORPH_ELLIPSE

	Mat kernel = getStructuringElement(erosion_type,Size(2*erode_size+1,2*erode_size+1),Point(erode_size,erode_size));
	erode(src,erode_dst,kernel);
	imshow( Erode_Win, erode_dst );
}

在这里插入图片描述

推荐参考:

  1. https://blog.csdn.net/LYKymy/article/details/83153122
  2. https://blog.csdn.net/huanghuangjin/article/details/80945770
2019-11-26 19:26:06 qq_35985044 阅读数 167
  • 图像形态学操作-腐蚀与膨胀

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析测量编码开发技巧

    24863课时 0分钟 55人学习 贾志刚
    免费试看

图像形态学即数学形态学(Mathematical morphology)是一门建立在格伦和拓扑学基础上的图像分析学科,是数学形态学图像处理的基本理论;

常见图像形态学运算:腐蚀、膨胀、开运算、闭运算、骨架抽取、极线腐蚀、击中击不中变换、Top-hat变换、颗粒分析、流域变换、形态学梯度等;

最基本的形态学操作是:膨胀(dilation)和腐蚀(erosion)

膨胀和腐蚀的主要用途:

消除噪声;

分割出独立的图像元素,在图像中连接相邻的元素;

寻找图像中明显的极大值或极小值区;

求出图像的梯度;

【注】:

腐蚀和膨胀是对像素值大的部分而言的,即高亮白部分而不是黑色部分;

膨胀是图像中的高亮部分进行膨胀,领域扩张,效果图拥有比原图更大的高亮区域;

腐蚀是图像中的高亮部分被腐蚀掉,领域缩减,效果图拥有比原图更小的高亮区域;

膨胀原理:

膨胀:求局部最大值;

①定义一个卷积核B,

核可以是任何的形状和大小,且拥有一个单独定义出来的参考点-锚点(anchorpoint);

通常和为带参考点的正方形或者圆盘,可将核称为模板或掩膜;

②将核B与图像A进行卷积,计算核B覆盖区域的像素点最大值;

③将这个最大值赋值给参考点指定的像素;

因此,图像中的高亮区域逐渐增长。

 

腐蚀原理:

腐蚀:局部最小值(与膨胀相反);

①定义一个卷积核B,

核可以是任何的形状和大小,且拥有一个单独定义出来的参考点-锚点(anchorpoint);

通常和为带参考点的正方形或者圆盘,可将核称为模板或掩膜;

②将核B与图像A进行卷积,计算核B覆盖区域的像素点最小值;

③将这个最小值赋值给参考点指定的像素;

因此,图像中的高亮区域逐渐减小。

2018-10-30 15:56:28 u013230291 阅读数 6962
  • 图像形态学操作-腐蚀与膨胀

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析测量编码开发技巧

    24863课时 0分钟 55人学习 贾志刚
    免费试看

腐蚀的原理:

二值图像前景物体为1,背景为0.假设原图像中有一个前景物体,那么我们用一个结构元素去腐蚀原图的过程是这样的:遍历原图像的每一个像素,然后用结构元素的中心点对准当前正在遍历的这个像素,然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最小值,用这个最小值替换当前像素值。由于二值图像最小值就是0,所以就是用0替换,即变成了黑色背景。从而也可以看出,如果当前结构元素覆盖下,全部都是背景,那么就不会对原图做出改动,因为都是0.如果全部都是前景像素,也不会对原图做出改动,因为都是1.只有结构元素位于前景物体边缘的时候,它覆盖的区域内才会出现0和1两种不同的像素值,这个时候把当前像素替换成0就有变化了。因此腐蚀看起来的效果就是让前景物体缩小了一圈一样。对于前景物体中一些细小的连接处,如果结构元素大小相等,这些连接处就会被断开。

膨胀的原理:

二值图像前景物体为1,背景为0.假设原图像中有一个前景物体,那么我们用一个结构元素去膨胀原图的过程是这样的:遍历原图像的每一个像素,然后用结构元素的中心点对准当前正在遍历的这个像素,然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最大值,用这个最大值替换当前像素值。由于二值图像最大值就是1,所以就是用1替换,即变成了白色前景物体。从而也可以看出,如果当前结构元素覆盖下,全部都是背景,那么就不会对原图做出改动,因为都是0.如果全部都是前景像素,也不会对原图做出改动,因为都是1.只有结构元素位于前景物体边缘的时候,它覆盖的区域内才会出现0和1两种不同的像素值,这个时候把当前像素替换成1就有变化了。因此膨胀看起来的效果就是让前景物体胀大了一圈一样。对于前景物体中一些细小的断裂处,如果结构元素大小相等,这些断裂的地方就会被连接起来。

下篇文章附上底层源码
原文:https://blog.csdn.net/woainishifu/article/details/60778033

2018-07-01 15:11:10 chen134225 阅读数 6009
  • 图像形态学操作-腐蚀与膨胀

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析测量编码开发技巧

    24863课时 0分钟 55人学习 贾志刚
    免费试看
在图像处理技术中,有一些的操作会对图像的形态发生改变,这些操作一般称之为形态学操作(phology)。数学形态学是基于集合论的图像处理方法,最早出现在生物学的形态与结构中,图像处理中的形态学操作用于图像与处理操作(去噪,形状简化)图像增强(骨架提取,细化,凸包及物体标记)、物体背景分割及物体形态量化等场景中,形态学操作的对象是二值化图像。
有名的形态学操作中包括腐蚀,膨胀,开操作,闭操作等。其中腐蚀,膨胀是许多形态学操作的基础。
  
腐蚀操作:
  顾名思义,是将物体的边缘加以腐蚀。具体的操作方法是拿一个宽m,高n的矩形作为模板,对图像中的每一个像素x做如下处理:像素x至于模板的中心,根据模版的大小,遍历所有被模板覆盖的其他像素,修改像素x的值为所有像素中最小的值。这样操作的结果是会将图像外围的突出点加以腐蚀。如下图的操作过程:

                                                                             图5 腐蚀操作原理

上图演示的过程是背景为黑色,物体为白色的情况。腐蚀将白色物体的表面加以“腐蚀”。在opencv的官方教程中,是以如下的图示说明腐蚀过程的,与我上面图的区别在于:背景是白色,而物体为黑色(这个不太符合一般的情况,所以我没有拿这张图作为通用的例子)。读者只需要了解背景为不同颜色时腐蚀也是不同的效果就可以了。


                                                        图6 腐蚀操作原理2

简单来说在背景为白(1),情景为黑的图像(0)中,核与其覆盖的图像部分做“与”操作,如果全为1,则该像素点为1,否则为0;也就是0容易得到,图像更多的地方变黑了,白色部分被腐蚀了。


膨胀操作:

  膨胀操作与腐蚀操作相反,是将图像的轮廓加以膨胀。操作方法与腐蚀操作类似,也是拿一个矩形模板,对图像的每个像素做遍历处理。不同之处在于修改像素的值不是所有像素中最小的值,而是最大的值。这样操作的结果会将图像外围的突出点连接并向外延伸。如下图的操作过程:


                                                                           图7 膨胀操作原理

下面是在opencv的官方教程中,膨胀过程的图示:


                                                       图8 膨胀操作原理2

简单来说在背景为白(1),前景为黑(0)的图像中,核与其覆盖的图像部分做“与”操作,如果全为0,则该像素点为0,否则为1;也就是1容易得到,图像更多的地方变白了,白色部分膨胀了。


开操作:

作用:放大裂缝和低密度区域,消除小物体,在平滑较大物体的边界时,不改变其面积。消除物体表面的突起。

开操作就是对图像先腐蚀,再膨胀。其中腐蚀与膨胀使用的模板是一样大小的。为了说明开操作的效果,请看下图的操作过程:


                                                                          图9 开操作原理

由于开操作是先腐蚀,再膨胀。因此可以结合图5和图7得出图9,其中图5的输出是图7的输入,所以开操作的结果也就是图7的结果。


闭操作:

作用:排除小型黑洞,突触了比原图轮廓区域更暗的区域,将两个区域连接起来,形成连通域。

  闭操作就是对图像先膨胀,再腐蚀。闭操作的结果一般是可以将许多靠近的图块相连称为一个无突起的连通域。在我们的图像定位中,使用了闭操作去连接所有的字符小图块,然后形成一个车牌的大致轮廓。闭操作的过程我会讲的细致一点。为了说明字符图块连接的过程。在这里选取的原图跟上面三个操作的原图不大一样,是一个由两个分开的图块组成的图。原图首先经过膨胀操作,将两个分开的图块结合起来(注意我用偏白的灰色图块表示由于膨胀操作而产生的新的白色)。接着通过腐蚀操作,将连通域的边缘和突起进行削平(注意我用偏黑的灰色图块表示由于腐蚀被侵蚀成黑色图块)。最后得到的是一个无突起的连通域(纯白的部分)。


                                                                         图10 闭操作原理

4.代码

  在opencv中,调用闭操作的方法是首先建立矩形模板,矩形的大小是可以设置的,由于矩形是用来覆盖以中心像素的所有其他像素,因此矩形的宽和高最好是奇数。

  通过以下代码设置矩形的宽和高。

Mat element = getStructuringElement(MORPH_RECT, Size(m_MorphSizeWidth, m_MorphSizeHeight) );
在这里,我们使用了类成员变量,这两个类成员变量在构造函数中被赋予了初始值。宽是17,高是3.
  设置完矩形的宽和高以后,就可以调用形态学操作了。opencv中所有形态学操作有一个统一的函数,通过参数来区分不同的具体操作。例如MOP_CLOSE代表闭操作,MOP_OPEN代表开操作。
morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);