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_BGR2GRAYCOLOR_BGR2RGBCOLOR_BGR2HSV 等。别记混了。

我曾经在做一个全景拼接项目时,因为忘记转换色彩空间,导致拼接结果的颜色完全不对。排查了半天,最后发现是中间某一步用了matplotlib显示,但保存时没转回BGR。嗯,从那以后,我每次保存前都会确认一下色彩顺序。

好了,这一节的内容就到这里。图像的基础操作,说白了就是读、显、存,再加上理解BGR和RGB的区别。这些是后续所有图像处理工作的基石,一定要熟练掌握。