2、OpenCV基础与图像操作:图像读取与显示、图像属性获取、图像保存、图像缩放与裁剪、颜色空间转换、图像绘制(矩形、圆形、文字)
好,咱们正式开始动手了。这一章是地基中的地基——图像的基本操作。你想想看,不管后面做多复杂的目标检测,第一步永远是把图像读进来、看一眼、确认没问题,然后才谈得上处理。我见过不少新手,模型调得飞起,结果连图像路径写错了都不知道,白白浪费半天时间。
所以这一章,咱们把 OpenCV 最常用的几个图像操作彻底搞明白。别嫌简单,这些操作我到现在每天都在用。
2.1 图像读取与显示
OpenCV 读取图像用 cv2.imread(),显示用 cv2.imshow()。就这么两个函数,但细节不少。
核心函数速查
cv2.imread(path, flag)— 读取图像,返回 NumPy 数组cv2.imshow(winname, img)— 显示图像cv2.waitKey(delay)— 等待键盘输入,单位毫秒cv2.destroyAllWindows()— 关闭所有窗口
读取时的 flag 参数很关键。我个人习惯用这三个:
| Flag 值 | 含义 | 适用场景 |
|---|---|---|
cv2.IMREAD_COLOR (1) |
彩色图像,忽略透明度 | 绝大多数情况 |
cv2.IMREAD_GRAYSCALE (0) |
灰度图像 | 边缘检测、模板匹配 |
cv2.IMREAD_UNCHANGED (-1) |
包含 Alpha 通道 | PNG 透明图处理 |
来看一段最基础的代码:
import cv2
# 读取图像
img = cv2.imread('cat.jpg')
# 检查是否读取成功
if img is None:
print("图像读取失败,检查路径!")
exit()
# 显示图像
cv2.imshow('My Image', img)
# 等待按键,0 表示无限等待
cv2.waitKey(0)
# 清理窗口
cv2.destroyAllWindows()
我曾经踩过的坑:图像路径不要带中文!OpenCV 底层用的是 C 函数,中文路径直接返回 None,而且不报错。你查半天还以为是代码问题,其实就是路径里有「测试.jpg」这种名字。另外,cv2.imread() 不会抛异常,读取失败只返回 None,所以一定要加 if img is None 检查。
2.2 图像属性获取
图像在 OpenCV 里就是一个 NumPy 数组。它的属性就是数组的属性。我经常用这三个:
- shape — 返回 (高度, 宽度, 通道数)。注意!OpenCV 里 shape 的第一个值是行数(高度),第二个是列数(宽度),跟很多人直觉相反。
- dtype — 数据类型,通常是
uint8,范围 0-255。 - size — 像素总数,等于 高 × 宽 × 通道数。
print(f"图像形状: {img.shape}") # 例如 (480, 640, 3)
print(f"数据类型: {img.dtype}") # uint8
print(f"像素总数: {img.size}") # 480*640*3 = 921600
这里有个容易混淆的点:img.shape[0] 是高度,img.shape[1] 是宽度。我刚开始学的时候老搞反,后来记住「先高后宽,先行后列」就再也没错过。
2.3 图像保存
保存用 cv2.imwrite()。很简单,但有个细节:
cv2.imwrite('output.jpg', img)
它会根据文件扩展名自动选择编码格式。保存 JPG 时可以控制质量:
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
质量参数范围 0-100,默认 95。数值越大文件越大,画质越好。我一般用 90-95,平衡体积和画质。
小技巧:保存 PNG 时可以设置压缩级别,cv2.IMWRITE_PNG_COMPRESSION,范围 0-9,默认 3。数值越大压缩越狠,但速度越慢。我一般用 6,体积和速度比较均衡。
2.4 图像缩放与裁剪
这两个操作在目标检测预处理里几乎必用。YOLO 输入尺寸通常是 640×640,你拍的照片可能是 1920×1080,不缩放怎么行?
缩放:cv2.resize()
# 指定目标尺寸
resized = cv2.resize(img, (640, 480))
# 按比例缩放
scale_percent = 50 # 缩小到 50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))
插值方法的选择:
| 插值方法 | 适用场景 |
|---|---|
cv2.INTER_NEAREST |
速度最快,效果最差,有锯齿 |
cv2.INTER_LINEAR |
默认方法,速度和质量均衡 |
cv2.INTER_CUBIC |
放大时效果好,但速度慢 |
cv2.INTER_AREA |
缩小时效果好,避免摩尔纹 |
我个人习惯:缩小用 INTER_AREA,放大用 INTER_LINEAR 或 INTER_CUBIC。
裁剪:NumPy 切片
裁剪不需要调用函数,直接用数组切片就行:
# 裁剪 [y1:y2, x1:x2]
cropped = img[100:400, 200:500]
注意顺序:先 y 后 x,先高后宽。跟 shape 的顺序一致。
注意:裁剪时坐标不要越界,否则会报错或者得到空数组。我一般会加个边界检查:y2 = min(y2, img.shape[0])。
2.5 颜色空间转换
OpenCV 默认读取的图像是 BGR 格式,不是 RGB。这一点太容易踩坑了。你用 matplotlib 显示图像,颜色会偏蓝偏怪,就是因为 OpenCV 的 BGR 和 matplotlib 的 RGB 顺序反了。
# BGR 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR 转 RGB
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
HSV 在颜色检测里特别有用。比如你要检测红色物体,在 BGR 空间里很难设定阈值,但在 HSV 空间里,只需要锁定 H 通道的范围就行。
经验之谈:OpenCV 里 HSV 的 H 范围是 0-180,不是 0-360。S 和 V 范围是 0-255。这个跟其他库不一样,注意区分。
2.6 图像绘制
画框、画圆、写字——目标检测结果可视化全靠这些。YOLO 检测到目标后,你要在图像上画出边界框、标出类别和置信度,这些操作就是基础。
画矩形
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
参数依次是:图像、左上角坐标、右下角坐标、颜色 (BGR)、线宽。线宽为 -1 时填充整个矩形。
画圆形
cv2.circle(img, (cx, cy), radius, (0, 0, 255), -1)
参数:图像、圆心、半径、颜色、线宽。线宽 -1 表示实心圆。
写文字
cv2.putText(img, 'Cat', (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
参数:图像、文字内容、左下角坐标、字体、字号、颜色、线宽。
来看一个完整的绘制示例:
import cv2
import numpy as np
# 创建空白图像
img = np.zeros((400, 600, 3), dtype=np.uint8)
img[:] = (240, 240, 240) # 浅灰色背景
# 画矩形
cv2.rectangle(img, (50, 50), (250, 200), (0, 255, 0), 3)
# 画圆形
cv2.circle(img, (400, 125), 75, (0, 0, 255), -1)
# 写文字
cv2.putText(img, 'OpenCV Demo', (50, 350),
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 0), 2)
cv2.imshow('Drawing', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
本章知识结构
下面这张图把本章的核心操作串起来了。从读取图像开始,到各种处理,再到保存结果,流程很清晰。
嗯,这一章的内容就这些。看起来操作很多,其实核心就是几个函数来回用。你把这些练熟了,后面做目标检测的预处理和后处理就会非常顺手。记住:图像是 NumPy 数组,操作数组就是操作图像。这个观念贯穿整个 OpenCV。