尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于YOLO的社交距离检测实战解析

基于YOLO的社交距离检测实战解析 简介这是一份面向计算机视觉初学者与深度学习实践者的社交距离检测项目资源基于YOLOv3目标检测模型实现人群间距实时分析适用于疫情防控、智慧安防等实际场景。资源包共13个文件包含3个核心Python脚本含主检测逻辑、配置管理与工具函数、YOLO-COCO模型所需cfg与names配置文件、预置测试视频pedestrians.mp4、GIF效果演示、HTML说明页及环境依赖清单整体压缩包仅21.17MB轻量易部署。已有50人学习下载适合希望快速上手目标检测落地应用的开发者。资源附带完整项目结构说明与可复现的CPU运行流程涵盖虚拟环境搭建、依赖安装、权重加载指引及命令行参数详解并提供YouTube配套讲解视频链接便于理解算法原理与代码组织逻辑。1. 项目整体设计与思路拆解1.1 为什么选择YOLO来做社交距离检测提到社交距离检测很多人第一反应是“这不就是两个人之间画条线量距离吗”。但真正落地过类似项目的人都知道难点从来不在“量距离”而在“怎么知道哪里有人”。监控画面是二维图像人可能站着、坐着、走动、重叠光线时好时坏摄像头角度千奇百怪。如果没有一个稳定可靠的人体检测层距离计算就是空中楼阁。YOLO系列在这个场景里几乎是首选。原因很直接第一它把检测做成了单阶段回归问题速度足够快能在实时视频流上跑而不是先出候选框再做二次分类第二YOLO在COCO数据集上预训练好的权重可以直接拿来做迁移学习自己手里那点标注数据不需要太多也能微调出不错的精度第三社区生态成熟PyTorch、Ultralytics等框架封装得很友好几行代码就能加载模型像我这种不想从零手写卷积网络的人也能快速把方案跑通。有人会问为什么不用Faster R-CNN或者SSDFaster R-CNN精度确实高但推理速度在CPU或者普通GPU上很难满足实时分析多路视频的需求SSD对小目标不友好而监控画面里人往往只占几十个像素容易漏检。YOLO在当前硬件条件下是精度和速度平衡得最好的选项尤其是YOLOv5、YOLOv8这些版本工程化程度很高。1.2 核心流程与技术选型整个社交距离检测项目本质上是一条流水线视频帧 - 人体检测 - 获取检测框坐标 - 计算检测框底边中心点或脚点- 计算两两欧氏距离 - 根据像素距离阈值判断是否小于安全距离 - 可视化标红告警。简单画个逻辑链比想象中要清晰得多。这里面有几个关键决策点值得展开说用检测框的哪个点作为“人的位置”。如果直接取框的中心点站起来的人和蹲下的人中心点高度差异很大容易导致距离误判。更稳妥的做法是用检测框底边的中点近似代表脚部位置。因为人在图像中的平面位置主要靠脚点来确定这样计算出的距离更贴近地面的真实投影距离。距离用什么单位。像素距离不等于真实距离。不同摄像头分辨率、焦距、安装高度都会影响同样的“1米”在画面里占多少像素。所以需要一个换算系数把像素距离映射成实际距离如米。这个系数可以通过标定获得或者直接假设一个近似值例如某些场景下1米≈70像素先跑通流程再优化精度。阈值怎么定。如果安全距离设定为2米那在像素层面就要把2米对应的像素数算出来再遍历所有检测目标对只要是距离小于该像素阈值且人不是同一人的就标记为“近距离接触”。在技术选型上我采用了YOLOv8作为检测器后处理用NumPy做距离计算可视化用OpenCV画框和连线。整体代码量不大核心逻辑大概两百行以内但工程上要考虑的东西比算法本身多得多比如视频流断线重连、模型推理时间统计、告警帧去重等这些在后面实操部分详细讲。2. 核心细节解析与实操要点2.1 人体检测模型的选择与处理YOLO家族迭代到现在各个版本的差异对于普通工程应用来说并没有想象中那么大。我做这个项目时首先试了YOLOv8n参数量最小、推理最快但小目标人体检测率在远距离画面里表现一般后来换成YOLOv8s和YOLOv8m精度明显提升FPS从100多降到40左右但依然够用。如果你有GPU推荐用YOLOv8s起步速度和精度的甜点区如果部署在树莓派或Jetson NX这类边缘设备上YOLOv8n可能更现实。一个特别容易踩的坑直接用COCO预训练权重时检测类别索引是0代表人但如果你用的是自定义训练版本或者某些剪裁过的权重类别顺序可能变化。代码里必须显式过滤出class_id为0的结果否则你可能会把“人”和“自行车”都框进去因为人群中有时候骑自行车、推婴儿车这些目标会被识别为其他类别。我在处理模型输出时会做三步过滤置信度阈值过滤一般设0.4-0.5太低会有一堆误检框、类别过滤只保留person、非极大值抑制YOLO输出里通常已经做了NMS但如果你在批量推理时手动拼接结果就需要再做一次。只有把框的质量提上来后续距离计算才有意义。2.2 距离计算与阈值判定逻辑这一步是整个项目的核心算法部分重点在于“到底怎么算距离才是对的”。先说理论公式。假设一张图像中第i个人的脚点坐标为xi, yi第j个人的脚点为xj, yj。像素欧氏距离d_pixel sqrt((xi - xj)² (yi - yj)²)然后需要把d_pixel换算成实际距离。最理想的办法是摄像头的单应性变换——通过标定地面平面得到像素坐标到世界坐标的映射矩阵。但这对大多数快速原型项目来说过于复杂所以更实用的折衷策略是已知摄像头的安装高度H米和俯仰角θ度根据镜头焦距和传感器参数可以推导出图像中每个像素对应的地面距离。但一般人不一定拿得到这些参数所以简单粗暴的方案是人工标定在画面中找一段已知长度的地面线段比如地砖是60cm数出10块砖就是6米在图像上量出对应的像素长度算出每米对应的像素数。由于透视关系近处和远处的比例尺不同所以最稳妥做法是在画面不同纵深位置分别标定几个比例尺然后对每个目标对按其中点所在深度区域选择对应的比例系数。这种做法能达到80%以上的准确率对于预警类应用已经够用。阈值设定直接用“最小安全距离”比如2米。假设当前深度区域中2米对应100像素那么当两个脚点像素距离小于100像素时判定为违规接触。这里还有一个非常容易忽略的问题两个检测框属于同一个人的相邻帧或者同一个人因为遮挡被拆成两个框后距离计算会把自己和自己算成一对。为了避免这种闹剧我在代码里增加了目标跟踪ID简单的IOU追踪即可只对不同的track ID计算距离并且当两个坐标完全重合时跳过计算。2.3 数据与标注说明如果你准备在自己的数据集上微调模型而不是直接用预训练权重那么数据这块的建议是不要自己去标几千张图先找开源数据集比如COCO的person类、CrowdHuman、MOT数据集等。社交距离检测通常不需要非常精细的分割只需要人体框所以标注相对简单但要注意边界情况。我实际使用中遇到的问题是公开数据集里大多是站立或行走的人对于蹲着、坐着、趴着的人模型漏检率会增高。这种情况下我会从自己摄像头采集几百张截图用LabelImg标注人物框加入训练集进行微调。每次采集几十张就够不要贪多因为模型权重已经很强微调只是让它适应你的场景角度和尺度分布。值得提醒的是标注框的紧致程度会影响脚点位置估算。如果标注框包含了太多头顶以上背景底边中点会偏高导致脚点位置偏差。所以标注时要让框尽量贴合人身体尤其是底部要精确贴合脚下地面。3. 实操过程与核心环节实现3.1 环境搭建与依赖安装这个项目我用的环境是Python 3.10 PyTorch 2.0 CUDA 11.8GPU是RTX 3060。如果是纯测试CPU也能跑只是速度只有每帧约2-3秒满足不了实时性要求。安装依赖时建议直接用Ultralytics的YOLO包一步到位pip install ultralytics opencv-python numpy需要说明的是Ultralytics会默认拉取torch如果你的环境里已经有特定版本的torch建议先手动安装torch再用pip install ultralytics --no-deps避免依赖冲突。我在实际部署到云服务器时就因为torch和CUDA版本对不上折腾了半天后面干脆换成CPU推理先验证逻辑再把模型搬到GPU推理。3.2 关键代码实现检测、映射、距离计算核心代码分为三个模块视频读取、推理检测、距离计算与可视化。我先放一个精简版的完整实现后面再逐段拆解。import cv2 import numpy as np from ultralytics import YOLO # 加载模型 model YOLO(yolov8s.pt) # 参数配置 SAFE_DISTANCE_METER 2.0 # 假设当前场景每米对应像素数实际需要通过标定获得 PIXELS_PER_METER 60 safe_distance_pixel SAFE_DISTANCE_METER * PIXELS_PER_METER CONF_THRESHOLD 0.5 # 视频读取 cap cv2.VideoCapture(test.mp4) while cap.isOpened(): success, frame cap.read() if not success: break results model(frame, verboseFalse)[0] boxes results.boxes persons [] for box in boxes: if int(box.cls) ! 0: # 只保留person类别 continue conf float(box.conf) if conf CONF_THRESHOLD: continue x1, y1, x2, y2 map(int, box.xyxy[0]) persons.append((x1, y1, x2, y2)) # 计算脚点坐标 foot_points [] for (x1, y1, x2, y2) in persons: foot_x (x1 x2) // 2 foot_y y2 foot_points.append((foot_x, foot_y, (x1, y1, x2, y2))) # 两两计算距离 violations set() for i in range(len(foot_points)): for j in range(i 1, len(foot_points)): fx1, fy1, box1 foot_points[i] fx2, fy2, box2 foot_points[j] dist_pixel np.sqrt((fx1 - fx2) ** 2 (fy1 - fy2) ** 2) if dist_pixel safe_distance_pixel: violations.add(i) violations.add(j) # 可视化 for idx, (x1, y1, x2, y2) in enumerate(persons): color (0, 0, 255) if idx in violations else (0, 255, 0) cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) label fperson {idx} if idx in violations: label violation! cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.imshow(Social Distancing, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码能跑通但距离映射很粗糙直接用一个固定的PIXELS_PER_METER。真实场景中因为透视效应画面近处的人一米占200像素远处一米可能只占30像素固定比例尺会导致近处过度告警、远处漏报。我在实验中被这个问题坑了很久后来采用了一个简单的分区标定策略在图像上预定义三个横向“深度带”每个带对应不同的像素米比。# 假设画面从上往下分别是远、中、近三个区域 ZONES [ {y_start: 0, y_end: 300, ppm: 30}, # 远区域 {y_start: 300, y_end: 500, ppm: 60}, # 中区域 {y_start: 500, y_end: 720, ppm: 100} # 近区域 ] def get_ppm_by_y(y): for zone in ZONES: if zone[y_start] y zone[y_end]: return zone[ppm] return ZONES[-1][ppm] # 在距离计算中采用两个脚点的平均y坐标来确定区域取ppm avg_y (fy1 fy2) // 2 ppm get_ppm_by_y(avg_y) safe_distance_pixel SAFE_DISTANCE_METER * ppm if dist_pixel safe_distance_pixel: # 判违规这样做比固定比例要合理很多因为两个人同处一个区域时比例尺一致跨区域时用平均位置误差也在可接受范围内。3.3 可视化与告警提示除了画框一个可用的项目还需要告警。最简单的是在画面左上角显示一个“当前违规对数”的计数器同时在违规人数大于0时输出一条日志。如果接到实际业务里可以用log日志记录时间戳和检测结果或者通过HTTP POST把违规人数推到后端。我在这块的经验是把“检测”和“展示”分离。检测线程只负责输出结果列表bbox坐标、违规标记展示线程负责渲染。这样当视频流卡顿或断线时检测逻辑不会直接崩溃。下面是简单的线程化示意import threading class SocialDistDetector: def __init__(self, video_source): self.cap cv2.VideoCapture(video_source) self.frame None self.result_frame None self.running True def detect_loop(self): while self.running and self.cap.isOpened(): ret, self.frame self.cap.read() if not ret: continue # 在这里执行检测和距离计算 self.result_frame processed_frame当然这只是工程优化初学者可以先跑通单线程代码再迭代升级。4. 常见问题与排查技巧实录4.1 检测精度与误报问题问题一人体检测框时有时无尤其是穿着深色衣服的人在低照度场景下。这个问题我遇到过两次排查后发现一个原因是视频帧直接缩放到了640x640原始小目标信息丢失严重。另一原因是置信度阈值设太高。建议把输入图像分辨率调大到1280Ultralytics支持imgsz参数同时降低置信度阈值到0.35再通过检测框尺寸过滤掉过小的噪点。还有一个容易被忽视的点YOLO的锚框对“人坐在椅子上身体被桌子遮挡”这种情况容易产生半身框或三个框。解决方法是给模型训练时加入更多部分遮挡样本或者在后处理中合并重叠度高的框保留置信度最高的那个。4.2 距离映射误差问题很多人问我为什么明明两个人实际离得很远系统却报警。大多数是映射比例尺没标定好或者使用了固定比例尺导致远处误报。一个典型的例子画面中左侧靠近摄像头右侧在远处如果统一用近处比例尺那么远处一对真实相距3米的人因为像素距离小会判定为安全相反用远处比例尺近处真实距离1.5米的两人会被判定为安全而事实上可能已经违规。因此做分区标定是必须的。如果还想更精细可以标定多个已知坐标点的地面坐标然后用cv2.findHomography计算单应矩阵将脚点像素坐标投射到实际平面坐标再算距离。这样精度最高我在另一个工业项目中就是这么做的误差能控制在5%以内。4.3 性能优化建议在Jetson Nano或CPU上跑实时检测时经常遇到FPS只有个位数。我的优化顺序是改用更小的模型yolov8n推理速度提升2-3倍。将视频帧缩放处理后再送入模型而不是直接用原始分辨率比如1080p降到768或640。检测帧率与画面刷新率分离比如每秒只检测5帧中间帧沿用上一帧检测结果但画面上依然流畅。使用ONNX Runtime或TensorRT导出模型推理时间能再减半。我用TensorRT导出YOLOv8s后在RTX 3060上每帧推理时间从15ms降到了6ms四路视频同时处理时总体FPS依然超过30。如果你的部署环境固定强烈建议做这一步。另外还有一个隐蔽的性能问题如果用OpenCV的waitKey(1)做循环CPU占用率往往会很高。可以加上cv2.waitKey(30)让每帧间隔30ms兼顾流畅度和资源占用。5. 写在最后的个人体会从确定选题到跑通整个社交距离检测流程我只花了两天时间但把精度调到让人满意的程度前前后后用了好几个版本。最深刻的教训是算法模型只是整个项目的一道前菜真正决定项目能否落地的是对实际场景的理解——摄像头装多高、地面有多少遮挡、人流量峰值时怎么保证帧率这些问题比单纯的loss值更重要。这个项目后续可以延展的方向非常多比如把检测框换成跟踪算法ByteTrack、DeepSORT做多人轨迹记录统计一段时间内近距离接触的时常或者接入人体关键点检测判断接触时是否发生碰撞再或者直接把告警信息推送到微信/钉钉机器人做成一套完整的实时防疫预警系统。核心的YOLO检测和距离计算逻辑不变扩展起来非常方便。如果你想拿这个项目练手建议先用自己的摄像头录一段半分钟的办公室或走廊视频按我上面的步骤把demo跑起来然后尝试调整分区标定参数观察告警效果的变化。这种动手调试的过程比看十篇技术博客都有用。本文还有配套的精品资源点击获取
返回列表