膨胀腐蚀及底层实践,拒绝opencv,matlab等方式
(使用底层方法)
------ 从入门到上手的形态学操作指南
形态学操作是数字图像处理 的核心技术之一,膨胀(Dilation)和腐蚀(Erosion)是它的两大基础操作,而结构元素 就是这两个操作的"工具"。
最近学习数字图像处理课程,刚好要做一个相关算法的实现,所以记录下来。当然,用opencv和matlab会很快。但是从底层的png读取出发会更好。相关代码已验证。
- 环境:jupyter notebook
- 语言:python 3.13
一、知识点
1、核心概念总览 📖
先明确一个前提:我们讨论的膨胀和腐蚀,主要针对二值图像(像素只有0和1两种值,比如黑白文字图、轮廓图,1代表前景,0代表背景)。
| 概念 | 核心作用 | 通俗类比 |
|---|---|---|
| 结构元素 | 操作的"工具模板" | 盖章用的印章 |
| 膨胀 | 让前景区域"变大变胖" | 给画好的线条加粗 |
| 腐蚀 | 让前景区域"变小变瘦" | 给画好的线条变细 |
2、结构元素(Structuring Element)🔧
2.1 什么是结构元素?
结构元素是一个小的矩阵模板(通常是正方形、圆形或十字形),它是膨胀和腐蚀的"操作核心"。
- 矩阵里有锚点(Anchor Point):一般是矩阵的中心像素,相当于印章的"把手",操作时锚点会遍历图像的每个像素。
- 矩阵值只有0和1:1的区域代表"有效作用范围",0的区域不参与计算。
2.2 常见的结构元素类型
举几个最常用的例子(以3×3矩阵为例):
-
正方形结构元素
1 1 1
1 1 1
1 1 1
→ 作用范围最大,处理后边缘更平滑。
-
十字形结构元素
0 1 0
1 1 1
0 1 0
→ 只在上下左右四个方向作用,适合保留图像的"线条感"。
-
圆形结构元素
0 1 0
1 1 1
0 1 0
(3×3的圆形和十字形类似,更大的圆形结构元素会更接近圆形轮廓)
2.3 结构元素的关键属性
- 大小:比如3×3、5×5,越大操作效果越明显(比如膨胀5×5的结构元素比3×3的加粗效果更强)。
- 形状:决定操作的"方向偏好"(比如十字形只影响横竖方向,正方形影响所有方向)。
- 锚点位置:默认是中心,也可以自定义(比如左上角),锚点位置会影响最终图像的偏移。
3、腐蚀(Erosion)⚔️
3.1 腐蚀的核心思想
腐蚀是让前景(1)区域缩小的操作,原理可以类比成:用结构元素这个"印章"在图像上盖章,只有当印章覆盖的所有像素都是1时,锚点对应的像素才保留为1,否则变为0。
→ 简单说:只要周围有一个0,中心就变0,前景被"侵蚀"。
3.2 腐蚀的步骤(以3×3正方形结构元素为例)
- 把结构元素的锚点放在图像的某个前景像素上。
- 检查结构元素覆盖的所有像素是否都是1。
- ✅ 是 → 锚点像素保持1。
- ❌ 否 → 锚点像素变为0。
- 遍历图像的每一个像素,重复步骤1-2。
3.3 腐蚀的效果 & 应用
效果:
- 前景区域变小、变瘦。
- 可以消除小的噪声点(比如图像里的小黑点)。
- 可以断开两个连接的前景区域。
应用场景:
- 去除二值图像的噪声。
- 分离重叠的物体(比如一堆硬币的图像,腐蚀后可以分开硬币边缘)。
4、膨胀(Dilation)🔍
4.1 膨胀的核心思想
膨胀是让前景(1)区域扩大的操作,原理和腐蚀相反:用结构元素盖章,只要印章覆盖的区域有一个像素是1,锚点对应的像素就变为1。
→ 简单说:只要周围有一个1,中心就变1,前景被"扩张"。
4.2 膨胀的步骤(以3×3正方形结构元素为例)
- 把结构元素的锚点放在图像的某个像素上。
- 检查结构元素覆盖的区域是否有至少一个像素是1。
- ✅ 是 → 锚点像素变为1。
- ❌ 否 → 锚点像素保持0。
- 遍历图像的每一个像素,重复步骤1-2。
4.3 膨胀的效果 & 应用
效果:
- 前景区域变大、变胖。
- 可以填补前景区域的小空洞(比如文字笔画里的小缺口)。
- 可以连接断裂的前景区域。
应用场景:
- 修复二值图像的断裂部分(比如扫描的文字有笔画缺失)。
- 增强目标区域的轮廓。
5、膨胀和腐蚀的"黄金搭档":开运算 & 闭运算 🎎
膨胀和腐蚀很少单独使用,通常会组合起来形成开运算 和闭运算,这才是形态学操作的常用姿势!
5.1 开运算:先腐蚀,后膨胀
公式:开运算 = 膨胀(腐蚀(图像))
作用 :去除小噪声点 + 不改变前景的整体大小。
→ 比如图像上有很多小噪点,先腐蚀把噪点去掉,再膨胀把前景恢复到原来的大小。
5.2 闭运算:先膨胀,后腐蚀
公式:闭运算 = 腐蚀(膨胀(图像))
作用 :填补小空洞 + 不改变前景的整体大小。
→ 比如文字笔画有小缺口,先膨胀把缺口补上,再腐蚀把前景恢复到原来的粗细。
6、初学者常见误区 ❌
- 混淆膨胀和腐蚀的作用对象
→ 膨胀是扩大前景 ,不是背景;腐蚀是缩小前景,不是背景。 - 认为结构元素越大越好
→ 结构元素太大,会导致图像变形严重(比如文字膨胀过度会糊成一团),要根据需求选择大小。 - 忽略二值化的重要性
→ 膨胀和腐蚀对灰度图也能操作,但效果不直观,二值图是最佳操作对象。
7、总结 📝
| 概念 | 核心操作 | 关键工具 | 典型应用 |
|---|---|---|---|
| 结构元素 | 矩阵模板 | 大小、形状、锚点 | 决定膨胀/腐蚀的作用方式 |
| 腐蚀 | 前景缩小 | 结构元素遍历检查 | 去噪、分离物体 |
| 膨胀 | 前景扩大 | 结构元素遍历检查 | 补洞、连接物体 |
膨胀和腐蚀就像图像处理的"塑形工具",而结构元素就是"塑形的模具",掌握这三个概念,你就能玩转形态学操作的大部分应用啦!
二、jupyter+纯底层实践
实践要求

