文章结构
- 腐蚀
- 膨胀
- 开运算
- 闭运算
- 形态学方法
- 梯度运算
- 顶帽运算
- 黑帽运算
腐蚀
腐蚀操作可以让图像沿着自己的边界向内收缩。OpenCV通过”核“来实现收缩计算。“核”在形态学中可以理解为”由n个像素组成的像素块“,像素块包含一个核心(通常在中央位置,也可以定义在其他位置)。像素块会在图像的边缘移动,在移动过程中,核会将图像边缘那些与核重合但又没有越过核心的像素点都抹除,效果如下:
OpenCV将腐蚀操作封装成了erode()方法:
dst = ccv2.erode(src, kernel, anchor, iterations, borderType, borderValue)
- src: 原始图像
- kernel: 腐蚀使用的核
- anchor:(可选)核的锚点位置
- iteration:(可选)腐蚀操作的迭代次数,默认值为1
- borderType:(可选)边界样式,建议默认
- borderValue:(可选)边界值,建议默认
- dst :经过腐蚀之后的图像
在OpenCV做腐蚀或其他形态学操作时,通常使用NumPy模块来创建核数组,例如:
import numpy as np
k = np.ones((5,5), np.uint8)
这两行代码创建了一个数组,可以当作erode()方法的核参数。除了5×5的结构,还可以使用3×3、9×9等其他结构,行列数越大,计算出来的效果就越粗糙,行列数越小,计算出的效果就越精细。
实例1: 将仙人球图像中的刺都抹除掉
import cv2
import numpy as np
img = cv2.imread("cactus.jpg") # 读取原图
k = np.ones((3, 3), np.uint8) # 创建3*3的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.erode(img, k) # 腐蚀操作
cv2.imshow("dst", dst) # 显示腐蚀效果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下:
如果是(1,1),等于没削;如果是(5,5),直接削皮了。
膨胀
膨胀操作与腐蚀操作正好相反,膨胀操作可以让图像沿着自己的边界向外扩张。同样是通过核计算,当核在图像的边缘移动时,核会将图像边缘填补新的像素,效果如下:
dst = cv2.dilate(src, kernel, anchor, iterations, borderType, borderValue)
- src: 原始图像
- kernel: 膨胀使用的核
- iteration:(可选)膨胀操作的迭代次数,默认值为1
- borderType:(可选)边界样式,建议默认
- borderValue:(可选)边界值,建议默认
- dst :经过膨胀之后的图像
实例2: 将图像加工成”近视眼“效果
import cv2
import numpy as np
img = cv2.imread("sunset.jpg") # 读取原图
k = np.ones((9, 9), np.uint8) # 创建9*9的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.dilate(img, k) # 膨胀操作
cv2.imshow("dst", dst) # 显示膨胀效果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下:
开运算
开运算就是先将图像进行腐蚀操作,再进行膨胀操作。开运算可以用来抹除图像外部的细节(或者噪声)
例如图 7.13 是一个简单的二叉树,父子节点之间都有线连接。如果对此图像进行腐蚀操作,可以得出如图 7.14 所示的图像,连接线消失了,节点也比原图节点小一圈。此时再执行膨胀操作,让缩小的节点膨胀回原来的大小,就得出了如图 7.15 所示的效果。
这三张图就是开运算的过程,从结果可以明显地看出: 经过开运算之后,二叉树中的连接线消失了,只剩下光秃秃的节点。因为连接线被核当成“细节”抹除了,所以利用检测轮廓的方法就可以统计出二叉树节点数量,也就是说在某些情况下,开运算的结果还可以用来做数量统计。
实例3: 抹除黑种草图像中的针状叶子
import cv2
import numpy as np
img = cv2.imread("nigella.png") # 读取原图
k = np.ones((5, 5), np.uint8) # 创建5*5的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.erode(img, k) # 腐蚀操作
dst = cv2.dilate(dst, k) # 膨胀操作
cv2.imshow("dst", dst) # 显示开运算结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下:
闭运算
闭运算就是将图像先进行膨胀操作,再进行腐蚀操作。闭运算可以抹除图像内部的细节(或者噪声)。
例如图 7.19 是一个身上布满斑点的小蜘蛛,这些斑点就是图像的内部细节。先将图像进行膨胀操作,小蜘蛛身上的斑点 (包括小眼睛)就被抹除掉,效果如图 7.20 所示。然后再将图像进行腐蚀操作,让膨胀的小蜘蛛缩回原来的大小,效果如图 7.21 所示。
闭运算除了会抹除图像内部的细节,还会让一些里的较近的区域合并成一块区域。
形态学方法
OpenCV提供了一个morphologyEx()形态学方法,包含了所有常用的运算。
dst = cv2.morphologyEx(src, op, kernel, anchor, iterations, borderType, borderValue)
- src: 原始图像
- op: 操作类型
参数值 | 含义 |
---|---|
cv2.MORPH_ERODE | 腐蚀操作 |
cv2.MORPH_DILATE | 膨胀操作 |
cv2.MORPH_OPEN | 开运算,先腐蚀后膨胀 |
cv2.MORPH_CLOSE | 闭运算,先膨胀后腐蚀 |
cv2.MORPH_GRADIENT | 梯度运算,膨胀图减腐蚀图,可以得出简易的轮廓 |
cv2.MORPH_TOPHAT | 顶帽运算,原始图像减开运算图像 |
cv2.MORPH_BLACKHAT | 黑帽运算,闭运算图像减原始图像 |
- kernel: 操作过程中所使用的核
- anchor:(可选),核的锚点位置
- iteration:(可选)操作的迭代次数,默认值为1
- borderType:(可选)边界样式,建议默认
- borderValue:(可选)边界值,建议默认
- dst :操作之后得到的图像
梯度运算
这里的梯度指的是图像梯度,可以简单地理解为像素的变化程度。几个连续的像素,其像素值跨度越大,则梯度值越大。
梯度运算就是让原图的膨胀图像减去原图的腐蚀图像。因为膨胀图比原图大,腐蚀图像比原图小,利用腐蚀图像将膨胀图像掏空,就得到了原图的 轮廓图像(大概,并不精准)。
实例4: 通过梯度运算画出小蜘蛛的轮廓
import cv2
import numpy as np
img = cv2.imread("spider.png") # 读取原图
k = np.ones((5,5), np.uint8) # 创建5*5的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, k) # 进行梯度运算
cv2.imshow("dst", dst) # 显示梯度运算结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下:
顶帽运算
顶帽运算就是让原图减去原图的开运算图像,得到图像的外部细节。
实例5: 通过顶帽运算画出小蜘蛛的腿
import cv2
import numpy as np
img = cv2.imread("spider.png") # 读取原图
k = np.ones((5, 5), np.uint8) # 创建5*5的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, k) # 进行顶帽运算
cv2.imshow("dst", dst) # 显示顶帽运算结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下:
黑帽运算
黑帽运算就是让原图的闭运算图像减去原图,得到原图像的内部细节。
实例6: 通过黑帽运算画出小蜘蛛身上的花纹
import cv2
import numpy as np
img = cv2.imread("spider2.png") # 读取原图
k = np.ones((5, 5), np.uint8) # 创建5*5的数组作为核
cv2.imshow("img", img) # 显示原图
dst = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, k) # 进行黑帽运算
cv2.imshow("dst", dst) # 显示黑帽运算结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
结果如下: