3、图像IO实战:imread()的flags参数详解、imwrite()保存图片、imshow()显示窗口、waitKey()与destroyAllWindows()的正确用法

图像IO,说白了就是图片的读写和显示。这是OpenCV最基础的操作,但也是坑最多的地方。我记得刚入行时,就因为imread的flags参数没搞明白,调试了一整天——图片读进来全是黑的,还以为是摄像头坏了。

今天咱们就把这几个函数彻底讲透。你跟着我走一遍,以后绝对不会在这些基础操作上翻车。

3.1 imread():图片读取的玄机

先看最常用的函数:cv2.imread()。它的原型很简单:

img = cv2.imread(filename, flags=None)

第一个参数是文件路径,第二个参数是读取模式。这个flags参数,我建议你认真对待。它决定了图片以什么方式加载到内存中。

flags参数详解

标志 数值 含义
cv2.IMREAD_COLOR 1 默认模式。加载彩色图片,忽略透明度。输出是BGR三通道
cv2.IMREAD_GRAYSCALE 0 以灰度模式加载。输出是单通道
cv2.IMREAD_UNCHANGED -1 加载原图所有通道,包括Alpha通道(透明度)
cv2.IMREAD_ANYCOLOR 4 以任何可能的颜色深度读取
cv2.IMREAD_ANYDEPTH 2 保留原图的深度信息(如16位、32位浮点图)
cv2.IMREAD_REDUCED_COLOR_2 17 将图片缩小为1/2后再加载为彩色
cv2.IMREAD_REDUCED_GRAYSCALE_2 16 将图片缩小为1/2后再加载为灰度

重点提醒:OpenCV默认使用BGR色彩空间,不是RGB!这一点和matplotlib、PIL完全不同。我在项目中就因为这个坑,把红蓝通道搞反了,显示出来的图片颜色怪怪的。

我个人习惯这样写:

import cv2

# 彩色读取
img_color = cv2.imread('photo.jpg', cv2.IMREAD_COLOR)
# 或者直接写 1
img_color = cv2.imread('photo.jpg', 1)

# 灰度读取
img_gray = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE)
# 或者直接写 0
img_gray = cv2.imread('photo.jpg', 0)

# 保留Alpha通道
img_png = cv2.imread('logo.png', cv2.IMREAD_UNCHANGED)

小技巧:如果你不确定图片是否存在,可以用 if img is None: 来判断。我曾经在批量处理图片时,因为路径写错了一个字母,程序跑完才发现全是空数据。从那以后,我每次读图都会加这个判断。

3.2 imwrite():保存图片的正确姿势

保存图片用 cv2.imwrite()。它的用法也很直接:

cv2.imwrite(filename, img, params=None)

第一个参数是保存路径,第二个参数是要保存的图像数据,第三个参数是可选参数,用来控制图片质量。

不同格式的保存参数

格式 参数 说明
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,质量设为90
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

# 保存为PNG,压缩级别设为9(最小文件体积)
cv2.imwrite('output.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 9])

注意:imwrite() 要求图片数据必须是8位无符号整数(uint8)或16位无符号整数(uint16)。如果你处理的是浮点型数据,记得先转换类型。我见过有人把归一化后的0-1浮点图直接保存,结果得到一张全白的图片。

3.3 imshow():显示窗口的艺术

cv2.imshow() 用来显示图片。它的用法是:

cv2.imshow(winname, mat)

第一个参数是窗口名称,第二个参数是要显示的图像。

嗯,这里要注意:imshow() 本身不会阻塞程序。它只是把图像数据推送到显示缓冲区。真正让窗口显示出来并等待用户操作的是 waitKey()

cv2.imshow('My Image', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()  # 关闭所有窗口

核心要点:imshow() + waitKey() + destroyAllWindows() 这三个函数必须配合使用。少了任何一个,窗口显示都会出问题。

3.4 waitKey():键盘事件的正确用法

waitKey() 是OpenCV中一个非常关键的函数。它的作用是:

  • 等待用户按键
  • 处理窗口事件(比如窗口重绘)
  • 返回按键的ASCII码
key = cv2.waitKey(delay)

delay参数的单位是毫秒:

  • waitKey(0):无限等待,直到用户按键
  • waitKey(30):等待30毫秒,超时后继续执行
  • waitKey(1):等待1毫秒,常用于视频处理

我常用的一个模式是:

while True:
    cv2.imshow('Video', frame)
    key = cv2.waitKey(30) & 0xFF
    if key == ord('q'):  # 按q退出
        break
    elif key == ord('s'):  # 按s保存
        cv2.imwrite('capture.jpg', frame)
        print('截图已保存')

为什么用 & 0xFF? 因为不同操作系统下,waitKey()返回的值可能不同。加上 & 0xFF 可以确保只取低8位,保证跨平台兼容性。这个坑我踩过——在Windows上跑得好好的,换到Linux上按键就不灵了。

3.5 destroyAllWindows():优雅地关闭窗口

destroyAllWindows() 用来关闭所有由OpenCV创建的窗口。它的用法很简单:

cv2.destroyAllWindows()

如果你只想关闭特定窗口,可以用:

cv2.destroyWindow('My Image')

重要提醒:在Jupyter Notebook或某些IDE中,如果不调用destroyAllWindows(),窗口可能会一直停留在屏幕上,甚至导致内核崩溃。我建议你在每个显示操作后都加上这个清理步骤。

3.6 完整实战示例

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

import cv2

# 1. 读取图片
img = cv2.imread('lena.jpg', cv2.IMREAD_COLOR)
if img is None:
    print('图片读取失败,请检查路径')
    exit()

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

# 3. 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray', gray)

# 4. 保存灰度图
cv2.imwrite('lena_gray.jpg', gray, [cv2.IMWRITE_JPEG_QUALITY, 95])

# 5. 等待按键
print('按任意键退出...')
key = cv2.waitKey(0) & 0xFF

# 6. 清理
cv2.destroyAllWindows()

print(f'你按下了键: {chr(key) if 32 < key < 127 else "非打印字符"}')

这个例子涵盖了今天讲的所有知识点。你可以在自己的电脑上跑一下,看看效果。

说实话,图像IO看起来简单,但真正用好它,需要理解每个参数背后的含义。尤其是flags参数,它直接影响后续所有图像处理的结果。我建议你多试试不同的flags组合,看看输出有什么不同。

下一章,我们会深入图像的基本操作——像素访问、ROI提取、通道拆分与合并。这些都是图像处理的基本功,准备好了吗?