原图

步骤0:头文件
python
#头文件读取
import numpy as np
import matplotlib.pyplot as plt
import struct
import zlib
步骤1:纯手动读取PNG图像(解析二进制,替代cv2.imread)
代码
PNG图像本质是二进制数据流,我们通过struct解析文件头、尺寸、像素数据,最终转为灰度矩阵
python
def read_png_manual(file_path):
"""
解析PNG图像文件并返回灰度像素矩阵。
本函数实现PNG格式的底层解析,支持灰度图像和RGB彩色图像的自动灰度转换。
函数严格按照PNG规范处理文件结构,包含文件头验证、数据块解析和像素数据解压。
参数:
file_path (str): PNG文件的完整或相对路径。文件必须存在且具有读取权限。
返回值:
numpy.ndarray: 灰度像素矩阵,数据类型为uint8,形状为(height, width)。
像素值范围0-255,其中0表示纯黑,255表示纯白。
异常:
FileNotFoundError: 指定路径的文件不存在。
PermissionError: 没有权限读取指定文件。
ValueError: 文件格式不符合PNG标准、包含不支持的图像参数或数据损坏。
zlib.error: 压缩数据解压失败,通常表示文件已损坏。
支持的图像格式:
- 位深度: 仅支持8位
- 颜色类型:
* 0: 灰度图像
* 2: RGB彩色图像(自动转换为灰度)
- 压缩方法: 仅支持标准zlib压缩(方法0)
- 过滤方法: 仅支持自适应过滤(方法0)
已知限制:
1. 不支持带Alpha通道的图像(颜色类型4和6)
2. 不支持调色板图像(颜色类型3)
3. 不验证数据块的CRC校验码
4. 忽略隔行扫描标志,按非隔行方式处理
5. 仅处理8位深度,不支持1、2、4、16位深度
实现说明:
函数遵循PNG文件格式规范进行实现,确保与标准PNG解码器的兼容性。
RGB到灰度的转换采用ITU - R BT.601标准亮度系数。
示例:
>>> image_data = read_png_manual("sample.png")
>>> print(f"图像尺寸: {image_data.shape[1]}x{image_data.shape[0]}")
>>> print(f"像素值统计: 最小值={image_data.min()}, 最大值={image_data.max()}")
"""
with open(file_path, 'rb') as f:
# 验证PNG文件签名
png_header = f.read(8)
if png_header != b'\x89PNG\r\n\x1a\n':
raise ValueError("文件不是有效的PNG格式:签名验证失败")
# 解析IHDR数据块
ihdr_len = struct.unpack('!I', f.read(4))[0]
ihdr_type = f.read(4)
if ihdr_type != b'IHDR':
raise ValueError(f"IHDR块缺失或格式错误:期望'IHDR',实际为'{ihdr_type.decode('ascii', errors='ignore')}'")
# 提取图像元数据
ihdr_data = f.read(13)
width = struct.unpack('!I', ihdr_data[0:4])[0]
height = struct.unpack('!I', ihdr_data[4:8])[0]
bit_depth = ihdr_data[8]
color_type = ihdr_data[9]
# 验证支持的图像参数
if color_type not in [0, 2]:
raise ValueError(
f"不支持的颜色类型{color_type}。仅支持灰度(0)和RGB(2)格式。")
if bit_depth != 8:
raise ValueError(f"不支持的位深度{bit_depth}。仅支持8位深度。")
# 跳过CRC校验
f.read(4)
# 收集所有IDAT块的压缩数据
idat_data = b''
while True:
chunk_len_bytes = f.read(4)
if not chunk_len_bytes:
raise zlib.error("PNG文件不完整:未找到IEND终止块")
chunk_len = struct.unpack('!I', chunk_len_bytes)[0]
chunk_type = f.read(4)
if chunk_type == b'IDAT':
idat_data += f.read(chunk_len)
f.read(4)
elif chunk_type == b'IEND':
break
else:
f.read(chunk_len + 4)
if not idat_data:
raise ValueError("PNG文件缺少图像数据:未找到有效的IDAT块")
# 解压像素数据
try:
decompressed_data = zlib.decompress(idat_data)
except zlib.error as e:
raise zlib.error(f"图像数据解压失败:{str(e)}")
# 重构像素数据行
pixel_data = []
stride = width * (3 if color_type == 2 else 1)
expected_len = height * (stride + 1)
if len(decompressed_data) != expected_len:
raise ValueError(
f"像素数据长度不匹配:期望{expected_len}字节,实际{len(decompressed_data)}字节"
)
for y in range(height):
row_start = y * (stride + 1)
filter_byte = decompressed_data[row_start]
row_data = decompressed_data[row_start + 1 : row_start + 1 + stride]
pixel_data.append(row_data)
# 构建灰度图像矩阵
img_gray = np.zeros((height, width), dtype=np.uint8)
for y in range(height):
row = pixel_data[y]
for x in range(width):
if color_type == 2:
r = row[x*3]
g = row[x*3+1]
b = row[x*3+2]
gray = int(0.299 * r + 0.587 * g + 0.114 * b)
img_gray[y, x] = gray
else:
img_gray[y, x] = row[x]
return img_gray
if __name__ == "__main__":
try:
img = read_png_manual('digits.png')
print(f"图像尺寸:高度={img.shape[0]}, 宽度={img.shape[1]}")
print(f"像素值范围:{img.min()} ~ {img.max()}")
except Exception as e:
print(f"读取PNG失败:{type(e).__name__} - {str(e)}")
读取图像测试结果

