第二章:图像采集与预处理——摄像头选型、标定与滤波增强
各位同学,大家好。今天我们进入一个非常实战的环节——图像采集与预处理。说白了,就是解决“怎么把物理世界的光信号,变成算法能啃的数字矩阵”。
我做了这么多年视觉项目,发现一个规律:80%的跟踪失败,根源不在算法,而在前端的图像质量。你想想看,如果摄像头选错了,或者图像全是噪声,再牛的卡尔曼滤波也救不回来。所以这一章,咱们把地基打牢。
2.1 摄像头选型:别让硬件拖后腿
选摄像头,不是参数越高越好。我见过有人用工业相机拍传送带上的药瓶,结果帧率太高,数据量把工控机直接干死机了。嗯,这里要注意:选型要匹配你的应用场景。
我个人习惯,先问自己三个问题:
- 目标运动多快?——决定帧率(FPS)。一般动态追踪建议≥60fps,高速场景(如机械臂抓取)需要≥120fps。
- 环境光线如何?——决定传感器类型。CCD适合低噪声、稳定光源;CMOS性价比高,但卷帘快门容易产生果冻效应。
- 需要什么颜色信息?——决定彩色/黑白。很多追踪任务其实用灰度图就够了,彩色反而增加数据量。
核心参数速查表:
| 参数 | 低速场景 | 高速场景 | 我的建议 |
|---|---|---|---|
| 帧率 | 30 FPS | ≥120 FPS | 留20%余量 |
| 分辨率 | 640×480 | 1280×720 | 够用就行,别贪高 |
| 快门类型 | 卷帘快门 | 全局快门 | 高速必选全局 |
| 接口 | USB 3.0 | GigE / Camera Link | USB容易掉线,工业场景慎用 |
💡 我曾经在一个AGV项目中,选了USB接口的摄像头。结果车子一震动,连接就松了。后来换成GigE接口,带锁紧螺丝,再也没出过问题。所以,工业环境,接口的可靠性比速度更重要。
2.2 摄像头标定:把镜头畸变算回来
摄像头标定,说白了就是给镜头做“视力矫正”。任何镜头都有畸变,尤其是广角镜头。如果不标定,你测出来的目标位置全是歪的。
我常用的方法是张正友标定法。你只需要打印一张棋盘格,拍十几张不同角度的照片,就能算出内参和畸变系数。
为什么会这样?因为棋盘格的角点坐标是已知的(物理世界),而图像上的坐标是观测到的。通过两者的映射关系,就能反推出镜头的数学模型。
下面是我常用的标定代码片段(基于OpenCV):
import cv2
import numpy as np
# 准备棋盘格尺寸
chessboard_size = (9, 6) # 内角点数量
objp = np.zeros((chessboard_size[0]*chessboard_size[1], 3), np.float32)
objp[:,:2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1,2)
# 存储所有图像的点
objpoints = [] # 3D点
imgpoints = [] # 2D点
images = [...] # 你的标定图片列表
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
# 去畸变
undistorted = cv2.undistort(img, mtx, dist, None, mtx)
⚠️ 避坑指南:我曾经因为标定板没贴平,导致标定结果偏差很大。后来发现,标定板必须完全平整,哪怕有一点点弯曲,算出来的畸变系数都是错的。建议用玻璃板或铝板做底板,别用纸板。
2.3 图像滤波:去噪是预处理的第一步
图像滤波,说白了就是把图像里的“脏东西”擦掉。传感器噪声、环境干扰,都会让图像变得不干净。我常用的两种滤波:高斯滤波和中值滤波。
2.3.1 高斯滤波——平滑噪声
高斯滤波适合处理高斯噪声(比如传感器热噪声)。它的原理是用一个高斯核去卷积图像,相当于给每个像素做加权平均。
我个人习惯,核大小选3×3或5×5。太大容易把边缘也模糊掉,太小又去不掉噪声。你想想看,如果核是9×9,那一个边缘像素会被周围一大片区域平均,边缘就没了。
# 高斯滤波
blurred = cv2.GaussianBlur(image, (5, 5), 1.5)
2.3.2 中值滤波——对付椒盐噪声
中值滤波是对付椒盐噪声的利器。什么是椒盐噪声?就是图像上随机出现的白点或黑点,像撒了盐和胡椒一样。
中值滤波的原理很简单:把像素值排序,取中间值。这样,一个孤立的噪声点(极大或极小值)就会被直接剔除。我在项目中遇到过,用高斯滤波去椒盐噪声效果很差,换成中值滤波,一下就干净了。
# 中值滤波
median = cv2.medianBlur(image, 5) # 核大小必须是奇数
经验总结:
- 高斯噪声 → 高斯滤波
- 椒盐噪声 → 中值滤波
- 两者都有 → 先中值后高斯,或者用双边滤波(后面会讲)
2.4 图像增强:让目标“跳”出来
图像增强,说白了就是把对比度拉开,让目标更明显。最经典的方法就是直方图均衡化。
2.4.1 直方图均衡化
直方图均衡化,就是把图像的灰度分布“拉平”。如果一张图整体偏暗(灰度值集中在低区域),均衡化后会扩展到整个0-255范围。
为什么会这样?因为算法会重新映射灰度值,让每个灰度级的像素数量尽量相等。效果就是暗的地方变亮,亮的地方变暗,细节就出来了。
# 全局直方图均衡化
equ = cv2.equalizeHist(gray_image)
# 自适应直方图均衡化(CLAHE)——我推荐这个
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_result = clahe.apply(gray_image)
💡 我个人强烈推荐用CLAHE(自适应直方图均衡化),而不是全局均衡化。全局均衡化容易把噪声也放大,而CLAHE只在局部区域做均衡,效果更自然。我在一个夜间追踪项目中,用了CLAHE后,目标识别率从60%提升到了92%。
2.5 本章知识体系
下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个检查清单:
嗯,这一章的内容就到这里。记住一句话:预处理做得好,追踪算法事半功倍。下一章我们会讲特征提取,但前提是你得先把图像整干净了。