在OpenCV中限制Roboflow模型仅在用户自定义区域推理
解决方案:在OAK设备端实现ROI区域的硬件级裁剪推理
核心思路是利用OAK设备的硬件加速图像裁剪功能,在图像送入模型推理前就只截取用户指定的矩形区域,避免模型处理整帧画面,从根源上提升效率。以下是具体实现步骤:
1. 基础准备:获取用户绘制的ROI坐标
通过OpenCV鼠标回调获取用户绘制的矩形坐标(x1, y1为左上角,x2, y2为右下角),确保坐标符合相机输出尺寸范围,同时统一坐标顺序(保证x1 < x2、y1 < y2)。
2. 构建支持ROI裁剪的DepthAI Pipeline
OAK的DepthAI框架提供ImageManip节点,可在硬件层完成图像裁剪与缩放,无需CPU介入。结合roboflowoak库,我们可以自定义Pipeline实现ROI推理:
import depthai as dai import cv2 from roboflowoak import RoboflowOak # 初始化ROI(可通过鼠标回调动态更新) roi_x1, roi_y1 = 150, 150 roi_x2, roi_y2 = 450, 450 roi_w = roi_x2 - roi_x1 roi_h = roi_y2 - roi_y1 # 创建DepthAI Pipeline pipeline = dai.Pipeline() # 配置相机输入 cam_rgb = pipeline.create(dai.node.ColorCamera) cam_rgb.setPreviewSize(640, 480) # 匹配模型输入尺寸或相机原生尺寸 cam_rgb.setInterleaved(False) # 添加硬件裁剪节点 manip = pipeline.create(dai.node.ImageManip) # 设置裁剪区域(参数为相对比例:x_start, y_start, width_ratio, height_ratio) manip.initialConfig.setCropRect(roi_x1/640, roi_y1/480, roi_w/640, roi_h/480) # 将裁剪后的图像缩放到模型要求的输入尺寸(例如640x640) manip.initialConfig.setResize(640, 640) cam_rgb.preview.link(manip.inputImage) # 初始化RoboflowOak,传入自定义Pipeline rf = RoboflowOak( model="your-billiards-model-id", confidence=0.5, overlap=0.5, version="your-model-version", api_key="your-roboflow-api-key", pipeline=pipeline )
3. 动态更新ROI(可选)
如果需要用户随时调整ROI,可在鼠标回调中动态发送新的裁剪配置到ImageManip节点:
def mouse_callback(event, x, y, flags, param): global roi_x1, roi_y1, roi_x2, roi_y2, drawing if event == cv2.EVENT_LBUTTONDOWN: drawing = True roi_x1, roi_y1 = x, y elif event == cv2.EVENT_MOUSEMOVE and drawing: roi_x2, roi_y2 = x, y elif event == cv2.EVENT_LBUTTONUP: drawing = False # 修正坐标顺序 roi_x1, roi_x2 = min(roi_x1, roi_x2), max(roi_x1, roi_x2) roi_y1, roi_y2 = min(roi_y1, roi_y2), max(roi_y1, roi_y2) roi_w = roi_x2 - roi_x1 roi_h = roi_y2 - roi_y1 # 发送新的裁剪配置 config = dai.ImageManipConfig() config.setCropRect(roi_x1/640, roi_y1/480, roi_w/640, roi_h/480) config.setResize(640, 640) manip.inputConfig.send(config) # 绑定鼠标回调 cv2.namedWindow("Billiards Detection") cv2.setMouseCallback("Billiards Detection", mouse_callback)
4. 推理结果映射回原帧
由于模型是在裁剪后的图像上推理,检测框坐标需要映射回原帧的全局坐标:
drawing = False while True: result, _, raw_frame, _ = rf.detect() # 绘制用户指定的ROI cv2.rectangle(raw_frame, (roi_x1, roi_y1), (roi_x2, roi_y2), (0, 0, 255), 2) # 映射检测框坐标并绘制 for pred in result.get("predictions", []): # 裁剪后的图像上的坐标转换为原帧坐标 pred_x = pred["x"] * (roi_w / 640) + roi_x1 pred_y = pred["y"] * (roi_h / 640) + roi_y1 pred_w = pred["width"] * (roi_w / 640) pred_h = pred["height"] * (roi_h / 640) # 计算检测框的左上角和右下角 top_left = (int(pred_x - pred_w/2), int(pred_y - pred_h/2)) bottom_right = (int(pred_x + pred_w/2), int(pred_y + pred_h/2)) cv2.rectangle(raw_frame, top_left, bottom_right, (0, 255, 0), 2) cv2.putText(raw_frame, pred["class"], top_left, cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2) cv2.imshow("Billiards Detection", raw_frame) if cv2.waitKey(1) == ord('q'): break cv2.destroyAllWindows()
关键优势对比
- 原方案:模型处理整帧→CPU过滤结果,浪费VPU算力,大帧幅下延迟高
- 优化方案:硬件裁剪ROI→模型仅处理目标区域,算力利用率提升,延迟显著降低
注意事项
- 确保
ImageManip的缩放尺寸与模型输入尺寸一致,避免推理错误 - 动态更新ROI时,需保证坐标在相机输出尺寸范围内,防止裁剪参数越界
- 坐标映射时要考虑裁剪区域与原帧的比例关系,避免检测框偏移
内容的提问来源于stack exchange,提问作者Ben gann
相关产品推荐
相关产品推荐