步骤2:灰度与二值化图像
代码
python
def setup_matplotlib_chinese():
"""
配置matplotlib以支持中文字符显示。
直接使用matplotlib会有中文乱码问题
此函数自动检测操作系统并尝试加载相应的中文字体,确保图表中的中文标签和标题正确渲染。
同时配置负号显示格式,避免数学符号显示异常。
参数:
无
返回值:
无
系统支持:
- Windows: 使用Microsoft YaHei, SimHei, SimSun等字体
- macOS: 使用PingFang SC, Heiti SC等字体
- Linux: 使用WenQuanYi Micro Hei, DejaVu Sans等字体
说明:
1. 函数会临时禁用matplotlib的字体缺失警告
2. 如果找不到指定的中文字体,将回退到系统默认的sans-serif字体
3. 此配置仅影响当前Python会话中的matplotlib显示设置
"""
# 抑制字体缺失警告
warnings.filterwarnings('ignore', category=UserWarning, module='matplotlib')
# 系统字体映射配置
font_config = {
'Windows': ['Microsoft YaHei', 'SimHei', 'SimSun'],
'Darwin': ['PingFang SC', 'Heiti SC'],
'Linux': ['WenQuanYi Micro Hei', 'DejaVu Sans']
}
import platform
system = platform.system()
target_fonts = font_config.get(system, ['DejaVu Sans'])
# 查找并设置可用中文字体
available_fonts = [f.name for f in fm.fontManager.ttflist]
for font_name in target_fonts:
if font_name in available_fonts:
plt.rcParams['font.sans-serif'] = [font_name]
break
# 配置负号显示
plt.rcParams['axes.unicode_minus'] = False
# 初始化中文字体配置
setup_matplotlib_chinese()
def binaryzation_manual(img_gray, threshold=127):
"""
对灰度图像执行二值化处理,生成黑白二值图像。
此函数采用逐像素处理的方式实现图像二值化,将灰度值大于阈值的像素设为白色,
其余像素设为黑色。适用于需要基础图像分割或文档数字化的场景。
参数:
img_gray (numpy.ndarray): 输入灰度图像矩阵,要求为2维uint8数组,形状为(height, width)。
threshold (int): 二值化阈值,范围0-255,默认值为127。高于此值的像素将设为255,否则设为0。
返回值:
numpy.ndarray: 二值化后的图像矩阵,数据类型为uint8,形状与输入图像相同,像素值仅为0或255。
异常:
TypeError: 输入参数类型不符合要求。
ValueError: 图像格式或参数值超出有效范围。
图像要求:
1. 必须是2维numpy数组(单通道灰度图)
2. 数据类型必须为uint8(像素值范围0-255)
3. 不支持多通道图像或调色板图像
算法特点:
- 采用全局固定阈值
- 逐像素处理,便于理解二值化基本原理
- 输出仅包含黑白两色,适合后续形态学处理或特征提取
注意事项:
1. 对于大尺寸图像,逐像素处理可能影响性能
2. 固定阈值可能不适用于光照不均匀的图像
3. 建议对输入图像进行预处理(如去噪、直方图均衡化)以获得更佳效果
示例:
>>> gray_image = np.array([[100, 150, 200], [50, 180, 30]], dtype=np.uint8)
>>> binary_image = binaryzation_manual(gray_image, threshold=128)
>>> print(binary_image)
[[ 0 255 255]
[ 0 255 0]]
"""
# 验证输入参数有效性
if not isinstance(img_gray, np.ndarray):
raise TypeError(f"输入图像必须为numpy数组,实际类型:{type(img_gray)}")
if img_gray.ndim != 2:
raise ValueError(f"仅支持2维灰度图,实际维度:{img_gray.ndim}维")
if img_gray.dtype != np.uint8:
raise ValueError(f"图像数据类型必须为uint8,实际类型:{img_gray.dtype}")
if not isinstance(threshold, int):
raise TypeError(f"阈值必须为整数,实际类型:{type(threshold)}")
if not (0 <= threshold <= 255):
raise ValueError(f"阈值必须在0-255之间,实际值:{threshold}")
# 获取图像尺寸
height, width = img_gray.shape
# 初始化二值图像
img_binary = np.zeros((height, width), dtype=np.uint8)
# 遍历所有像素进行阈值分割
for y in range(height):
for x in range(width):
pixel_value = img_gray[y, x]
if pixel_value > threshold:
img_binary[y, x] = 255
return img_binary
## 图像处理与可视化示例
if __name__ == "__main__":
try:
# 执行二值化处理
img_binary = binaryzation_manual(img, threshold=127)
# 创建可视化图表
plt.figure(figsize=(10, 4))
# 显示原始灰度图像
plt.subplot(121)
plt.imshow(img, cmap='gray')
plt.title("灰度图像", fontsize=10)
plt.axis('off')
# 显示二值化结果
plt.subplot(122)
plt.imshow(img_binary, cmap='gray')
plt.title("二值化图像", fontsize=10)
plt.axis('off')
# 优化布局并显示
plt.tight_layout()
plt.show()
except NameError:
print("未找到图像数据,请先加载灰度图像")
except Exception as e:
print(f"图像处理失败:{type(e).__name__} - {str(e)}")
灰度与二值化结果

