4、ROI提取实战:使用NumPy切片提取ROI,绘制矩形框标注ROI区域
好了,咱们直接进入正题。上一章讲了ROI的理论基础,这一章咱们就动手干。说白了,ROI提取在OpenCV里就是个数组切片操作,没你想的那么玄乎。
我记得刚带团队那会儿,有个新人问我:“老师,ROI提取是不是要用什么高级算法?”我笑了笑,告诉他:“你学过NumPy切片吗?会了那个,ROI提取就学会了八成。”他当时一脸懵,后来看完代码直呼简单。
4.1 NumPy切片提取ROI的核心原理
OpenCV读取的图像,本质上就是一个NumPy数组。彩色图像是三维数组——高、宽、通道数。灰度图像是二维数组——高、宽。
提取ROI,就是从这个大数组里切出一小块来。语法很简单:
roi = image[y_start:y_end, x_start:x_end]
注意顺序:先y(行),后x(列)。这个顺序跟数学坐标系不一样,很多新手在这里栽跟头。我刚开始也搞反过,调试了半天才发现是行列顺序搞错了。
核心要点:
- image.shape 返回 (height, width, channels)
- 切片顺序:image[行范围, 列范围]
- y_start:y_end 控制垂直方向
- x_start:x_end 控制水平方向
4.2 实战:从图像中提取人脸ROI
咱们用一个实际例子来演示。假设有一张图片,我们要提取人脸区域。先看完整代码:
import cv2
import numpy as np
# 读取图像
image = cv2.imread('face.jpg')
if image is None:
print("图片加载失败,请检查路径")
exit()
# 显示图像尺寸
h, w = image.shape[:2]
print(f"图像尺寸:{w}x{h}")
# 假设人脸区域在图像中央偏上位置
# 手动指定ROI坐标(实际项目中会用检测算法自动获取)
x_start, y_start = 200, 150
x_end, y_end = 400, 350
# 使用NumPy切片提取ROI
face_roi = image[y_start:y_end, x_start:x_end]
# 在原始图像上绘制矩形框标注ROI
cv2.rectangle(image, (x_start, y_start), (x_end, y_end), (0, 255, 0), 2)
# 显示结果
cv2.imshow('Original with ROI', image)
cv2.imshow('Face ROI', face_roi)
cv2.waitKey(0)
cv2.destroyAllWindows()
这段代码做了三件事:读取图片、切片提取ROI、画框标注。你想想看,是不是很简单?
我的经验:实际项目中,ROI坐标很少手动指定。一般会用YOLO或Haar Cascade先检测出目标位置,再把检测框坐标传给切片操作。但理解手动切片是基础,后面自动化检测才能用得顺手。
4.3 绘制矩形框的细节
cv2.rectangle这个函数,参数顺序别搞混了:
cv2.rectangle(img, 左上角坐标, 右下角坐标, 颜色, 线宽)
颜色用BGR格式,不是RGB。比如绿色是(0, 255, 0),红色是(0, 0, 255)。这个坑我踩过——用matplotlib的习惯写RGB颜色,结果画出来颜色不对,排查了半天才发现是OpenCV的BGR顺序问题。
| 颜色 | BGR值 | 常见用途 |
|---|---|---|
| 绿色 | (0, 255, 0) | 检测到目标 |
| 红色 | (0, 0, 255) | 警告/错误 |
| 蓝色 | (255, 0, 0) | 参考区域 |
| 黄色 | (0, 255, 255) | 待确认区域 |
线宽设为-1可以填充矩形,设为2或3就是边框。我个人习惯用2,太粗会遮挡图像细节。
4.4 多ROI提取与批量标注
实际场景中,一张图里可能有多个目标。比如监控画面里有多个人脸,或者工业检测中有多个缺陷区域。这时候就需要批量处理。
# 定义多个ROI区域
roi_list = [
(50, 50, 200, 200), # (x1, y1, x2, y2)
(300, 100, 450, 300),
(100, 300, 250, 450)
]
# 批量提取和标注
for i, (x1, y1, x2, y2) in enumerate(roi_list):
# 提取ROI
roi = image[y1:y2, x1:x2]
# 保存ROI到文件
cv2.imwrite(f'roi_{i}.jpg', roi)
# 绘制矩形框
cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 添加标签
cv2.putText(image, f'ROI {i}', (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
避坑指南:我曾经在批量处理时,没检查ROI坐标是否越界。结果有一张图的ROI超出了图像边界,程序直接崩溃。后来我加了个边界检查:
# 确保坐标不越界
x1 = max(0, min(x1, w-1))
y1 = max(0, min(y1, h-1))
x2 = max(0, min(x2, w-1))
y2 = max(0, min(y2, h-1))
这个习惯救了我很多次,建议你也加上。
4.5 ROI提取与YOLO检测的衔接
为什么要先提取ROI再送YOLO检测?说白了就是减少计算量。YOLO处理整张大图可能耗时50ms,但如果只处理ROI区域,可能只需要10ms。对于实时检测场景,这40ms的差距就是流畅和卡顿的区别。
我做过一个项目,在4K监控画面中检测小目标。整图检测帧率只有8fps,后来改成先提取关键ROI再检测,帧率直接飙到30fps。效果立竿见影。
具体做法是:先用轻量级算法(如背景差分、运动检测)定位ROI,再用YOLO在ROI上做精确检测。这个流程咱们后面章节会详细展开。
4.6 本章小结
嗯,到这里你应该掌握了:
- 用NumPy切片提取ROI:image[y1:y2, x1:x2]
- 用cv2.rectangle绘制矩形框标注ROI
- 批量处理多个ROI区域
- 边界检查的重要性
- ROI提取如何加速YOLO检测
下一章咱们会讲如何用OpenCV的鼠标交互功能,手动框选ROI区域。这样就不用硬编码坐标了,灵活得多。到时候见!