2、图像基础操作:图像的读取、显示、保存,像素操作,色彩空间转换

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

这一章,说白了就是 OpenCV 的「基本功」。你想想看,不管后面要做多复杂的目标检测、多炫酷的跟踪算法,第一步永远是把图像弄进来、看一眼、存起来。然后才能谈得上处理。

我个人习惯,拿到一张图,先做三件事:读进来、显示出来、检查一下像素值。这三步走顺了,心里才有底。

2.1 图像的读取:imread() 的坑与妙用

读取图像,用的是 cv2.imread()。这函数看着简单,但我在项目中遇到过不少坑。

import cv2

# 默认以彩色模式读取
img = cv2.imread('cat.jpg')

# 以灰度模式读取
img_gray = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)

# 读取包含透明通道的图像
img_unchanged = cv2.imread('cat.png', cv2.IMREAD_UNCHANGED)

这里有个关键点,很多人一开始不知道——OpenCV 默认读取的是 BGR 格式,不是 RGB。嗯,这里要注意,后面做色彩空间转换时会用到。

⚠️ 我曾经踩过的坑: 路径不能有中文!OpenCV 的 imread() 对中文路径支持不好,返回 None 还不报错。我调试了半天,最后发现是文件夹名字用了中文。建议所有路径都用英文。

另外,imread() 如果读不到文件,不会抛异常,而是返回 None。所以安全写法是:

img = cv2.imread('cat.jpg')
if img is None:
    print("图片没读到,检查路径!")
    exit()

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

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

cv2.imshow('My Window', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()

为什么会这样?waitKey(0) 的意思是无限等待键盘输入。你不加这一行,窗口一闪就没了。我刚开始学的时候,窗口闪一下消失,还以为代码写错了。

💡 小技巧: 如果你想让窗口自适应大小,用 cv2.WINDOW_NORMAL 标志:
cv2.namedWindow('My Window', cv2.WINDOW_NORMAL)
cv2.imshow('My Window', img)
这样窗口就可以用鼠标拖拽缩放了。

2.3 图像的保存:imwrite() 的格式控制

处理完了,得存起来。用 cv2.imwrite()

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

# 控制 JPEG 质量(0-100,默认95)
cv2.imwrite('output_high.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])

# 控制 PNG 压缩级别(0-9,默认3)
cv2.imwrite('output_lossless.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 0])

我个人习惯,做实验时用 PNG 保存,因为无损。最终交付时再用 JPEG 压缩,节省空间。

2.4 像素操作:图像的「细胞」

图像是什么?说白了就是一个多维数组。每个像素点就是一个数值。

对于灰度图,每个像素是 0-255 的数值。0 是黑,255 是白。

对于彩色图,每个像素是三个数值(B、G、R)。

# 获取像素值
pixel = img[100, 200]  # 第100行,第200列的像素
print(pixel)  # 彩色图返回 [B, G, R]

# 修改像素值
img[100, 200] = [255, 0, 0]  # 改成蓝色

# 获取单通道值
blue_value = img[100, 200, 0]  # 第0通道(B通道)
🔑 核心理解: OpenCV 中图像的坐标系是 (行, 列),也就是 (y, x)。这和数学中的 (x, y) 是反的。很多初学者在这里搞混,画图时位置总是不对。

我记得有一次做目标检测的标注,画矩形框时死活对不上位置。查了半天,才发现是行列顺序搞反了。从那以后,我每次写像素操作都会默念一遍:先 y 后 x,先行后列

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

这是图像处理里最基础也最重要的操作之一。

2.5.1 BGR 与 RGB

前面说了,OpenCV 默认是 BGR。但很多其他库(比如 matplotlib)用的是 RGB。所以显示时经常需要转换。

# BGR 转 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# RGB 转 BGR
img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)

你想想看,如果你用 matplotlib 显示 OpenCV 读进来的图,颜色会偏蓝偏黄。就是因为 BGR 和 RGB 的通道顺序反了。

2.5.2 BGR 转灰度

灰度图只有亮度信息,没有颜色。很多算法(比如边缘检测、人脸识别)第一步就是转灰度,因为数据量小、处理快。

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

灰度转换的公式是:Gray = 0.299*R + 0.587*G + 0.114*B。嗯,人眼对绿色最敏感,所以绿色权重最高。

2.5.3 BGR 转 HSV

HSV 色彩空间,说白了就是把颜色拆成三部分:色调(H)、饱和度(S)、明度(V)

为什么用 HSV?因为它在做颜色分割时特别好用。比如你要从一张图里找出红色的物体,在 BGR 空间里很难界定「什么是红」,但在 HSV 空间里,只需要设定 H 的范围就行。

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

# HSV 范围(OpenCV 中):
# H: 0-179
# S: 0-255
# V: 0-255
💡 我建议: 做颜色检测时,先用 cv2.inRange() 配合 HSV 做掩码,效果比 BGR 好得多。比如检测红色:
lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)

2.6 综合示例:一个完整的图像处理流程

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

import cv2
import numpy as np

# 1. 读取图像
img = cv2.imread('cat.jpg')
if img is None:
    print("图片没读到")
    exit()

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

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

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

# 5. 修改像素:在图像中心画一个蓝色方块
h, w = img.shape[:2]
center_y, center_x = h // 2, w // 2
img[center_y-10:center_y+10, center_x-10:center_x+10] = [255, 0, 0]

# 6. 保存修改后的图像
cv2.imwrite('cat_modified.jpg', img)

cv2.waitKey(0)
cv2.destroyAllWindows()

这个例子虽然简单,但涵盖了读取、显示、转换、像素操作、保存这五个核心步骤。你把它跑通了,图像基础操作就算入门了。

2.7 本章小结

操作 函数 注意事项
读取 cv2.imread() 路径不能有中文,返回 None 时不报错
显示 cv2.imshow() 必须配合 waitKey(),否则窗口一闪而过
保存 cv2.imwrite() JPEG 有损,PNG 无损
像素操作 img[y, x] 先 y 后 x,先行后列
色彩转换 cv2.cvtColor() OpenCV 默认 BGR,HSV 中 H 范围 0-179

嗯,这一章的内容就这些。说白了就是四个字:读、显、存、转。把这四个字练熟了,后面的章节你会走得很顺。

下一章,咱们聊聊图像的几何变换——缩放、旋转、仿射变换。到时候你会发现,像素操作和色彩空间转换的知识全都能用上。