4. OpenCV 核心基础(上):图像读取、显示、保存、色彩空间转换

各位同学,欢迎来到第四讲。从这一章开始,我们正式进入 OpenCV 的代码实操环节。

说白了,图像处理的第一步,就是让计算机「看见」一张图片。怎么把硬盘里的 .jpg 文件变成程序能操作的数据?怎么在屏幕上看一眼?处理完了怎么存回去?这些就是今天要解决的基本问题。

另外,色彩空间转换也是绕不开的坎。BGR、RGB、灰度、HSV…… 我刚开始学的时候也被这些概念搞晕过。别急,咱们一个一个拆开讲。

本章核心目标: 掌握 OpenCV 图像 I/O 的三大函数(imread、imshow、imwrite),理解 BGR 与 RGB 的差异,学会在灰度、HSV 之间自由切换。

4.1 图像读取:imread 的细节与坑

OpenCV 读取图像用的是 cv2.imread()。函数签名很简单:

img = cv2.imread(filename, flags)

filename 是文件路径,flags 是读取模式。常用的 flags 有这些:

flags 值 含义 实际场景
cv2.IMREAD_COLOR (1) 加载彩色图像,忽略透明度 默认模式,日常 90% 场景用这个
cv2.IMREAD_GRAYSCALE (0) 加载为单通道灰度图 边缘检测、模板匹配前常用
cv2.IMREAD_UNCHANGED (-1) 加载原图所有通道(含 alpha) 处理 PNG 透明图时必用

这里有个大坑,我必须强调一下:OpenCV 默认读取的彩色图像是 BGR 顺序,不是 RGB。你想想看,大部分图像库(比如 matplotlib、PIL)用的都是 RGB,如果你混着用,颜色就会偏蓝偏黄。我当年第一次用 OpenCV 配合 matplotlib 显示图像,结果人脸变成了阿凡达…… 排查了半天才发现是通道顺序的问题。

避坑指南: 如果你用 cv2.imread() 读取图片,然后用 matplotlib 的 plt.imshow() 显示,必须先用 cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 转换通道顺序,否则颜色会完全错乱。

4.2 图像显示:imshow 与窗口管理

显示图像用 cv2.imshow(),配合 cv2.waitKey() 和 cv2.destroyAllWindows() 一起使用。基本套路是这样的:

cv2.imshow('窗口标题', img)
cv2.waitKey(0)      # 等待按键,0 表示无限等待
cv2.destroyAllWindows()

几个要点:

  • waitKey() 的参数单位是毫秒。设成 0 就是按任意键才关闭,设成 1000 就是显示 1 秒后自动关闭。
  • 窗口标题不要用中文,某些 OpenCV 版本对中文支持不好,会显示乱码。
  • 如果图像太大超出屏幕,可以用 cv2.namedWindow() 配合 cv2.WINDOW_NORMAL 标志,这样窗口可以手动缩放。
小技巧: 调试时我习惯把 waitKey(1) 放在循环里,配合 cv2.imshow() 做视频流实时预览。但如果是单张图片调试,用 waitKey(0) 更稳妥,不会一闪而过。

4.3 图像保存:imwrite 的格式与质量

保存图像用 cv2.imwrite():

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

OpenCV 会根据文件扩展名自动选择编码格式。但有些参数可以手动控制:

格式 参数 说明
JPEG cv2.IMWRITE_JPEG_QUALITY 0-100,默认 95。值越大质量越高,文件也越大
PNG cv2.IMWRITE_PNG_COMPRESSION 0-9,默认 3。值越大压缩率越高,保存越慢
WebP cv2.IMWRITE_WEBP_QUALITY 0-100,默认 80。适合网络传输场景

举个例子,保存高质量 JPEG:

