【youcans 的图像处理学习课】11. 形态学图像处理(上)

专栏地址:『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\} AB={z(B)zA}

用卷积来描述腐蚀操作,结构元素 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)

注意事项:

  1. 函数支持就地模式,腐蚀操作可以迭加使用多次。
  2. 在多通道图像的情况下,每个通道独立处理 。

例程 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 \} AB={z(B^)zA=}

用卷积来描述膨胀操作,中心为 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)

注意事项:

  1. 函数支持就地模式,腐蚀操作可以迭加使用多次。

  2. 在多通道图像的情况下,每个通道独立处理 。


例程 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 AB=(AB)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)

注意事项:

  1. 函数支持就地模式,开运算操作可以迭加使用多次。
  2. 迭代次数是应用腐蚀和膨胀操作的次数,注意两次迭代的开操作相当于应用“腐蚀→腐蚀→膨胀→膨胀”,而不是“腐蚀→膨胀→腐蚀→膨胀”。

例程 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 AB=(AB)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(AB)=A(AB)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)=ABA=(AB)BA

闭运算可以删除亮背景下的暗区域,底帽变换可以得到原图中的暗区域,因此又称黑底帽变换。

底帽运算突出了比原图轮廓周围的区域更暗的区域,而且效果与核的大小相关,可以分离比邻近点暗的斑块。

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=(AB)(AB)

闭运算通过填充图像的凹角来实现图像滤波,结构元大小的不同将导致滤波效果的不同,不同结构元素的选择导致不同的分割。

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) IB1,2=(AB1)(AcB2)

结构元 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 的图像处理学习课 - 总目录』

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/565300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Sdk 安装配置

在我们安装了Android Studio之后,我们发现不能使用,因为缺少一个Sdk的配置,下面就给大家详细介绍一下Sdk的配置。 打开我们的Android Studio。 选择Do not import settinge,然后点击OK。 点击 Cancel 退出即可,后面…

爬虫requests高阶篇详细教程

文章目录 一、前言 二、SSL验证 三、代理设置 四、超时设置 ​ 五、身份认证 1)基本身份认证 2)摘要式身份认证 六、总结 一、前言 本篇文高阶篇,上一篇为基础篇,希望你一定要学完基础再来看高阶篇内容 基础篇文章可以看大…

Android Studio 创建第一个项目应用

选择打开"Start a new Android Studio project",新建一个Android项目。 选择一个空白的Activity,然后点击Next。 修改相应Android项目的名称、包名、项目位置等,以符合项目要求,点击Finish。 第一次创建项目可能会这样…

主成分分析(PCA)及其可视化——matlab

本文所用为matlab2016a matlab安装:待更新 matlab基础知识:待更新 如果本文内容已学会,可以看看python的哦 主成分分析(PCA)及其可视化——python_菜菜笨小孩的博客-CSDN博客 文章目录 一、主成分分析的原理 二…

Android studio真机调试

在我们进行Android开发的时候,调试这个环节是必不可少的也是至关重要的,使用真机调试可以更加准确清晰的显示效果。 真机调试Android应用 1.用数据线将手机连接到电脑,打开手机的开发者选项。 由于各个手机厂商的打开方式都不同&#xff0…

主成分分析(PCA)及其可视化——python

可以看看这个哦python入门:Anaconda和Jupyter notebook的安装与使用_菜菜笨小孩的博客-CSDN博客 如果你学会了python 可以看看matlab的哦 主成分分析(PCA)及其可视化——matlab_菜菜笨小孩的博客-CSDN博客 目录 一、主成分分析的原理 二…

Emulator: PANIC: Cannot find AVD system path. Please define ANDROID_SDK_ROOT

我们安装完毕Android Studio之后,创建第一个项目,我们想要使用模拟器进行运行,但是当我们启动模拟器的时候却发现无法启动,而且出现了错误信息。 首先我们要知道错误信息是什么? 提示信息的意思是说“avd系统路径找不…

多元线性回归模型-数学建模类-matlab详解

如果本文有点小难理解的话,可以看看我之前的基础线性规划啥的,有lingo,matlab还有python 就不给大家放链接了,想看的话,点击头像即可!! 文章目录 (1)一元线性回归之旧…

