上篇讲了相机标定的完整流程,从标定板选择、图片拍摄到参数求解和验证。标定之后拿到的是校正过的图像,但这些原始图像通常还需要进一步预处理才能用于后续的视觉算法。
面试时候被问"你做过哪些图像预处理",很多人只会说"去噪和直方图均衡化"。但实际项目中的预处理远不止这些,而且不同的预处理方法选择背后都有讲究。
今天把图像预处理的核心技术系统讲一遍。从去噪、增强到直方图均衡化,每种方法的原理、适用场景和代码实现都讲清楚。
图像去噪
图像在采集和传输过程中不可避免地会引入噪声。常见的噪声类型有两种:高斯噪声和椒盐噪声。高斯噪声是每个像素值都叠加了一个小的随机扰动,看起来图像整体"发灰";椒盐噪声是随机出现的黑白亮点,看起来像老式电视的雪花。
高斯滤波是最基础的去噪方法。用一个卷积核在图像上滑动,每个像素的值变成邻域像素的加权平均。OpenCV里直接用GaussianBlur:
import cv2 img = cv2.imread('noisy.jpg') # 高斯核大小必须是奇数 blurred = cv2.GaussianBlur(img, (5, 5), sigmaX=1.0)高斯滤波的问题是会把边缘也模糊掉。你希望去掉噪声但保留物体的轮廓边界,这时候就需要更智能的方法。
双边滤波(Bilateral Filter)在去噪的同时能很好地保留边缘。它的原理是:不仅考虑像素的空间距离(离得近的权重大),还考虑像素值的相似度(值接近的权重大)。这样在边缘两侧,虽然空间距离近,但像素值差异大,权重就小,边缘就被保留了。
# 双边滤波 # d: 邻域直径,sigmaColor: 颜色空间标准差,sigmaSpace: 坐标空间标准差 denoised = cv2.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75)双边滤波的缺点是速度比较慢,因为每个像素都要计算邻域的权重。实时应用中可以考虑用引导滤波(Guided Filter)替代,效果接近但速度快很多。
非局部均值去噪(Non-local Means)是另一种效果很好的方法。它的思路是:对于图像中的每个小块,在整个图像中搜索相似的块,然后用这些相似块的加权平均来去噪。OpenCV提供了快速版本fastNlMeansDenoising:
# 灰度图去噪 denoised = cv2.fastNlMeansDenoising(gray, h=10, templateWindowSize=7, searchWindowSize=21) # 彩色图去噪 denoised_color = cv2.fastNlMeansDenoisingColored(img, h=10, hForColorComponents=10, templateWindowSize=7, searchWindowSize=21)参数h控制去噪强度,越大去噪越强但细节损失也越多。一般从10开始调,根据效果增减。
中值滤波对椒盐噪声特别有效。它把每个像素的值替换为邻域内所有像素的中值,这样孤立的噪声点(特别亮或特别暗)就被去掉了。
# 中值滤波,核大小5x5 denoised = cv2.medianBlur(img, 5)去噪方法的选择策略
实际项目中,选择哪种去噪方法取决于几个因素:噪声类型、实时性要求、以及后续算法的需求。
如果是高斯噪声(大多数传感器噪声都是这种类型),首选双边滤波或引导滤波,因为它们在去噪的同时能保留边缘。如果对实时性要求很高(比如视频流处理),双边滤波可能太慢,这时候可以用引导滤波或者简化版的双边滤波。
如果是椒盐噪声(比如图像传输中的丢包错误),中值滤波是最直接有效的选择。核大小根据噪声密度来定:噪声稀疏用3x3,噪声密集用5x5或7x7。
如果噪声比较复杂(混合类型),非局部均值去噪通常效果最好,但速度最慢。适合离线处理或者对质量要求极高的场景。
还有一种情况:如果你的后续算法是深度学习模型(比如目标检测、语义分割),预处理策略会有所不同。很多深度学习论文建议不做去噪,让网络自己学习处理噪声。因为去噪过程可能会丢失一些对网络有用的细节信息。但如果噪声特别严重(比如低照度下的高ISO噪声),适当的去噪还是有帮助的。
面试时候如果被问"你怎么选择去噪方法",不要只回答"用XX滤波"。要把选择的理由讲清楚:噪声类型是什么、实时性要求多高、后续算法是什么、去噪效果怎么评估。这种回答才能体现你的工程思维。
图像增强
去噪之后,图像可能还存在对比度低、亮度不合适等问题。图像增强的目的是让图像中的有用信息更突出。
亮度与对比度调整是最基础的操作。OpenCV的convertScaleAbs可以一次性调整:
# alpha控制对比度(>1增加,<1降低),beta控制亮度(正值增亮,负值变暗) adjusted = cv2.convertScaleAbs(img, alpha=1.2, beta=10)但这种方法有个问题:全局调整对比度可能会让某些区域过曝或者欠曝。更好的方法是自适应直方图均衡化,后面会讲。
Gamma校正用于调整图像的非线性亮度。人眼对暗部的变化更敏感,Gamma校正可以增强暗部细节或者压缩亮部细节:
import numpy as np def gamma_correction(img, gamma=1.0): """gamma < 1 增强暗部,gamma > 1 增强亮部""" inv_gamma = 1.0 / gamma table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8") return cv2.LUT(img, table) # 增强暗部细节 brightened = gamma_correction(img, gamma=0.5)Gamma校正在机器人视觉中很常用。比如室外场景中,天空很亮但地面阴影区域很暗,用gamma < 1可以增强阴影区域的细节,让障碍物更容易被检测到。
面试中怎么聊
面试官问图像预处理,按这个框架回答:先说去噪(根据噪声类型选择高斯/双边/非局部均值/中值滤波),再说增强(亮度对比度调整、gamma校正),然后说直方图均衡化(全局vs CLAHE),最后说形态学操作(腐蚀膨胀开闭运算)。
关键是要说出为什么选某种方法,而不是只说"我用了XX滤波"。比如"因为要保留边缘所以用双边滤波","因为图像光照不均匀所以用CLAHE而不是全局均衡化"。这种回答说明你理解每种方法的适用场景和局限性。
如果面试官追问"预处理流程怎么设计",你可以说:先分析图像的主要问题(噪声类型、对比度、光照条件),然后按顺序处理——先去噪,再增强对比度,最后根据后续算法的需要做特定的预处理(比如二值化前的形态学操作)。每一步的效果都要用后续算法的最终性能来评估,不能孤立地看预处理效果。
最后,如果面试官问"你怎么评估预处理的效果",你可以说:预处理本身没有绝对的好坏标准,最终要看对下游任务的影响。比如去噪后目标检测的mAP有没有提升,增强后识别的准确率有没有提高。用量化指标来评估,而不是凭肉眼感觉。
补充一个实用建议:预处理流程中每一步都要保存中间结果,方便对比和调试。我一般会写一个工具函数,输入原始图像,输出每一步处理后的图像并排显示。这样你能直观地看到每一步的效果,快速定位哪一步处理出了问题。特别是在调参阶段,有个可视化的对比工具能省很多时间。面试时候如果你能描述这种工程化的调试习惯,也是加分项。
下一篇讲深度相机原理——结构光、ToF和双目视觉的对比和选型。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列
上一篇:第152篇 相机标定实操——内参/外参/畸变系数的标定流程
下一篇预告:第154篇 深度相机原理对比——结构光/ToF/双目各自优劣
有任何问题欢迎评论区留言,我会尽量回复。