2、OpenCV核心基础(一):图像的读取、显示与保存、像素操作与色彩空间转换(BGR/RGB/HSV)
好,咱们正式开始动手写代码了。这一章是OpenCV的“地基”,说白了就是怎么把一张图片从硬盘里请出来,在屏幕上亮个相,再存回去。另外,还得搞清楚像素到底是个啥,以及为什么OpenCV读出来的颜色跟你想象的不太一样。
我个人习惯,每学一个新库,第一件事就是跑通它的“Hello World”——也就是图像读写。这一步通了,后面所有视觉任务才有了操作对象。
2.1 图像的读取、显示与保存
OpenCV里最常用的三个函数:imread、imshow、imwrite。名字很直白,读、显、写。
2.1.1 读取图像:cv2.imread()
函数签名很简单:
img = cv2.imread(filename, flags)
filename是图片路径,支持路径和相对路径。flags是读取模式,常用这几个:
| flags 值 | 含义 | 场景 |
|---|---|---|
cv2.IMREAD_COLOR (默认) |
加载彩色图,忽略透明度 | 日常彩色图像处理 |
cv2.IMREAD_GRAYSCALE |
加载为单通道灰度图 | 边缘检测、二值化前 |
cv2.IMREAD_UNCHANGED |
加载所有通道(含Alpha) | 处理PNG透明图 |
imread对中文路径支持不好,返回None还不报错。我早期一个项目,客户传的图片名全是中文,调试了半天才发现是路径问题。建议项目里统一用英文路径,或者用cv2.imdecode配合np.fromfile处理中文路径。
2.1.2 显示图像:cv2.imshow()
显示图像需要配合两个函数:
cv2.imshow('窗口标题', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows() # 关闭所有窗口
waitKey(0)表示无限等待,直到你按任意键。如果你想让窗口自动关闭,可以设置毫秒数,比如waitKey(1000)就是显示1秒后自动关闭。
waitKey(0) & 0xFF == ord('q')作为退出条件,这样按'q'键退出,按其他键继续。这在视频处理里特别实用。
2.1.3 保存图像:cv2.imwrite()
cv2.imwrite('output.jpg', img)
保存格式由文件扩展名决定。JPEG可以指定质量参数:
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
质量范围0-100,默认95。数值越大,文件越大,画质越好。
2.2 像素操作:图像的本质是矩阵
你想想看,一张图像在计算机眼里是什么?就是一个多维数组。灰度图是二维数组,彩色图是三维数组(高×宽×通道数)。
OpenCV读取彩色图时,通道顺序是BGR,不是我们熟悉的RGB。这一点特别容易搞混。
2.2.1 访问和修改像素值
import cv2
import numpy as np
img = cv2.imread('lena.jpg')
# 访问(100, 200)位置的像素
px = img[100, 200]
print(px) # [B, G, R] 三个值
# 修改像素
img[100, 200] = [255, 0, 0] # 变成蓝色
# 访问单通道
blue = img[100, 200, 0] # B通道
green = img[100, 200, 1] # G通道
red = img[100, 200, 2] # R通道
img[row, col],row是y方向(高度),col是x方向(宽度)。千万别搞反了,我见过不少新手把行列顺序写反,结果图像显示出来是扭曲的。
2.2.2 图像属性
print(img.shape) # (高度, 宽度, 通道数)
print(img.size) # 总像素数 = 高×宽×通道数
print(img.dtype) # 数据类型,通常是 uint8
shape返回的是一个元组。灰度图只有两个值,彩色图有三个。
2.2.3 ROI(感兴趣区域)操作
用切片就能提取图像的一块区域:
# 提取从(50,100)到(200,300)的区域
roi = img[50:200, 100:300]
# 把这块区域复制到另一个位置
img[10:160, 10:210] = roi
这个操作在目标检测里太常用了。比如YOLO检测到目标后,你要把目标区域截出来做进一步分析,用的就是ROI切片。
2.3 色彩空间转换:BGR、RGB、HSV
为什么OpenCV用BGR而不是RGB?这其实是个历史遗留问题。早期相机硬件厂商用BGR顺序,OpenCV为了兼容就这么定了。你只要记住:OpenCV默认是BGR,显示的时候没问题,但如果你用matplotlib显示,颜色就全乱了。
2.3.1 BGR ↔ RGB 转换
# 方法一:用cvtColor
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 方法二:手动翻转通道
rgb_img = img[:, :, ::-1] # 用切片反转通道顺序
我个人更推荐用cvtColor,语义清晰,不容易出错。
2.3.2 BGR ↔ HSV 转换
HSV色彩空间把颜色分解成色调(H)、饱和度(S)、明度(V)。为什么需要HSV?因为它在做颜色分割时比BGR/RGB好用得多。
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
HSV各通道范围:
| 通道 | OpenCV范围 | 说明 |
|---|---|---|
| H (色调) | 0 - 179 | 标准HSV是0-360,OpenCV压缩了一半 |
| S (饱和度) | 0 - 255 | 0为灰色,255最鲜艳 |
| V (明度) | 0 - 255 | 0为黑色,255最亮 |
2.3.3 实战:用HSV做颜色检测
假设我们要检测图像中的蓝色物体:
import cv2
import numpy as np
img = cv2.imread('objects.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 蓝色的HSV范围(OpenCV范围)
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])
# 生成掩码
mask = cv2.inRange(hsv, lower_blue, upper_blue)
# 提取蓝色区域
result = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
这段代码就是颜色检测的经典套路。YOLO检测到目标后,如果你还想根据颜色做二次筛选,HSV就是最好的工具。
2.4 本章知识体系
下面这张图把本章的核心知识点串起来了:
2.5 本章小结
这一章的内容,说白了就是OpenCV的“三板斧”:
- 图像读写:
imread、imshow、imwrite,记住路径别带中文 - 像素操作:图像就是矩阵,用
[row, col]访问,用切片做ROI - 色彩空间:BGR是OpenCV的默认顺序,HSV做颜色分割特别好用
这些基础操作,后面每一章都会用到。尤其是HSV颜色检测,在YOLO检测后的二次过滤里非常实用。你可以在自己的项目里试试:拍一张有彩色物体的照片,用HSV提取特定颜色,看看效果怎么样。