ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YoloV5的手语识别:从数据标注到树莓派部署实战

基于YoloV5的手语识别:从数据标注到树莓派部署实战 简介基于YoloV5的手语识别系统项目包面向计算机视觉、无障碍交互开发者和学习者解决手部关键特征定位与手语词汇实时分类问题。包内共181个文件压缩后约49.17MB主要包含75组图像与XML标注、模型配置与训练权重、Python脚本及Jupyter Notebook等XML标注用于训练目标检测模型配置与权重文件可直接加载推理Notebook则方便分步复现实验流程整体目录结构清晰便于按需检索。该资源配套详细的项目解读从预处理、特征提取、模型训练到识别应用均有说明可支撑教学演示、课程设计或手势交互场景的二次开发。已有640人学习下载适合需要YoloV5具体落地方案的研究者和开发者参考实践。1. 把“找手”和“认手型”分开手语识别才能落地在摄像头画面里做手语识别最容易踩的坑不是算法选型而是把“识别手语”理解成“识别整个人”。手语的有效信息集中在手掌、手指和手腕背景、手臂晃动、人物移动都是噪声。YoloV5解决的是第一步在画面里定位到手并给出一个类别置信度把这个检测结果当作整条识别流水线的输入比用图像分类直接对全图打分要可靠得多。手语识别系统真正适合的技术路径是单帧检测加后续时序处理。YoloV5的COCO预训练权重已经学会了通用的物体特征迁移到“手掌”这种小目标上收敛很快用几百张自采数据就能得到一个可演示的原型。本文面向准备自己采集数据、训练模型并最终部署到边缘设备比如树莓派5的工程师讲清楚数据、训练、部署这条线以及每步的取舍。2. 手语数据采集与YoloV5环境配置先凑齐能训练的样本2.1 手语视频拆帧与Yolo标注的细节手语识别的训练数据不推荐去网上下载现成图片集。手语的表达存在地域差异不同人打同一手势的节奏和形态都不一样网图很难覆盖实际使用场景。常见做法是自己录制视频再拆帧。录一段手势视频用ffmpeg按固定帧率抽帧ffmpeg -i sign_hello.mp4 -vf fps15,scale640:-1 frames/frame_%04d.jpgfps设成15而不是30是因为手语动作中手掌形变较慢每秒15帧已经能覆盖完整轮廓30帧会产生大量重复相近帧标起来浪费人力。scale640:-1把画面缩放到640宽这一步可以提前模拟YoloV5训练时的输入尺度让标注框更接近后续实际训练分布。标注工具用LabelImg输出格式选YOLO。标注框要贴着手指尖和手腕不要框到小臂。手语识别里最常见的标注错误是把手掌下方的小臂一起框进去引入多余纹理干扰。建议每个类别采集30到60段不同角度、不同距离的视频并保证背景有深浅变化。数据总量不需要像通用目标检测那样动辄上万张手部特征在同一个人身上相对稳定几百张已经能启动训练。注意手语词汇中静态手型占多数动态词汇例如“谢谢”这类有明显轨迹变化的在单帧检测里根本无法区分这类数据要么不纳入一期要么给后续时序模型单独准备。2.2 用conda固定YoloV5训练环境避免依赖漂移环境配置是复现的第一道坎。YoloV5对依赖版本敏感尤其是numpy和opencv在版本升级后经常出现推理结果异常。我一般用conda独立环境隔离项目conda create -n sign_yolov5 python3.9 -y conda activate sign_yolov5 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplerequirements.txt从YoloV5官方仓库里拿。如果你用的是ultralytics的整合版安装最省事pip install ultralytics注意PyTorch的安装方式。训练机有NVIDIA显卡时先到PyTorch官网挑对应CUDA版本的安装命令再装torch和torchvision最后装其他依赖训练机上只有CPU时不要指望训练速度手语数据集即使只有几百张图CPU上跑100个epoch也够等。装完环境后用官方预训练权重快速验证环境正常python detect.py --weights yolov5s.pt --source data/images/bus.jpg这条命令会下载yolov5s.pt并跑出一张带检测框的图片。若这一步报错优先检查torch和torchvision版本是否匹配以及opencv是否正常。2.3 数据集目录结构与data.yaml配置YoloV5要求图片和标签分开存放用data.yaml描述数据集路径path: /home/user/hand_sign train: images/train val: images/val nc: 10 names: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]path建议写成绝对路径比相对路径稳。目录结构固定如下hand_sign/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamltrain和val的比例按80/20切。每个jpg对应一个同名txt标签里面每行是“类别序号 x_center y_center width height”四个坐标值都归一化到0到1之间。LabelImg会自动生成这个格式不用手写。这里有个容易踩的坑标签类别序号必须和data.yaml里的names顺序一致。比如names里第3项是‘2’那么标签文件里类别序号为2的框代表的是‘2’这个手势而不是数字2。如果你中途调整过names顺序必须回头改标签文件否则训练不会报错但识别结果全部错位。3. 训练YoloV5手语识别模型与超参数调整3.1 选yolov5s还是yolov5n小目标的网络结构考量训练手语模型前先理解YoloV5网络结构图的基本盘。Backbone是CSPDarknet负责从输入图像中提取多层特征Neck是PANet把浅层的位置信息和深层的语义信息融合Head部分输出三个尺度的检测结果分别对应小、中、大目标。手语识别中“手”在画面里属于中偏小目标依赖大尺寸特征图。官方提供yolov5n、yolov5s、yolov5m、yolov5l这几种尺寸n和s的Backbone更薄推理更快m和l精度更高但开销大。对于静态手型识别yolov5s是性价比起点如果目标是部署到树莓派5这类ARM设备建议直接用yolov5n训练减少后续模型转换时的精度损失。3.2 YoloV5训练命令与超参数表在提前准备好的conda环境里执行训练python train.py \ --data hand_sign/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --hyp hyp.scratch-low.yaml五个关键参数说明参数推荐值说明epochs100小数据集100轮足够超过200轮容易过拟合除非每轮都做增强batch-size16显存不够就调成8batch-size减小时初始学习率也应同步调低imgsz640训练输入分辨率手部小目标用640不要降到416以下hyphyp.scratch-low.yaml基础增强策略手语不建议上high增强weightsyolov5s.pt使用COCO预训练权重做迁移学习超参数表是YoloV5调优的核心。打开hyp.scratch-low.yaml重点关注下面几个值超参数默认值手语场景建议lr00.01迁移学习时可保持收敛快warmup_epochs3.0前3轮学习率从小往上升防震荡fliplr0.5左右翻转增强手语里可能导致左右手混淆改为0degrees0.0旋转角度0到5足够应对摄像头晃动hsv_h0.015色相扰动手语肤色变化小保持默认mosaic1.0四图拼接增强对小目标有效但标签多时显存占用高fliplr是手语识别里必须关注的参数。6和7这类手势在左右翻转后视觉形态基本不变但类别含义完全不同如果训练集里同时存在左右手习惯的手势建议把fliplr直接设为0。degrees和perspective同理手语拍摄基本是正面平视过度旋转增强只会让模型学到不存在的视角。3.3 训练日志解读判断过拟合与欠拟合训练完成后看runs/train/exp/下的results.png和混淆矩阵不要只看loss。手语模型的重点指标是验证集上的precision和recallprecision高、recall低说明模型把框打得保守手部区域大量漏检需要补充不同角度和比例的训练样本precision低、recall高说明模型框了一堆背景噪声需要检查标签框是否贴手太宽两个都低首先怀疑标注错位和类别数量不均衡而不是调超参。如果训练到后期loss在训练集上持续下降、验证集loss反而回升这是过拟合的明确信号。手语数据量少时尤其明显应对策略是减少epochs到原先的2/3或者提高mosaic增强的样本权重不要盲目加数据。模型保存为runs/train/exp/weights/best.pt后续导出都用这个文件而不是last.pt。4. 部署YoloV5模型到树莓派5边缘端实时推理4.1 导出ONNX模型并固定opset版本树莓派5上不会直接跑PyTorch模型PyTorch的运行时和算子实现在ARM上太重。常规路线是先把best.pt导出为ONNX再通过ONNX Runtime在树莓派5上推理。导出命令python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --opset 12 \ --simplifyopset 12是一个稳定且兼容性好的算子集版本树莓派5上的ONNX Runtime对它的支持完整。simplify参数会移除计算图冗余节点减少模型体积和推理时间。导出后用检查工具确认输出名和shapepython -c import onnx; monnx.load(best.onnx); print([o.name for o in m.graph.output])手语模型输入一般是“images:1x3x640x640”输出是“output0:1x25200x15”。25200是三个尺度特征图上的anchor数量总和15代表“4个坐标 1个置信度 10个类别”。看到这个输出就能确认导出正常。4.2 树莓派5上的运行环境与推理代码树莓派5安装运行依赖python -m venv ~/sign_env source ~/sign_env/bin/activate pip install onnxruntime opencv-python-headless numpy推荐使用opencv-python-headless而非标准opencv-python前者不携带GUI依赖在无桌面环境下启动快。ONNX Runtime用CPU版即可树莓派5没有可用的GPU加速后端。推理主力代码import cv2 import numpy as np import onnxruntime as ort import time session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name input_shape session.get_inputs()[0].shape # [1, 3, 640, 640] def letterbox(img, size640): h, w img.shape[:2] r min(size / h, size / w) new_w, new_h int(w * r), int(h * r) resized cv2.resize(img, (new_w, new_h)) canvas np.full((size, size, 3), 114, dtypenp.uint8) canvas[0:new_h, 0:new_w] resized return canvas, rletterbox将原图等比例缩放并填充灰色边框保持宽高比不变。填充色114是YoloV5训练时的默认像素值推理时不要随意改成黑色或白色会影响检测精度。预处理与后处理def preprocess(img): img, r letterbox(img, 640) img img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB并变为CHW img np.ascontiguousarray(img, dtypenp.float32) img / 255.0 img np.expand_dims(img, axis0) return img, r def postprocess(output, r, conf_thres0.35, iou_thres0.45): output output[0] # shape [25200, 15] boxes, scores, class_ids [], [], [] for row in output: class_scores row[5:] max_score class_scores.max() if max_score conf_thres: continue x, y, w, h row[:4] boxes.append([(x - w/2) / r, (y - h/2) / r, (x w/2) / r, (y h/2) / r]) scores.append(float(max_score)) class_ids.append(int(class_scores.argmax())) if not boxes: return [], [], [] # NMS用cv2.dnn.NMSBoxes做置信度阈值和IoU阈值分开控制 idxs cv2.dnn.NMSBoxes(boxes, scores, conf_thres, iou_thres) ...conf_thres设为0.35是经验值。手语检测中手部与背景对比度低阈值太高容易漏检低于0.3又会产生大量误报适合在摄像头画面里保留一个手势时使用0.35到0.4之间。摄像头循环cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, frame cap.read() if not ret: break start time.time() input_data, r preprocess(frame) output session.run(None, {input_name: input_data})[0] boxes, scores, ids postprocess(output, r) for box, score, cls_id in zip(boxes, scores, ids): x1, y1, x2, y2 [int(v) for v in box] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, class_names[cls_id], ...) cv2.imshow(sign, frame) if cv2.waitKey(1) ord(q): break识别结果直接输出为中文名会有字体渲染问题建议类别名在训练时就使用英文推理时再通过映射表转成中文显示。4.3 树莓派5上的速度优化参数树莓派5实测数据参考输入640的yolov5s在CPU上大约6到10 FPS输入416的yolov5n大约12到18 FPS。手语识别场景里识别结果不需要每帧都刷新帧率维持在10 FPS以上就能保证交互不卡顿。优化方向有四个一是把imgsz降到416损失一点小目标精度换将近一倍帧率二是把后处理中的循环向量化减少Python层开销三是打开ONNX Runtime的图优化它默认开启不需要额外配置四是如果使用树莓派5的官方摄像头优先用libcamera而不是OpenCV的V4L2后端。5. 视频回放验证与误识别排查的三个具体技巧5.1 用录屏回放而不是实时盯屏判断结果实时看摄像头窗口很难注意到单帧闪断。正确做法是先把识别结果逐帧写入视频文件然后回放对照fourcc cv2.VideoWriter_fourcc(*mp4v) writer cv2.VideoWriter(result.mp4, fourcc, 10, (640, 480)) # 每帧推理后写入 writer.write(frame)结束后再统一播放回放时重点观察频繁出现的标签跳变。比如某个手语手势在连续几帧内从“1”跳到“7”又跳回“1”这不是模型不稳定而是这两个手势的形态特征在某个角度下高度相似。处理办法是给输出结果加一个简单的滑动窗口滤波取最近5帧中出现次数最多的类别作为当前识别结果。5.2 手语识别独有的三类误测翻转、肤色与动态词汇第一类是翻转类误测。YoloV5训练的fliplr增强会让模型把左右手镜像当成同一物体如果数据集同时存在“6”和“9”这类仅靠朝向区分的数字就会产生系统性混淆。处理方法是在训练前统计类别间的混淆矩阵对混淆度高的类别对重新检查标注框是否贴合指尖。第二类是肤色与背景融合。当手部颜色和衣服或墙面色调接近时检测框会漂移到整个手掌的一半。检查方式是把训练集中的暗光照片单独筛出来看avg置信度低于0.3就要补充光照样本而不是调低conf_thres。第三类是动态手语词汇。YoloV5本身是单帧检测器对于“谢谢”“请”这类包含动作轨迹的手语单帧识别天然不适用。如果你想识别这类词汇正确的做法是在YoloV5外面套一个滑动窗口加LSTM的分类器把连续30帧的手部检测框序列作为输入单帧模型只负责提供定位不负责对动态词汇下结论这个边界要在系统设计时就讲清楚。5.3 检查手部在画面中的像素占比最后给一个可复用的检查脚本统计训练集中所有标注框占图片面积的百分比用来判断手部是否属于小目标python analyze_annotations.py --labels hand_sign/labels/train脚本统计所有标签文件中每一行width和height的平均值如果平均框宽高低于图片尺寸的10%说明目标过小。此时优先把训练输入分辨率提高或者在数据采集阶段把摄像头靠近手部。记住YoloV5能识别的精度上限由训练数据里目标的像素密度决定单纯调超参数救不了像素不足的问题。本文还有配套的精品资源点击获取
返回列表