步骤3:构建结构元素
代码
python
def create_kernel_manual_4x4():
"""
创建4x4结构元素矩阵,用于形态学图像处理操作。
返回的结构元素矩阵定义了一个特定的形态学处理模板,其中:
- 0值表示背景区域(白色)
- 1值表示前景区域(黑色)
矩阵布局说明:
矩阵坐标以(行,列)表示,原点位于第2行第2列(基于0的索引)
矩阵大小为4x4,所有元素初始化为0(白色背景)
特定位置设置为1(黑色前景),形成L形结构模式
返回:
numpy.ndarray: 4x4结构元素矩阵,数据类型为uint8。
矩阵内容为:
[[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]]
用途:
此结构元素适用于基本的形态学操作,如膨胀、腐蚀、开运算和闭运算。
可用于边缘检测、噪声去除、形状分析等图像处理任务。
注意事项:
1. 矩阵采用uint8数据类型,与标准图像处理库兼容
2. 原点位置(2,2)在形态学操作中作为参考点
3. 矩阵中的1值构成非对称模式,适用于特定方向的形态学操作
"""
# 初始化4x4全零矩阵
kernel = np.zeros((4, 4), dtype=np.uint8)
# 定义前景点位置(形成L形模式)
kernel[1, 2] = 1 # 第1行第2列
kernel[2, 1] = 1 # 第2行第1列
kernel[2, 2] = 1 # 第2行第2列(原点位置)
return kernel
# 生成结构元素
kernel = create_kernel_manual_4x4()
# 输出结构元素信息
print("4x4结构元素矩阵:")
print("0表示背景(白色),1表示前景(黑色)")
print(kernel)
# 可视化展示
plt.figure(figsize=(2, 2))
plt.imshow(kernel, cmap='gray')
plt.title("结构元素矩阵")
plt.text(2, 2, '●', color='red', fontsize=14,
ha='center', va='center', fontweight='bold')
plt.axis('off')
plt.show()
构建结构矩阵结果

