2. OpenCV 核心基础:图像读取与显示

好,咱们正式开始动手了。这一章我带你过一遍 OpenCV 最基础的操作——说白了就是怎么把一张图片弄进程序里,怎么看看它长什么样,再把它存回去。这些操作你以后每天都会用到,所以咱们一次把它搞透。

本章核心知识点一览

  • 图像读取与显示:imread()、imshow()
  • 图像属性:shape、dtype
  • 颜色空间转换:BGR→RGB、BGR→Gray
  • 图像缩放与裁剪
  • 图像保存:imwrite()
OpenCV 图像基础操作流程 图像文件 imread() 读取 属性查看 shape/dtype 颜色空间转换 缩放与裁剪 imwrite() 保存 注意:OpenCV 默认 使用 BGR 通道顺序

2.1 图像读取:imread()

在 OpenCV 里读图,就用 cv2.imread()。这个函数很简单,传一个文件路径进去就行。但我得提醒你一个坑——路径别带中文。我刚开始用的时候,图省事把图片放在「桌面/测试图片」文件夹里,结果死活读不出来。后来才发现,OpenCV 的 imread() 对中文路径支持不好,换成英文路径就一切正常了。

import cv2

# 读取彩色图像(默认)
img = cv2.imread('cat.jpg')
print(img)  # 输出一个三维数组

imread() 的第二个参数可以控制读取方式,常用的就这三种:

参数 含义 数值
cv2.IMREAD_COLOR 加载彩色图像,忽略透明度 1
cv2.IMREAD_GRAYSCALE 以灰度模式加载 0
cv2.IMREAD_UNCHANGED 加载原图,包含 Alpha 通道 -1

我的习惯:一般直接用数字 1、0、-1 代替,少打几个字。但团队协作时建议用常量名,代码可读性更好。

2.2 图像显示:imshow()

读进来之后,想看一眼?用 cv2.imshow()。它会弹出一个窗口,把图像显示出来。配合 cv2.waitKey() 和 cv2.destroyAllWindows() 一起用,这是标准三件套。

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

这里有个细节:waitKey() 的参数单位是毫秒。如果你传 1000,窗口显示 1 秒后自动关闭。传 0 的话,你得手动按任意键才会关。我调试的时候经常用 0,方便慢慢看。

我曾经踩过的坑:在 Jupyter Notebook 里用 imshow() 会导致内核卡死。后来我改用 matplotlib 的 imshow() 来显示,或者用 cv2.imshow() 配合 cv2.waitKey(1) 在循环里刷新。总之,Notebook 里慎用阻塞式显示。

2.3 图像属性:shape 和 dtype

图像读进来之后,它其实就是一个 NumPy 数组。所以你可以直接用 .shape 和 .dtype 查看它的尺寸和数据类型。

print(img.shape)   # (高度, 宽度, 通道数)
print(img.dtype)   # uint8

举个例子,一张 640×480 的彩色图片,shape 返回 (480, 640, 3)。注意顺序:行数(高度)在前,列数(宽度)在后。很多人一开始会搞反,你想想看,数组索引是先 row 后 column,所以 shape 也是先高后宽。

dtype 通常是 uint8,也就是 0~255 的整数。如果是 16 位深图或者浮点图,dtype 会相应变化。我做过一个项目,需要处理 12 位 RAW 图,那时候 dtype 就是 uint16,很多函数默认不支持,得手动转一下。

2.4 颜色空间转换

OpenCV 默认用 BGR 顺序,但其他库(比如 matplotlib)用的是 RGB。所以显示的时候经常需要转一下。

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

# BGR → 灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

为什么要转?因为 imread() 读进来是 BGR,而 matplotlib 的 imshow() 默认按 RGB 解析。你不转的话,图像颜色会偏蓝偏怪。我第一次用 matplotlib 显示 OpenCV 图片时,猫变成蓝猫了,还以为是图片坏了……其实就是通道顺序搞反了。

颜色空间转换对照表

转换类型 OpenCV 常量 常见用途
BGR → RGB COLOR_BGR2RGB matplotlib 显示
BGR → GRAY COLOR_BGR2GRAY 灰度处理、边缘检测
BGR → HSV COLOR_BGR2HSV 颜色分割、物体跟踪
BGR → LAB COLOR_BGR2LAB 颜色分析、特征提取

2.5 图像缩放与裁剪

缩放用 cv2.resize(),裁剪其实就是 NumPy 的切片操作。这两个组合起来,基本能搞定大部分预处理需求。

# 缩放:指定目标尺寸
img_resized = cv2.resize(img, (320, 240))

# 缩放:按比例缩放(fx, fy)
img_scaled = cv2.resize(img, None, fx=0.5, fy=0.5)

# 裁剪:切片操作 [y1:y2, x1:x2]
img_cropped = img[100:300, 200:400]

缩放的时候,cv2.resize() 默认用双线性插值。如果你要缩小图片,用 cv2.INTER_AREA 效果更好;放大图片用 cv2.INTER_CUBIC 或 cv2.INTER_LINEAR。我一般缩小用 INTER_AREA,放大用 INTER_LINEAR,又快又稳。

裁剪就简单了,直接对 NumPy 数组做切片。注意切片顺序:[行范围, 列范围]。比如 img[100:300, 200:400] 表示取第 100 行到第 299 行、第 200 列到第 399 列的区域。

避坑指南:缩放时目标尺寸是 (宽度, 高度),而 shape 是 (高度, 宽度, 通道数)。这两个顺序是反的,写代码时容易搞混。我习惯在 resize 前先打印一下 shape,确认宽高再写参数。

2.6 图像保存:imwrite()

处理完的图像,用 cv2.imwrite() 保存。第一个参数是文件名,第二个参数是要保存的图像数据。

cv2.imwrite('output.jpg', img_resized)
cv2.imwrite('output.png', img_cropped)

保存格式由文件扩展名决定。JPEG 可以指定质量参数,PNG 可以指定压缩级别。比如:

# JPEG 质量 95(默认 95,范围 0~100)
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])

# PNG 压缩级别 3(默认 3,范围 0~9,越大文件越小但越慢)
cv2.imwrite('output.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 3])

我做过一个批量处理图片的项目,需要把几百张图从 JPEG 转成 PNG。当时没注意质量参数,结果 JPEG 压缩太狠,转成 PNG 后文件反而变大了。后来我把 JPEG 质量调到 100,再转 PNG,文件大小才降下来。嗯,细节决定成败。

注意:imwrite() 保存的图像是 BGR 顺序。如果你之前转成了 RGB,保存前要再转回 BGR,否则颜色会错乱。我刚开始做图像处理时,就因为这个原因,保存的图片颜色一直不对,排查了半天才发现是通道顺序的问题。

2.7 综合示例:完整流程

把上面这些串起来,就是一个完整的图像处理流程:

import cv2

# 1. 读取图像
img = cv2.imread('cat.jpg')

# 2. 查看属性
print('原始尺寸:', img.shape)
print('数据类型:', img.dtype)

# 3. 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 4. 缩放
gray_resized = cv2.resize(gray, (300, 300))

# 5. 裁剪
cropped = gray_resized[50:200, 50:200]

# 6. 保存
cv2.imwrite('cat_processed.jpg', cropped)

print('处理完成!')

这个例子虽然简单,但涵盖了图像处理中最常用的几个操作。你以后做目标检测项目时,数据预处理基本就是这些步骤的组合——读图、转灰度或 RGB、缩放到统一尺寸、裁剪感兴趣区域、保存结果。