图像形态学是根据图像的形状进行的操作,它可以从图像中提取出用于表示图像形状最基本的信息,使得计算机能够更好的理解和识别图像信息。
形态学操作一般针对的是二值图像进行操作。这里简单说下二值图、灰度图、彩色图的区别:
- 二值图:图像中的每个像素值为 0 或者 255,即:每个像素点要不是 0 、要不是 255
- 彩色图:由 R、G、B 三原色组成的图像。即:有 3 个通道,每个通道像素值的取值范围为 0~255
- 灰度图:灰度图只有 1 个通道,每个像素的取值范围为 0 ~ 255
下面是是一段生成三种图像的示例代码:
import numpy as np
import matplotlib.pyplot as plt
if __name__ == '__main__':
# 固定随机数种子
np.random.seed(0)
# 生成图像尺寸
size = (16, 16)
# 1. 二值图
image1 = np.random.choice([0, 255], size=size)
# 2. 灰度图
image2 = np.random.randint(0, 256, size=size)
# 3. 彩色图
size += (3,) # 通道数
image3 = np.random.randint(0, 256, size=size)
# imshow 函数默认显示 RGB(A) 彩色图,通过指定 cmap='gray' 显示灰度图
for index, (image, title) in enumerate([(image1, '二值图'), (image2, '灰度图'), (image3, '彩色图')]):
plt.subplot(131 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
程序输出结果:

灰度图和二值图看起来都是黑白图,不同的是灰度图的每个像素点是有明亮变化。
1. 腐蚀和膨胀
腐蚀: 卷积核对应的原图像的像素值中全部是 1,则中心元素的像素值就是 1,否则为 0。这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。
膨胀: 卷积核对应的原图像的像素值中只要有一个是 1,则中心元素的像素值就是 1,否则为 0。腐蚀去掉白噪声的同时,也会使前景对象变小。膨胀使前景对象变大,它也可以用来连接两个分开的物体。
import numpy as np
import matplotlib.pyplot as plt
import cv2
def test():
np.random.seed(0)
images = []
# 读取图像
orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
images.append((orign_image, "原图"))
# 添加噪声
h, w = orign_image.shape
noise_num = 200000
hp = np.random.randint(0, h, size=(noise_num,))
wp = np.random.randint(0, w, size=(noise_num,))
noise_image = np.copy(orign_image)
for n1, n2 in zip(hp, wp):
noise_image[n1][n2] = 255
images.append((noise_image, "噪声"))
# 腐蚀操作
# kernel size 越大腐蚀程度越强
kernel = np.ones((5, 5), np.int8)
# iterations 表示重复多少次腐蚀或者膨胀
erode_image = cv2.erode(noise_image, kernel, iterations=2)
images.append((erode_image, "腐蚀"))
# 膨胀操作
dilate_image = cv2.dilate(erode_image, kernel, iterations=2)
images.append((dilate_image, "膨胀"))
# 图像显示
for index, (image, title) in enumerate(images):
plt.subplot(141 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
if __name__ == '__main__':
test()
程序输出结果:

2. 开闭运算
开运算:先腐蚀再膨胀,它可以被用来去除噪声。例如上面的例子一样,先腐蚀之后去除了噪声,但是对象会变小,再进行膨胀,增大对象,从而去除噪声。
闭运算:先膨胀再腐蚀。它可以被用来填充前景物体中的小洞,或者前景物体上的小黑点。下面的例子演示下这个效果。
import numpy as np
import matplotlib.pyplot as plt
import cv2
def test():
np.random.seed(0)
images = []
# 读取图像
orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
images.append((orign_image, "原图"))
# 添加噪声
h, w = orign_image.shape
hp, wp = range(0, h), range(0, w)
noise_image = np.copy(orign_image)
for x in hp:
for y in wp:
if noise_image[x][y] == 255:
if np.random.randint(1, 11) <= 2:
noise_image[x][y] = 0
images.append((noise_image, "噪声"))
# 闭运算
kernel = np.ones((5, 5), np.int8)
# op=cv2.MORPH_OPEN 参数指定开运算
# op=cv2.MORPH_CLOSE 参数指定闭运算
open_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_CLOSE, kernel=kernel, iterations=2)
images.append((open_image, "闭运算"))
# 图像显示
for index, (image, title) in enumerate(images):
plt.subplot(131 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
if __name__ == '__main__':
test()
程序执行结果:

从这里可以看到,开运算可以去除背景中的噪声,闭运算则可以去除前景中的噪声。
3. 形态学梯度
形态学梯度:图像膨胀与腐蚀的差,结果看上去就像前景物体的轮廓。梯度用于描述目标边界灰度剧烈变化的区域,突出高亮区域的边界。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def test():
images = []
image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
images.append((image, '原图'))
kernel = np.ones(shape=(5, 5))
image = cv2.morphologyEx(image, op=cv2.MORPH_GRADIENT, kernel=kernel, iterations=1)
images.append((image, '梯度'))
# 图像显示
for index, (image, title) in enumerate(images):
plt.subplot(121 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
if __name__ == '__main__':
test()
程序输出结果:

4. 顶帽和黑帽
顶帽:原图与开运算之后得到的差,用于显示开运算忽略的图像信息。
黑帽:原图与闭运算之后得到的差,用于显示闭运算忽略的图像信息。
4.1 顶帽示例
import numpy as np
import matplotlib.pyplot as plt
import cv2
def test():
np.random.seed(0)
images = []
# 读取图像
orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
# 添加噪声
h, w = orign_image.shape
noise_num = 150000
hp = np.random.randint(0, h, size=(noise_num,))
wp = np.random.randint(0, w, size=(noise_num,))
noise_image = np.copy(orign_image)
for n1, n2 in zip(hp, wp):
noise_image[n1][n2] = 255
images.append((noise_image, "原图"))
# 开运算
kernel = np.ones((5, 5), np.int8)
open_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_OPEN, kernel=kernel, iterations=2)
images.append((open_image, "开运算"))
# 顶帽
tophat_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_TOPHAT, kernel=kernel, iterations=2)
images.append((tophat_image, "顶帽"))
# 图像显示
for index, (image, title) in enumerate(images):
plt.subplot(131 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
if __name__ == '__main__':
test()
程序执行结果:

4.2 黑帽示例
import numpy as np
import matplotlib.pyplot as plt
import cv2
def test():
np.random.seed(0)
images = []
# 读取图像
orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
# 添加噪声
h, w = orign_image.shape
hp, wp = range(0, h), range(0, w)
noise_image = np.copy(orign_image)
for x in hp:
for y in wp:
if noise_image[x][y] == 255:
if np.random.randint(1, 11) <= 2:
noise_image[x][y] = 0
images.append((noise_image, "原图"))
# 闭运算
kernel = np.ones((5, 5), np.int8)
close_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_CLOSE, kernel=kernel, iterations=2)
images.append((close_image, "闭运算"))
# 黑帽
black_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_BLACKHAT, kernel=kernel, iterations=2)
images.append((black_image, "黑帽"))
# 图像显示
for index, (image, title) in enumerate(images):
plt.subplot(131 + index)
plt.imshow(image, cmap='gray')
plt.title(title)
plt.axis('off')
plt.show()
if __name__ == '__main__':
test()
程序执行结果:


冀公网安备13050302001966号