手把手教你爬虫requests实战演练——python篇

文章目录 一、前言 二、实战 1)获取百度网页并打印 2)获取帅哥图片并下载到本地 4) 获取美女视频并下载到本地 5)搜狗关键词搜索爬取 6)爬取百度翻译 7)爬取豆瓣电影榜单 8)JK妹子爬取 总结&#xff…

Android studio 3.x 安装genymotion插件

在日常的Android开发中,模拟器是必不可少的,下面就给大家讲解一下如何在Android studio 3.x 安装genymotion插件。 对于Android studio 3.x之前的版本,可以直接在插件那里在线安装genymotion插件。 Android studio 3.0版本之后就搜索不到了…

图片弹框

用js实现图片弹框的特效。 效果展示 代码展示 html内容 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title></title></head><body><img id"myImg" src"img/c_7…

最短路径和距离及可视化——matlab

文章目录 一、前言 二、最短路线 2.1 教程 2.1.1 sparse创建稀疏矩阵 2.1.2 有向图最短路径&#xff08;1&#xff09; 2.1.3 有向图最短路径&#xff08;2&#xff09; 2.1.4 无向图最短路径&#xff08;1&#xff09; 2.1.5无向图最短路径&#xff08;2&#xff09; …

没有bug队——加贝——Python 练习实例 1,2

目录 1.题目&#xff1a; 2.题目&#xff1a; 1.题目&#xff1a; 有四个数字&#xff1a;1、2、3、4&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;各是多少&#xff1f; 程序分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的…

【OpenCV 例程300篇】250. 梯度算子的传递函数

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程300篇】250. 梯度算子的传递函数 1. 空间卷积与频域滤波 空间域图像滤波是图像与滤波器核的卷积&#xff0c;而空间卷积的傅里叶变换是频率域中相应变换的乘积&#xff0c;因此频率域图像滤波是频率域滤波器…

JS贪吃蛇

Js实现贪吃蛇小游戏。 程序解析&#xff1a; 画表格&#xff0c;画出相对应大小的表格速度的快慢调节随机生成事物的位置使用键盘的方向键控制移动位置当贪吃蛇碰到四周游戏结束&#xff0c;弹框显示效果演示 代码演示 html内容 <!DOCTYPE html> <html><he…

没有bug队——加贝——Python 练习实例 3,4

3.题目&#xff1a; 一个整数&#xff0c;它加上100后是一个完全平方数&#xff0c;再加上168又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 程序分析&#xff1a; 假设该数为 x。 1、则&#xff1a;x 100 n2, x 100 168 m2 2、计算等式&#xff1a;m2 …

lcd4linux 1602,详解一种LCD1602 的4线接法

描述虽然LCD1602的显示屏幕与显示字符都较小&#xff0c;实用性并不强&#xff0c;但是在一般的教学实验中&#xff0c;它仍不失为一个常用的输出显示设备。LCD1602与单片机连接的线路共有11条&#xff0c;其中有8条数据线&#xff0c;3条控制线。如果把它们都连接上&#xff0…

没有bug队——加贝——Python 练习实例 5,6

目录 5.题目&#xff1a; 6.题目&#xff1a; 5.题目&#xff1a; 输入三个整数x,y,z&#xff0c;请把这三个数由小到大输出。 程序分析&#xff1a;我们想办法把最小的数放到x上&#xff0c;先将x与y进行比较&#xff0c;如果x>y则将x与y的值进行交换&#xff0c;然后再…

MATLAB教程(1) MATLAB 基础知识(1)

第一部分&#xff1a;MATLAB显示桌面的基本布局 桌面基础知识- MATLAB & Simulink- MathWorks 中国 1.启动MATLAB后&#xff0c;桌面会显示默认布局&#xff0c;主要有以下三个部分&#xff1a; 当前文件夹命令行窗口工作空间 2.在MATLAB中一般需要创建变量和调用函数。…

【OpenCV 例程 300篇】247. 特征检测之最大稳定极值区域(MSER)

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】247. 特征检测之最大稳定极值区域&#xff08;MSER&#xff09; 1. 最大稳定极值区域&#xff08;MSER&#xff09; 最大稳定极值区域&#xff08;MSER-Maximally Stable Extremal Regions&#xf…