OpenCV 滤波与卷积之 —— 形态学操作

2022-08-09 15:05:15 浏览数 (1)

本文摘录OpenCV 中的卷积、滤波相关操作内容,重点介绍 Opencv 中的形态学操作。

形态学操作

OpenCV 还提供了一种高效且易用的图像形态学变换接口。图像形态学有其特定的发展领域,特别是在计算机视觉发展早期,已经发展出了很多的形态学方法。大部分都是为某个特定目的而产生的,其中一些更是沿用了很长一段时间。基本上,所有的形态学操作都基于两种原始操作,接下来的讲述也将以这两点开始,循序渐进发展到更加复杂的操作,每个更加复杂的操作都将通过前面的方法来表示。

膨胀和腐蚀

最基础的形态学变换是膨胀和腐蚀,它们在许多方面得到了应用,比如消除噪声、元素分割和连接等。基于这两种操作,可以实现更复杂的形态学操作,用来定位强度峰值或孔洞、另一种形式的图像梯度等。

膨胀 / cv2.dilate

膨胀是一种卷积操作,它将目标像素的值替换为卷积核覆盖区域的局部最大值。这是一个非线性核的例子,因此不能用图表表示。通常,膨胀采用的核是一个四边形或圆形的实心核,其锚点在中心。膨胀的作用是使图中填充区域生长,填充为附近的最大值。

  • 官方文档
  • 数学表达
operatorname{dst}(x, y)=max _{left(x^{prime}, y^{prime}right): operatorname{ement}left(x^{prime}, y^{prime}right) neq 0} operatorname{src}left(x x^{prime}, y y^{prime}right)
  • 函数使用
代码语言:javascript复制
cv2.dilate(
	src, 					# 源图像
	kernel[, 				# 膨胀核
	dst[, 					# 输出图像
	anchor[, 				# 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
	iterations[, 			# 膨胀轮数
	borderType[, 			# 像素外推法
	borderValue]]]]]		# 如果使用常数外推的话需要配置该值
	) → dst

  • 示例代码
代码语言:javascript复制
img = mt.cv_rgb_imread('img1.jpg', gray=False)
kernal = np.ones([1, 58])
res = cv2.dilate(img, kernal)
PIS(img, res)

腐蚀 / cv2.erode

与膨胀对应,腐蚀是与之相反的操作,腐蚀操作计算的是核覆盖范围内的局部最小值。

图像的形态学操作通常在阈值化操作后的布尔图像上进行,不过由于膨胀和腐蚀只是最大和最小操作,因此形态学操作也可以在强度图像上进行。

  • 官方文档
  • 数学表达
operatorname{dst}(x, y)=min _{left(x^{prime}, y^{prime}right): operatorname{element}left(x^{prime}, y^{prime}right) neq 0} operatorname{src}left(x x^{prime}, y y^{prime}right)
  • 函数使用
代码语言:javascript复制
cv2.erode(
	src, 					# 源图像
	kernel[, 				# 腐蚀核
	dst[, 					# 输出图像
	anchor[, 				# 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
	iterations[, 			# 腐蚀轮数
	borderType[, 			# 像素外推法
	borderValue]]]]]		# 如果使用常数外推的话需要配置该值
	) → dst

  • 示例代码
代码语言:javascript复制
img = mt.cv_rgb_imread('img1.jpg', gray=False)
kernal = np.ones([1, 58])
res = cv2.erode(img, kernal)
PIS(img, res)

通用形态学函数

cv2.morphologyEx

包含了若干形态学操作的通用函数 官方文档

  • 函数使用
代码语言:javascript复制
cv2.morphologyEx(
	src, 				# 源图像
	op, 				# 形态学操作
	kernel[, 			# 核
	dst[, 				# 输出图像
	anchor[, 			# 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
	iterations[, 		# 操作轮数
	borderType[, 		# 像素外推法
	borderValue]]]]])	# 如果使用常数外推的话需要配置该值
    → dst

  • op 可选值

操作值

形态学操作

cv2.MORPH_ERODE

腐蚀操作

cv2.MORPH_DILATE

膨胀操作

cv2.MOP_OPEN

开操作

cv2.MOP_CLOSE

闭操作

cv2.MOP_GRADIENT

形态学梯度

cv2.MOP_TOPHAT

顶帽操作

