3、Python与OpenCV基础回顾:图像读取、显示、保存,色彩空间转换,图像几何变换,ROI提取

各位同学,欢迎来到第三章。在正式开始人脸识别和姿态估计之前,咱们得先把地基打牢。OpenCV 和 Python 的组合,说白了就是边缘设备上做视觉处理的「倚天剑」。这一章我会带大家快速过一遍最核心的基础操作,这些都是我这些年做项目时天天用的东西,一个都跑不掉。

3.1 图像读取、显示与保存

这是最基础的三板斧。我刚开始接触 OpenCV 时,第一件事就是验证这三步能不能跑通。你想想看,如果连图片都读不进来,后面的一切都是空谈。

3.1.1 读取图像

cv2.imread() 就行。路径对了,一切好说。路径错了,程序直接崩给你看。我个人习惯用绝对路径,或者用 os.path.join() 拼接,避免在 Windows 和 Linux 之间来回切换时踩坑。

import cv2

# 读取图像,默认是彩色模式
img = cv2.imread('test.jpg')
print(img.shape)  # (高度, 宽度, 通道数)
注意:OpenCV 默认读取的是 BGR 格式,不是 RGB。这一点我吃过亏。有一次做颜色识别,怎么调参数都不对,折腾了半天才发现是通道顺序搞反了。

3.1.2 显示图像

cv2.imshow() 配合 cv2.waitKey()。嗯,这里有个细节:waitKey(0) 表示等待按键按下,waitKey(1) 表示等待 1 毫秒。在视频处理中,我通常用 waitKey(1) 来保持画面刷新。

cv2.imshow('窗口标题', img)
cv2.waitKey(0)      # 按任意键关闭
cv2.destroyAllWindows()

3.1.3 保存图像

cv2.imwrite()。格式由文件后缀决定,比如 .jpg.png。我在项目中经常用它来保存中间结果,方便调试。

cv2.imwrite('output.jpg', img)

3.2 色彩空间转换

为什么需要色彩空间转换?说白了,BGR 格式在颜色识别时并不友好。比如你要检测肤色,用 HSV 空间就比 BGR 稳定得多。我曾经在做一个手势识别项目时,用 BGR 做肤色分割,结果灯光一变就全乱了。换成 HSV 之后,鲁棒性明显提升。

3.2.1 BGR 转灰度

灰度图只有单通道,计算量小,适合做边缘检测、人脸检测的前处理。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

3.2.2 BGR 转 HSV

HSV 把颜色分解成色调、饱和度、明度三个维度。做颜色识别时,你只需要关注色调范围就行。

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
小技巧:在边缘设备上,频繁的色彩空间转换会消耗算力。我一般只在预处理阶段转一次,后续操作都在同一色彩空间下完成。

3.3 图像几何变换

几何变换在姿态估计中特别常用。比如你要把检测到的人脸对齐到标准位置,就需要用到仿射变换。

3.3.1 缩放

cv2.resize()。注意插值方法的选择:缩小用 INTER_AREA,放大用 INTER_LINEARINTER_CUBIC

resized = cv2.resize(img, (224, 224))  # 固定尺寸

3.3.2 旋转

旋转需要先获取旋转矩阵,然后用 cv2.warpAffine() 执行。我习惯用 cv2.getRotationMatrix2D() 来生成矩阵。

h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)  # 旋转45度
rotated = cv2.warpAffine(img, M, (w, h))

3.3.3 平移与仿射变换

平移也是用 warpAffine,只是矩阵不同。仿射变换可以同时实现旋转、缩放、平移,是姿态估计中的核心操作。

# 平移矩阵
M_trans = np.float32([[1, 0, 50], [0, 1, 100]])
translated = cv2.warpAffine(img, M_trans, (w, h))

3.4 ROI 提取

ROI,也就是感兴趣区域。在人脸识别中,我们通常只关心人脸那一小块区域,而不是整张图片。提取 ROI 其实就是数组切片,简单但极其高效。

# 假设人脸区域在 (x, y, w, h) 范围内
face_roi = img[y:y+h, x:x+w]
核心思路:先检测,再提取,最后处理。在边缘设备上,ROI 提取能大幅减少计算量。我做过测试,只处理人脸区域比处理全图快了将近 3 倍。

你可能会问:ROI 提取后怎么用?举个例子,你可以把提取到的人脸区域单独保存,或者送入神经网络做特征提取。在姿态估计中,ROI 通常就是人体或人脸所在的 bounding box。

3.5 综合示例:人脸 ROI 提取与保存

下面这个例子,我会把前面讲的内容串起来。先用 Haar 级联检测人脸,然后提取 ROI,最后保存到本地。

import cv2

# 加载人脸检测器
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

# 读取图像
img = cv2.imread('group_photo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测人脸
faces = face_cascade.detectMultiScale(gray, 1.1, 5)

# 提取并保存每个人脸
for i, (x, y, w, h) in enumerate(faces):
    face_roi = img[y:y+h, x:x+w]
    cv2.imwrite(f'face_{i}.jpg', face_roi)
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

cv2.imshow('Detected Faces', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
避坑指南:我曾经在树莓派上跑这个代码,发现检测速度很慢。后来把图像缩放到 640x480 再检测,速度提升明显。记住,边缘设备上「先缩小,再处理」是个好习惯。

3.6 本章小结

这一章的内容,说白了就是 OpenCV 的「基本功」。图像读写、色彩转换、几何变换、ROI 提取,这四个模块在后续的人脸识别和姿态估计中会反复出现。我个人建议你把这些代码亲手敲一遍,不要复制粘贴。敲代码的过程中,你会遇到各种小问题——路径不对、类型不匹配、坐标越界——这些都是宝贵的经验。

下一章,我们会进入人脸检测的核心算法。到时候你会发现,今天学的 ROI 提取和色彩空间转换,就是检测流程中的关键拼图。好了,先练好基本功,咱们下章见。