公司动态

基于深度学习与目标检测的交警手势识别:从数据集构建到动态视频推理实战

📅 2026/8/30 5:42:43
基于深度学习与目标检测的交警手势识别:从数据集构建到动态视频推理实战
简介本资源是一套完整的中国交通警察指挥手势识别毕业设计项目面向计算机、人工智能、自动化等专业本科生及初学者解决交通手势图像分类与关键点检测的实际问题适用于课程设计、大作业及毕设参考。压缩包共34个文件含31个Python源码涵盖数据预处理、姿态估计模型训练、手势识别推理、结果可视化等核心模块、1个详细README说明文档、1个Git忽略配置及1个项目演示GIF整体仅4.42MB轻量易部署。已有207人学习下载项目经答辩评审获98分全部代码调试通过并可直接运行。读者可获得从视频骨骼提取prepare_skeleton_from_video.py、PAFs网络构建pafs_network.py、手势分类模型gesture_recognition_model.py到端到端预测gesture_pred.py的全流程实现目录结构按功能分层清晰配套human_keypoint_pred.py与play_gesture_results.py等工具脚本显著降低学习门槛。 每年一到毕设选题季就会有人拿着“基于Python深度学习的中国交通警察指挥手势识别”这个题目来找我。说句实话这题看着体面难度也适中很适合作毕业设计——但前提是你得先把数据集和动态手势这两道坎迈过去。我花了大半个学期把它完整跑通最后整理出了源码和一套自建的交警手势数据集这篇就说说整个过程里最关键的几个决策、踩过的坑以及答辩时真正会被问到的东西。这题目表面上是“深度学习图像识别”但实际上模型结构反而是整条链路里最省心的部分。真正的功夫在数据怎么采、标签怎么定、输入形式怎么设计、以及最终如何让一个静态分类器在动态视频里稳定工作。下面按我实际推进的顺序把这套完整方案拆开讲。1. 这个课题真正的门槛先想清楚“识别什么、怎么识别”1.1 交警手势的种类与判定标准国内交警指挥手势常见的是八种停止、直行、左转弯、左转弯待转、右转弯、变道、减速慢行、靠边停车。实际细分还可能把右转弯待转单独拿出来但常规项目里按八类来做就够了。这里有个容易被忽略的点每个手势都是动态动作不是一张静态图能完全表示的。比如“停止”信号从手臂下垂到小臂向上伸直再到手掌立起整个过程都在传达信息。如果只采最终姿态模型看到的特征就非常局限一旦视频里动作停在中间帧识别就乱套了。所以数据集构建的第一原则是把动作的全过程都录进去起、承、转、合一个不落。1.2 静态识别还是动态识别要先定边界做毕设时最容易犯的错是一上来就想要一个“能看视频、识别手势”的完整系统然后发现工作量根本收不住。你需要先明确输入形式单帧图片输入把每一帧当作独立样本做分类模型不需要考虑前后帧关系。优点是数据集好建、训练简单、便于用ImageNet预训练缺点是答辩时容易被老师质疑“动态手势你拿静态帧做合理吗”。视频序列输入用3D-CNN或CNNLSTM处理连续帧理论上更贴合真实场景但数据量要求成倍增加训练难度和调试成本也高不少。我最终选择的折中方案是静态分类模型为主推理阶段用视频流检测框滑动窗口投票。这样既没有做视频分类那么重又能在实际运行时有时间维度的纠错能力答辩时也说得通。1.3 这门课本身值不值得做坦诚说这个选题性价比不错。数据自己可以录不用求人技术栈主流Python、PyTorch、YOLO、ResNet一套下来简历上也好写展示效果又直观做个简单界面就能跑视频。但务必提前意识到公开数据集几乎没有完整覆盖八种手势的网上能搜到的相关项目要么质量堪忧要么类别不全很多还是静态摆拍做出来的模型泛化能力很差。所以自建数据集是绕不开的环节。2. 没有现成数据集那就自己造一份规规矩矩的数据集2.1 采集方案录制视频加逐帧提取我当时的做法是找两三位同学配合穿上深浅不同的上衣有条件的话戴一双白手套实际执勤会戴白手套也能让前景和背景区分更明显。如果弄得到黄色反光背心效果更好可以显著降低衣服颜色对模型的干扰。拍摄场景选在校园空地和操场背景相对单纯避开真实马路既安全又不会引入太多无关行人或车辆。拍摄时用手机竖屏或横屏都可以固定机位1080p分辨率、30fps就够。重点在于每个手势要单独录一段时长五到十秒动作从头到尾做完整。比如“左转弯”要包括抬臂、伸出、收臂的整个流程不要只摆一个标准pose。不同角度也录一些。实测下来正面机位最好识别侧面和斜前方容易混淆特别是“左转弯”和“左转弯待转”侧面看几乎只差一个手臂伸展角度。视频录完用OpenCV按帧抽取。不要一帧不落全存一是有大量冗余二是模糊帧很多。我按每秒取五帧的频率抽再用拉普拉斯方差做简单清晰度过滤import cv2 import os video_path videos/turn_left_01.mp4 output_dir dataset/images/turn_left/turn_left_01 os.makedirs(output_dir, exist_okTrue) cap cv2.VideoCapture(video_path) fps cap.get(cv2.CAP_PROP_FPS) frame_interval int(fps / 5) count 0 save_count 0 while True: ret, frame cap.read() if not ret: break if count % frame_interval 0: gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) laplacian_var cv2.Laplacian(gray, cv2.CV_64F).var() if laplacian_var 50: # 低于阈值判定为模糊帧 cv2.imwrite(os.path.join(output_dir, f{save_count:04d}.jpg), frame) save_count 1 count 1 cap.release()这里有个细节拉普拉斯方差的阈值不能定太高否则会把动作中间帧里手臂快速运动导致的运动模糊全部滤掉导致数据集只剩“姿势停留帧”和“缓慢动作帧”模型在真实视频里遇到快速挥臂就会懵。2.2 标注策略与目录结构标注方案直接决定后续建模方式。我对每一类手势按动作类别归档同时额外用标注工具给交警画检测框。先画检测框再按手势类别归档。推荐顺序是对抽出的每一帧用labelimg标注人形边框标签统一叫“police”。把标注后的图片按手势类别归类到不同文件夹。这样就形成“检测分类”两条数据线dataset/ ├── images/ │ ├── stop/ │ ├── straight/ │ ├── turn_left/ │ ├── turn_left_wait/ │ ├── turn_right/ │ ├── change_lane/ │ ├── slow_down/ │ └── pull_over/ ├── labels/ │ ├── police_bbox.json ├── train.txt ├── val.txt └── test.txt每个类别的样本量我建议不低于一千张最好到一千五到两千张。八类加起来就是一万二到一万六千张对ResNet、MobileNet级别的模型完全够用。不要贪多关键是质量。我自己就是每类二十段视频、每段五秒抽完清洗后留下约一千六百张。提示同一段视频抽出来的帧彼此太相似。划分训练集和验证集时必须按视频整体切分不能把所有帧混在一起随机切否则会出现“训练集和验证集其实来自同一段视频”的数据泄露导致验证集指标虚高换一段真实新视频就露馅。2.3 数据增强但别把左右搞反增强我用的是albumentations主要包括随机旋转±15度亮度、对比度随机抖动饱和度调整随机擦除高斯噪声一个容易踩的雷是水平翻转。做分类增强时水平翻转后“左转弯”会变成“右转弯”。如果不把对应标签同步交换模型学到的特征就是矛盾的。我建议要么构建一个标签映射表翻转的同时把左和右相关标签对调要么干脆不做水平翻转省得给自己挖坑。3. 模型选型与整体方案设计为什么是“检测分类”而不是端到端3.1 四类技术路线对比我在动手前把主流的方案都列了一遍简单对比技术路线优点缺点适合场景静态图像分类ResNet等实现简单、预训练多、训练快易受背景干扰无法利用时序信息图片输入目标检测分类YOLOMobileNet抗背景干扰多人场景可扩展链路较长需要检测框标注视频输入、实际部署姿态关键点MediaPipe/HRNetLSTM更关注人体动作本身对服饰变化鲁棒对遮挡敏感小模型精度欠缺通用动作识别视频分类3D-CNN/TSM充分利用时序上下文数据需求大、训练慢、调试复杂长视频动作识别决定方案时我考虑了两点第一实际场景中交警在画面里通常只占一小块区域如果直接整图分类模型会把大量注意力花在背景上得不偿失第二毕设周期有限视频分类方案训练成本高调参复杂一旦翻车很难补救。3.2 我最终采用的pipeline最终方案是两阶段用YOLOv8n训练一个“police”检测器负责在画面中框出交警。对检测框区域裁剪、缩放、归一化送入一个MobileNetV3-Large分类器做手势分类。推理时整条链路是“视频帧 → YOLO检测交警 → 裁剪最大检测框 → 手势分类 → Softmax → 滑动窗口投票”。YOLOv8n本身很小检测交警这种大目标绰绰有余。分类器用MobileNetV3是因为它轻量、运行快准确率也不输ResNet50太多。如果对精度要求更高可以换成ResNet50代价是单帧推理慢个两三毫秒倒也不会影响实时性。关键代码如下import cv2 import torch from torchvision import transforms from ultralytics import YOLO detector YOLO(runs/detect/police/weights/best.pt) classifier torch.load(checkpoints/mbv3_large_best.pth, map_locationcpu) classifier.eval() cls_names [stop, straight, turn_left, turn_left_wait, turn_right, change_lane, slow_down, pull_over] mean [0.485, 0.456, 0.406] std [0.229, 0.224, 0.225] transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean, std) ]) def predict_frame(frame): results detector(frame, verboseFalse) boxes results[0].boxes if len(boxes) 0: return None # 取面积最大的检测框 areas (boxes.xyxy[:, 2] - boxes.xyxy[:, 0]) * (boxes.xyxy[:, 3] - boxes.xyxy[:, 1]) idx areas.argmax() x1, y1, x2, y2 boxes.xyxy[idx].tolist() crop frame[int(y1):int(y2), int(x1):int(x2)] crop cv2.resize(crop, (224, 224)) crop crop[:, :, ::-1] # BGR转RGB tensor transform(crop).unsqueeze(0) with torch.no_grad(): logits classifier(tensor) prob torch.softmax(logits, dim1) cls_id prob.argmax().item() return cls_names[cls_id], prob.max().item()3.3 环境配置与工程组织环境我用的是Ubuntu 22.04Python 3.8以上PyTorch 2.xultralytics库装好就行。Windows也能跑但GPU版本驱动和CUDA版本最好提前对好Ubuntu下相对省心。CPU也能训练只是MobileNetV3-Large在64G内存的机器上训练一个epoch要比较久建议有NVIDIA显卡哪怕是GTX 1660 Super这种4G显存都够。工程结构按模块划分traffic-police-gesture/ ├── data_prepare/ │ ├── extract_frames.py │ └── split_dataset.py ├── train/ │ ├── train_detector.py │ ├── train_classifier.py │ └── eval.py ├── inference/ │ ├── predict.py │ └── smooth.py └── checkpoints/这样训练、评估、推理分开答辩演示和后续扩展都方便。4. 训练过程与关键参数从ResNet到MobileNet的反复横跳4.1 第一阶段纯分类模型的基准测试我最早先用ResNet50做了个baseline直接在整图上分类。训练配置是ImageNet预训练权重AdamW优化器初始学习率1e-3weight decay 1e-4batch size 32输入尺寸224×224训练约30个epoch早停法收敛。结果验证集准确率在86%-88%左右。乍看还行但一看混淆矩阵就发现错误高度集中在“turn_left”和“turn_left_wait”之间以及“slow_down”和“pull_over”之间。这两个方向都有点模棱两可尤其左转和左转待转开合角度接近单帧区分确实难。接着我把整图分类换成“先检测后分类”的流程再测验证集准确率直接跳到93%左右。差异基本来自背景干扰被排除掉了。整图模型学了很多背景特征换一个场景就性能下降裁剪后模型只能看到人和手臂学到的特征更接近手势本身。4.2 第二阶段模型轻量化与精度平衡检测器YOLOv8n训练起来很快。交警这种目标在画面里明显标签单一训练50个epochmAP能到0.95以上。分类器我从ResNet50换成了MobileNetV3-Large精度几乎没掉推理速度快了一截。换模型时要注意两个模型的输入预处理完全一致ImageNet的mean和std都是[0.485, 0.456, 0.406]和[0.229, 0.224, 0.225]不能混用。我又把分类器输入从224×224提到320×320准确率涨了大约1.5个百分点。对大一点的检测框区域来说高分辨率确实保留了更多手势细节。代价是推理速度慢几毫秒无伤大雅。4.3 时序平滑把静态模型变成可用的动态系统即使分类准确率到了95%单帧预测在视频里仍然会抖。原因很简单某些中间帧本身太像别的类别。我用一个长度为7的滑动窗口做多数投票效果立竿见影视频测试的明显误判从约10%降到了2%以内。from collections import deque, Counter history deque(maxlen7) def smooth_predict(frame): result predict_frame(frame) if result is None: return None history.append(result[0]) counter Counter(history) return counter.most_common(1)[0][0]如果连续七帧里五帧是“turn_left”一帧是“turn_right”一帧是“stop”最终结果就是“turn_left”。这种策略虽然简单但对“动作中间帧模糊”的鲁棒性提升非常大。最终测试集评估指标整体准确率94%左右加权F1约0.94。这个成绩在毕设场景下够看了重点是整个方案稳定、可复现、可说清。5. 真实场景测试中的坑不是训练完了就天下太平5.1 背景干扰检测到多个人的处理策略在真实视频里画面中可能同时出现多人。YOLO会把所有人都框出来甚至把路人的手臂当成交警手势。我的做法是取面积最大的police检测框因为实际场景中交警通常处于画面中心或较近位置。如果两个框大小接近则取置信度高的那个。这个策略在绝大多数单警场景下够用。如果你要处理多交警同时出现的情况那就得再加一道逻辑比如按区域或优先级分配那已经超出毕设范围了。5.2 相似手势的静帧歧义“左转弯”和“左转弯待转”这两类即使在人工标注时也容易犹豫。除了增加样本外我在采集时专门让模拟者把动作放慢多做几个过程过渡帧尽量让模型学到更多中间状态。但坦率说单帧静态模型无法彻底解决这个歧义。要彻底解决得上时序模型。毕设答辩时这个点老师大概率会问到我建议如实说清局限并给出改进方向比遮掩更稳。5.3 训练集和验证集划分不当前面说过按视频切分是铁律。我一开始图省事把所有帧混在一起随机划分验证集准确率高达98%结果换一段肉眼可见的简单视频预测一塌糊涂。查了很久才发现训练集里包含了很多验证集视频的相邻帧模型几乎是“背题”考的。之后改成按视频切分指标立刻真实了很多。5.4 通道顺序和推理环境导致性能骤降一个特别常见的低级错误用OpenCV读视频图片是BGR通道顺序模型训练时用的是RGB。如果推理代码忘了做BGR到RGB的转换性能会掉得离谱而且不易察觉。我自己踩过这个坑一度以为模型训练有问题最后发现是通道顺序反了。建议在推理函数里固定写一次frame[:, :, ::-1]别依赖外部传入。还有部署时模型跑在GPU上输入tensor的dtype要统一。PyTorch里如果你用CPU推理float32和float64混在一起也会报错。干脆在所有入口统一用torch.float32。5.5 光照差异与拍摄设备差异我最初的数据有一部分是阴天拍的一部分是傍晚拍的结果模型在强光下测试时会把“停止”误判成“减速慢行”。后来做了亮度抖动增强并在采集中尽量覆盖不同时段的自然光问题明显缓解。如果条件允许用两台不同手机各录一部分数据让网络不要过拟合到某一种摄像头色彩倾向。这一点对实际使用非常关键。6. 答辩前必须打通的几个问题别让老师问住你6.1 “为什么用YOLO分类而不是端到端手势识别网络”这是一个必然被问的问题要能答出设计权衡。端到端视频分类需要大量带动作标签的连续视频样本数据规模动不动上万段毕设阶段很难自建到可用量级。检测分类把复杂任务拆成两个简单子问题检测器负责“人在哪里”分类器负责“手在做什么”每个子问题都能独立优化、独立评估。推理时可解释性更强分类错误时我可以查看是检测框偏了还是分类器判断错了调试起来高效很多。6.2 “数据集是怎么构建的有多少张如何标注”围绕自采视频、逐帧提取、清晰度过滤、类别归档、检测框标注五步展开。清晰表达数据总规模八类约一万三千张其中每个类别约一千六百张训练验证测试按8:1:1切分并按视频整体切分避免数据泄露。检测框用labelimg标注标签为police。6.3 “模型是怎么处理动态手势的”如实承认骨干模型是单帧分类但推理阶段引入了滑动窗口多数投票利用时间一致性来降低单帧误判。并且数据采集时覆盖了动作全过程这本身就是一种隐式的时序信息。如果想进一步改进可以替换为LSTM或TSM结构。6.4 “实时性怎么样”给出可验证数据GTX 1660 Super上YOLOv8n单帧约8msMobileNetV3-Large约3ms加上预处理和投票逻辑整体约20ms能达到50fps左右。如果没有GPUCPU模式下大约是200ms到300ms一帧离线处理视频完全够用。6.5 “为什么准确率没有做到99%”可以从数据集难度和相似手势两个层面回答。误差主要来自“左转弯”与“左转弯待转”在特定视角下的静帧形态接近以及极少数光照过暗的视频帧检测框偏移。这不是模型bug而是单帧信息本身的局限性。如果要做更高精度需要更精细的时序建模和更大的数据规模。结尾一点个人体会做完这个项目我最大的感受是深度学习的模型结构只要选定一个主流方案调参再多也难有大突破反而是数据采集和数据处理的质量决定了项目上限。这个题目真正的价值在于把数据工程、模型训练、推理部署、误差分析这些环节完整串了一遍每一环都会出问题也只能靠实打实地排查才能解决。最后整理源码和数据集的时候我把整个流程固定成了脚本保证以后换一批新数据也能一键复现。后来再有人拿这个题做毕设我基本都让他们直接按这个链路走省下来的时间多去跑几组实验、把混淆矩阵琢磨透比什么都值。本文还有配套的精品资源点击获取