简介:基于YOLOv5的手语识别系统资源包,面向计算机视觉学习者和无障碍交互开发者,用于解决实时手势检测与手语词汇识别问题。资源共181个文件,以XML标注与JPG手势图像为主,辅以模型配置、权重检查点、Python训练脚本和使用说明,压缩包整体49.17MB,目录结构清晰,便于按数据、模型、配置模块分段查阅。已有640人学习,适合希望从零复现目标检测手语识别流程的读者。内容覆盖图像预处理、YOLOv5特征提取、模型训练与实时推理等关键步骤,包含可用的预训练模型与迁移权重、pipeline配置文件、检查点以及protoc等辅助工具,可直接用于搭建训练环境、理解标注格式并扩展自定义手势类别。通过阅读源码和配置文件,可以掌握从数据集组织到模型调优的完整链路,为后续接入智能交互场景提供基础。
1. 基于YoloV5的手语识别:先想清楚它到底在识别什么
做一个基于YoloV5的手语识别系统,很多人第一反应是“用目标检测模型翻译手语”,这个方向对,但容易高估结果。它本质上不是端到端的手语句子翻译,而是把“识别”拆成一个目标检测任务:对每一帧画面里的手部区域做定位,再给这个区域打上类别标签,输出“这个手势是数字几”或“这个手型是哪个词”。后续的语句拼接、时序理解要靠上层逻辑做,模型本身只负责单帧手型。CVPR上那些端到端手语翻译模型确实做出过惊艳的效果,但公开数据集封闭、复现成本高,落地时多数团队还是会退回到检测框架——这也是YoloV5在这类项目里被选中的原因:训练闭环短,单帧精度可控,换场景后重训代价小。
这个方案适合先落地的场景很明确:数字手语、字母手语、固定词表的手势词(比如“谢谢”“你好”“要”)。它不适合理解语法完整的连续手语句子。如果你手里有树莓派、有摄像头、有几十个类别的采集环境,这篇可以帮你从零跑通整个闭环。别一开始就想做全词表,先做一个能用的小系统,比什么都重要。
2. 数据集与标注:手语识别的成败在标注框,不在网络结构
2.1 用公开数据集起步:自采数据前先有一个能跑的基线
手语识别最花时间的不是训练,是数据。YoloV5再强,没有贴合场景的数据也白搭。常见做法是先找公开数据集跑通流程,再去自采数据扩充。公开数据集里,ASLLVD这类偏字母手语,RWTH-PHOENIX偏连续句子、需要拆帧标注,挑哪个取决于你的词表。注意先看数据集的使用许可,很多公开数据仅限科研用途。
拿公开数据只是建立基线。你训练出来的模型要在你自己的摄像头、你自己的光照、你自己的拍摄角度下工作,公开数据的背景和手型风格往往跟你的现场差异很大。所以我会把公开数据当“预训练素材”,用它把网络权重预热一遍,再用手工标注的自采数据做微调。这套流程和YoloV5训练自己的数据集的标准做法一致:先有个能识别大部分类别的模型,再针对短板类别补数据。
2.2 自采手势数据:采集脚本与三个必须遵守的拍摄纪律
自采数据的关键不是数量,是覆盖度。一个手势只对着摄像头拍100张,不如换五个角度、三个背景各拍20张。采集脚本用OpenCV写很简单,我一般会直接抽帧保存:
import cv2 import os out_dir = 'dataset/raw' os.makedirs(out_dir, exist_ok=True) cap = cv2.VideoCapture(0) # 0 表示默认摄像头 count = 0 while count < 500: ret, frame = cap.read() if not ret: break cv2.imshow('capture', frame) key = cv2.waitKey(1) & 0xFF if key == ord('s'): filename = os.path.join(out_dir, f'frame_{count:04d}.jpg') cv2.imwrite(filename, frame) count += 1 elif key == ord('q'): break cap.release() cv2.destroyAllWindows()这段脚本的逻辑很简单:按s保存当前帧,按q退出。count用来控制每个类别采集数量,我这里设成500,实际不用这么多,每个手势300张左右足够起步。关键参数是保存格式用jpg,原始帧不压缩,后面标注和增强都基于这份原始数据。
拍摄时有三个纪律要遵守。第一,同一个手势要在不同距离、不同角度各拍一部分,手占画面比例大概在30%到70%之间,太小了模型学不到细节,太大了失去空间上下文。第二,背景要换,同一块纯色背景拍出来的模型,换到真实环境必翻车。第三,手指颜色和背景不能太接近,肤色和同色系背景会让边界糊成一团。这三个纪律决定了你的标注能不能做下去。
2.3 把图片转成YOLO标注:转换脚本和标签文件长什么样
YoloV5用的不是矩形框坐标加绝对像素,而是归一化的中心坐标加宽高。标签文件是每张图片对应一个同名txt文件,每行一个目标,格式是类别id x_center y_center w h。手工标注我习惯用Labelme,它导出JSON格式,方便脚本转换。
import json import os from glob import glob def labelme_to_yolo(json_path, class_names): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) image_w = data['imageWidth'] image_h = data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_names: continue points = shape['points'] x1, y1 = points[0] x2, y2 = points[1] x_center = (x1 + x2) / 2 / image_w y_center = (y1 + y2) / 2 / image_h w = abs(x2 - x1) / image_w h = abs(y2 - y1) / image_h cls_id = class_names.index(label) lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines if __name__ == '__main__': class_names = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] for json_file in glob('labels_json/*.json'): txt_lines = labelme_to_yolo(json_file, class_names) txt_path = os.path.join( 'labels_txt', os.path.basename(json_file).replace('.json', '.txt') ) with open(txt_path, 'w', encoding='utf-8') as f: f.write('\n'.join(txt_lines))这段转换逻辑里最值得注意的两个细节:一是x_center和y_center必须除以图片宽高,做归一化;二是width和height用绝对值后同样归一化,不能直接填像素值。YoloV5在训练时会按imgsz缩放图片,如果标签是绝对像素值,一旦图片尺寸变化,所有框全部错位,这是新手最容易踩的坑。class_names的顺序一旦定好就不要改,改一次意味着所有标签文件全部要重换id。
3. 训练一个自己的手语检测模型:环境、超参数与命令
3.1 配置yolov5环境:conda + pip 的免踩坑顺序
yolov5环境配置的坑主要出在torch版本和CUDA不匹配。先装PyTorch再装其他依赖,顺序反了会出现依赖冲突。我一般用conda建独立环境,避免污染系统Python:
conda create -n yolov5 python=3.10 -y conda activate yolov5 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118cu118对应CUDA 11.8的wheel源,你的显卡驱动如果比较新,装这个通常没问题。没有NVIDIA显卡的机器,直接pip install torch torchvision装CPU版也能跑通流程,只是训练慢很多。装完后建议先验证一次能不能调用GPU:
import torch print(torch.cuda.is_available()) # True 则说明GPU可用 print(torch.cuda.get_device_name(0))接下来拿源码和装依赖。写上这一步是因为很多人直接在别处copy项目文件,导致缺少组件、版本对不上。标准做法是:
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txtrequirements.txt里列了所有运行依赖,包括opencv-python、pandas、matplotlib这些。装完跑一下python train.py --help能正常打印参数说明,就说明环境通了。整套下来约需10到15分钟,剩下的时间基本都会花在数据准备上。依赖装完不建议立即换版本,yolov5主干更新较频繁,锁定你装好的版本更稳。
3.2 训练命令:从yolov5s到yolov5m的选择与参数含义
之前标注的数据集要整理成yolov5的约定结构:图片和txt标签同名,放在同一个目录里,train和val分开。然后在yolov5目录下写一个数据配置文件:
# hand_sign.yaml path: /path/to/hand_sign_dataset train: images/train val: images/val nc: 10 names: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']path指数据集根目录,train和val是相对根目录的图片文件夹路径。YoloV5会自己去同名txt文件里找标签。nc是类别数,names是类别名列表,顺序必须和转换脚本里的class_names完全一致,错一个就是灾难。
训练命令是整套流程的核心:
python train.py \ --data hand_sign.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --hyp data/hyps/hyp.scratch-low.yaml \ --device 0逐项说明参数:--weights yolov5s.pt指定预训练权重,yolov5会自动下载COCO预训练模型;手语数据不是COCO类别,但预训练权重提供了底层特征提取能力,迁移学习效果远好于从头训练。--imgsz 640是训练输入分辨率,手语手势目标相对不大,640是个平衡点。--batch-size根据显存来调。--hyp指定超参数文件,手语这样的小数据集优先用hyp.scratch-low.yaml,它做了保守的数据增强,训练更稳。
如果发现模型检测精度上不去,可以把yolov5s.pt换成yolov5m.pt。s和m的区别是网络深度和宽度,m更重但精度更高。手语类别粒度细,手指微小的弯曲差距决定了类别归属,s模型容易在这种细节上丢精度。优先用s跑通流程,卡在某个类别精度时再换m。
3.3 yolov5超参数里真正值得动的:四个参数的含义与调节顺序
新手拿到hyp.scratch-low.yaml容易什么都想改,我建议先只动四个参数,其余保持默认。调参的先后顺序比参数本身更重要。
第一个是lr0,初始学习率。手语数据集通常只有几千张,偏小,默认的lr0=0.01容易震荡收敛不稳,可以先降到0.005。判断标准是看训练前几个epoch的loss曲线,如果loss在快速跳变,就降学习率。
第二个是mosaic,马赛克增强。yolov5默认mosaic: 1.0,它把四张图拼成一张训练。但手语手势本身是小目标,拼接后手又被缩得更小,反而增加学习难度。我会把mosaic降到0.5左右。若手在画面中占比小、漏检多,这个参数优先调。
第三个是fliplr,水平翻转。COCO上水平翻转能提升泛化性,但手语有一个坑:手语方向有语义。左手掌朝上表示“要”的时候,镜像翻转后可能变成另一个语义的逻辑起点。数字手语相对安全,但方向敏感的手势词会被翻转坑到。所以手语识别项目里我一般把fliplr设成0.1甚至0.0。
第四个是copy_paste,复制粘贴增强。这个参数把目标从一张图复制到另一张图上,对目标检测提升明显,但手语场景里复制出的“手”可能和背景光影不融合,反而制造假样本。建议设置成0.1,只做轻量使用。
改完超参数后验证方式很简单:训练完看验证集mAP,同时打开runs/train/exp/confusion_matrix.png看混淆情况。只盯着mAP一个数字会掩盖类别间的差异,这在下一章展开讲。
4. 手语识别训练避坑指南:5个高频翻车现场
4.1 类别不平衡:常见手语词全被漏检
现象:训练完mAP看着还行,但实际推理时“谢谢”这个类别几乎检测不出来,召回率特别低。
原因:手语词频差异大。常用词你采集得多,冷门手势词只拍了一百张,导致模型把常见词学得很好,冷门词学不动。更隐蔽的是,很多人标注时偷懒,冷门词只标了一个角度、一个场景,模型学到的其实是背景而不手型。
解决:采集完数据先做一次类别数量统计,把低于平均数的类补到中位数的1.5倍以上。另一个做法是在数据yaml里临时提高小类别权重,但最根本的还是补数据。每类尽量不要低于200个标注实例。
4.2 小目标漏检:手在画面里占比太小
现象:指节动作、手指间的缝隙识别不准,检测框完整但类别老错。放大图片人能看清,但模型就是分不出来。
原因:训练时输入尺寸是640,手势目标映射到网络特征图只有几十个像素;而深度可分离卷积堆叠后,小目标的细节特征在前几层就丢了。手语识别对手指间细粒度差异要求很高,这正好是目标检测模型的弱项。
解决:一是把imgsz从640提高到960,代价是训练速度变慢、显存占用增加;二是把mosaic降下来,避免手势在拼接进一步增强中缩得更小;三是如果你的手势固定出现在画面中央区域,采集时裁出ROI区域再训练,去掉无用手部区域的环境干扰。
4.3 验证集指标虚高:mAP 0.9,一部署就现原形
现象:训练时mAP一路涨到0.9,模型放到树莓派或换个摄像头实测,识别率直线下降。
原因:验证集和训练集来自同一时刻、同一摄像头、同一背景下采集的数据,背景特征和光照几乎一模一样。模型可能学到的是“某个场景下的手”,而不是“手本身”。
解决:从数据集里单独挑出一个时间段、换一个背景、甚至可以换个不同手型的人,专门用来做验证集。验证集建议保持100张以上,每类至少10张,虽然降低了训练数据量,但换来的指标可信度值得。保存模型时以这个独立验证集的mAP为准,而不是训练过程里的验证mAP。
4.4 两个手势长得太像:网络学到的是“像”,不是“是”
现象:数字“6”和“5”经常混淆,还有指尖触碰和指尖分离的细微差异类别,硬是分不开,损失再怎么降都降不动。
原因:手语里不少词的手型只差一个手指位置,在图像上像素级差异很小,神经网络很难仅靠外观做出区分。加上标注框位置集中在手部,不同类别共享相同的空间位置,模型只能在极小的局部特征上找差异。
解决:先看混淆矩阵找出具体的混淆对,然后针对性地采集这些干扰样本。把这对手势训练图裁剪放大,同时增加不同手型的采集覆盖。这类问题靠调参解决不明显,数据层面做文章才是关键。
4.5 训练时显存溢出:还没跑几个epoch程序就崩了
现象:训练到一半报CUDA out of memory,进程直接挂掉。
原因:yolov5的mosaic增强会在一次迭代里拼多张图,内存峰值是开mosaic的数倍;同时验证阶段也会额外加载模型做推理,显存峰值翻倍。
解决:先把batch-size减半,这是最直接的降压手段;如果还溢出,降低imgsz到480甚至416;再不行就在train.py里把--cache参数去掉,--cache会预加载所有图片到内存,虽然提速但内存开销极大。另外训练前把其他占显存的进程关掉,树莓派或嵌入式设备就别想着跑训练了,只做推理。
5. 推理与部署:把检测模型变成可用的手语识别工具
5.1 detect.py上手:视频与图片推理的最小命令并理解yolov5后处理
训练完成后模型在runs/train/exp/weights/best.pt。yolov5源码自带的detect.py可以直接做图片和视频推理,输出检测框和类别。最小命令:
python detect.py \ --weights runs/train/hand_sign/weights/best.pt \ --source demo.mp4 \ --conf-thres 0.4 \ --iou-thres 0.45 \ --max-det 10--source可以是视频文件、图片文件夹或摄像头(传0调用默认摄像头)。--conf-thres是置信度阈值,低于这个概率的检测框被丢弃;手语场景里0.4是个比较合理的起点,太低会出现大量误检,太高会漏检一些拍摄角度偏的样本。--iou-thres是NMS的IoU阈值,YoloV5内部会先做解码再做非极大值抑制,手语手势不像行人那样互相遮挡,框之间重叠少,0.45够用,设太低会合并不该合并的框。--max-det限制单帧最多输出几个目标。后处理这块yolov5做了不少工作,解码、NMS、类别过滤都在这一步完成,新手不用自己重新实现一遍。
5.2 连续视频流里的一个关键技巧:类别平滑
单帧检测的抖动是手语识别落地时最常被忽略的问题。手在两根手指之间轻微变化时,模型可能在两帧里输出不同类别,导致输出“5-6-5-6-5”跳变。处理办法是对检测结果做时间维度上的多数表决。
import collections from collections import Counter # 在检测循环外层维护一个最多保留5帧结果的队列 history = collections.deque(maxlen=5) def smoothed_label(det): history.append(det.cls.cpu().item()) counts = Counter(history) label, _ = counts.most_common(1)[0] return labeldeque(maxlen=5)会自动丢弃最旧的一帧,保证队列里永远是最近的5帧结果。most_common(1)取这5帧里出现次数最多的类别作为当前输出。这个技巧能显著平滑识别结果,代价是延迟了约5帧的时间,实时性要求不高的场景完全可接受。部署树莓派5这类设备时,这个平滑逻辑同样适用,而且能掩盖一部分模型推理的不稳定。
5.3 树莓派5上部署自己的yolov5模型:参数取舍与速度平衡
树莓派5上跑yolov5s是可行的,但必须做推理参数取舍。常见做法是直接用torch加载模型跑推理,也可以用export.py转成ONNX或TensorRT加速。最小推理参数调整是把检测尺寸缩到:
python detect.py \ --weights runs/train/hand_sign/weights/best.pt \ --source 0 \ --imgsz 320 \ --conf-thres 0.5 \ --max-det 1--imgsz 320把输入从640降到320,推理速度提升接近4倍,代价是小手手势的检测精度下降。--conf-thres 0.5提高到了0.5,因为320分辨率下误检率升高,需要提高阈值过滤。--max-det 1限制只输出一个目标,手语识别场景一帧里一般只关注一个人、一双手,这个参数能砍掉大量无用的后处理计算。如果仍然卡顿,考虑把模型继续裁剪或做int8量化,不过量化后精度下降明显,我会优先降分辨率而不是量化。
这套流程我做过不止一次,最让我吃亏的一次是在自采数据阶段偷懒,把所有易混淆手势放在同一个背景同一个角度拍,验证集指标高到离谱,换了个摄像头部署就直接废掉。重拍一遍之后,部署效果才和训练指标对上。从那以后我学到的教训就是:数据采集时多花一天,胜过训练调试时多花一周。希望帮到你。
本文还有配套的精品资源,点击获取