4. OpenCV图像处理基础:图像读取与显示、色彩空间转换、几何变换、滤波与边缘检测
各位同学,欢迎来到第四章。这一章,我们正式进入OpenCV的世界。
说实话,图像处理是镜头测试系统的地基。你想想看,不管你是测分辨率、测畸变,还是测色彩还原,最终都要落到像素级别的操作上。OpenCV就是干这个的——它把复杂的图像处理算法封装成了一个个函数,我们只需要学会怎么调用、怎么组合就行。
我个人习惯把这一章叫做“工具箱入门”。今天我们就来认识一下这个工具箱里最常用的几把扳手。
4.1 图像读取与显示:你的第一行代码
先来点最基础的。读取一张图片,显示出来,保存回去。这三板斧,几乎每个项目都会用到。
import cv2
# 读取图像
img = cv2.imread('test.jpg')
# 显示图像
cv2.imshow('My Image', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows()
# 保存图像
cv2.imwrite('output.jpg', img)
嗯,这里要注意几个坑。我在项目中遇到过好几次,新人一上来就报错,十有八九是路径问题。
- 路径别用中文:OpenCV底层用的是C++,对中文路径支持很差。我曾经因为图片放在“桌面/测试图片”文件夹里,折腾了半小时才找到原因。
- imread返回None:如果图片读不出来,先检查路径,再检查文件是否存在。别傻乎乎地往下传None。
- waitKey(0)不能省:没有这行,窗口一闪就没了,你根本看不到图片。
另外,imread默认是以BGR格式读取的。这一点和很多人的直觉不一样——我们平时用的RGB,OpenCV偏要反着来。为什么?历史原因,不用深究,记住就行。
4.2 色彩空间转换:BGR、Gray、HSV
色彩空间转换,说白了就是把图像从一种颜色表示方式变成另一种。最常见的三种:
- BGR:OpenCV的默认格式,蓝绿红三个通道
- Gray:灰度图,只有一个通道,0-255表示亮度
- HSV:色调、饱和度、明度,更接近人眼感知颜色的方式
# BGR转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR转HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# HSV转BGR
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
我个人建议,在做颜色相关的检测时,优先用HSV。为什么?因为HSV把颜色和亮度分开了。你想想看,同一个红色物体,在强光和弱光下,BGR值会差很多,但HSV里的H(色调)基本不变。这就方便多了。
4.3 图像几何变换:缩放、旋转、裁剪
几何变换,说白了就是改变图像的形状和位置。在镜头测试中,这一步经常用来做图像对齐和尺寸标准化。
4.3.1 缩放
# 指定尺寸缩放
resized = cv2.resize(img, (640, 480))
# 按比例缩放
scale_percent = 50 # 缩小到50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))
这里有个细节:resize的插值方式。默认是双线性插值,效果还行。但如果你要缩小图片,用cv2.INTER_AREA效果更好,不容易出现锯齿。放大图片的话,cv2.INTER_CUBIC或cv2.INTER_LANCZOS4更平滑。
4.3.2 旋转
# 获取图像尺寸
h, w = img.shape[:2]
# 计算旋转矩阵(绕中心旋转45度)
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)
# 执行旋转
rotated = cv2.warpAffine(img, M, (w, h))
旋转的时候要注意,图像可能会被裁剪掉一部分。如果你不想丢失边缘信息,可以适当扩大输出尺寸。我在做镜头畸变校正时,经常需要先旋转再裁剪,顺序搞反了结果就不对了。
4.3.3 裁剪
裁剪其实很简单,就是数组切片:
# 裁剪区域 [y1:y2, x1:x2]
cropped = img[100:400, 200:500]
嗯,这里要提醒一下:OpenCV的坐标是(x, y),但数组索引是[row, col],也就是(y, x)。我第一次用的时候也搞混过,写成了img[200:500, 100:400],结果裁剪出来的图像是歪的。
4.4 图像滤波与边缘检测
滤波和边缘检测,是图像预处理的核心。说白了,滤波是为了去噪,边缘检测是为了找到物体的轮廓。
4.4.1 滤波
常用的滤波有三种:
| 滤波类型 | 函数 | 特点 | 适用场景 |
|---|---|---|---|
| 均值滤波 | cv2.blur() |
简单平均,去噪效果一般 | 轻度噪声 |
| 高斯滤波 | cv2.GaussianBlur() |
加权平均,保留边缘更好 | 高斯噪声,最常用 |
| 中值滤波 | cv2.medianBlur() |
取中位数,对椒盐噪声效果好 | 椒盐噪声,比如传感器坏点 |
# 高斯滤波
blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波
median = cv2.medianBlur(img, 5)
核的大小很关键。核越大,去噪越强,但图像也越模糊。我一般从3x3开始试,效果不够再加大。别一上来就用15x15,那会把细节全抹掉。
4.4.2 边缘检测
边缘检测,最经典的就是Canny算法。它分四步:去噪、计算梯度、非极大值抑制、双阈值检测。
# Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)
两个阈值怎么设?我有个经验法则:低阈值设为高阈值的一半到三分之一。比如高阈值150,低阈值就设50-75。具体数值要看图像内容,没有万能公式。
知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个思维导图,方便复习。
好了,这一章的内容就到这里。图像处理是个熟能生巧的活,多写几行代码,多跑几个例子,慢慢就有感觉了。下一章我们会继续深入,讲一讲图像的特征提取和匹配,那是镜头测试中更高级的玩法。