深入理解闭运算的原理、实现与应用
闭运算是数学形态学的基本复合操作,定义为先膨胀后腐蚀,常用于填补对象中的小孔洞和连接相近的对象。
闭运算是数学形态学中的一种复合操作,定义为先对图像进行膨胀操作,然后再进行腐蚀操作。用数学公式表示为:
A • B = (A ⊕ B) ⊖ B
其中A是输入图像,B是结构元素,⊕表示膨胀操作,⊖表示腐蚀操作。
闭运算的特点:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def closing_builtin(image_path, kernel_size=3, iterations=1):
"""
使用OpenCV内置函数实现闭运算
:param image_path: 输入图像路径
:param kernel_size: 结构元素大小
:param iterations: 闭运算次数
:return: 原图和闭运算后的图像
"""
# 读取图像
img = cv2.imread(image_path)
# 创建结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
# 应用闭运算
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=iterations)
return img, closed
def manual_closing(image_path, kernel_size=3):
"""
手动实现闭运算(先膨胀后腐蚀)
:param image_path: 输入图像路径
:param kernel_size: 结构元素大小
:return: 闭运算后的图像
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化图像
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
height, width = binary.shape
pad_size = kernel_size // 2
# 对图像进行填充
padded = np.pad(binary, pad_size, mode='constant', constant_values=0)
# 第一步:膨胀
dilated = np.zeros_like(binary)
for i in range(height):
for j in range(width):
window = padded[i:i+kernel_size, j:j+kernel_size]
if np.max(window) == 255: # 如果窗口中存在白色像素,输出就是白色
dilated[i, j] = 255
else:
dilated[i, j] = 0
# 对膨胀结果进行填充(用于腐蚀)
padded_dilated = np.pad(dilated, pad_size, mode='constant', constant_values=255)
# 第二步:腐蚀
eroded = np.zeros_like(dilated)
for i in range(height):
for j in range(width):
window = padded_dilated[i:i+kernel_size, j:j+kernel_size]
if np.min(window) == 255: # 只有当所有像素都是白色时,输出才是白色
eroded[i, j] = 255
else:
eroded[i, j] = 0
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), cv2.cvtColor(eroded, cv2.COLOR_GRAY2BGR)
def closing_with_different_shapes(image_path):
"""
使用不同形状的结构元素进行闭运算
:param image_path: 输入图像路径
:return: 原图和不同形状结构元素的闭运算结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化图像
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 创建不同形状的结构元素
kernels = {
'Rectangle': cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)),
'Ellipse': cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)),
'Cross': cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
}
results = {}
for name, kernel in kernels.items():
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
results[name] = cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR)
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), results
def hole_filling_closing(image_path):
"""
使用闭运算填补孔洞
:param image_path: 输入图像路径
:return: 原图、含孔洞图像和填补孔洞后的图像
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化图像
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 创建含孔洞的图像
# 先腐蚀使对象变小,再膨胀使对象变大,但留下孔洞
kernel_small = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
kernel_large = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
# 先腐蚀使对象变小
eroded = cv2.erode(binary, kernel_small, iterations=2)
# 再膨胀使对象变大,但留下孔洞
dilated = cv2.dilate(eroded, kernel_large, iterations=1)
# 使用闭运算填补孔洞
closed = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, kernel_small)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(dilated, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR))
def object_connection_closing(image_path):
"""
使用闭运算连接分离的对象
:param image_path: 输入图像路径
:return: 原图、分离对象图像和连接对象后的图像
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化图像
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 创建分离的对象(通过腐蚀使对象分离)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
separated = cv2.erode(binary, kernel, iterations=2)
# 使用闭运算连接对象
connected = cv2.morphologyEx(separated, cv2.MORPH_CLOSE, kernel)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(separated, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(connected, cv2.COLOR_GRAY2BGR))
def black_hat_transform(image_path):
"""
黑帽变换 - 闭运算结果减去原图
:param image_path: 输入图像路径
:return: 原图和黑帽变换结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 创建结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
# 闭运算
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
# 黑帽变换(闭运算结果减去原图)
black_hat = cv2.subtract(closed, img)
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), cv2.cvtColor(black_hat, cv2.COLOR_GRAY2BGR)
def adaptive_closing(image_path):
"""
自适应闭运算 - 根据局部特性调整结构元素大小
:param image_path: 输入图像路径
:return: 原图和自适应闭运算结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 计算局部方差
block_size = 15
mean_local = cv2.blur(img, (block_size, block_size))
squared_img = img.astype(np.float64) ** 2
mean_squared_local = cv2.blur(squared_img, (block_size, block_size))
variance_local = mean_squared_local - mean_local.astype(np.float64) ** 2
# 根据局部方差调整闭运算核大小
height, width = img.shape
result = img.copy()
# 为了演示,我们使用分块处理
for i in range(0, height, 10):
for j in range(0, width, 10):
# 获取局部方差
end_i = min(i + 10, height)
end_j = min(j + 10, width)
local_var = np.mean(variance_local[i:end_i, j:end_j])
# 根据方差大小调整核大小
if local_var < 1000: # 平坦区域
kernel_size = 3
elif local_var < 5000: # 中等纹理区域
kernel_size = 5
else: # 高纹理区域
kernel_size = 7
# 创建结构元素并应用闭运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
roi = img[i:end_i, j:end_j]
closed_roi = cv2.morphologyEx(roi, cv2.MORPH_CLOSE, kernel)
result[i:end_i, j:end_j] = closed_roi
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
def morphological_gradient_combined(image_path):
"""
结合开运算和闭运算的形态学操作
:param image_path: 输入图像路径
:return: 原图、开运算、闭运算和组合结果
"""
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 创建结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 开运算
opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 闭运算
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
# 组合操作:先开运算再闭运算
opened_then_closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)
return (cv2.cvtColor(img, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(opened, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR),
cv2.cvtColor(opened_then_closed, cv2.COLOR_GRAY2BGR))
# 使用示例
if __name__ == "__main__":
# 注意:需要提供实际的图像路径
# img, result = closing_builtin('image.jpg', 3, 1)
# manual_img, manual_result = manual_closing('image.jpg', 3)
# shape_img, shape_results = closing_with_different_shapes('image.jpg')
# hole_orig, hole_img, hole_result = hole_filling_closing('image.jpg')
# conn_orig, conn_img, conn_result = object_connection_closing('image.jpg')
# blackhat_img, blackhat_result = black_hat_transform('image.jpg')
# adapt_img, adapt_result = adaptive_closing('image.jpg')
# comb_img, open_res, close_res, comb_res = morphological_gradient_combined('image.jpg')
pass