形态学操作其实就是改变物体的形状,比如腐蚀就是”变瘦”,膨胀就是”变胖”。
形态学操作一般作用于二值图,来连接相邻的元素或分离成独立的元素。
常见的形态学操作有:腐蚀、膨胀、开运算、闭运算、顶帽和黑帽、形态学梯度。
腐蚀和膨胀是相反的操作,腐蚀是求局部最小值的操作。腐蚀操作会使图像中的高亮区逐渐减小。
原图 腐蚀 腐蚀后结果
操作过程和卷积类似,只看卷积核中为1的部分对应的原图中的值,当1所对应的区域有0时,这个中心位置就为0
从图中可以看出,0的位置变得更多,腐蚀操作的结果就是让暗的区域变大。
膨胀就是求局部最大值的操作。从数学角度来说,就是将图像与核进行卷积,计算核B覆盖区域的像素点的最大值,并把这个最大值赋值给参考点指定的元素。这样就会使图像中的高亮区域逐渐增长。膨胀和腐蚀的操作正好相反,膨胀操作的结果就是让亮的区域变大。
(1)消除噪声
(2)分割出独立的图像元素,在图像中连接相邻的元素
(3)寻找图像中的极大值或者极小值区域
(4)求出图像的梯度
腐蚀和膨胀作用在二值图上。在实际的图像去噪中,通常是先腐蚀后膨胀,即所谓的开运算。其效果相比单一操作要好许多。
import cv2 as cv
import numpy as np
"""
形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像进行的操作。
需要输入两个参数,一个是原始图像,第二个被称为结构化元素或 核,它是用来决定操作的性质的。
两个基本的形态学操作是腐蚀和膨胀。他们 的变体构成了开运算,闭运算,梯度等
"""
def erode_demo(image):
print(image.shape)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow("binary", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dst = cv.erode(binary, kernel=kernel)
cv.imshow("erode_demo", dst)
def dilate_demo(image):
print(image.shape)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow("binary", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dst = cv.dilate(binary, kernel=kernel)
cv.imshow("dilate_demo", dst)
def main():
src = cv.imread("../images/01.jpg")
# erode_demo(src)
# dilate_demo(src)
# 彩色图像腐蚀,膨胀
img = cv.imread("../images/lena.jpg")
cv.imshow("img", img)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
# dst = cv.dilate(img, kernel=kernel)
dst = cv.erode(img, kernel=kernel)
cv.imshow("dilate", dst)
cv.waitKey(0) # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
cv.destroyAllWindows() # 关闭所有窗口
if __name__ == '__main__':
main()
开运算是先腐蚀后膨胀。主要用于消除小物体,在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积,同时抑制比结构元小的亮细节;也可以提取水平和垂直的线(比如,要检测垂直线,将cv2.getStructuringElement()中的核大小弄成(1, x) 这种形式)。(二值化后前景为白色,背景为黑色)
闭运算是先膨胀后腐蚀。用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积,同时抑制比结构元小的暗细节。
def open_demo(image):
print(image.shape)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
cv.imshow("binary", binary)
"""
在前面的例子中我们使用Numpy构建了结构化元素,它是正方形的。
但有时我们需要构建一个椭圆形 / 圆形的核。为了实现这种要求,提供了OpenCV
函数cv2.getStructuringElement()。你只需要告诉他你需要的核的形状和大小。
"""
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dst = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel=kernel)
cv.imshow("open_demo", dst)
def close_demo(image):
print(image.shape)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow("binary", binary)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dst = cv.morphologyEx(binary, cv.MORPH_CLOSE, kernel=kernel)
cv.imshow("close_demo", dst)
顶帽就是源图像和开运算得到的图像之间的差值图像。
黑帽就是闭运算得到的图像和源图像之间的差值图像。
我觉得黑帽和顶帽其实就是找出那些噪音点。
def hat_binary_demo(image): # 对二值图像进行顶帽、黑帽操作
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) # 获取结构元素
# dst = cv.morphologyEx(binary, cv.MORPH_BLACKHAT, kernel=kernel) # 黑帽操作
dst = cv.morphologyEx(binary, cv.MORPH_TOPHAT, kernel=kernel) # 顶帽操作
cv.imshow("top_hat_demo", dst)
def hat_gray_demo(image): # 对灰度图像进行顶帽、黑帽操作
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) # 获取结构元素
# dst = cv.morphologyEx(gray, cv.MORPH_BLACKHAT, kernel=kernel) # 黑帽操作
dst = cv.morphologyEx(gray, cv.MORPH_TOPHAT, kernel=kernel) # 顶帽操作
cimg = np.array(gray.shape, np.uint8)
cimg = 100
dst = cv.add(dst, cimg)
cv.imshow("top_hat_demo", dst)
def gradient_demo(image):
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dm = cv.dilate(image, kernel)
cm = cv.erode(image, kernel)
dst1 = cv.subtract(image, cm)
dst2 = cv.subtract(dm, image)
cv.imshow("internal gradient", dst1)
cv.imshow("external gradient", dst2)