3、图像基础操作:读取、显示、保存图像,理解BGR与RGB色彩空间
好,咱们正式开始动手了。
这一节,我带你搞定图像处理里最基础、也最绕不开的几个操作——读取、显示、保存。听起来简单吧?但这里头有个坑,几乎所有新手都会踩进去,那就是色彩空间的问题。
你想想看,你用手机拍了一张照片,在电脑上打开,颜色很正常。但用OpenCV一读,再显示出来,咦?怎么蓝汪汪的?或者红彤彤的?
别急,这就是BGR和RGB在搞鬼。我当年第一次遇到这情况,还以为是代码写错了,折腾了半天。后来才明白,这是OpenCV的“小脾气”。
3.1 读取图像:imread() 的用法与参数
读取图像,用的是 cv2.imread()。这个函数,说白了就是把硬盘上的图片文件,加载成一个NumPy数组。
import cv2
# 读取彩色图像
img_color = cv2.imread('example.jpg')
# 读取灰度图像
img_gray = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 读取原始图像(包含Alpha通道)
img_unchanged = cv2.imread('example.jpg', cv2.IMREAD_UNCHANGED)
这里有几个常用的读取模式,我整理了一下:
| 标志 | 数值 | 说明 |
|---|---|---|
cv2.IMREAD_COLOR |
1 | 默认模式,加载彩色图像,忽略Alpha通道 |
cv2.IMREAD_GRAYSCALE |
0 | 加载为灰度图像 |
cv2.IMREAD_UNCHANGED |
-1 | 加载原始图像,包括Alpha通道(如果有的话) |
我个人习惯,在不确定图像格式时,先用 cv2.IMREAD_UNCHANGED 读进来,然后检查一下数组的shape,再决定怎么处理。这样最保险。
imread() 不会报错,而是返回 None。所以,读取后一定要检查一下:
if img is None:
print("图片没读到,检查路径!")
我曾经因为这个bug,调试了半小时,结果发现是文件名多打了一个空格。
3.2 显示图像:imshow() 与窗口管理
图像读进来了,总得看一眼吧?用 cv2.imshow()。
cv2.imshow('窗口标题', img_color)
cv2.waitKey(0) # 等待按键,0表示无限等待
cv2.destroyAllWindows()
这里有个关键点:imshow() 之后,必须跟 waitKey(),否则窗口一闪就没了。你想想看,程序执行完就退出了,窗口还没来得及显示呢。
waitKey() 的参数是等待的毫秒数。0 表示一直等,直到你按任意键。如果你想让窗口自动关闭,比如显示1秒,就写 cv2.waitKey(1000)。
cv2.waitKey(1) & 0xFF == ord('q') 来做实时视频的退出检测。按 'q' 键退出,很实用。
另外,destroyAllWindows() 是关闭所有OpenCV创建的窗口。如果你只想关某一个,可以用 cv2.destroyWindow('窗口标题')。
3.3 保存图像:imwrite() 的注意事项
保存图像用 cv2.imwrite()。这个函数会根据文件扩展名自动选择保存格式。
# 保存为PNG
cv2.imwrite('output.png', img_color)
# 保存为JPEG,设置质量参数
cv2.imwrite('output.jpg', img_color, [cv2.IMWRITE_JPEG_QUALITY, 95])
保存时,有几个参数可以控制:
- JPEG质量:0-100,默认95。数值越大,质量越高,文件也越大。
- PNG压缩级别:0-9,默认3。数值越大,压缩率越高,但保存越慢。
嗯,这里要注意:imwrite() 保存的是BGR顺序的图像。如果你之前把图像转成了RGB,直接保存的话,颜色会乱掉。我就在这上面吃过亏——做图像拼接时,中间处理用了RGB,最后保存忘了转回来,结果输出了一堆“蓝脸”的照片。
3.4 核心概念:BGR vs RGB 色彩空间
好,重点来了。为什么OpenCV要用BGR?
说白了,这是个历史遗留问题。早期的一些相机和显示设备,硬件上就是BGR排列的。OpenCV为了兼容这些设备,就沿用了BGR顺序。而其他库,比如matplotlib、PIL,用的都是RGB。
所以,当你用OpenCV读图,然后用matplotlib显示时,颜色就乱了。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('example.jpg')
# 直接显示——颜色不对!
plt.imshow(img)
plt.show()
你会看到图像偏蓝。为什么?因为matplotlib以为数据是RGB,把B通道当成了R通道来显示。
解决办法很简单:转换一下。
# 方法1:用cv2.cvtColor
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
# 方法2:手动翻转通道
img_rgb = img[:, :, ::-1] # 把第3维的顺序反过来
plt.imshow(img_rgb)
我个人更推荐用 cv2.cvtColor(),因为它的意图更明确,代码可读性更好。手动翻转虽然快,但容易让人迷惑。
- OpenCV 读图 → BGR
- OpenCV 显示 → BGR(用imshow没问题)
- 其他库显示 → 需要转成RGB
- 保存图像 → OpenCV自动按BGR保存
3.5 实战:一个完整的图像处理流程
咱们把上面这些串起来,写一个完整的例子。假设我们要读一张图,转成灰度,再保存。
import cv2
# 1. 读取彩色图像
img = cv2.imread('input.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('output_gray.jpg', gray)
# 6. 等待按键,然后关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
这个流程,你在做图像拼接时,几乎每一步都会用到。读取源图像、显示中间结果、保存最终全景图,都离不开这些基础操作。
imshow(),看一眼中间结果。比如图像拼接时,特征匹配完了,我会显示一下匹配结果,确认没问题再继续。这样能及早发现问题,避免到最后一步才发现不对劲。
3.6 常见问题与避坑指南
最后,分享几个我踩过的坑:
- 路径问题: 中文路径有时会出问题。我建议所有路径都用英文,或者用
cv2.imdecode()处理中文路径。 - 图像尺寸: 显示超大图像时,窗口可能超出屏幕。可以用
cv2.namedWindow('win', cv2.WINDOW_NORMAL)创建可调整大小的窗口。 - 颜色转换: 记住,
cv2.cvtColor()的转换代码很多,比如COLOR_BGR2GRAY、COLOR_BGR2RGB、COLOR_BGR2HSV等。别记混了。
我曾经在做一个全景拼接项目时,因为忘记转换色彩空间,导致拼接结果的颜色完全不对。排查了半天,最后发现是中间某一步用了matplotlib显示,但保存时没转回BGR。嗯,从那以后,我每次保存前都会确认一下色彩顺序。
好了,这一节的内容就到这里。图像的基础操作,说白了就是读、显、存,再加上理解BGR和RGB的区别。这些是后续所有图像处理工作的基石,一定要熟练掌握。