cv2.imwrite('high_quality.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 100])

我曾经在做一个批量处理项目时,默认保存 JPEG 导致图片质量严重下降,后来才发现 OpenCV 的 JPEG 默认质量是 95,但有些场景需要无损保存。所以,如果你对图像质量敏感,建议保存为 PNG 格式,或者显式指定 JPEG 质量为 100。

4.4 色彩空间转换:BGR、RGB、灰度、HSV

色彩空间转换是图像处理的基础操作。OpenCV 提供了 cv2.cvtColor() 函数,支持 150 多种转换类型。但实际开发中,常用的就这几种:

4.4.1 BGR ↔ 灰度

灰度图只有一个通道,每个像素值 0-255,0 是纯黑,255 是纯白。转换方法:

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

为什么不是直接取 RGB 的平均值?因为 OpenCV 用的是加权平均法:Gray = 0.299 * R + 0.587 * G + 0.114 * B。这个权重考虑了人眼对不同颜色的敏感度——说白了,人眼对绿色最敏感,对蓝色最不敏感。

4.4.2 BGR ↔ RGB

这个转换纯粹是通道顺序调整,没有数学计算:

rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

记住,只有显示或与其他库交互时才需要转 RGB。如果你全程用 OpenCV 处理,保持 BGR 就行,省一次转换开销。

4.4.3 BGR ↔ HSV

HSV 色彩空间把颜色分解为色调(Hue)、饱和度(Saturation)、明度(Value)。为什么需要 HSV?

  • 色调 H:0-180(OpenCV 里 H 范围是 0-180,不是 0-360),代表颜色种类。
  • 饱和度 S:0-255,代表颜色纯度。0 是灰色,255 是最鲜艳。
  • 明度 V:0-255,代表亮度。0 是纯黑,255 是最亮。

转换代码:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

HSV 最大的优势是:对光照变化不敏感。比如你要做颜色识别,在 BGR 空间里,同一个红色物体在不同光照下 R、G、B 值会剧烈变化,但在 HSV 空间里,H 值基本稳定。我做过一个交通标志识别项目,用 HSV 做颜色分割,效果比 BGR 好太多了。

实战建议: 做颜色检测时,先用 HSV 空间,再通过 cv2.inRange() 提取特定颜色区域。这是 OpenCV 颜色分割的标准流程。

4.5 本章知识体系总览

下面这张 SVG 图把本章的核心知识点串起来了。你可以把它当作一个快速索引,复习时扫一眼就能回忆起关键内容。

OpenCV 核心基础(上)知识体系 图像读取 imread 图像显示 imshow 图像保存 imwrite 色彩空间转换 cvtColor BGR ↔ 灰度 BGR ↔ RGB BGR ↔ HSV BGR ↔ HLS 💡 核心要点 • imread 默认 BGR 顺序,与 RGB 不同,跨库使用时必须转换 • HSV 空间对光照变化不敏感,颜色检测首选 • 保存 JPEG 时注意质量参数,敏感场景建议用 PNG

4.6 完整示例:从读取到保存的完整流程

最后,给你一个完整的代码示例,把今天讲的内容串起来:

import cv2

# 1. 读取图像(BGR 格式)
img = cv2.imread('input.jpg', cv2.IMREAD_COLOR)
if img is None:
    print("图像读取失败,检查路径")
    exit()

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

# 3. 转灰度并显示
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Grayscale', gray)

# 4. 转 HSV 并显示
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV', hsv)

# 5. 保存结果
cv2.imwrite('output_gray.jpg', gray)
cv2.imwrite('output_hsv.jpg', hsv)

cv2.waitKey(0)
cv2.destroyAllWindows()

这段代码你直接复制就能跑。注意把 input.jpg 换成你自己的图片路径。如果图片读取失败,img 会是 None,所以加了个判空检查——这个习惯我在项目里一直保持,能省很多调试时间。

课后练习: 找一张彩色照片,分别保存为 JPEG(质量 50、90、100)和 PNG(压缩率 0、3、9),对比文件大小和视觉质量。你会发现 JPEG 在低质量下会有明显的块状伪影,而 PNG 是无损的。