专栏地址:『youcans 的图像处理学习课』
文章目录:『youcans 的图像处理学习课 - 总目录』
【youcans 的图像处理学习课】11. 形态学图像处理(上)
【youcans 的图像处理学习课】11. 形态学图像处理(中)
【youcans 的图像处理学习课】11. 形态学图像处理(下)
【youcans 的 OpenCV 学习课】11. 形态学图像处理(上)
文章目录
- 【youcans 的 OpenCV 学习课】11. 形态学图像处理(上)
- 1. 形态学图像处理简介
- 2. 形态学基本操作
- 2.1 腐蚀
- 例程 10.1:图像的腐蚀 (cv.erode)
- 2.2 膨胀
- 例程 10.2:图像的膨胀 (cv.dilate)
- 2.3 开运算
- 例程 10.3:图像的开运算 (morphologyEx)
- 2.4 闭运算
- 例程 10.4:图像的闭运算(cv.morphologyEx)
- 2.5 顶帽运算和底帽运算
- 例程 10.5:形态学之顶帽运算
- 例程 10.6:形态学之底帽运算
- 2.6 形态学梯度
- 例程 10.7:图像的形态学梯度
- 2.7 击中-击不中变换(HMT)
- 例程 10.8:击中-击不中变换进行图像细化
- 例程 10.9:击中-击不中变换进行特征识别
1. 形态学图像处理简介
形态学是生物学的概念,主要研究动植物的形态和结构。数学形态学(Mathematical morphology)是建立在集合论和拓扑学基础之上的图像分析学科。
图像处理中的形态学是指基于形状的图像处理操作,以数学形态学为工具从图像中提取表达和描绘区域形状的图像结构信息,如边界、骨架、凸壳等,还包括用于预处理或后处理的形态学过滤、细化和修剪等。形态学图像处理最初用于处理二值图,进而推广到灰度级图像处理,其运算简单、效果良好。
形态学图像处理的运算是用集合定义的,基本运算包括:二值腐蚀和膨胀,二值开闭运算,骨架抽取,极限腐蚀,击中击不中变换,形态学梯度,顶帽变换,颗粒分析,流域变换,灰值腐蚀和膨胀,灰值开闭运算,灰值形态学梯度等。
形态学的基本思想是利用结构元素测量或提取输入图像中的形状或特征,以便进行图像分析和目标识别。结构元的功能是检查给定图像中的特定结构特征。形态学操作都是基于各种形状的结构元,结构元对输入图像进行操作得到输出图像。
在二值图像中,所有黑色像素的集合是图像完整的形态学描述。假定二值图像 A 和形态学处理的结构元素 B 是定义在笛卡儿网格上的集合,结构元素(Structuring Elements,SE)可以是任意形状,如矩形、十字形。结构元有一个锚点 O,定义为结构元的中心。
2. 形态学基本操作
2.1 腐蚀
腐蚀和膨胀是图像处理中最基本的形态学操作,是很多高级处理方法的基础。
腐蚀和膨胀是对白色部分(高亮部分)而言的,膨胀就是图像中的高亮部分进行膨胀,腐蚀就是原图中的高亮部分被腐蚀。
腐蚀使图像中白色高亮部分被腐蚀,“邻域被蚕食”,腐蚀的效果拥有比原图更小的高亮区域,可以去掉毛刺,去掉孤立的像素,提取骨干信息。
腐蚀的原理是求局部最小值的操作,将 0 值扩充到邻近像素,从而扩大黑色值范围、压缩白色值范围。
结构元 B 对集合 A 的腐蚀定义为:
A⊖B={z∣(B)z⊆A}A \ominus B = \{ z | (B)_z \subseteq A\} A⊖B={z∣(B)z⊆A}
用卷积来描述腐蚀操作,结构元素 B 是中心为 1、其它为 0 的卷积模板(核):
(1)卷积核 B 沿着图像滑动,扫描图像 A 的每一个像素;
(2)用结构元素与其覆盖的二值图像进行 “或操作”;
(3)如果图像与卷积核对应区域的所有像素值都是 1,则图像的该像素值仍为 1;否则为 0。
OpenCV 提供了函数 cv.erode 可以实现图像的腐蚀。
函数说明:
cv.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst
函数 cv.erode 使用指定的结构元(卷积核)侵蚀源图像,结构元确定像素邻域的形状,在该邻域上取最小值:
dst(x,y)=min(x′,y′):element(x′,y′≠0)src(x+x′,y+y′)dst(x,y)= \min_{\enspace (x',y'):element(x',y' \neq 0)} \enspace src(x+x',y+y') dst(x,y)=(x′,y′):element(x′,y′=0)minsrc(x+x′,y+y′)
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- kernel:结构元(卷积核),null 时使用 3*3 矩形结构元素
- anchor:卷积核的锚点位置,默认值 (-1, -1) 表示以卷积核的中心为锚点
- iterations:应用腐蚀操作的次数,可选项,默认值为 1
- borderType:边界扩充的类型
- borderValue:当 borderType=BORDER_CONSTANT 时以常量 value 填充扩充边界,默认值为 (0,0,0)
注意事项:
- 函数支持就地模式,腐蚀操作可以迭加使用多次。
- 在多通道图像的情况下,每个通道独立处理 。
例程 10.1:图像的腐蚀 (cv.erode)
# 10.1 图像的腐蚀 (cv.erode)# 读取原始图像imgGray = cv2.imread("../images/Fig0905a.tif", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 图像腐蚀kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgErode1 = cv2.erode(imgBin, kernel=kernel) # 图像腐蚀kSize = (9, 9)kernel = np.ones(kSize, dtype=np.uint8)imgErode2 = cv2.erode(imgBin, kernel=kernel)kSize = (25, 25)kernel = np.ones(kSize, dtype=np.uint8)imgErode3 = cv2.erode(imgBin, kernel=kernel)plt.figure(figsize=(10, 5))plt.subplot(141), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(142), plt.title("eroded kSize=(3,3)"), plt.axis('off')plt.imshow(imgErode1, cmap='gray', vmin=0, vmax=255)plt.subplot(143), plt.title("eroded kSize=(9,9)"), plt.axis('off')plt.imshow(imgErode2, cmap='gray', vmin=0, vmax=255)plt.subplot(144), plt.title("eroded kSize=(25,25)"), plt.axis('off')plt.imshow(imgErode3, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.2 膨胀
膨胀使图像中的白色高亮部分进行膨胀,“邻域扩张”,膨胀效果拥有比原图更大的高亮区域,可以填补图像缺陷,用来扩充边缘或填充小的孔洞,也可以用来连接两个分开的物体。
膨胀的原理是求局部最大值的操作,将 1 值扩充到邻近像素,从而扩大白色值范围、压缩黑色值范围。
结构元 B 对集合 A 的膨胀定义为:
A⊕B={z∣(B^)z∩A≠∅}A \oplus B = \{ z | (\hat B)_z \cap A \neq \varnothing \} A⊕B={z∣(B^)z∩A=∅}
用卷积来描述膨胀操作,中心为 1、其它为 0 的卷积核沿着图像滑动卷积,与卷积核对应的原图像的像素值中只要有一个为 1, 则图像的中心元素的像素值为 1,否则(全 0)为 0。
用卷积来描述膨胀操作,结构元素 B 是中心为 1、其它为 0 的卷积模板(核):
(1)卷积核 B 沿着图像滑动,扫描图像 A 的每一个像素;
(2)用结构元素与其覆盖的二值图像进行 “与操作”;
(3)如果图像与卷积核对应区域的所有像素值都是 0,则图像的该像素值仍为 0;否则为 1。
在去噪声时通常先进行腐蚀,在去掉白噪声的同时,使前景对象变小;然后再对进行膨胀,此时噪声已经被去除,可以增加前景。
OpenCV 提供了函数 cv.dilate 可以实现图像的膨胀。
函数说明:
cv.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst
函数 cv.dilate 使用指定的结构元(卷积核)膨胀源图像,结构元确定像素邻域的形状,在该邻域上取最大值:
dst(x,y)=max(x′,y′):element(x′,y′≠0)src(x+x′,y+y′)dst(x,y)= \max_{\enspace (x',y'):element(x',y' \neq 0)} \enspace src(x+x',y+y') dst(x,y)=(x′,y′):element(x′,y′=0)maxsrc(x+x′,y+y′)
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
- anchor:卷积核的锚点位置,默认值 (-1, -1) 表示以卷积核的中心为锚点
- iterations:应用膨胀的次数,可选项,默认值为 1
- borderType:边界扩充的类型
- borderValue:当 borderType=BORDER_CONSTANT 时以常量 value 填充扩充边界,默认值为 (0,0,0)
注意事项:
-
函数支持就地模式,腐蚀操作可以迭加使用多次。
-
在多通道图像的情况下,每个通道独立处理 。
例程 10.2:图像的膨胀 (cv.dilate)
# 10.2 图像的膨胀 (cv.dilate)# cv.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst# 读取原始图像imgGray = cv2.imread("../images/handwriting01.png", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 图像膨胀kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgDilate1 = cv2.dilate(imgBin, kernel=kernel) # 图像膨胀kSize = (5, 5)kernel = np.ones(kSize, dtype=np.uint8)imgDilate2 = cv2.dilate(imgBin, kernel=kernel) # 图像膨胀kSize = (7, 7)kernel = np.ones(kSize, dtype=np.uint8)imgDilate3 = cv2.dilate(imgBin, kernel=kernel) # 图像膨胀plt.figure(figsize=(10, 5))plt.subplot(141), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(142), plt.title("dilate kSize=(3,3)"), plt.axis('off')plt.imshow(imgDilate1, cmap='gray', vmin=0, vmax=255)plt.subplot(143), plt.title("dilate kSize=(5,5)"), plt.axis('off')plt.imshow(imgDilate2, cmap='gray', vmin=0, vmax=255)plt.subplot(144), plt.title("dilate kSize=(7,7)"), plt.axis('off')plt.imshow(imgDilate3, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.3 开运算
膨胀扩展集合的组成部分,而腐蚀缩小集合的组成部分。开运算就是先腐蚀后膨胀的过程,通常用于去除噪点、断开狭窄的狭颈、消除细长的突出、平滑物体边界但不改变面积。
结构元 B 对集合 A 的开运算定义为:
A∘B=(A⊖B)⊕BA \circ B = (A \ominus B) \oplus B A∘B=(A⊖B)⊕B
首先 B 对 A 腐蚀,然后 B 对腐蚀结果膨胀。
开运算是一个基于几何运算的滤波器,结构元大小的不同将导致不同的滤波效果,提取出不同的特征 。
OpenCV 提供了函数 cv.morphologyEx 可以实现图像的开运算。
函数说明:
cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )→ dst
函数 cv.morphologyEx 使用侵蚀(erosion)和膨胀(dilation)作为基本操作来执行高级形态转换。
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- op:形态学运算类型
- cv.MORPH_ERODE:腐蚀
- cv.MORPH_DILATE:膨胀
- cv.MORPH_OPEN:开运算, 先腐蚀再膨胀
- cv.MORPH_CLOSE:闭运算, 先膨胀再腐蚀
- cv.MORPH_GRADIENT:形态学梯度, 膨胀图与腐蚀图之差
- cv.MORPH_TOPHAT:顶帽变换, 原图像与开运算之差
- cv.MORPH_BLACKHAT:黑帽变换, 闭运算图与原图像之差
- cv.MORPH_HITMISS: 击中击不中运算
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
- anchor:卷积核的锚点位置,负值表示以卷积核的中心为锚点
- iterations:应用腐蚀和膨胀的次数,可选项,默认值为 1
- borderType:边界扩充的类型
- borderValue:当 borderType=BORDER_CONSTANT 时以常量 value 填充扩充边界,默认值为 (0,0,0)
注意事项:
- 函数支持就地模式,开运算操作可以迭加使用多次。
- 迭代次数是应用腐蚀和膨胀操作的次数,注意两次迭代的开操作相当于应用“腐蚀→腐蚀→膨胀→膨胀”,而不是“腐蚀→膨胀→腐蚀→膨胀”。
例程 10.3:图像的开运算 (morphologyEx)
# 10.3 图像的开运算 (cv.morphologyEx)# 读取原始图像imgGray = cv2.imread("../images/Fig0905a.tif", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 图像腐蚀kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgErode = cv2.erode(imgBin, kernel=kernel) # 图像腐蚀# 图像的开运算kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgOpen = cv2.morphologyEx(imgGray, cv2.MORPH_OPEN, kernel)plt.figure(figsize=(9, 5))plt.subplot(131), plt.axis('off'), plt.title("Origin")plt.imshow(imgGray, cmap='gray', vmin=0, vmax=255)plt.subplot(132), plt.title("Eroded kSize=(3,3)"), plt.axis('off')plt.imshow(imgErode, cmap='gray', vmin=0, vmax=255)plt.subplot(133), plt.title("Opening kSize=(3,3)"), plt.axis('off')plt.imshow(imgOpen, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.4 闭运算
闭运算就是先膨胀后腐蚀的过程,通常用于弥合狭窄的断裂和细长的沟壑,消除小孔,填补轮廓中的缝隙,消除噪点,连接相邻的部分。
结构元 B 对集合 A 的闭运算定义为:
A∙B=(A⊕B)⊖BA \bullet B = (A \oplus B) \ominus B A∙B=(A⊕B)⊖B
首先 B 对 A 膨胀,然后 B 对膨胀结果腐蚀。
闭运算通过填充图像的凹角来实现图像滤波,结构元大小的不同将导致滤波效果的不同,不同结构元素的选择导致不同的分割。
OpenCV 中的函数 cv.morphologyEx 也可以实现图像的闭运算,但要将参数 op 设为 MORPH_CLOSE 。
函数说明:
cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )→ dst
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- op:形态学运算类型
- cv.MORPH_ERODE:腐蚀
- cv.MORPH_DILATE:膨胀
- cv.MORPH_OPEN:开运算, 先腐蚀再膨胀
- cv.MORPH_CLOSE:闭运算, 先膨胀再腐蚀
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
- anchor:卷积核的锚点位置,负值表示以卷积核的中心为锚点
- iterations:应用腐蚀和膨胀的次数,可选项,默认值为 1
例程 10.4:图像的闭运算(cv.morphologyEx)
# 10.4 图像的闭运算 (cv.morphologyEx)# 读取原始图像imgGray = cv2.imread("../images/handwriting01.png", flags=0) # flags=0 读取为灰度图像mu, sigma = 0.0, 10.0noiseGause = np.random.normal(mu, sigma, imgGray.shape)imgNoisy = imgGray + noiseGauseimgNoisy = np.uint8(cv2.normalize(imgNoisy, None, 0, 255, cv2.NORM_MINMAX)) # 归一化为 [0,255]ret, imgBin = cv2.threshold(imgNoisy, 125, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 图像的闭运算kSize = (2, 2) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgClose1 = cv2.morphologyEx(imgBin, cv2.MORPH_CLOSE, kernel)kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgClose2 = cv2.morphologyEx(imgBin, cv2.MORPH_CLOSE, kernel)kSize = (5, 5) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgClose3 = cv2.morphologyEx(imgBin, cv2.MORPH_CLOSE, kernel)plt.figure(figsize=(10, 5))plt.subplot(141), plt.axis('off'), plt.title("Origin")plt.imshow(imgNoisy, cmap='gray', vmin=0, vmax=255)plt.subplot(142), plt.title("Closed kSize=(2,2)"), plt.axis('off')plt.imshow(imgClose1, cmap='gray', vmin=0, vmax=255)plt.subplot(143), plt.title("Closed kSize=(3,3)"), plt.axis('off')plt.imshow(imgClose2, cmap='gray', vmin=0, vmax=255)plt.subplot(144), plt.title("Closed kSize=(5,5)"), plt.axis('off')plt.imshow(imgClose3, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.5 顶帽运算和底帽运算
顶帽变换和底帽变换用结构元通过开操作或闭操作从一副图像中删除物体,得到仅保留已删除分量的图像。顶帽变换用于暗背景上的亮物体,而底帽变换则用于用于亮背景上的暗物体,常用于校正不均匀光照的影响。
结构元 B 对集合 A 的顶帽运算定义为原图像减去图像开运算结果:
That(A)=A−(A∘B)=A−(A⊖B)⊕BT_{hat}(A) = A - (A \circ B) = A - (A \ominus B) \oplus B That(A)=A−(A∘B)=A−(A⊖B)⊕B
开运算可以删除暗背景下的亮区域,顶帽变换可以得到原图中的亮区域,因此又称白顶帽变换。
顶帽运算可以提取图像的噪声信息,也用于校正不均匀光照的影响,用来分离比邻近点亮的斑块。
结构元 B 对集合 A 的底帽运算定义为图像闭运算结果减去原图像:
Bhat(A)=A∙B−A=(A⊕B)⊖B−AB_{hat}(A) = A \bullet B - A = (A \oplus B) \ominus B - A Bhat(A)=A∙B−A=(A⊕B)⊖B−A
闭运算可以删除亮背景下的暗区域,底帽变换可以得到原图中的暗区域,因此又称黑底帽变换。
底帽运算突出了比原图轮廓周围的区域更暗的区域,而且效果与核的大小相关,可以分离比邻近点暗的斑块。
OpenCV 中的函数 cv.morphologyEx 可以实现图像的顶帽运算和底帽运算,参数 op 则要分别设为 MORPH_TOPHAT、MORPH_BLACKHAT。
函数说明:
cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )→ dst
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F 。
- op:形态学运算类型
- cv.MORPH_OPEN:开运算, 先腐蚀再膨胀
- cv.MORPH_CLOSE:闭运算, 先膨胀再腐蚀
- cv.MORPH_GRADIENT:形态学梯度, 膨胀图与腐蚀图之差
- cv.MORPH_TOPHAT:顶帽变换, 原图像与开运算之差
- cv.MORPH_BLACKHAT:黑帽变换, 闭运算图与原图像之差
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
例程 10.5:形态学之顶帽运算
# 10.5 形态学之顶帽运算# 读取原始图像imgGray = cv2.imread("../images/Fig0726a.tif", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 205, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理kernel = np.ones((5, 5), np.uint8) # 卷积核imgOpen = cv2.morphologyEx(imgBin, cv2.MORPH_OPEN, kernel) # 开运算imgThat = cv2.morphologyEx(imgBin, cv2.MORPH_TOPHAT, kernel) # 顶帽运算plt.figure(figsize=(9, 5))plt.subplot(131), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(132), plt.title("MORPH_OPEN"), plt.axis('off')plt.imshow(imgOpen, cmap='gray', vmin=0, vmax=255)plt.subplot(133), plt.title("MORPH_TOPHAT"), plt.axis('off')plt.imshow(imgThat, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
例程 10.6:形态学之底帽运算
# 10.6 形态学之底帽运算# 读取原始图像imgGray = cv2.imread("../images/Fig0338a.tif", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理kernel = np.ones((5, 5), np.uint8) # 卷积核imgClose = cv2.morphologyEx(imgBin, cv2.MORPH_CLOSE, kernel) # 闭运算imgBhat = cv2.morphologyEx(imgBin, cv2.MORPH_BLACKHAT, kernel) # 底帽运算plt.figure(figsize=(9, 5))plt.subplot(131), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(132), plt.title("MORPH_CLOSE"), plt.axis('off')plt.imshow(imgClose, cmap='gray', vmin=0, vmax=255)plt.subplot(133), plt.title("MORPH_BLACKHAT"), plt.axis('off')plt.imshow(imgBhat, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.6 形态学梯度
图像的形态学梯度运算,是膨胀图像与腐蚀图像之差 ,可以得到图像的轮廓,通常用于提取物体边缘。
结构元 B 对集合 A 的形态学梯度运算定义为:
G=(A⊕B)−(A⊖B)G = (A \oplus B) - (A \ominus B) G=(A⊕B)−(A⊖B)
闭运算通过填充图像的凹角来实现图像滤波,结构元大小的不同将导致滤波效果的不同,不同结构元素的选择导致不同的分割。
OpenCV 中的函数 cv.morphologyEx 可以实现形态学梯度运算,但要将参数 op 设为 MORPH_GRADIENT。
函数说明:
cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )→ dst
函数 cv.morphologyEx 使用侵蚀(erosion)和膨胀(dilation)作为基本操作来执行高级形态转换。
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- op:形态学运算类型
- cv.MORPH_ERODE:腐蚀
- cv.MORPH_DILATE:膨胀
- cv.MORPH_OPEN:开运算, 先腐蚀再膨胀
- cv.MORPH_CLOSE:闭运算, 先膨胀再腐蚀
- cv.MORPH_GRADIENT:形态学梯度, 膨胀图与腐蚀图之差
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
例程 10.7:图像的形态学梯度
# 10.7 图像的形态学梯度运算 (cv.morphologyEx)# 读取原始图像imgGray = cv2.imread("../images/handwriting03.png", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 15, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 图像的形态学梯度kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgGrad1 = cv2.morphologyEx(imgBin, cv2.MORPH_GRADIENT, kernel) # 形态学梯度kSize = (5, 5) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgGrad2 = cv2.morphologyEx(imgBin, cv2.MORPH_GRADIENT, kernel) # 形态学梯度kSize = (3, 3) # 卷积核的尺寸kernel = np.ones(kSize, dtype=np.uint8) # 生成盒式卷积核imgOpen = cv2.morphologyEx(imgBin, cv2.MORPH_OPEN, kernel) # 开运算imgOpenGrad = cv2.morphologyEx(imgOpen, cv2.MORPH_GRADIENT, kernel) # 形态学梯度plt.figure(figsize=(10, 5))plt.subplot(141), plt.axis('off'), plt.title("Origin")plt.imshow(imgGray, cmap='gray', vmin=0, vmax=255)plt.subplot(142), plt.title("Gradient (size=3)"), plt.axis('off')plt.imshow(imgGrad1, cmap='gray', vmin=0, vmax=255)plt.subplot(143), plt.title("Gradient (size=5)"), plt.axis('off')plt.imshow(imgGrad2, cmap='gray', vmin=0, vmax=255)plt.subplot(144), plt.title("Opening -> Gradient"), plt.axis('off')plt.imshow(imgOpenGrad, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
2.7 击中-击不中变换(HMT)
击中-击不中是形态检测的基本工具,可以实现对象的细化和剪枝操作,常用于物体识别、图像细化。
击中-击不中变换变换定义为两个结构元 B1、B2 对对集合 A 的运算:
I⊛B1,2=(A⊖B1)∩(Ac⊖B2)I \circledast B_{1,2} = (A \ominus B_1) \cap (A^c \ominus B_2) I⊛B1,2=(A⊖B1)∩(Ac⊖B2)
结构元 B1 对图像上进行腐蚀、结构元 B2 对图像的补集进行腐蚀,二者的结果相减得到击中-击不中变换。
击中击不中变换有两个结构元,B1 是当前位置可以有的形状,B2是当前位置不可以有的形状,虽然总体而言这两个结构元应该被视为一体,但在实际运算中是先后进行两次腐蚀运算,然后取交集。
严格来说,击中击不中并非相当于腐蚀操作,而是类似于严格的模板匹配。只有符合要求的形状,才会在最终结果中显示出来。
OpenCV 中的函数 cv.morphologyEx 可以实现击中-击不中变换,但要将参数 op 设为 cv.MORPH_HITMISS,并使用 CV_32SC 数据类型。
函数说明:
cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )→ dst
函数 cv.morphologyEx 使用侵蚀(erosion)和膨胀(dilation)作为基本操作来执行高级形态转换。
参数说明:
- src:输入图像,可以为单通道或多通道,图像深度必须为 CV_8U, CV_16U, CV_16S, CV_32F 或 CV_64F
- dst:输出图像,大小和类型与 src 相同
- op:形态学运算类型
- cv.MORPH_ERODE:腐蚀
- cv.MORPH_DILATE:膨胀
- cv.MORPH_HITMISS: 击中击不中运算
- kernel:结构元(卷积核),null 时使用 3*3 矩形卷积核
例程 10.8:击中-击不中变换进行图像细化
# 10.8 击中-击不中变换进行图像细化# 读取原始图像imgGray = cv2.imread("../images/handwriting03.png", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 25, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 二值化处理# 闭运算kernel = np.ones((3, 3), dtype=np.uint8) # 生成盒式卷积核imgClose = cv2.morphologyEx(imgBin, cv2.MORPH_CLOSE, kernel) # 闭运算# 击中击不中变换kernB1 = np.array([[0, 0, 0],[0, -1, 1],[0, 0, 0]], dtype=np.int32) # B1kernB2 = np.array([[0, 0, 0],[1, -1, 0],[0, 0, 0]], dtype=np.int32) # B2imgH1 = cv2.morphologyEx(imgClose, cv2.MORPH_HITMISS, kernB1)imgH2 = cv2.morphologyEx(imgClose, cv2.MORPH_HITMISS, kernB2)imgHMT = cv2.add(imgH1, imgH2) # 击中击不中plt.figure(figsize=(10, 5))plt.subplot(141), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(142), plt.title("kern B1"), plt.axis('off')plt.imshow(imgH1, cmap='gray', vmin=0, vmax=255)plt.subplot(143), plt.title("kern B2"), plt.axis('off')plt.imshow(imgH2, cmap='gray', vmin=0, vmax=255)plt.subplot(144), plt.title("HMT"), plt.axis('off')plt.imshow(imgHMT, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
例程 10.9:击中-击不中变换进行特征识别
# 10.9 击中-击不中变换进行特征识别# 读取原始图像imgGray = cv2.imread("../images/imgNetrope.png", flags=0) # flags=0 读取为灰度图像ret, imgBin = cv2.threshold(imgGray, 25, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # 二值化处理# 击中击不中变换kernal1 = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5), (-1, -1))kernal2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (9,9), (-1, -1))imgHMT1 = cv2.morphologyEx(imgBin, cv2.MORPH_HITMISS, kernal1)imgHMT2 = cv2.morphologyEx(imgBin, cv2.MORPH_HITMISS, kernal2)plt.figure(figsize=(9, 5))plt.subplot(131), plt.axis('off'), plt.title("Origin")plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)plt.subplot(132), plt.title("HMT (5,5)"), plt.axis('off')plt.imshow(imgHMT1, cmap='gray', vmin=0, vmax=255)plt.subplot(133), plt.title("HMT (9,9)"), plt.axis('off')plt.imshow(imgHMT2, cmap='gray', vmin=0, vmax=255)plt.tight_layout()plt.show()
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/127133784)
Copyright 2022 youcans, XUPT
欢迎关注 『youcans 的 OpenCV 学习课』 系列,持续更新
文章目录:『youcans 的图像处理学习课 - 总目录』