cv2.MOP_BLACKHAT

底帽操作

cv2.MORPH_HITMISS

击中击不中操作

布尔图像上的开操作和闭操作

开操作和闭操作实际上是腐蚀和膨胀操作非常简单的组合。

开运算
  • 开操作先将图像腐蚀,然后对腐蚀的结果膨胀。
闭运输
  • 闭操作先将图像进行膨胀,然后对膨胀的结果进行腐蚀
非布尔图像上的开操作和闭操作

当对一幅非布尔型图像进行形态学操作时,闭操作最明显的效果是消除值小于邻域内的点的孤立异常值,而开操作消除的是大于邻域内点的孤立异常值。

开运算
闭运算
形态学梯度

接下来要叙述的操作是形态学梯度(Morphological Gradient),可以描述为膨胀与腐蚀的差值,用如下表达式描述:

operatorname{gradient}(mathrm{src})=operatorname{dilate}(mathrm{src})-operatorname{erode}(mathrm{src})

形态学梯度通常用于显示明亮区域的边界,然后便可以将他们看作目标或者目标的部分。用扩张的图像减去了收缩的图像,如此一来就找出了完整的边界。这与计算梯度不同,它不会关注某一个物体的周围。

顶帽和黑帽

最后两种操作是顶帽黑帽。这两种操作分别用于显示与其邻域相比更亮或更暗的部分。当试图根据物体的亮度变化分离依附于物体的某些部分时,就会用到这些方法。在生物体或细胞的显微镜图像上经常会用到这些方法。两种操作都是根据基础操作定义的,如下所示:

顶帽操作
黑帽操作
击中击不中操作

源图像仅支持二值图,会匹配模板,模板中的 1 对应源图的1,模板-1对应源图0,模板0忽略源图像素,按照当前规则匹配源图,匹配成功为1,失败为0,返回结果图

  • 示例代码
代码语言:javascript复制
binary_array = (np.random.random([20, 20]) > 0.5).astype('uint8')
kernal = np.array([[1, -1, 1], [-1, 1, 1], [1, 1, 0]])
res = cv2.morphologyEx(binary_array, cv2.MORPH_HITMISS, kernal)
PIS(binary_array, res, kernal)

自定义核

OpenCV可以让你创建自己的核。在形态学上,核常常称为“构造元素”。因此供开发者创建自定义形态学核的函数叫cv2.getStructuringElement()

  • 函数使用
代码语言:javascript复制
cv2.getStructuringElement(
	shape,		# 核形状 
	ksize, 		# 核尺寸
	anchor		# 锚点位置,默认(-1, -1),表示在核中心
	)

  • shape

形状值

元素

描述

cv2.MORPH_RECT

矩形

$mathrm{E}_{i, j}=1, forall i, j$

cv2.MORPH_ELLIPSE

椭圆形

以ksize.width和ksize.height为两个半径做椭圆

cv2.MORPH_CROSS

交叉

$mathrm{E}_{i, j}=1, i=anchor.y text{ or } j=anchor.x$

  • 示例代码
代码语言:javascript复制
cross = cv2.getStructuringElement(cv2.MORPH_CROSS, [5,5])
rect = cv2.getStructuringElement(cv2.MORPH_RECT, [5,5])
ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, [5,5])

-->
cross
array([[0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)
       
rect
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=uint8)
       
ellipse
array([[0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0]], dtype=uint8)

汇总示例

  • 示例代码
代码语言:javascript复制
img = mt.cv_rgb_imread('test.jpg', gray=True)
kernal_1d = cv2.getGaussianKernel(5, 1)
kernal_2d = kernal_1d* kernal_1d.T
erode = cv2.morphologyEx(img, cv2.MORPH_ERODE, kernal_2d)
dilate = cv2.morphologyEx(img, cv2.MORPH_DILATE, kernal_2d)
open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernal_2d)
close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernal_2d)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernal_2d)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernal_2d)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernal_2d)
PIS([img, 'origin'], [erode, 'erode'], [dilate, 'dilate'], [open, 'open'], [close, 'close'], [gradient, 'gradient'], [tophat, 'tophat'], [blackhat, 'blackhat'])

示例源码

  • https://github.com/zywvvd/Python_Practise

参考资料

  • 《学习 OpenCV3》 第十章

0 人点赞