期末考试临近,很多同学在复习《数字图像处理》时都有同感:上课听老师讲概念好像都明白,一到自己做实验、画直方图、写滤波代码,就开始头皮发麻;翻开书又发现知识点又多又碎,空域滤波、频域滤波、图像分割、形态学处理……每章都有考点,却不知道哪些是重点。
这篇文章正是为你准备的。我会结合合肥学院《数字图像处理》课程的常见教学范围,参考冈萨雷斯《数字图像处理》这本经典教材的体系,把整门课的复习框架、核心公式、典型算法、实验代码、期末常见题型全部梳理一遍。不管你是想期末不挂科,还是准备考研复试,这篇文章都能直接当复习提纲用。
为了做到“复习可操作”,我会尽量把每个算法的原理、数学表达、代码实现、适用场景放在一起讲,不搞空对空的概念复述。文末还整理了一份高频考点自查表和一批容易被忽略的细节,帮你考前快速扫描知识盲区。
1. 课程到底在学什么?先建立整体框架
1.1 数字图像处理不是“P图”
很多同学第一次接触这门课时,会误以为数字图像处理就是 Photoshop 修图。这个理解说对了一半,但不完整。
数字图像处理(Digital Image Processing)是用计算机对图像信号进行数字化后,再通过数学和算法手段完成去噪、增强、复原、压缩、分割、特征提取等操作的过程。它处理的对象是“数字图像”,也就是已经被离散化后的二维矩阵数据。
从课程结构上看,合肥学院的《数字图像处理》通常覆盖这样一条主线:
- 图像如何变成数字(采样、量化、像素间关系)
- 图像质量如何改善(灰度变换、直方图处理、空间滤波、频域滤波)
- 图像内容如何恢复(图像复原、退化模型)
- 图像中的物体如何分离(边缘检测、阈值分割、区域分割)
- 图像结构如何分析(形态学处理、描述子)
这个顺序刚好也是冈萨雷斯教材的目录顺序,也是大多数高校期末考试的出题顺序。
1.2 数字图像处理与相关领域的关系
复习前,建议先把一些容易混淆的方向划清楚:
| 方向 | 核心任务 | 典型工具/方法 |
|---|---|---|
| 图像处理 | 图像到图像,改善视觉效果 | 滤波、增强、复原 |
| 图像分析 | 图像到数值/符号,描述图像内容 | 分割、特征提取、测量 |
| 计算机视觉 | 图像到语义理解,感知三维世界 | 目标检测、识别、三维重建 |
| 机器学习/深度学习 | 数据驱动模型替代手工设计 | CNN、Transformer |
期末课程重点在前两项。考试中如果出现“图像处理与计算机视觉的区别和联系”,能用自己的话说清楚即可,不必过度展开。
理解这层关系还有一个好处:考研复试时老师很喜欢问“你对数字图像处理这个方向的理解”,你如果能从“底层处理到高层理解”这个层次讲清楚,面试印象会好很多。
1.3 为什么很多同学觉得这门课难
《数字图像处理》的难点不在代码,而在数学。卷积、傅里叶变换、概率统计这些大一、大二学过的数学工具,到了这门课里突然全部“活”了过来:
- 空间滤波的本质是卷积运算;
- 频域滤波的本质是傅里叶变换与频域乘法;
- 图像复原里的维纳滤波,需要高斯噪声模型与最小均方误差估计;
- 图像分割里的Otsu阈值,要用类间方差最大化。
所以复习这门课,我建议的顺序是:先理解物理含义,再看数学表达,最后回到代码验证。如果你一上来就死记公式,考试时换个条件你就不会了。
2. 复习环境准备:把 Python + OpenCV 搭好
复习数字图像处理,动手实验比只看书有效得多。期末前你至少需要能把书本上的直方图均衡化、中值滤波、Canny边缘检测这些基础实验跑通。本文代码统一用 Python + OpenCV。
2.1 环境版本建议
版本不一定必须和我完全一致,只要能运行 OpenCV 即可。下面是我建议的基础环境:
操作系统:Windows 10/11 或 Ubuntu 20.04/22.04 Python:3.8 ~ 3.11 OpenCV:4.5.0 及以上 NumPy:1.21 及以上 Matplotlib:3.5 及以上如果你的电脑上还没装 Python 环境,可以安装 Anaconda,然后使用 conda 创建独立环境,避免把系统环境搞乱。
2.2 创建虚拟环境与安装依赖
打开终端(Windows 下可以使用 Anaconda Prompt),依次输入下面命令:
conda create -n dip python=3.9 -y conda activate dip pip install opencv-python opencv-contrib-python numpy matplotlib安装完成后验证一下:
import cv2 import numpy as np import matplotlib.pyplot as plt print("OpenCV 版本:", cv2.__version__) print("NumPy 版本:", np.__version__)能够正常输出版本号,说明环境已经可以用了。
2.3 关于实验图片的准备
复习时建议准备两张图:
- 一张灰度图,例如
lena.png或cameraman.tif,用于灰度变换、直方图、滤波实验; - 一张有噪声的图,可以自己给原图添加高斯噪声或椒盐噪声,用于复原实验。
如果没有标准测试图,也可以用下面代码生成一张测试图:
import cv2 import numpy as np img = np.zeros((256, 256), dtype=np.uint8) img[50:200, 50:200] = 200 img = cv2.GaussianBlur(img, (5, 5), 0) # 添加椒盐噪声 np.random.seed(0) noise = np.random.rand(256, 256) img_noise = img.copy() img_noise[noise < 0.05] = 0 img_noise[noise > 0.95] = 255 cv2.imwrite("test_image.png", img_noise) print("测试图已生成")代码中的GaussianBlur是高斯平滑,用于模拟模糊退化,后续做图像复原实验可以直接用这张图。
3. 核心考点分块拆解:从图像数字化到频域处理
下面,我按照期末复习的常见顺序,把必考的核心知识分成若干个模块,每个模块讲清楚三件事:它是什么、它的核心公式/方法是什么、考试通常怎么出题。
3.1 图像数字化:采样、量化与像素关系
这是课程第一章内容,概念性强,小题频出。
什么是采样与量化
模拟图像要变成数字图像,必须经过两个步骤:
- 采样:在空间上对连续的图像函数进行离散化,即用有限个点表示图像。
- 量化:把采样点上的连续灰度值取整成有限个离散级别。
这两个概念常考简答题,例如“说明采样和量化的含义及它们对图像质量的影响”。
数字图像表示
一幅大小为 M×N 的灰度图,可以用矩阵表示为:
| f(0,0) f(0,1) ... f(0,N-1) | f = | f(1,0) f(1,1) ... f(1,N-1) | | ... ... ... ... | | f(M-1,0) f(M-1,1) ... f(M-1,N-1) |灰度级通常取 2 的整数次幂,常见 256 级,即每个像素占 8 bit。存储一幅 M×N、灰度级为 2^k 的图像需要的比特数:
b = M × N × k这是期末计算题的常客。比如:一幅 1024×768、256 级灰度的图像占多少比特?
b = 1024 × 768 × 8 = 6,291,456 bit = 786,432 Byte = 768 KB注意单位换算不要出错,尤其是“位”和“字节”的区别。
像素间的基本关系
这一节里最容易出选择题或简单计算题的是“像素间距离”的定义。
- 欧氏距离(Euclidean Distance):实际几何距离。
- 城区距离(City-Block Distance,D4):水平/垂直方向步行距离。
- 棋盘距离(Chessboard Distance,D8):8 邻域内前进所需步数。
定义公式为:
D4 = |x - s| + |y - t| D8 = max(|x - s|, |y - t|)例如,坐标 (2,3) 与 (5,7) 之间的 D4 距离为 |2-5|+|3-7| = 7,D8 距离为 max(3, 4) = 4。
相邻像素
4-邻域、8-邻域、对角邻域也是高频选择题,画图记忆即可。
3.2 灰度变换与直方图处理:最实用的入门模块
这一章是大部分同学第一次接触“增强”的地方,也是实验课最常安排的内容。
灰度变换的常见函数
图像增强中常用的灰度变换函数有:
- 线性变换:
s = a * r + b,其中 r 是原灰度,s 是变换后灰度。 - 对数变换:
s = c * log(1 + r),常用于扩展暗部细节。 - 幂次变换:
s = c * r^γ,γ<1 扩展暗部,γ>1 扩展亮部。 - 反转变换:
s = L - 1 - r,相当于负片效果。
复习时要能根据题意判断选用哪个函数。比如题目说“图像暗部细节不清楚,应该用什么变换来增强?”,优先考虑对数变换或 γ<1 的幂次变换。
直方图的概念
灰度直方图是一个一维离散函数,表示图像中每种灰度级出现的像素个数:
h(r_k) = n_k其中 r_k 是第 k 级灰度,n_k 是灰度级为 r_k 的像素数量。
直方图能反映图像的对比度特征:
- 直方图窄且集中在低灰度区域:图像偏暗。
- 直方图窄且集中在高灰度区域:图像偏亮。
- 直方图成分分布在较宽灰度范围但不均匀:对比度较低。
- 直方图覆盖整个灰度范围且分布均匀:对比度高,视觉效果较好。
直方图均衡化(必考)
直方图均衡化是最重要、最高频的考点。它的目标是把原始图像的直方图通过灰度变换,调整为近似均匀分布,从而提高图像对比度。
核心步骤如下:
计算原始图像的灰度直方图,得到各灰度级概率 p(r_k) = n_k / (M×N)。
计算累积分布函数(CDF):
s_k = (L - 1) * Σ_{j=0}^{k} p(r_j)对 s_k 取整,映射到新的灰度值。
期末计算题常给一张 4×4 或 8×8 的小图,让你手动计算均衡化后的灰度分布。只要按照上面步骤一步步写,不太会出错。建议考前至少徒手做一遍。
我在第 4 节会给出直方图均衡化的完整代码和手动推算示例。
直方图匹配(规定化)
与均衡化不同,直方图匹配是希望变换后图像的直方图接近预先指定的形状。理解它和均衡化的区别即可,考代码的概率低。
3.3 空间滤波:卷积的直观入门
空间滤波是第二章的重点,几乎必考。它的数学本质是“模板卷积”,但你可以先把它理解成一个滑动窗口对像素邻域做加权平均。
空间滤波表达式
设图像 f 大小为 M×N,滤波器模板 w 大小为 m×n(通常 m、n 为奇数),则在点 (x,y) 处的滤波结果为:
g(x,y) = Σ_{s=-a}^{a} Σ_{t=-b}^{b} w(s,t) * f(x+s, y+t)其中 a=(m-1)/2,b=(n-1)/2,也就是模板中心对准正在处理的像素。
平滑滤波:均值模糊与高斯模糊
均值滤波是取邻域内像素的均值作为输出,主要用于模糊图像、降低噪声。
高斯滤波是加权平均,越靠近中心点的像素权重越大。3×3 高斯模板常见的近似形式为:
| 1 2 1 | 1/16 | 2 4 2 | | 1 2 1 |高斯滤波相比均值滤波能更好地保留边缘信息,这也是 OpenCV 中GaussianBlur被广泛使用的原因。
统计排序滤波:中值滤波
中值滤波的原理很简单:把邻域内的像素灰度值排序,取中间值作为结果。它对抗椒盐噪声效果特别好,因为在排序后,孤立的极值噪声会被“挤到两端”。
考试常问:“中值滤波适合去除什么噪声?为什么?”答案是:椒盐噪声,因为椒盐噪声是以黑白点的形式出现,与邻域差异极大,排序取中值后基本可以消除。
锐化滤波:拉普拉斯算子
锐化滤波器用于突出图像的边缘和细节。拉普拉斯算子是最常用的二阶微分算子。常见的 3×3 拉普拉斯模板有两种形式:
| 0 -1 0 | | -1 -1 -1 | | -1 4 -1 | | -1 8 -1 | | 0 -1 0 | | -1 -1 -1 |如果直接把拉普拉斯结果叠加到原图,可以实现锐化增强:
g(x,y) = f(x,y) + c * [∇² f(x,y)]其中若使用中心系数为正的模板,c 通常取 1;若使用中心系数为负的模板,c 通常取 -1。
空间滤波做题提示
空间滤波题目经常给一个小矩阵和一个模板,问你“该模板输出中心的像素值是多少”。这类题只要记得“模板中心对应待处理像素,模板系数与图像对应位置相乘再求和”,一般不会错。计算时注意模板边界是否需要补 0,通常如果没有特殊说明,默认不处理边界或补 0。
3.4 频域滤波:傅里叶变换的直观理解
频域滤波是很多人的畏难点,因为傅里叶变换的数学公式看起来不友好。但从考试角度看,理解以下内容基本就够应付大部分题目了。
二维离散傅里叶变换(2D DFT)
二维离散傅里叶变换公式:
F(u,v) = Σ_{x=0}^{M-1} Σ_{y=0}^{N-1} f(x,y) * e^(-j2π(ux/M + vy/N))这个公式不需要你背得很痛苦,但你要知道它做了什么:把图像从空间域变换到频率域。低频对应图像中灰度变化缓慢的区域,高频对应边缘、细节和噪声。
频域滤波的基本步骤
标准流程如下:
- 对输入图像做傅里叶变换:F(u,v) = DFT[f(x,y)]。
- 将 F(u,v) 中心化(把低频移到频谱中心)。
- 用滤波器函数 H(u,v) 乘以频谱:G(u,v) = H(u,v) · F(u,v)。
- 对 G(u,v) 做傅里叶逆变换,取实部得到滤波后图像。
这个过程常出现在简答题或实验报告中,要求你描述频域高通/低通滤波的处理流程。
低通、高通、带通与带阻
- 低通滤波器:保留低频、抑制高频,作用是平滑图像、去噪,代价是边缘变模糊。
- 高通滤波器:保留高频、抑制低频,作用是锐化边缘,代价是丢失平坦区域信息。
- 带通滤波器:保留特定频带。
- 带阻滤波器:抑制特定频带,常用于去除周期性噪声。
常见的几种滤波器函数包括理想滤波器、巴特沃斯滤波器和高斯滤波器。其中,理想滤波器由于在截止频率处突变,会产生振铃效应;高斯滤波器过渡平滑,不会出现振铃。这个结论常考。
3.5 图像复原:与图像增强的爱恨纠葛
图像复原与图像增强的目标看起来很像,都是“改善图像质量”,但两者有本质区别:
- 图像增强是主观过程,目标是让图像“看起来更好”,不追究图像为什么变差。
- 图像复原是客观过程,需要建立图像退化模型,并根据退化原因进行逆操作。
期末简答题如果问“图像增强与图像复原的区别”,就按这个思路回答。
图像退化模型
图像退化模型可以写为:
g(x,y) = h(x,y) * f(x,y) + η(x,y)其中 h(x,y) 是退化函数(点扩散函数),η(x,y) 是加性噪声,* 是卷积。图像复原的目标就是在已知 g 和 h、η 的情况下,尽可能恢复 f。
逆滤波与维纳滤波
- 逆滤波:直接用频域除法恢复,公式为 F̂(u,v) = G(u,v) / H(u,v)。但 H(u,v) 在零点附近很小,噪声会被无限放大,因此实际很少直接使用。
- 维纳滤波:在最小均方误差准则下,兼顾去卷积与噪声抑制,公式带有一个噪声相关的参数项,对噪声有更好的鲁棒性。
理解“逆滤波为什么不可行,维纳滤波如何改进”这个逻辑链,就能应对大多数图像复原简答题。
常见噪声模型
高斯噪声、椒盐噪声(脉冲噪声)、均匀噪声等名称和特点要能辨析。噪声参数估计和滤波选择也是考研选择题的常见素材。
3.6 彩色图像处理:会做通道拆分即可
这一章在期末中占比通常不大,重点是彩色模型。
需要掌握:
- RGB 模型用红绿蓝三通道表示颜色;
- HSI 模型用色调、饱和度、强度表示颜色,更符合人的视觉感知;
- CMY/CMYK 用于打印;
- 灰度图转彩色是伪彩色处理。
实验题常要求拆分 RGB 三通道并显示。这个用 OpenCV 做非常简单,后面实战部分会演示。
3.7 形态学图像处理:结构元素去“修”二值图
形态学处理通常以二值图像为对象,核心操作有膨胀、腐蚀、开运算、闭运算。
腐蚀与膨胀
腐蚀是取结构元素覆盖区域内的最小值,等价于“缩小”前景区域,能去除小且细的噪声点。膨胀是取最大值,等价于“扩大”前景区域,能填补前景中的小孔。
开运算与闭运算
- 开运算 = 先腐蚀后膨胀,作用是去除细小物体、分离相连物体。
- 闭运算 = 先膨胀后腐蚀,作用是填充小孔、连接邻近物体。
这两个“先后顺序”很容易记混,我提供一个记忆方法:开运算的“开”是先腐蚀,像把物体拆开;闭运算的“闭”是先膨胀,像把缺口闭合。
公式记忆:
A ∘ B = (A ⊖ B) ⊕ B // 开运算 A • B = (A ⊕ B) ⊖ B // 闭运算其中 ⊕ 表示膨胀,⊖ 表示腐蚀。
3.8 图像分割:从边缘到区域
图像分割是期末和考研的重点模块,公式不算多,但算法概念非常多。
边缘检测
边缘是图像灰度发生剧烈变化的地方。常用算子有:
- Roberts 算子:计算对角线方向差分,对噪声敏感。
- Prewitt 算子:利用 3×3 模板做一阶微分。
- Sobel 算子:结合平滑与微分的 3×3 模板,是 Prewitt 的改进。
- Canny 算子:最经典的边缘检测算法,分多步完成:高斯滤波去噪 → 计算梯度幅值和方向 → 非极大值抑制 → 双阈值检测和边缘连接。
考试常让你写出 Sobel 的水平/垂直模板,或者描述 Canny 边缘检测的步骤。后者一定会考,务必背熟。
阈值分割
阈值分割是最简单的图像分割方法。全局阈值:选取一个阈值 T,将像素分为目标和背景。如果能通过直方图的波谷确定阈值,称为峰谷法。
Otsu 算法(大津法)是期末热点。它的思想是:在 0~255 内遍历每个灰度级作为阈值,计算目标与背景的类间方差,取类间方差最大时的灰度值作为最优阈值。
Otsu 的实现代码比较简单,下面的实验实战部分会给出完整示例。考试可能会手动让你算两类的均值和概率,然后求类间方差,准备时应掌握公式:
σ_B² = ω0 * ω1 * (μ0 - μ1)²其中 ω0、ω1 分别是目标和背景像素占比,μ0、μ1 分别是目标与背景的平均灰度。
区域分割
区域生长、区域分裂合并是另一类分割方法。概念题为主,需要理解“从种子点出发,按相似性准则逐步扩展区域”的基本思想。
4. 期末必会实验:手把手带你写代码
期末复习阶段,如果能独立完成以下 4 个小实验,你对这门课的理解会明显提升。每个实验我都给出可直接运行的代码,建议在 Jupyter Notebook 或 PyCharm 里逐个跑一遍。
4.1 实验一:直方图均衡化
直方图均衡化是最常考的实验,OpenCV 中已经有了现成函数cv2.equalizeHist,但为了理解原理,我推荐先自己实现一遍,再用现成函数验证。
# 文件路径:lab01_hist_equalization.py import cv2 import numpy as np import matplotlib.pyplot as plt # 1. 读取灰度图 img = cv2.imread("test_image.png", cv2.IMREAD_GRAYSCALE) if img is None: print("请确认图片路径是否正确") exit() # 2. 手动实现直方图均衡化 def my_equalize_hist(image): h, w = image.shape total = h * w # 统计灰度直方图 hist = np.zeros(256, dtype=np.float64) for i in range(h): for j in range(w): hist[image[i, j]] += 1 # 计算累积分布函数并映射 lut = np.zeros(256, dtype=np.uint8) accum = 0.0 for k in range(256): accum += hist[k] # L-1=255 lut[k] = int(255 * accum / total + 0.5) # 进行灰度映射 result = lut[image] return result # 3. 对比 OpenCV 自带的均衡化函数 manual_result = my_equalize_hist(img) opencv_result = cv2.equalizeHist(img) # 4. 绘制原图、结果图和直方图 plt.figure(figsize=(12, 8)) plt.subplot(3, 2, 1) plt.imshow(img, cmap="gray") plt.title("原始图像") plt.axis("off") plt.subplot(3, 2, 2) plt.imshow(manual_result, cmap="gray") plt.title("手动均衡化结果") plt.axis("off") plt.subplot(3, 2, 3) plt.imshow(opencv_result, cmap="gray") plt.title("OpenCV 均衡化结果") plt.axis("off") plt.subplot(3, 2, 4) plt.hist(img.ravel(), bins=256, range=[0, 256], color="blue", alpha=0.7) plt.title("原始直方图") plt.subplot(3, 2, 5) plt.hist(manual_result.ravel(), bins=256, range=[0, 256], color="red", alpha=0.7) plt.title("均衡化后直方图") plt.tight_layout() plt.show()运行这段代码,你会明显看到输出图像的直方图分布比原来更平缓,对比度更高。如果原图偏暗,效果会更明显。
这里需要注意边界像素的取值问题:手动实现时如果累加值乘以 255 后取整,应与 OpenCV 结果基本一致。若不完全相同,通常是舍入方式差异造成的,不影响理解。
4.2 实验二:空间滤波对比
这个实验用来直观感受均值滤波、高斯滤波、中值滤波在不同噪声下的效果差异。重点是理解三种滤波器的适用场景。
# 文件路径:lab02_spatial_filter.py import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图片并转为灰度 img = cv2.imread("test_image.png", cv2.IMREAD_GRAYSCALE) if img is None: print("请确认图片路径是否正确") exit() # 添加高斯噪声 def add_gaussian_noise(image, mean=0, sigma=20): noise = np.random.normal(mean, sigma, image.shape) noisy = image + noise return np.clip(noisy, 0, 255).astype(np.uint8) # 添加椒盐噪声 def add_salt_pepper_noise(image, prob=0.05): noisy = image.copy() h, w = image.shape for i in range(h): for j in range(w): rand = np.random.rand() if rand < prob / 2: noisy[i, j] = 0 elif rand < prob: noisy[i, j] = 255 return noisy gaussian_noisy = add_gaussian_noise(img) sp_noisy = add_salt_pepper_noise(img) # 各种滤波 mean_filtered_g = cv2.blur(gaussian_noisy, (3, 3)) gaussian_filtered_g = cv2.GaussianBlur(gaussian_noisy, (3, 3), 0) median_filtered_g = cv2.medianBlur(gaussian_noisy, 3) mean_filtered_sp = cv2.blur(sp_noisy, (3, 3)) gaussian_filtered_sp = cv2.GaussianBlur(sp_noisy, (3, 3), 0) median_filtered_sp = cv2.medianBlur(sp_noisy, 3) # 展示结果 plt.figure(figsize=(12, 9)) plt.subplot(3, 3, 1) plt.imshow(gaussian_noisy, cmap="gray") plt.title("高斯噪声图") plt.axis("off") plt.subplot(3, 3, 2) plt.imshow(mean_filtered_g, cmap="gray") plt.title("均值滤波 - 高斯噪声") plt.axis("off") plt.subplot(3, 3, 3) plt.imshow(gaussian_filtered_g, cmap="gray") plt.title("高斯滤波 - 高斯噪声") plt.axis("off") plt.subplot(3, 3, 4) plt.imshow(median_filtered_g, cmap="gray") plt.title("中值滤波 - 高斯噪声") plt.axis("off") plt.subplot(3, 3, 5) plt.imshow(sp_noisy, cmap="gray") plt.title("椒盐噪声图") plt.axis("off") plt.subplot(3, 3, 6) plt.imshow(mean_filtered_sp, cmap="gray") plt.title("均值滤波 - 椒盐噪声") plt.axis("off") plt.subplot(3, 3, 7) plt.imshow(gaussian_filtered_sp, cmap="gray") plt.title("高斯滤波 - 椒盐噪声") plt.axis("off") plt.subplot(3, 3, 8) plt.imshow(median_filtered_sp, cmap="gray") plt.title("中值滤波 - 椒盐噪声") plt.axis("off") plt.subplot(3, 3, 9) plt.imshow(cv2.medianBlur(sp_noisy, 5), cmap="gray") plt.title("中值滤波(5x5) - 椒盐噪声") plt.axis("off") plt.tight_layout() plt.show()运行结果会告诉你一个重要结论:去除椒盐噪声时,中值滤波效果最好;去除高斯噪声时,高斯滤波效果优于均值滤波;均值滤波会把边缘变得模糊,而中值滤波在去除椒盐噪声时细节保持相对更好。
4.3 实验三:Canny 边缘检测
Canny 边缘检测需要先知道两个核心参数:threshold1和threshold2。threshold2用于检测强边缘,threshold1用于连接弱边缘。实际应用中,这两个值通常按 1:2 或 1:3 的比例设置。
# 文件路径:lab03_canny_edge.py import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("test_image.png", cv2.IMREAD_GRAYSCALE) if img is None: print("请确认图片路径是否正确") exit() # 使用 Canny 边缘检测 edges1 = cv2.Canny(img, 50, 150) edges2 = cv2.Canny(img, 100, 200) edges3 = cv2.Canny(img, 30, 100) # 对比不同阈值的影响 plt.figure(figsize=(12, 4)) plt.subplot(1, 4, 1) plt.imshow(img, cmap="gray") plt.title("原始图像") plt.axis("off") plt.subplot(1, 4, 2) plt.imshow(edges1, cmap="gray") plt.title("Canny(50, 150)") plt.axis("off") plt.subplot(1, 4, 3) plt.imshow(edges2, cmap="gray") plt.title("Canny(100, 200)") plt.axis("off") plt.subplot(1, 4, 4) plt.imshow(edges3, cmap="gray") plt.title("Canny(30, 100)") plt.axis("off") plt.tight_layout() plt.show() print("Canny 边缘检测完成")观察输出可以发现:阈值设置越高,检测出的边缘越少,但留下的边缘通常更清晰;阈值设置越低,细小边缘和噪声点也会被当作边缘检测出来。
考场上如果让你描述 Canny 算法的步骤,记住五个阶段即可:
- 高斯滤波平滑图像,减少噪声;
- 计算梯度幅值和方向(通常用 Sobel 算子);
- 对梯度幅值做非极大值抑制;
- 双阈值检测,将像素分为强边缘、弱边缘和非边缘;
- 通过滞后连接,将弱边缘与强边缘连接的像素保留。
4.4 实验四:Otsu 自适应阈值分割
Otsu 算法是图像分割中最重要的自适应阈值方法,不需要人为指定阈值,算法自动寻找类间方差最大的灰度值。
# 文件路径:lab04_otsu.py import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("test_image.png", cv2.IMREAD_GRAYSCALE) if img is None: print("请确认图片路径是否正确") exit() # 手动实现 Otsu def my_otsu(image): hist = cv2.calcHist([image], [0], None, [256], [0, 256]).ravel() hist = hist / image.size # 归一化为概率 best_thresh = 0 best_var = 0.0 for t in range(256): # 背景类:0 ~ t w0 = hist[:t+1].sum() # 目标类:t+1 ~ 255 w1 = hist[t+1:].sum() if w0 == 0 or w1 == 0: continue # 均值计算 mu0 = np.sum(np.arange(0, t+1) * hist[:t+1]) / w0 mu1 = np.sum(np.arange(t+1, 256) * hist[t+1:]) / w1 # 类间方差 var_between = w0 * w1 * (mu0 - mu1) ** 2 if var_between > best_var: best_var = var_between best_thresh = t return best_thresh # 计算阈值 my_thresh = my_otsu(img) _, cv_thresh_result = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) _, my_thresh_result = cv2.threshold(img, my_thresh, 255, cv2.THRESH_BINARY) print("手动 Otsu 阈值:", my_thresh) print("OpenCV Otsu 阈值:", cv_thresh_result) # 可视化 plt.figure(figsize=(12, 4)) plt.subplot(1, 3, 1) plt.imshow(img, cmap="gray") plt.title("原始图像") plt.axis("off") plt.subplot(1, 3, 2) plt.imshow(cv_thresh_result, cmap="gray") plt.title("OpenCV Otsu 分割") plt.axis("off") plt.subplot(1, 3, 3) plt.imshow(my_thresh_result, cmap="gray") plt.title(f"手动 Otsu 分割 (T={my_thresh})") plt.axis("off") plt.tight_layout() plt.show()手动实现的 Otsu 遍历了 0 到 255 每一个灰度级,计算每个阈值对应的类间方差。类间方差越大,说明前景背景分离得越彻底。实际工程中用cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)一行代码即可完成,但了解底层计算逻辑,在考试中更重要。
4.5 综合练习:图像增强与分割的流水线
如果上面 4 个实验都能独立完成,可以试着把多个知识点串成一个完整任务:
# 文件路径:lab05_enhance_and_segment.py """ 任务:对一张含噪图像完成以下流程: 1. 添加椒盐噪声; 2. 使用中值滤波去噪; 3. 使用直方图均衡化增强对比度; 4. 使用 Otsu 方法分割出主要物体。 """ import cv2 import numpy as np import matplotlib.pyplot as plt np.random.seed(42) # 生成原始图像(模拟前景与背景) img = np.zeros((256, 256), dtype=np.uint8) cv2.circle(img, (128, 128), 60, 200, -1) cv2.rectangle(img, (30, 30), (90, 90), 120, -1) # 添加椒盐噪声 def add_salt_pepper_noise(image, prob=0.05): noisy = image.copy() h, w = image.shape mask = np.random.rand(h, w) noisy[mask < prob / 2] = 0 noisy[mask > 1 - prob / 2] = 255 return noisy noisy_img = add_salt_pepper_noise(img) # 中值滤波去噪 denoised = cv2.medianBlur(noisy_img, 5) # 直方图均衡化增强 enhanced = cv2.equalizeHist(denoised) # Otsu 分割 _, segmented = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 绘制结果 plt.figure(figsize=(12, 4)) plt.subplot(1, 4, 1) plt.imshow(img, cmap="gray") plt.title("原始图像") plt.axis("off") plt.subplot(1, 4, 2) plt.imshow(noisy_img, cmap="gray") plt.title("添加椒盐噪声") plt.axis("off") plt.subplot(1, 4, 3) plt.imshow(enhanced, cmap="gray") plt.title("中值滤波 + 均衡化") plt.axis("off") plt.subplot(1, 4, 4) plt.imshow(segmented, cmap="gray") plt.title("Otsu 分割结果") plt.axis("off") plt.tight_layout() plt.show()对简单的二值图来说,中值滤波去噪效果非常明显,均衡化可以增强微弱区域,Otsu 能自动将物体从背景中分离出来。这套流水线也是很多图像预处理任务的基础流程。
5. 考研与期末的区别:复习侧重点提示
5.1 期末重计算,考研重原理
如果是准备合肥学院的期末考试,我的建议是:课后习题一定要自己动手算。尤其是直方图均衡化的手算过程、空间滤波的模板计算、距离度量计算,这些题目只要练熟了就是送分题。
如果是准备考研,题目往往更侧重“算法设计思想”和“方法对比分析”。比如:
- 为什么频域滤波会出现振铃效应?
- 图像复原与图像增强在目标和方法上有什么区别?
- 中值滤波为什么会破坏图像的细线结构?
- 与一阶微分算子相比,拉普拉斯算子对噪声更敏感的原因是什么?
这些问题不是背诵能解决的,需要你在平时的实验中去观察现象、思考原因。比如你在实验二里实际跑过中值滤波,就会注意到它虽然能去椒盐噪声,但图像中细小的线状结构会因为排序操作被“抹平”,这就是中值滤波的局限性。
5.2 图像处理“2026 年新应用”热点的理解
复习时如果还有余力,可以稍微关注一下数字图像处理在医学影像、自动驾驶、工业检测、遥感图像分析中的最新应用。复试中老师可能通过“你知道数字图像处理目前有哪些新应用”这类问题来考察你对学科前沿的兴趣。
目前比较常见的方向包括:
- 医学影像中的图像分割与病灶检测;
- 自动驾驶场景下的道路分割与目标检测;
- 工业质检中的表面缺陷检测;
- 手机摄影中的超分辨率与 HDR 合成;
- 遥感图像中的地物分类与变化检测。
值得强调的是,传统数字图像处理仍然是这些应用的基础。深度学习解决的是“特征自动提取”的问题,但数据预处理、边缘检测、形态学后处理,仍然大量依赖你在本科学到的经典图像处理方法。
6. 实验与代码常见问题排查
6.1 OpenCV 读取图片后显示一片空白
现象:用 matplotlib 显示 cv2.imread 读取的“彩色图”时,颜色完全不对,例如红蓝颠倒。
原因:OpenCV 默认使用 BGR 通道顺序,而 matplotlib 使用 RGB 顺序。如果直接读取彩色图并用 matplotlib 展示,就会出现通道顺序错乱。
解决:把 BGR 转为 RGB 再显示。
img_bgr = cv2.imread("image.jpg") img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)6.2 图片路径包含中文导致读取失败
现象:cv2.imread返回 None。
原因:OpenCV 的 imread 函数对中文路径支持不友好。
解决:先使用imdecode方式读取。
import cv2 import numpy as np def cv_imread(file_path): """ 解决 OpenCV 读取中文路径返回 None 的问题 """ data = np.fromfile(file_path, dtype=np.uint8) img = cv2.imdecode(data, cv2.IMREAD_COLOR) if img is None: img = cv2.imdecode(data, cv2.IMREAD_GRAYSCALE) return img6.3 灰度图显示为绿色或伪彩色
现象:plt.imshow(img, cmap="gray")后发现显示的是绿色图像。
原因:matplotlib 在使用 imshow 时如果没有指定 vmin 和 vmax,可能自动调整显示范围。此外,如果传入的数组维度不是二维,而是三通道,cmap 参数不生效。
解决:确认传入的是二维数组,必要时打印 img.shape 查看维度。
print(img.shape)如果是(height, width, 3),需要先转成灰度。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)6.4 手动实现 Otsu 与 OpenCV 结果不一致
现象:自己写的 Otsu 阈值和cv2.threshold的 Otsu 阈值略有出入。
原因:OpenCV 内部的阈值计算可能使用了不同的等号判定或浮点数精度处理。
解决:如果你的训练结果和 OpenCV 相差 1 个灰度级以内,通常属于正常范围,不需要过度纠结。重点是理解类间方差最大化的思想。
6.5 Canny 边缘检测出现大量短线噪声
现象:边缘图中除了目标边缘,还有大量杂乱短线。
原因:阈值设置过低,或者原图噪声没有去除完全。
解决:先做一次高斯滤波,再调大 Canny 阈值。
blurred = cv2.GaussianBlur(img, (5, 5), 0) edges = cv2.Canny(blurred, 80, 200)7. 期末高频考点自查表与复习策略
7.1 高频考点一览
下面这张表是我的建议复习清单,你可以逐条打勾:
| 章节 | 高频考点 | 掌握程度 |
|---|---|---|
| 图像数字化 | 采样与量化概念、存储容量计算、像素间距离 | 必会 |
| 灰度变换 | 对数/幂次变换选择、图像反转变换 | 必会 |
| 直方图 | 直方图含义、均衡化手算步骤 | 必会 |
| 空间滤波 | 卷积计算、均值/中值/高斯选择、拉普拉斯锐化 | 必会 |
| 频域滤波 | DFT 流程、低通/高通/带阻作用、振铃效应 | 重点 |
| 图像复原 | 退化模型、逆滤波缺陷、维纳滤波思想 | 重点 |
| 彩色图像 | RGB/HSI 模型、伪彩色处理 | 了解 |
| 形态学 | 腐蚀/膨胀、开/闭运算 | 必会 |
| 图像分割 | 梯度算子、Canny 步骤、Otsu 思想、区域生长 | 必会 |
| 图像压缩/编码(如有) | 冗余概念、无损/有损压缩、JPEG 原理 | 了解 |
7.2 考前两周怎么复习效率最高
这里分享一个比较实用的三轮复习法,适合完整跟完课程但还没系统复习的同学:
第一轮(3~4 天):按章节过教材和课件,只梳理概念、公式和框图。遇到看不懂的公式先跳过,不恋战。
第二轮(5~7 天):专攻计算题和代码题。把直方图均衡化、空间滤波、边缘检测、Otsu 这些典型题目用手算一遍,再用代码验证一遍。考研的同学这个阶段需要补充推导能力。
第三轮(2~3 天):集中背诵概念性考点,包括各种算法的优点缺点、算子之间的区别、图像增强与复原的差异等。同时把自己的实验报告拿出来重新过一遍,尤其是老师实验课上强调过的结论。
三轮结束以后,如果还有时间,可以做 1~2 套往年试卷或课后重点题。不要盲目刷题,这门课更注重“算法思想是否梳理得清楚”。
7.3 考场上的注意细节
期末考场上,图像处理的计算题很容易因为符号写错而丢分。这里给你几个提醒:
- 均衡化计算时,累积概率乘以 (L-1) 后要取整,不要四舍五入到 256 或 0 之外。
- 卷积计算时,先把模板旋转 180° 再滑动相乘,才是严格意义上的卷积。不过因为大多数模板是对称的,这一步通常被省略,但如果模板不对称,要留意题目考查的是“卷积”还是“相关”。
- 写简答题时,先写定义,再写公式,最后写优缺点或适用场景。分点回答更容易拿全分。
- 拉普拉斯模板不是唯一的,不同教材中心系数可能是 4 或 8,做题前先看模板。
7.4 考研党的后续学习建议
如果你是为了考研复试准备这门课,建议在期末复习之后,再往前多看一步:
- 读冈萨雷斯教材的英文版术语,因为复试时老师可能会直接说英文术语,例如 spatial filtering、histogram equalization、morphological processing。
- 了解 OpenCV 官方文档中的基本 API,面试时如果涉及项目,你至少能说清楚图像处理代码的基本流程。
- 找一个你感兴趣的小项目做透,比堆砌十个简单实验更有价值。例如“手机拍照文档去阴影”“票据关键区域定位”,一个完整项目可以同时覆盖预处理、分割、形态学、特征提取等多个知识模块。
复试被问到本科实验时,最忌讳的回答是“老师让我们做,我就照着做了”。如果你能说清楚每个步骤的目的、参数调整的依据、遇到问题后如何排查,面试官就会认为你真正掌握这门课。
8. 写在最后:数字图像处理是“越学越有用”的课
《数字图像处理》看起来是一门偏理论的课程,但它的每个知识点都对应实际场景:手机拍照去噪,靠的是空间滤波;医学影像增强,靠的是直方图处理;工业零件检测,靠的是边缘检测与形态学;自动文档扫描,靠的是阈值分割与透视校正。
如果这学期你只是跟班听了一遍,现在正好通过复习把碎片知识串起来。建议你打开电脑,把上面的实验代码逐个运行一遍,边跑边在纸上画一画每个算法到底做了什么,你会发现这门课程远没有想象中那么难。
本文的代码你可以直接复制到本机运行,实验图片换成自己的一张普通照片也能观察效果。对于更复杂的方向,例如深度学习图像分割、生成对抗网络图像修复,也建议在打好经典图像处理基础后再去接触,那时你会更快上手,也更加明白经典方法的价值所在。
如果这篇文章对你的期末复习或考研准备有帮助,收藏备用即可。祝你在《数字图像处理》考试中拿到满意成绩。