2. 图像基础操作:读取、显示、保存与颜色空间转换

好,咱们正式开始动手了。

这一章,我带你搞定图像处理里最基础、也最常用的几个操作。说白了,就是怎么把一张图片弄进程序里,怎么看看它长什么样,处理完了怎么存回去,以及最核心的——颜色空间到底是个啥玩意儿。

你别小看这些基础操作。我见过不少新手,上来就搞高大上的检测算法,结果连图片路径写错了都不知道,程序报错一脸懵。嗯,咱们先把地基打牢。

2.1 图像的读取:imread() 的玄机

在 OpenCV 里,读取图像用的是 cv2.imread()。这个函数很简单,但有几个坑,我当年刚入行时就踩过。

import cv2

# 读取彩色图像
img = cv2.imread('lena.jpg')
print(type(img))  # <class 'numpy.ndarray'>
print(img.shape)  # (高度, 宽度, 通道数)

你看,读进来的图像其实就是一个 NumPy 数组。形状是 (高度, 宽度, 通道数)。

⚠️ 注意: OpenCV 默认读取的是 BGR 格式,不是 RGB!这一点太重要了。我刚开始做项目时,用 matplotlib 显示图像,结果颜色全不对,蓝的变成红的,调试了半天才发现是通道顺序的问题。

imread() 的第二个参数可以控制读取方式:

参数 含义 数值
cv2.IMREAD_COLOR 读取彩色图像(默认) 1
cv2.IMREAD_GRAYSCALE 读取为灰度图 0
cv2.IMREAD_UNCHANGED 保留原图所有通道(含 Alpha) -1
# 读取灰度图
gray_img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
print(gray_img.shape)  # (高度, 宽度) —— 只有两个维度
💡 我的习惯: 我一般直接用数字 0、1、-1,因为写起来快。但团队协作时建议用常量名,方便别人理解。

2.2 图像的显示:imshow() 与窗口管理

读进来了,总得看一眼吧?用 cv2.imshow()

cv2.imshow('窗口标题', img)
cv2.waitKey(0)      # 等待按键,参数是毫秒数,0 表示无限等待
cv2.destroyAllWindows()  # 关闭所有窗口

这里有个关键点:waitKey() 必须要有。没有它,窗口一闪就没了,你根本看不到图像。

为什么会这样?因为 OpenCV 的显示机制是事件驱动的。你不调用 waitKey(),它就不处理窗口事件,直接往下执行程序,然后退出。

🔑 核心要点:

  • waitKey(0):等待任意按键,常用于单张图片显示
  • waitKey(30):等待 30 毫秒,常用于视频流显示
  • 按 ESC 键(ASCII 码 27)通常用来退出
# 一个完整的显示示例
img = cv2.imread('lena.jpg')
cv2.imshow('My Image', img)

key = cv2.waitKey(0)
if key == 27:  # ESC
    cv2.destroyAllWindows()
elif key == ord('s'):  # 按 s 键保存
    cv2.imwrite('output.jpg', img)
    cv2.destroyAllWindows()

嗯,这里我顺便把保存也讲了。你看,按 s 键就能保存图像,挺方便的吧?

2.3 图像的保存:imwrite() 的注意事项

cv2.imwrite() 用来保存图像。它根据文件扩展名自动选择编码格式。

cv2.imwrite('output.png', img)   # 保存为 PNG
cv2.imwrite('output.jpg', img)   # 保存为 JPEG
⚠️ 避坑指南: 我曾经在保存 JPEG 时发现图像质量很差,后来才意识到 JPEG 是有损压缩。如果你需要无损保存,用 PNG。另外,保存路径如果不存在,OpenCV 不会自动创建目录,会直接报错。

JPEG 的压缩质量可以通过 cv2.IMWRITE_JPEG_QUALITY 控制:

# 设置 JPEG 质量为 95(范围 0-100)
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])

2.4 颜色空间转换:BGR 与灰度

终于到重点了。颜色空间转换,说白了就是把图像从一种颜色表示方式变成另一种。

最常用的就是 BGR 转灰度图。为什么需要灰度图?因为很多算法(比如边缘检测、特征提取)在单通道上计算更快,而且不受颜色干扰。

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

你想想看,一张彩色图有三个通道,灰度图只有一个通道。处理速度能快三倍,这可不是小数目。

反过来,灰度图也可以转回 BGR:

color = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)

但注意,这种转换只是把灰度值复制到三个通道上,并不是真的恢复了颜色信息。丢失的颜色是找不回来的。

🔑 常用转换标志:

  • cv2.COLOR_BGR2GRAY:BGR 转灰度
  • cv2.COLOR_GRAY2BGR:灰度转 BGR
  • cv2.COLOR_BGR2RGB:BGR 转 RGB(用于 matplotlib 显示)
  • cv2.COLOR_BGR2HSV:BGR 转 HSV(颜色检测常用)

2.5 实战:一个完整的图像处理流程

来,咱们把今天学的串起来,写一个完整的例子。

import cv2

# 1. 读取图像
img = cv2.imread('lena.jpg')
if img is None:
    print("图片没找到,检查路径!")
    exit()

# 2. 显示原图
cv2.imshow('Original', img)

# 3. 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 4. 显示灰度图
cv2.imshow('Gray', gray)

# 5. 保存灰度图
cv2.imwrite('lena_gray.jpg', gray)

# 6. 等待按键
cv2.waitKey(0)
cv2.destroyAllWindows()
💡 我的建议: 每次读取图像后,一定要检查 img is None。我在项目里吃过这个亏——路径写错了,程序没报错,但后面所有操作都崩溃,排查了半天才发现是图片没读进来。

2.6 本章小结

今天咱们干了这几件事:

  • imread() 读取图像,注意 BGR 格式和路径问题
  • imshow() + waitKey() 显示图像,别忘了等待按键
  • imwrite() 保存图像,注意格式和质量
  • cvtColor() 做颜色空间转换,BGR 和灰度是最常用的

这些操作看起来简单,但它们是所有图像处理任务的基石。你想想看,后面我们要做的运动检测、行为分析,哪一步离得开这些基础操作?

下一章,咱们聊聊图像的几何变换——缩放、旋转、裁剪。这些操作在预处理阶段特别重要,我到时候会分享一些实际项目中的小技巧。

好,今天就到这儿。动手试试吧,把代码敲一遍,感受一下。