步骤4:腐蚀算法
代码:
python
def erosion_manual(img_binary, kernel):
"""
对二值图像执行腐蚀操作,实现形态学图像处理。
腐蚀是一种基本的形态学操作,通过结构元素在图像上滑动,仅当结构元素完全包含在
图像前景区域内时,才保留中心像素。此操作能够去除小尺寸噪声、断开细连接、
并缩小图像中的前景区域。
参数:
img_binary (numpy.ndarray): 输入的二值图像,要求为2维uint8数组,
像素值必须为0(背景)或255(前景)。
kernel (numpy.ndarray): 结构元素矩阵,定义腐蚀操作的邻域形状和大小,
像素值必须为0(背景)或1(前景)。
返回值:
numpy.ndarray: 腐蚀处理后的二值图像,数据类型为uint8,
像素值为0(背景)或255(前景)。
算法原理:
1. 遍历输入图像的每个像素作为结构元素的原点对齐位置
2. 对于每个位置,检查结构元素中所有前景点是否都能在图像中找到对应的前景像素
3. 如果结构元素完全包含在图像前景区域内,则输出图像对应位置设为前景
4. 否则,输出图像对应位置设为背景
边界处理:
当结构元素超出图像边界时,对应位置被视为不匹配,输出背景值。
结构元素原点:
函数假设结构元素的原点位于矩阵的(2,2)位置(基于0的索引)。
此配置适用于标准的4x4结构元素。
性能说明:
采用四层嵌套循环实现,适用于教学和理解腐蚀算法基本原理。
对于大尺寸图像,建议使用优化的形态学库函数。
示例:
>>> binary_img = np.array([[255, 0, 255], [0, 255, 0], [255, 0, 255]], dtype=np.uint8)
>>> kernel = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.uint8)
>>> eroded = erosion_manual(binary_img, kernel)
"""
# 获取图像和结构元素尺寸
img_h, img_w = img_binary.shape
kernel_h, kernel_w = kernel.shape
# 定义结构元素原点位置(适用于4x4结构元素)
kernel_origin_y = 2 # 原点行索引
kernel_origin_x = 2 # 原点列索引
# 初始化输出图像(全背景)
erosion_img = np.zeros((img_h, img_w), dtype=np.uint8)
# 遍历图像每个像素作为结构元素对齐点
for i in range(img_h):
for j in range(img_w):
match = True # 匹配状态标识
# 检查结构元素是否完全包含在图像前景中
for m in range(kernel_h):
for n in range(kernel_w):
# 跳过结构元素的背景点
if kernel[m, n] == 0:
continue
# 计算图像中对应位置坐标
img_y = i + (m - kernel_origin_y)
img_x = j + (n - kernel_origin_x)
# 检查边界条件
if img_x < 0 or img_x >= img_w or img_y < 0 or img_y >= img_h:
match = False
break
# 检查像素匹配条件
# 结构元素的前景点(1)要求图像对应位置为前景(255)
if img_binary[img_y, img_x] != 255:
match = False
break
if not match:
break
# 根据匹配结果设置输出像素
if match:
erosion_img[i, j] = 255
return erosion_img
# 执行形态学腐蚀操作
erosion_result = erosion_manual(img_binary, kernel)
# 可视化展示处理结果
plt.figure(figsize=(5, 4))
plt.imshow(erosion_result, cmap='gray')
plt.title("形态学腐蚀结果")
plt.axis('off')
plt.show()
腐蚀算法结果

