4、OpenCV基础:图像读取与显示、颜色空间转换、图像滤波与边缘检测
好,咱们进入正题。这一章是OpenCV的入门实操课。说白了,就是让你学会怎么把一张图片“请”进程序里,再对它做点手脚,最后看看效果。
我刚开始做嵌入式视觉那会儿,觉得OpenCV就是个调库的活儿。后来踩的坑多了才发现——基础不牢,地动山摇。图像读取的编码方式搞错,颜色空间没转对,滤波参数选得离谱……这些看似简单的问题,在边缘设备上会被放大十倍。
所以,咱们稳扎稳打。今天这四块内容,你吃透了,后面的行人检测和跟踪才能跑得顺。
4.1 图像读取与显示:你的第一行视觉代码
先来最基础的。把一张图片从硬盘里读进来,再显示到屏幕上。OpenCV里用的是 imread() 和 imshow()。
import cv2
# 读取图像
img = cv2.imread('pedestrian.jpg')
# 显示图像
cv2.imshow('Pedestrian Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
嗯,这里要注意一个坑。 imread() 默认是以 BGR 格式读取的。对,你没看错,不是RGB。这是OpenCV的历史遗留问题。我曾经在项目里直接用 matplotlib 显示OpenCV读进来的图,结果颜色全乱了,找了半天bug……
matplotlib 显示OpenCV图像,记得先转换颜色空间。否则你会看到一张“蓝脸”的行人。
另外,在边缘设备上,比如树莓派或者Jetson Nano,显示图像可能会有点卡。我建议你调试阶段用 imshow,部署阶段直接保存到文件或者通过管道传输。
4.2 颜色空间转换:为什么非转不可?
颜色空间转换,说白了就是把图像从一种颜色表达方式变成另一种。最常见的操作是 BGR 转灰度 和 BGR 转 HSV。
你想想看,行人检测里,很多时候我们并不需要颜色信息。一张灰度图,计算量直接降到原来的三分之一。在边缘设备上,这就是实打实的性能提升。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
我个人习惯在做任何图像处理之前,先看一眼图像的通道数和数据类型。用 img.shape 和 img.dtype 检查一下,能避免很多低级错误。
4.3 图像滤波:让图像“干净”一点
滤波,说白了就是去噪。摄像头采集的图像,尤其是低成本摄像头,噪声多得让人头疼。我遇到过最夸张的一次,是在一个光线很差的停车场,图像上全是椒盐噪声,行人轮廓都看不清。
常用的滤波有三种:
| 滤波类型 | 函数 | 特点 | 适用场景 |
|---|---|---|---|
| 均值滤波 | cv2.blur() |
简单快速,但会模糊边缘 | 噪声较轻的场景 |
| 高斯滤波 | cv2.GaussianBlur() |
权重分配,边缘保留稍好 | 高斯噪声,行人检测预处理 |
| 中值滤波 | cv2.medianBlur() |
对椒盐噪声效果极佳 | 摄像头坏点、强噪声环境 |
# 高斯滤波,核大小5x5
blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波,核大小5
median = cv2.medianBlur(img, 5)
为什么核大小通常是奇数?因为需要有一个中心像素点。你想想看,如果是偶数,中心点怎么定义?
4.4 边缘检测:找到物体的“轮廓”
边缘检测,是计算机视觉里最经典的操作之一。它帮我们找到图像中亮度变化剧烈的地方——说白了,就是物体的边界。
最常用的方法是 Canny边缘检测。它分四步:去噪、计算梯度、非极大值抑制、双阈值检测。
edges = cv2.Canny(blurred, 50, 150)
这里两个阈值是关键。 50 是低阈值, 150 是高阈值。低于低阈值的点被丢弃,高于高阈值的点被保留为强边缘,介于两者之间的点如果与强边缘相连则保留。
我曾经在一个项目中,为了调这两个阈值,花了整整一个下午。后来发现,不同的光照条件下,最优阈值差别很大。所以我后来写了个自适应阈值的函数,根据图像的平均亮度动态调整。
另外,在边缘设备上,Canny的计算量不小。如果你对实时性要求高,可以考虑用 Sobel算子 或者 Laplacian算子 做简化版边缘检测。
# Sobel边缘检测
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
sobel = cv2.magnitude(sobelx, sobely)
嗯,这里要注意,Sobel算子的输出是浮点数,需要转成 uint8 才能正常显示。
4.5 实战组合:从读取到边缘检测的完整流程
咱们把今天学的串起来,写一个完整的例子。这个流程,我在多个行人检测项目里都用过,很稳。
import cv2
# 1. 读取图像
img = cv2.imread('pedestrian.jpg')
# 2. 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 3. 高斯滤波去噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 4. Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)
# 5. 显示结果
cv2.imshow('Original', img)
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
这个流程,你可以在自己的图片上试试。我建议你用一张行人比较清晰的图片,看看边缘检测能不能把人的轮廓勾出来。
GaussianBlur 的核大小从5x5改成3x3,速度能提升不少,效果差别不大。
好了,今天的内容就到这里。图像读取、颜色空间转换、滤波、边缘检测——这四个基础操作,是后续所有视觉算法的基石。你把这些练熟了,后面学行人检测和跟踪,会轻松很多。
下一章,咱们聊聊特征提取。到时候你会看到,今天学的边缘检测,其实是很多高级特征的基础。