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(色调)基本不变。这就方便多了。

💡 小技巧: 在镜头测试中,我们经常用HSV来做颜色分割。比如检测镜头有没有偏色,可以先把图像转到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_CUBICcv2.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。具体数值要看图像内容,没有万能公式。

🔍 实战经验: 在镜头测试中,我们经常用Canny来检测测试卡上的线条。比如测MTF(调制传递函数)时,需要找到黑白条纹的边缘。如果边缘检测不准,MTF值就会偏大或偏小。我曾经因为阈值设得太低,把噪声也当成了边缘,结果MTF曲线全是毛刺,折腾了两天才发现是预处理的问题。

知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个思维导图,方便复习。

OpenCV图像处理基础 图像读取与显示 imread / imshow / imwrite 路径问题 / 窗口管理 色彩空间转换 BGR / Gray / HSV cvtColor / 颜色分割 图像几何变换 缩放 / 旋转 / 裁剪 resize / warpAffine 图像滤波与边缘检测 均值 / 高斯 / 中值滤波 Canny边缘检测 镜头测试应用 MTF测量 / 畸变校正 偏色检测 / 分辨率测试 核心逻辑:读取 → 预处理(滤波/转换) → 变换 → 分析 每一步都是为下一步服务的,顺序很重要

好了,这一章的内容就到这里。图像处理是个熟能生巧的活,多写几行代码,多跑几个例子,慢慢就有感觉了。下一章我们会继续深入,讲一讲图像的特征提取和匹配,那是镜头测试中更高级的玩法。


专注资料整理