本文摘录OpenCV 中的卷积、滤波相关操作内容,重点介绍 Opencv 中的形态学操作。
OpenCV 还提供了一种高效且易用的图像形态学变换接口。图像形态学有其特定的发展领域,特别是在计算机视觉发展早期,已经发展出了很多的形态学方法。大部分都是为某个特定目的而产生的,其中一些更是沿用了很长一段时间。基本上,所有的形态学操作都基于两种原始操作,接下来的讲述也将以这两点开始,循序渐进发展到更加复杂的操作,每个更加复杂的操作都将通过前面的方法来表示。
最基础的形态学变换是膨胀和腐蚀,它们在许多方面得到了应用,比如消除噪声、元素分割和连接等。基于这两种操作,可以实现更复杂的形态学操作,用来定位强度峰值或孔洞、另一种形式的图像梯度等。
膨胀是一种卷积操作,它将目标像素的值替换为卷积核覆盖区域的局部最大值。这是一个非线性核的例子,因此不能用图表表示。通常,膨胀采用的核是一个四边形或圆形的实心核,其锚点在中心。膨胀的作用是使图中填充区域生长,填充为附近的最大值。
cv2.dilate(
src, # 源图像
kernel[, # 膨胀核
dst[, # 输出图像
anchor[, # 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
iterations[, # 膨胀轮数
borderType[, # 像素外推法
borderValue]]]]] # 如果使用常数外推的话需要配置该值
) → dst
img = mt.cv_rgb_imread('img1.jpg', gray=False)
kernal = np.ones([1, 58])
res = cv2.dilate(img, kernal)
PIS(img, res)
与膨胀对应,腐蚀是与之相反的操作,腐蚀操作计算的是核覆盖范围内的局部最小值。
图像的形态学操作通常在阈值化操作后的布尔图像上进行,不过由于膨胀和腐蚀只是最大和最小操作,因此形态学操作也可以在强度图像上进行。
cv2.erode(
src, # 源图像
kernel[, # 腐蚀核
dst[, # 输出图像
anchor[, # 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
iterations[, # 腐蚀轮数
borderType[, # 像素外推法
borderValue]]]]] # 如果使用常数外推的话需要配置该值
) → dst
img = mt.cv_rgb_imread('img1.jpg', gray=False)
kernal = np.ones([1, 58])
res = cv2.erode(img, kernal)
PIS(img, res)
包含了若干形态学操作的通用函数 官方文档
cv2.morphologyEx(
src, # 源图像
op, # 形态学操作
kernel[, # 核
dst[, # 输出图像
anchor[, # 锚在元素中的位置,默认为 (-1, -1),表示锚点在核中心
iterations[, # 操作轮数
borderType[, # 像素外推法
borderValue]]]]]) # 如果使用常数外推的话需要配置该值
→ dst
操作值 | 形态学操作 |
---|---|
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),可以描述为膨胀与腐蚀的差值,用如下表达式描述:
形态学梯度通常用于显示明亮区域的边界,然后便可以将他们看作目标或者目标的部分。用扩张的图像减去了收缩的图像,如此一来就找出了完整的边界。这与计算梯度不同,它不会关注某一个物体的周围。
最后两种操作是顶帽和黑帽。这两种操作分别用于显示与其邻域相比更亮或更暗的部分。当试图根据物体的亮度变化分离依附于物体的某些部分时,就会用到这些方法。在生物体或细胞的显微镜图像上经常会用到这些方法。两种操作都是根据基础操作定义的,如下所示:
源图像仅支持二值图,会匹配模板,模板中的 1 对应源图的1,模板-1对应源图0,模板0忽略源图像素,按照当前规则匹配源图,匹配成功为1,失败为0,返回结果图
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()
。
cv2.getStructuringElement(
shape, # 核形状
ksize, # 核尺寸
anchor # 锚点位置,默认(-1, -1),表示在核中心
)
形状值 | 元素 | 描述 |
---|---|---|
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$ |
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)
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'])