步骤5:膨胀算法
代码
python
def dilation_manual(img_binary, kernel):
"""
对二值图像执行膨胀操作,实现形态学图像处理。
膨胀是一种基本的形态学操作,通过结构元素在图像上滑动,当结构元素与图像前景区域
存在任何重叠时,将输出图像的对应位置设为前景。此操作能够连接相邻的前景区域、
填充小孔洞、并扩大图像中的前景区域。
参数:
img_binary (numpy.ndarray): 输入的二值图像,要求为2维uint8数组,
像素值必须为0(背景)或255(前景)。
kernel (numpy.ndarray): 结构元素矩阵,定义膨胀操作的邻域形状和大小,
像素值必须为0(背景)或1(前景)。
返回值:
numpy.ndarray: 膨胀处理后的二值图像,数据类型为uint8,
像素值为0(背景)或255(前景)。
算法原理:
1. 遍历输入图像的每个像素作为结构元素的原点对齐位置
2. 对于每个位置,检查结构元素中所有前景点是否与图像前景像素存在重叠
3. 如果存在至少一个重叠点,则输出图像对应位置设为前景
4. 否则,输出图像对应位置设为背景
边界处理:
当结构元素超出图像边界时,超出部分将被忽略,不影响重叠判断。
结构元素原点:
函数假设结构元素的原点位于矩阵的(2,2)位置(基于0的索引)。
此配置适用于标准的4x4结构元素。
性能说明:
采用四层嵌套循环实现,适用于教学和理解膨胀算法基本原理。
对于大尺寸图像,建议使用优化的形态学库函数。
与腐蚀操作的关系:
膨胀是腐蚀的对偶操作,能够恢复被腐蚀操作去除的部分前景区域,
但无法恢复完全消失的结构信息。
示例:
>>> binary_img = np.array([[255, 0, 255], [0, 255, 0], [255, 0, 255]], dtype=np.uint8)
>>> kernel = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.uint8)
>>> dilated = dilation_manual(binary_img, kernel)
"""
# 获取图像和结构元素尺寸
img_h, img_w = img_binary.shape
kernel_h, kernel_w = kernel.shape
# 定义结构元素原点位置(适用于4x4结构元素)
kernel_origin_y = 2 # 原点行索引
kernel_origin_x = 2 # 原点列索引
# 初始化输出图像(全背景)
dilation_img = np.zeros((img_h, img_w), dtype=np.uint8)
# 遍历图像每个像素作为结构元素对齐点
for i in range(img_h):
for j in range(img_w):
overlap = False # 重叠状态标识
# 检查结构元素是否与图像前景存在重叠
for m in range(kernel_h):
for n in range(kernel_w):
# 跳过结构元素的背景点
if kernel[m, n] == 0:
continue
# 计算图像中对应位置坐标
img_y = i + (m - kernel_origin_y)
img_x = j + (n - kernel_origin_x)
# 检查边界条件
if img_x < 0 or img_x >= img_w or img_y < 0 or img_y >= img_h:
continue
# 检查重叠条件
# 只要结构元素的前景点与图像前景像素重叠即满足条件
if img_binary[img_y, img_x] == 255:
overlap = True
break # 发现重叠即终止当前结构元素检查
if overlap:
break # 已确认重叠,终止外层循环
# 根据重叠结果设置输出像素
if overlap:
dilation_img[i, j] = 255
return dilation_img
# 执行形态学膨胀操作
dilation_result = dilation_manual(erosion_result, kernel)
# 可视化展示处理结果对比
plt.figure(figsize=(10, 4))
# 显示腐蚀结果
plt.subplot(121)
plt.imshow(erosion_result, cmap='gray')
plt.title("腐蚀操作结果")
plt.axis('off')
# 显示膨胀结果
plt.subplot(122)
plt.imshow(dilation_result, cmap='gray')
plt.title("膨胀操作结果")
plt.axis('off')
plt.tight_layout()
plt.show()
综合算法结果
