
简介一套基于YOLOv5视觉检测的图书馆占座行为实时监测与治理效果分析系统源码面向高校图书馆、公共自习空间管理人员以及智慧校园和计算机视觉方向开发者解决占座行为难发现、治理效果难量化、座位资源分配不透明等问题。压缩包共106个文件约15.36MB包含33个Python源码、40个yaml与11个yml配置、5个Shell脚本同时提供Dockerfile、Git配置、教程ipynb、模型权重pt和测试图片完整覆盖模型配置、核心逻辑、容器部署、版本控制与演示验证。已有357人学习下载。系统利用YOLOv5对座位占用进行实时检测与行为分析可输出不同时段占座趋势、识别长时间占座行为并通过治理前后数据对比评估管理措施是否有效源码结构清晰学习者既能直接复现图书馆智能监测流程也可借鉴其工程化设计掌握目标检测训练与推理、Python项目组织、Shell自动化运维等技能。这份代码适合作为高校课设、毕业设计或智慧园区视觉项目的完整参考具有较强的实用与学习价值。1. 为什么图书馆占座监测要用YOLOv5视觉检测图书馆占座问题在很多高校都存在。管理员靠肉眼巡检效率低且无法覆盖所有时段纯预约系统又感知不到座位上的真实行为——人离开但书还在人坐在那里玩手机也算“使用”。基于YOLOv5视觉检测的图书馆占座行为实时监测就是把摄像头画面转成结构化数据检测人、物品和座位区域判断“空闲”“使用中”“占座”三种状态并将这些状态填入治理效果分析报表。这套系统适合毕业设计、图书馆信息化改造以及任何想用YOLOv5走通采集、训练、部署、分析完整链路的人。难点不在于把模型mAP刷高而在于从检测框到业务逻辑的映射是否可靠。2. YOLOv5视觉检测模型选型与训练数据集准备在YOLOv5环境配置完成后首先需要确定用哪个尺寸的模型以及训练数据要标到什么程度。YOLOv5的学习曲线比Faster R-CNN平滑社区资料多遇到anchor或loss异常时能快速搜到解决方案。要理解YOLOv5网络结构中的几个关键点才能知道后面的训练参数怎么改。2.1 YOLOv5网络结构里真正影响占座检测的组件2.1.1 Backbone与Neck的分工YOLOv5的BackboneCSPDarknet负责在多尺度上提取特征NeckPANet负责把高层语义和底层细节融合。很多教程只强调Backbone的残差结构但在占座监测里书、水杯这类小目标更依赖Neck。默认的yolov5s.yaml中depth_multiple0.33width_multiple0.50如果数据集里“book”和“bottle”的标注框普遍小于32x32像素建议把depth_multiple提到0.50让Neck的卷积层更宽小目标特征传导更多。此时模型从s变为m单张推理时间在RTX 3060上约为8ms到12ms仍然适合实时轮询。2.1.2 Anchor自适应与默认值的差异YOLOv5在训练前会重新计算anchor但默认的通用anchor偏大适合COCO的80类目标。图书馆里人和书的尺度差异很大第一次运行训练时会打印“Analyzing anchors...”这时可以手动检查输出。如果发现最大的anchor比我们要检测的书本大很多可在data/hyps里设置anchor_t4.0扩大正样本匹配范围缓解小目标匹配不到的问题。不要直接改模型yaml里的anchor列表训练器的自适应逻辑会覆盖它。不同backbone宽度对占座检测的影响可以直观地看下表模型depth_multiplewidth_multiple参数量640x640推理时间RTX 3060yolov5s0.330.507.3M8msyolov5m0.500.7521.2M12ms如果摄像头同时监控超过8个座位建议用yolov5s因为目标多且彼此重叠少如果只监控单张书桌用yolov5m精度收益更明显。2.2 用自己的数据集训练YOLOv5的最小命令2.2.1 标注类别和目录组织建议用labelimg这类工具类别只设四个person、bag、book、bottle。不要加“seat”因为座位状态由后端坐标区域计算不是检测目标。数据集目录如下dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/每个图片对应同名txt文件每行格式为class x_center y_center width height坐标归一化到0到1。图书馆摄像头多为俯视角度人只标上半身或头肩区域否则桌子和电脑的遮挡会造成大量无效标注。标注时还有一个容易忽略的规则当一个包被放在椅子上且露出部分很少时仍然要框出完整可见区域不要因为遮挡而跳过。2.2.2 训练命令行与关键参数python train.py \ --data data/seat.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 32 \ --epochs 150 \ --device 0 \ --hyp data/hyps/hyp.scratch-low.yamlseat.yaml中nc: 4names列表和标注类别顺序一致。hyp.scratch-low.yaml的增强幅度比较保守适合室内固定场景。如果显卡显存小于8GB把batch降到16--img可降到512但训练和推理的--img必须一致否则检测框坐标会偏移。训练完成后用python detect.py --source test.jpg --weights runs/train/exp/weights/best.pt --conf 0.35快速看效果确认数据标签是否有错位。3. 实时监测系统的前后端架构与模型集成系统设计源码通常采用基于Django Vue的Web架构摄像头RTSP流推给推理服务推理服务把状态写进数据库和WebSocket通道。下面说明如何把YOLOv5检测结果变成图书馆可用的业务状态。3.1 Django后端如何接管YOLOv5推理结果3.1.1 避免在视图里跑detect.py不要直接把detect.py的main函数塞进Django视图命令行参数和日志输出会拖垮请求线程。常见的做法是将yolov5作为一个库导入或者在项目里复制models/common.py中的DetectMultiBackend。最省事的版本是使用torch.hub加载自定义权重import torch model torch.hub.load(/path/to/yolov5, custom, pathweights/best.pt, sourcelocal) model.conf 0.35 model.iou 0.45 def analyze_frame(frame): results model(frame, size640) rows results.pandas().xyxy[0] return rows[[class, name, xmin, ymin, xmax, ymax, confidence]].to_dict(records)torch.hub.load支持本地源不需要每启动都联网。模型应在Django的AppConfig.ready()中加载一次否则每个请求都会初始化一次权重显存占用飙升。model.conf是全局置信度阈值model.iou是NMS的IoU阈值图书馆场景下两者分别设为0.35和0.45比较均衡。3.1.2 抽帧频率与RTSP断流重连import cv2 def capture_loop(camera_url): cap cv2.VideoCapture(camera_url) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) frame_id 0 while True: ok, frame cap.read() if not ok: cap.open(camera_url) continue if frame_id % 9 0: yield frame frame_id 1占座状态一般几秒内不会变化所以每3秒抽一帧配合25fps就是每75帧足够。CAP_PROP_BUFFERSIZE设为1可以减少推流延迟避免读到几秒前的旧帧。如果摄像头断流cap.read()会返回False这里直接用cap.open()重新连接比重新创建VideoCapture对象更快。3.2 数据库表结构与座位状态流转3.2.1 两张核心表的设计实时状态表存当前识别结果历史表存状态变化记录字段类型说明seat_idCharField座位编号statusCharFieldidle/occupied/left_itemsperson_confFloatField人员检测置信度items_jsonJSONField检测到的物品类型和坐标update_timeDateTimeField最后更新时间历史表增加start_time、end_time、duration每次状态从occupied切到left_items时写一条完整记录方便后续算平均占座时长。items_json里存的是检测到的bag/book/bottle的归一化坐标不存人物框因为人物的坐标只对当次判断有用。3.2.2 用状态机避免单帧抖动state_machine {} # seat_id - (status, since_time) def update_seat(seat_id, person_boxes, item_boxes, now): status, since state_machine.get(seat_id, (idle, now)) new_status occupied if person_boxes else (left_items if item_boxes else idle) if new_status ! status: state_machine[seat_id] (new_status, now) save_history(seat_id, status, new_status, now)注意触发条件如果座位上有包但人只是暂时离开接水立刻变成left_items会导致误报。常见做法是设置一个宽限时间比如人离开超过3分钟才进入left_items这需要在状态机里加一个pending状态而不是直接跳转。3.3 Vue前端实时展示检测结果3.3.1 用WebSocket推送而非轮询Django Channels实现ASGI后前端订阅座位状态频道{seat_id: A-12, status: left_items, duration: 1860}前端拿到数据后更新座位图。检测框的可视化只在后台大屏上做用户端的平面图不需要显示bounding box避免视觉噪音。前端用Vue的canvas绘制座位网格每个格子颜色随status切换。WebSocket推送频率建议控制在每3秒一次即使座位状态没变也可以推送心跳帧方便前端判断连接是否存活。4. YOLOv5训练自己的数据集参数调整与占座特征优化模型训练出的“人”和“包”只是中间结果要在图书馆这种复杂背景里稳定识别需要针对场景调训练参数和推理逻辑。4.1 图书馆场景下的数据处理和增强参数4.1.1 YOLOv5超参数里最值得改的三个值默认的hyp.scratch-low.yaml对自然场景效果好但图书馆天花板射灯和窗边逆光让模型容易过拟合光线。实际调整时优先改这三个参数默认值建议值原因hsv_h0.0150.01降低色相偏移幅度hsv_v0.40.3亮度扰动减小避免灯光反光干扰degrees0.05.0允许轻微旋转模拟摄像头安装误差修改后将yaml通过--hyp hyp.library.yaml传入。另外--cache ram会把图片缓存到内存数据集不足10GB时可以明显缩短epoch间的时间。如果数据量超过5000张不要用ram缓存否则会占满内存导致进程被杀。4.1.2 用“难例挖掘”补足数据缺口训练后把所有val图片的检测结果保存人工挑出FP假正例和漏检图再加进训练集。这个过程重复两轮比直接增加随机图片有效。不要依赖自动标注工具做全部标注占座场景里书和包经常互相遮挡自动标注往往把两个目标合并成一个框导致后续后处理拿不到准确的目标数。4.2 后处理逻辑检测框如何决定“占座”4.2.1 用座位多边形过滤检测框模型输出的是全图坐标但系统只关心落在座位区域内的检测。在搭建系统时预先用鼠标标出每个座位的多边形区域然后判断目标中心点是否落在多边形内def is_inside(bbox, seat_polygon): cx (bbox[0] bbox[2]) / 2 cy (bbox[1] bbox[3]) / 2 return cv2.pointPolygonTest(seat_polygon, (cx, cy), False) 0pointPolygonTest返回正数表示点在内部负数表示外部。物品检测框的中心可能位于座位区域边缘所以对bag和book的判断可以用“中心点往八个方向偏移几个像素任意一个在多边形内即算命中”。这一步能显著减少隔壁座位的干扰也是从“目标检测”走向“业务判断”的关键一步。4.2.2 区分“人离开但物品还在”的时长条件当状态变为left_items后再通过定时器累计持续时间。常见做法是每5分钟重新检测一次如果超过15分钟仍未变化就推送给管理员。这里的15分钟是业务配置值不要硬编码在代码里应放到Django的settings或数据库配置表中方便图书馆管理员自助调整。4.3 模型在真实图书馆环境下的排错路径4.3.1 用混淆矩阵定位类别问题运行python val.py --data data/seat.yaml --weights weights/best.pt --task val得到混淆矩阵。如果book经常被识别成bottle说明两者在俯视视角下形状相近需要补充不同角度和摆放状态的数据。如果person和背景混淆优先检查训练集里是否有大量远距离的小目标正样本。混淆矩阵里对角线上的数字接近1不代表绝对可靠还要看每个类别的精确率和召回率。4.3.2 置信度阈值调整的验证方法python detect.py --source ./val_imgs --weights best.pt --conf-thres 0.30 --save-txt --project runs/conf_test跑完不同阈值后统计每个图片的预测数量和人工标签数量选择F1最高的conf。不要直接调到0.15那样会把书脊纹理误检成人。如果某个类别始终误检可以在后处理里对类别单独设置置信度比如person的conf必须大于0.5book大于0.3。这种按类别的阈值策略比全局阈值更符合图书馆场景。4.3.3 常见漏检的根源除了模型参数还要检查推理时的输入尺寸。训练时用640推理时却用了416小目标会丢失。OpenCV读取的BGR帧和YOLOv5预处理的RGB切换错误也会导致结果异常。调试时可以先保存一帧预处理后的图片看它和训练集风格是否一致。如果检测结果一直很好只有某个摄像头不行优先排查那个摄像头的分辨率和焦距而不是重新训练模型。5. 治理效果分析指标与实时监测的验证技巧治理效果分析不能只靠“感觉占座少了”需要定义可计算指标并用系统记录落库。5.1 占座率与平均占座时长的计算口径占座率在统计时段内处于left_items状态的座位数总座位数。平均占座时长仅计算触发治理动作如关闭座位或放置提示牌后释放的座位避免把夜间无人清场的超长记录拉高平均值。SQL聚合示例SELECT DATE(start_time) AS day, COUNT(*) AS cnt, AVG(TIMESTAMPDIFF(MINUTE, start_time, end_time)) AS avg_minutes FROM seat_history WHERE statusleft_items AND action_taken IS NOT NULL GROUP BY DATE(start_time);这里的action_taken字段记录治理动作编码例如1表示提示单2表示工作人员介入。只有在治理动作执行后再结束的left_items记录才计入统计这样得到的是治理措施的响应时间。5.2 用A/B测试验证治理措施的有效性在一层贴提示单另一层保持原样用系统数据对比两周内占座时长变化。Django管理命令可以生成对比表python manage.py governance_analysis --group A --group B --start 2025-04-01 --end 2025-04-14重点看平均占座时长和高峰时段占座率的差异。如果两者差异低于5%说明治理动作未达到统计显著效果需要改变执行方式。系统设计时要把分组标签写入座位表这样治理效果分析可以直接按座位所属组聚合。5.3 验证监测系统本身的检出正确率治理效果分析可信的前提是系统判断没有被“质疑”。安排学生执行三种动作正常就座、放下物品离开、离开后叫管理员清理系统根据日志生成混淆矩阵计算Kappa系数。验证时要注意系统判断的“left_items”和人工记录的“占座”之间有时间偏差需要把系统状态切换时间点与人工时间点对齐后再计算。这个验证脚本应作为项目源码的一部分交付方便答辩或立项时展示。检测模型更新后也要重跑验证流程不能只看loss曲线。本文还有配套的精品资源点击获取