ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

YOLOv5+深度相机:获取目标框中心像素坐标与深度信息

YOLOv5+深度相机:获取目标框中心像素坐标与深度信息 简介一套基于YOLOv5与英特尔RealSense深度相机的目标检测工程面向计算机视觉、人工智能方向的开发者和学习者用于解决常规二维检测缺乏空间信息的问题。工程在识别目标的同时会输出目标anchor中心的像素坐标与深度值可应用于机器人避障、增强现实、智能监控等需要三维感知的实战场景尤其在移动机器人领域具有较高实用价值。压缩包共84个文件以Python脚本27个、YAML模型配置25个和预训练权重为核心并包含依赖清单、说明文档、PyInstaller编译缓存及工程配置文件整体约13.23MB目录按照模型定义、工具函数、配置参数等模块划分方便直接定位和二次开发。目前已有6199人学习下载适合希望从算法原理走向工程实现的读者也适合作为课程设计或竞赛项目的参考起点。工程内置多种YOLOv5规格如n/s/m/l/x的yaml配置和对应权重并提供推理脚本演示如何将RealSense深度图与检测结果对齐从而准确获取目标中心点的距离信息同时可根据实际相机型号调整输入参数灵活性较高。1. 用深度相机补上YOLOv5缺失的那一维第一次用 Intel Realsense D435i 跑 YOLOv5 目标检测时我以为把两个官方示例拼起来就行。实际上彩色图和深度图来自两个传感器像素坐标不经过对齐就采样深度得到的数据全是偏的。标题里的“标注目标物和目标anchor中心的像素坐标和深度信息”这句话本质就是解决 2D 检测框如何从深度相机那里精确取到距离的问题。这份 yolov5_detection 资源自带完整 YOLOv5 模型结构models/common.py 和 models/yolo.py 都在权重放的是 yolov5s.pt入口脚本是 rstest.py。它把目标检测、深度相机、像素坐标与深度信息融合到了一个流程里适合已经会跑基础目标检测、现在要做移动机器人和三维目标检测的开发者。下面按我的拆库顺序从原理讲到工程实现。2. YOLOv5网络结构、anchor与Realsense深度图的对齐在讲拼接代码之前得先把两件事说清楚YOLOv5 的预测结果里到底哪一项是目标 anchor 中心Realsense 深度相机又是怎样把深度图和彩色图对齐到同一个视角。这两件事没理清后面所有深度数值都不可信。2.1 从yolov5s.pt推理结果看anchor与预测框YOLOv5 的 yolov5 网络结构由 Backbone、Neck、Head 三段组成。Backbone 用 CSPDarknet 提取不同尺度特征Neck 用 PANet 做自上而下和自下而上的特征融合Head 最终在 80×80、40×40、20×20 三个特征层输出预测。每个网格会放置多个不同长宽比的 anchoranchor 只是预定义的先验框模型回归的是相对于 anchor 的偏移量最后解码出目标框和置信度。资源里的 models/yolov5s.yaml 定义了 Backbone 每一层的通道数weights/yolov5s.pt 则保存了在 COCO 数据集上训练好的参数。推理时输入一张 640×640 的 RGB 图输出可以整理成一个 1×25200×85 的张量25200 是三个特征层所有 anchor 位置相加的总数85 由 4 个框坐标、1 个置信分数和 80 个类别分数组成。注意这里“anchor 中心”在最终结果里并不是原始先验框中心而是模型预测框的中心但它仍然由 anchor 解码而来所以工程里习惯叫它目标 anchor 中心。import torch from models.experimental import attempt_load device torch.device(cuda if torch.cuda.is_available() else cpu) model attempt_load(weights/yolov5s.pt, map_locationdevice) model.eval() dummy torch.rand(1, 3, 640, 640).to(device) with torch.no_grad(): pred model(dummy)[0] print(pred.shape) # (1, 25200, 85)上面代码中 attempt_load 是 YOLOv5 本地工程加载权重的方式和 torch.hub.load 相比不依赖网络缓存。dummy 的 640×640 是常用输入分辨率实际使用时可以换成 416 或 1280但要注意模型权重的 anchor 在训练时是为特定尺度设计的。pred.shape 里的第一维是 batch size第二维是所有预测位置总和第三维是预测信息。拿到 pred 后还需要按 confidence 过滤并用 NMS 去重真正画框用的是经过 NMS 的列表。2.2 Realsense D435i彩色图与深度图对齐align的用法Intel Realsense D435i 的左右红外相机输出深度图右侧的 RGB 模块输出彩色图。由于两个模组在物理上存在位置偏移同一物体在深度图上的像素坐标和彩色图上的像素坐标并不一致。这就是为什么不能直接拿深度帧的 (u,v) 去读检测框中心必须先做对齐。pyrealsense2 的 rs.align 是最常用的对齐方式。它接收一个目标坐标系的 stream 类型内部通过硬件标定参数把深度图重投影到彩色图坐标系得到一张逐像素与彩色图对齐的新深度帧。import pyrealsense2 as rs pipeline rs.pipeline() config rs.config() config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) profile pipeline.start(config) align rs.align(rs.stream.color) frames pipeline.wait_for_frames() aligned_frames align.process(frames) aligned_depth_frame aligned_frames.get_depth_frame() color_frame aligned_frames.get_color_frame()这里有两个参数值得说明第一个是 depth 和 color 的分辨率都设成 640×480对齐后的深度图可以直接用彩色图像的像素坐标索引第二个是 rs.align 的构造参数 rs.stream.color它表示把深度帧对齐到彩色帧。如果写成 rs.align(rs.stream.depth)方向就反了后续从彩色检测框中心读取深度会得到偏移量很大的结果。在嵌入式平台上如果觉得 640×480 深度空洞明显可以尝试降低到 424×240但检测框分辨率也会跟着下降。2.3 深度内参与反投影像素坐标到三维坐标对齐完成后深度图上的每个 (u,v) 就和彩色图对应。此时读取到的深度值是 Z单位由 API 决定pyrealsense2 的 get_distance 返回的是浮点米直接访问像素数据得到的是 uint16 毫米。三维坐标可以按小孔成像模型反投影X (u - cx) * Z / fxY (v - cy) * Z / fy。这里 fx、fy、cx、cy 是深度相机内参通常可以用 K 矩阵从 rs.intrinsics 读取。参数含义对融合结果的影响fx水平方向焦距fx 不准确会让 X 方向距离误差放大fy垂直方向焦距同理影响 Y 方向cx图像主点 x 坐标偏移会导致目标越靠边缘偏差越大cy图像主点 y 坐标影响目标竖直位置depth_scale原始深度值到米的系数项目里通常为 0.001过滤时要用在工程实现中我一般直接用 depth_frame.get_distance(cx, cy) 取 center_px 的深度而不是频繁做内参矩阵计算。因为很多场景只需要中心到相机的直线距离并不需要重建点云。只有要做三维目标检测、计算实际尺寸时才需要把整个公式写出来。这一点也决定了后面的深度信息字段具体怎么输出。3. 环境配置与yolov5s.pt推理链路搭建原理讲清楚了接下来要能跑。这份 zip 里的目录结构一看就是官方 YOLOv5 的目录结构models 目录放网络结构定义utils 目录放数据集处理和评估工具weights 目录下放的是 yolov5s.pt根目录的 rstest.py 是接 Realsense 的检测脚本。之所以说它是工程包而不是教学代码是因为它没有把模型加载写在主循环里更好的组织方式是启动时加载一次然后循环读帧。3.1 requirements.txt与Realsense SDK安装先处理依赖。requirements.txt 里包含了 torch、torchvision、opencv-python、numpy、pyrealsense2 等常用库。如果之前已经装过 YOLOv5 官方环境那么只需要单独补装 pyrealsense2。这里最容易踩的坑是 Python 版本和 torch 版本不匹配。YOLOv5 对 torch 的版本要求并不苛刻但 Windows 下 pyrealsense2 的预编译 wheel 通常比 Linux 的发布晚如果 pip 找不到对应版本建议用 conda 建一个 Python 3.8 环境。python -m venv .venv source .venv/bin/activate # Windows 下是 .venv\Scripts\activate pip install -r requirements.txt python rstest.py --weights weights/yolov5s.pt --conf 0.4命令里的 --conf 0.4 是置信度阈值低于 0.4 的检测结果会被过滤。如果检测不到小目标可以调低到 0.25如果误报太多调到 0.6。第一次运行建议在 USB 3.0 接口上插相机Realsense D435i 的深度流在 USB 2.0 下带宽不够经常报 failed to set streaming request 之类的错误。如果已经安装过重型的 opencv建议把 opencv-python 换成 opencv-python-headless避免和 GUI 工具冲突。3.2 加载yolov5s.pt并完成单帧检测工程里加载权重有两种方式一是官方 detect.py 的 attempt_load二是 torch.hub.load。离线环境建议用第一种因为 attempt_load 直接读取项目内 models/yolo.py不需要联网下载仓库。加载后必须调用 model.eval()否则模型里的 dropout 和 BatchNorm 层仍处于训练模式同一张图每次预测结果都可能不一样。import cv2 import torch from models.experimental import attempt_load device torch.device(cuda if torch.cuda.is_available() else cpu) model attempt_load(weights/yolov5s.pt, map_locationdevice) model.eval() img_bgr cv2.imread(frame.jpg) img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) outputs model(img_rgb, size640) print(type(outputs)) # DetectionModel 的 forward 结果 print(outputs.xyxy[0][0]) # [x1, y1, x2, y2, conf, cls]上面代码最关键的是彩色图通道顺序。opencv 默认读进来是 BGR而 YOLOv5 训练时使用的是 RGB不转换会导致颜色语义错乱尤其是区分蓝色和绿色物体时准确率大幅下降。model(img_rgb, size640) 内部会完成 letterbox 缩放和归一化返回的 xyxy 坐标已经映射回原始图像尺寸不需要自己再做一次等比缩放。outputs.xyxy[0] 是一个 tensor每一行包含 6 个值最后两个分别是 confidence 和 class id。3.3 构造检测-深度融合循环实时目标检测应用里核心循环要处理四件事取彩色帧、做检测、取对齐深度帧、读取中心深度。建议把模型加载和相机 pipeline 放在循环外把单帧检测和深度读取封装成函数。下面是一个最简的循环骨架import cv2 while True: frames pipeline.wait_for_frames() aligned_frames align.process(frames) color_frame aligned_frames.get_color_frame() depth_frame aligned_frames.get_depth_frame() if not color_frame or not depth_frame: continue img np.asanyarray(color_frame.get_data()) results model(img, size640) dets results.xyxy[0].cpu().numpy() for det in dets: x1, y1, x2, y2, conf, cls det if conf 0.4: continue cx int((x1 x2) / 2) cy int((y1 y2) / 2) depth depth_frame.get_distance(cx, cy) cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(img, f{depth:.2f}m, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(yolov5 realsense, img) if cv2.waitKey(1) 0xFF ord(q): break这里第一处值得注意的地方是 results.xyxy[0] 的坐标是浮点类型画框前要转 int第二处是 depth_frame.get_distance(cx, cy) 返回单位是米和真实距离一致。循环里没有做边界检查如果检测框中心恰好在图像边缘会触发 out of bounds这个留给第4章专门处理。for 循环里每个 det 都要追加深度字段最终得到带距离的检测结果列表。4. 标注目标框与anchor中心的像素坐标、深度信息这一章做的是把第3章的循环拆细。目标检测结果通常以 x1,y1,x2,y2 的形式给出这是左上角和右下角的像素坐标。目标 anchor 中心的像素坐标就是框的中心点计算方式简单但要考虑位置有效性、深度图范围和相机量程三个限制条件。缺失这些限制深度信息这张表就是虚的。4.1 从检测框解算目标anchor中心像素坐标先明确计算口径YOLOv5 输出的 x1,y1,x2,y2 是在原始彩色图像尺寸上的坐标不是 640×640 特征图尺寸。所以 cx (x1 x2) / 2cy (y1 y2) / 2 得到的就是目标在彩色图中的 anchor 中心像素坐标。这个中心点对应深度图上同一个位置因为前面已经做了 align。def get_center_from_bbox(box): x1, y1, x2, y2, conf, cls box cx int((x1 x2) / 2) cy int((y1 y2) / 2) return cx, cy假如发现检测框中心不在物体上通常是因为检测框太瘦或置信度太低。比如人形目标只有上半身漏出来时中心点可能落在背景上深度会偏到更远的墙。此时可以改用框的下三分位点作为深度采样点或者先过滤掉宽度或高度小于 10 像素的框。这种启发式规则在移动机器人场景里很常见只要记录规则说明后期调参并不困难。这里还有一个经常被忽略的细节YOLOv5 在推理时对输入图像做了 letterbox多余部分用灰色像素填充因此返回的 xyxy 已经通过坐标逆变换映射回原图尺寸。也就是说只要用的是官方推理接口检测框中心直接落在原图像素上不需要自己做 unletterbox。如果你从 raw 输出张量手写解码就必须在解码后手动恢复坐标变换否则中心点会整体偏移深度采样全部失效。4.2 从对齐深度帧读取目标中心深度值读取深度时pyrealsense2 的 get_distance 方法返回浮点米而通过 numpy 直接访问 depth_frame 的 buffer 得到的是 uint16 毫米。两种方式选一种统一换算成米。还要做三类过滤坐标越界、深度为零或超量程、中心点落在深度空洞。Realsense 在强光、黑色物体表面经常产生零值空洞这时候直接丢弃深度比强行插值更安全。def read_depth_mm(aligned_depth_frame, cx, cy): h aligned_depth_frame.get_height() w aligned_depth_frame.get_width() if cx 0 or cy 0 or cx w or cy h: return None depth aligned_depth_frame.get_distance(cx, cy) if depth 0 or depth 10.0: return None return depth这段过滤逻辑里10.0 是 D435i 在 640×480 分辨率下的典型量程上限。实际量程会随分辨率、环境光照和配置参数改变不要把这个数当成固定阈值。get_distance 返回 0 表示该点无效大于 10 可能是反射造成的飞点。把返回 None 的检测结果标记成 depth0或者在可视化时用不同颜色显示比直接崩溃更容易定位问题。4.3 输出字段与可视化标注最终要把检测框、像素坐标和深度组合成一条记录。为了后面接机器人导航或三维目标检测我习惯把这条记录直接写成字典而不是只打印。这样既能存 log又能方便地转成 ROS topic。字段类型示例来源class_idint0模型输出class_namestrperson从 COCO names 查表scorefloat0.86模型输出置信度bbox_xyxytuple(120,45,310,220)模型输出映射到原图center_pxtuple(215,132)由 bbox 计算depth_mfloat1.23对齐深度帧采样可视化时cv2.rectangle 画检测框cv2.circle 在中心点画一个小圆再用 putText 把 depth_m 和 score 拼到一起写到检测框上方。如果发现深度数值在画面里缓慢漂移优先怀疑深度相机没有在运行时做温度补偿如果数值突然跳变到极大值大概率是中心点落到了金属反光区域。5. 工程化排错深度值滤波、小目标检测与推理提速到了真正接进系统的阶段最影响交付质量的是三个细节深度值抖动、小目标漏检和推理帧率。这三个问题互相关联处理顺序建议是先滤波再调网络输入最后做硬件加速。5.1 中心点深度抖动邻域中值滤波单像素深度在物体边缘上波动非常大尤其是衣服边缘和金属轮廓。直接取中心像素会让距离值有时跳到前景、有时跳到背景。常见做法是取以中心点为中心的 5×5 邻域计算这些有效深度值的中位数。中位数比均值稳健不会被一两个背景飞点带偏。import numpy as np def get_median_depth(aligned_depth_frame, cx, cy, radius2): depth_arr np.asanyarray(aligned_depth_frame.get_data()) patch depth_arr[cy-radius:cyradius1, cx-radius:cxradius1] valid patch[(patch 0) (patch 10000)] # mm if valid.size 0: return None return float(np.median(valid)) / 1000.0这个函数把深度帧转成 numpy 数组后截取邻域再按毫米值中值滤波。参数 radius2 对应 5×5 窗口窗口越大对噪声越平滑但同时也会让距离响应变慢。如果目标靠近相机窗口应适当缩小因为深度变化剧烈的大场景里大窗口容易把表面起伏平均掉。5.2 小目标检测调整输入分辨率与anchor配置小目标漏检在固定距离的监控场景里最明显。YOLOv5 默认在 640×640 下训练COCO 数据集中小目标占比不高。如果深度相机距离目标 5 米以上一个行人可能只有十几个像素这种情况下有两种调整第一把模型输入尺寸从 640 提高到 960 或 1280小目标分辨率变大召回率上涨第二重训时会用到 models/yolov5n.yaml 或 yolov5s.yaml 里的 anchoranchor 是模型预测的基准框默认参数并不一定适合你的场景需要使用 autoanchor 重新聚类。使用 autoanchor 的常见命令是在训练参数里开启 --noautoanchor 决定是否使用默认 anchor。更推荐直接用 YOLOv5 训练脚本在自定义数据集上做一轮自动 anchor 分析它会输出不同尺度下的最佳 anchor 长宽比。对于近距离的深度相机项目目标通常比 COCO 更大所以 anchor 的整体尺度也可以等比放大。单纯调高 confidence 阈值并不会改善小目标反而可能把低置信度的正确框滤掉。5.3 推理提速从CUDA到TensorRT深度相机每秒输出 30 帧目标检测如果卡在每帧 200 毫秒整个系统就失去了实时意义。优先检查模型是否跑在 CUDA 上yolov5s.pt 在 RTX 3060 上通常能跑到 30 帧以上。没有 GPU 的边缘设备可以先把检测尺寸降到 416 或 480再把模型导出成 ONNX 或 TensorRT。import torch model attempt_load(weights/yolov5s.pt, map_locationcpu) model.model[-1].export True dummy torch.zeros(1, 3, 640, 640) torch.onnx.export(model, dummy, yolov5s.onnx, opset_version12)ONNX 导出时 model.model[-1].export True 是为了让检测头输出原始格式否则导出后的模型会多一层推理后处理。真正部署到 Jetson 等平台时TensorRT 的 INT8 量化通常比 ONNX Runtime 再快一倍但量化需要校准集校准集应当包含目标物实际位置的深度分布否则量化误差会放大到检测置信度上。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表