2. OpenCV 核心基础:图像读取与显示
好,咱们正式开始动手了。这一章我带你过一遍 OpenCV 最基础的操作——说白了就是怎么把一张图片弄进程序里,怎么看看它长什么样,再把它存回去。这些操作你以后每天都会用到,所以咱们一次把它搞透。
本章核心知识点一览
- 图像读取与显示:
imread()、imshow() - 图像属性:
shape、dtype - 颜色空间转换:BGR→RGB、BGR→Gray
- 图像缩放与裁剪
- 图像保存:
imwrite()
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、缩放到统一尺寸、裁剪感兴趣区域、保存结果。