简介:HaGRID 手势识别图像数据集是一份面向计算机视觉研究者与深度学习开发者的数据资源,覆盖常见手势类别,可用于手势分类、交互应用等视觉任务的模型训练与评估。压缩包内共 55 个文件,以 JSON 标注文件为主,并含一个 ignore.txt 文本说明,整体约 337.51MB,目录划分训练集、验证集与测试集标注模块,便于按任务阶段取用。数据集由真实人手图像构成,涵盖不同光照、背景与手部姿态,能帮助提升模型在现实场景中的泛化能力。目前已有 470 人下载学习,兼顾入门练习与算法实验,适合动手实践手势识别流程的读者。
1. HaGRID是什么:一个zip包如何撑起手势识别落地
第一次解压 HaGRID 手势识别图像数据集.zip 的人,大概率会被里面的文件数量吓一跳:几十万张带标注的手部图像,全部是“手贴近脸”的真实摄像头画面。HaGRID(HAnd Gesture Recognition Image Dataset)是 AIRI 团队整理并开源的手势图像数据集,覆盖 call、fist、peace、ok 等 18 类常见交互手势,每张图都带手部区域框和类别 ID。它解决的是做手势识别时最头疼的数据问题:真实场景、规模够大、标注齐全,比从零采集数据再找人标注省下几周时间,适合正在做手势交互、边缘端 AI 应用,或刚接触目标检测想拿真实数据练手的人。下面按我自己的使用路径,从读懂标注到训练模型再到排坑,完整过一遍。
2. 读懂HaGRID:18类手势、标注格式与数据划分
2.1 HaGRID的设计取向:为什么“手贴近脸”的数据更有用
HaGRID 的拍摄场景是摄像头前方 0.5 到 2 米左右,画面主体是肩部以上,手部自然地出现在脸附近。这和手机前置摄像头、笔记本摄像头、车机摄像头的实际取景高度一致,所以拿它训练出来的模型,部署到这些设备上时,构图不会出现“水土不服”。
这一点看着简单,实际影响很大。全身动作识别类数据集里,手往往只占画面极小一块,模型很容易学到背景特征而不是手势本身,换一个环境就失灵。HaGRID 把手的尺度拉大,背景又杂,逼着模型去学手势的结构。另外,它不是合成渲染图,而是真人采集数据,包含自然光照变化、肤色差异、遮挡和运动模糊,这些干扰在真实上线时都会遇到。
需要提前有心理预期的是,它的标注质量不是黑匣子,但也不是完美标注。部分框会把脸沿或手指尖裁掉一点,少量框甚至明显偏大。抽十张图把框画出来扫一眼,心里大概就有数了。这种“弱标注”在真实采集数据里很正常,后面训练和过滤环节可以处理。
2.2 18个手势类别与标签ID对照表
| ID | 类别名 | 动作描述 | 常见应用语义 |
|---|---|---|---|
| 0 | call | 手掌张开放在耳边 | 打电话 |
| 1 | dislike | 拇指向下 | 否定、差评 |
| 2 | fist | 握拳 | 确认、用力 |
| 3 | four | 四指并拢竖起 | 数字 4 |
| 4 | like | 拇指向上 | 点赞、确认 |
| 5 | mute | 食指竖在嘴前 | 静音 |
| 6 | ok | 拇指与食指成圈 | OK、确认 |
| 7 | palm | 五指张开手掌平放 | 停止、展示 |
| 8 | peace | 食指中指伸出成 V | 胜利、数字 2 |
| 9 | peace_inverted | 手背朝外的 V 字形 | 同一动作的手背视角 |
| 10 | rock | 食指与小指伸出 | 摇滚 |
| 11 | rock_inverted | 手背朝外的 rock | 手背视角 |
| 12 | stop | 手掌前推 | 停止 |
| 13 | stop_inverted | 手背朝外的 stop | 手背视角 |
| 14 | three | 拇指、食指、中指伸出 | 数字 3 |
| 15 | three2 | 食指、中指、无名指伸出 | 数字 3 的另一种形态 |
| 16 | two_up | 食指中指朝上 | 数字 2 |
| 17 | two_up_inverted | 手背朝外的 two_up | 手背视角 |
注意看后面的几个 inverted 类别。同一个手势,正面看和手背朝外看,在像素层面完全是两种形态,所以数据里专门把它们拆成了不同类别。如果你自己增广时做水平翻转,就会把 like 翻成 dislike、把 two_up 翻成 two_up_inverted 这类语义反转,这也是后面要重点排查的坑。
2.3 数据划分与标注文件的读取逻辑
HaGRID 在划分上有自己的讲究:按人物身份拆分训练集和测试集,同一人不会同时出现在两侧。这个设计比随机划分更贴近真实上线——模型必须泛化到没见过的人,而不是记住某个人的手长什么样。
标注文件最常见的形式是每张图像对应一个同名 txt,每一行代表一个手部实例,格式是五列以上:
class_id x1 y1 x2 y2 instance_id其中前五列是核心:类别 ID、左上角像素坐标 x1/y1、右下角像素坐标 x2/y2。比如一行4 210 130 431 468,表示类别是 like(ID 为 4),手部框左上角在 (210, 130)、右下角在 (431, 468),图像尺寸按 640×480 估算的话,手部区域占了画面相当大一块。
动手训练前,建议先做一次简单的可视化抽查:随机挑 10 张图,用 OpenCV 把标注框画回原图,看框和手的贴合程度。这一步能发现三种典型问题:框大了、框小了、框偏了。如果是整体偏差,多半是读取坐标时把 x1y1x2y2 的顺序读错了;如果是偶发偏差,就是弱标注本身。后面还会提到,HaGRID 的坐标是像素坐标,不是 YOLO 的归一化坐标,这个转换不能省。
3. 把HaGRID转成YOLO能直接吃的目录:转换脚本、划分与坐标归一化
3.1 先校验压缩包,再解压
拿到HaGRID-HAnd手势识别图像数据集.zip之后,第一步不是急着解压,而是先校验完整性和磁盘空间。压缩包在几个 GB 量级,网络下载或拷贝过程中丢字节的情况并不少见,解压到一半报错再重新下载,浪费的时间更多。
# 先看磁盘剩余空间,确认足够 df -h . # 校验压缩包完整性 unzip -t 'HaGRID-HAnd手势识别图像数据集.zip' # 通过校验后再解压 unzip 'HaGRID-HAnd手势识别图像数据集.zip' -d hgrid_rawunzip -t会把压缩包里的中央目录和每个文件条目逐一比对,输出 OK 才算完整。如果中途报invalid zip archive: could not find eocd,基本可以断定是下载中断或跨介质拷贝丢字节,别挣扎,直接重下。Windows 上解压这类多层目录包,还容易碰到单条路径超过 260 字符的经典问题,现象是解压到一半报错或静默丢文件,解决办法是把包放到磁盘根目录下的短路径,比如C:\hgrid\,再用 7-Zip 解压,别在深层目录里硬解。
如果你后续要重新打包分发,注意用zip -r hagrid_yolo.zip hgrid_yolo/这种命令行压缩方式,别从图形界面直接拖拽,否则 macOS 的__MACOSX元数据目录会被一起打进去,污染交付包。
3.2 用Python脚本完成像素坐标转YOLO坐标与数据划分
解压完成后,HaGRID 的原始目录结构是按手势类别或人物整理的,和 YOLO 训练要求的images/、labels/同级目录不一致。另外,它的标注是像素坐标,YOLO 需要的是归一化坐标。一个脚本同时解决目录重组、坐标归一化、train/val 划分三件事:
from pathlib import Path import random import shutil from PIL import Image src = Path("hgrid_raw") # 解压根目录 out = Path("hgrid_yolo") # 输出目录,YOLO 格式 split_ratio = 0.9 # train 占比,剩余 0.1 给 val random.seed(42) for img_path in src.rglob("*.jpg"): label_path = img_path.with_suffix(".txt") if not label_path.exists(): continue # 每张图单独读宽高,避免依赖“统一 640x480”的假设 with Image.open(img_path) as im: w, h = im.size keep_lines = [] for line in label_path.read_text().splitlines(): parts = line.split() if len(parts) < 5: continue cls = int(parts[0]) x1, y1, x2, y2 = map(float, parts[1:5]) # 像素坐标转 YOLO 归一化坐标:中心点 + 宽高 cx = ((x1 + x2) / 2) / w cy = ((y1 + y2) / 2) / h bw = (x2 - x1) / w bh = (y2 - y1) / h # 过滤非法框:越界、零面积,避免训练时直接报错 if not (0 <= cx <= 1 and 0 <= cy <= 1 and bw > 0 and bh > 0): continue keep_lines.append(f"{cls} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") if not keep_lines: continue # 随机划分 train/val sub = "train" if random.random() < split_ratio else "val" # 复制图像,生成同名标签 shutil.copy(img_path, out / sub / "images" / img_path.name) (out / sub / "labels" / label_path.name).write_text("\n".join(keep_lines))逻辑上分三段:找同名 txt、转换坐标、写入目标目录。关键在于转换坐标那两行,((x1 + x2) / 2) / w是先算像素中心点再除以图像宽,得到 0~1 之间的中心坐标;(x2 - x1) / w是把像素宽度归一化。这里最容易抄错的是忘记除以宽高,直接把像素坐标当 YOLO 坐标写进去,训练时 loss 直接爆炸或者框全部跑到图像外。
参数方面,split_ratio = 0.9表示 90% 做训练、10% 做验证。这个比例可以按数据量调,50 万张的量级下 9:1 已经够用。random.seed(42)保证两次运行划分结果一致,方便复现实验。这里用的是随机划分,但对 HaGRID 来说,如果你想严谨地评估泛化能力,应该按人物分组划分,而不是按图片随机划分,因为同一人的多张图高度相似,随机划分会把相似帧同时分到 train 和 val,评估结果会偏乐观。
提示:原始标注是像素坐标,直接喂给 YOLO 会全部越界;转换脚本里除以实际宽高的那两行,是整个流程里最容易抄错的地方。
3.3 标签ID与实例ID的几个细节
转换过程中有几个细节值得单独说。第一,类别 ID 从 0 开始,0 是 call、17 是 two_up_inverted,后面写hagrid.yaml时 names 列表的顺序必须和这个 ID 一一对应,顺序错了模型输出的语义就全错。第二,原始标注里的 instance_id 在转 YOLO 格式时被丢弃了,如果以后要做手势跟踪,这个 ID 是有用的,最好在转换时单独存一份映射。第三,脚本里按 txt 是否存在来决定是否保留图片,但 HaGRID 理论上每张图都有标注,如果发现大量图片没有同名 txt,多半是解压时名字被改掉了,比如 Windows 下中文文件名被转码。
转换结束后,用两个命令快速验证结果是否合理:
# 统计总图片数和总标注数 find hgrid_yolo -name "*.jpg" | wc -l find hgrid_yolo -name "*.txt" | wc -l # 按类别统计样本量,确认没有类别缺失 cat hgrid_yolo/val/labels/*.txt | awk '{print $1}' | sort | uniq -c图片数和标注数应基本相等。类别统计里如果 18 个类都有且分布合理,就可以进入训练环节了。
4. 跑通一次手势检测训练:YOLOv8、参数设置与评估口径
4.1 为什么选YOLOv8当底座
HaGRID 是典型的单类目标检测任务,需求是“找到手在哪里 + 判断这是什么手势”。YOLO 系列在这个任务上是工业界默认选择:检测精度够用、推理速度快、部署生态成熟,从 PyTorch 训练到 ONNX/TensorRT 导出都有现成路径。自己搭一个 Faster R-CNN 或 DETR 不是不行,但对 50 万张图的数据量来说,训练成本和调参成本都更高,而且后期部署时 YOLO 的社区支持和工具链明显更省事。
如果你做的不是检测而是纯分类,比如只要判断“图里是什么手势”而不关心手的位置,那更合适的路径是按标注框裁出手部区域,再训练一个图像分类模型。HaGRID 的框能直接提供裁剪区域,这一步省不了。但大多数真实交互场景里,手势出现的位置不固定,先检测后识别才是通用方案,这也是下面训练走检测路线的理由。
4.2 训练命令与必调参数
假设已经按第 3 章转换好了目录,并且把数据配置文件写好后,用 YOLOv8 训练的最小命令如下:
yolo detect train \ model=yolov8n.pt \ data=hagrid.yaml \ imgsz=640 \ batch=32 \ epochs=80 \ lr0=0.01 \ mosaic=1.0 \ patience=10 \ device=0 \ project=runs_hagrid \ name=yolo8n_hagrid对应的hagrid.yaml长这样:
path: /path/to/hgrid_yolo # 建议写绝对路径 train: train/images val: val/images nc: 18 names: - call - dislike - fist - four - like - mute - ok - palm - peace - peace_inverted - rock - rock_inverted - stop - stop_inverted - three - three2 - two_up - two_up_inverted参数按实际硬件条件和任务特点来调。imgsz=640和 HaGRID 原始图像分辨率基本一致,不需要强行缩到 320,手势是相对小的目标,分辨率太低会掉精度。batch按显存定,24GB 显存跑 32 没问题,显存紧张就降到 16。mosaic=1.0对贴脸手势有效,但如果你后面扩展了自有数据,mosaic 拼接时框会被切开,训练后期建议降到 0.5。patience=10意思是 10 个 epoch 内 val 指标没有提升就早停,省时间。model=yolov8n.pt是官方预训练权重,用它初始化而不是从零训练,收敛快得多,这也是 HaGRID 数据量大但未必需要很大模型的原因。
4.3 用mAP和F1评估“能不能用”
训练完成后,先跑一遍验证集,别急着上线:
yolo detect val \ model=runs_hagrid/yolo8n_hagrid/weights/best.pt \ data=hagrid.yaml重点看三个输出:mAP50、mAP50-95、混淆矩阵。mAP50 衡量框和类别综合正确率,mAP50-95 更严格,对框的定位精度更敏感。按我复现 HaGRID 的经验,用 YOLOv8n 训练 80 个 epoch,mAP50 到 0.85 以上、mAP50-95 到 0.65 以上是正常水平,低于这个水平先检查数据和训练配置,而不是急着加模型复杂度。真正值得盯的是混淆矩阵里 like 和 dislike、peace 和 two_up 这两组相邻类别,它们形状相似,最容易互相认错。如果总 mAP 很高但这两组经常混,说明模型“大体能用,细节拉胯”,这种问题在总指标里看不出来,必须看混淆矩阵。
5. HaGRID高频翻车点:解压、类号、坐标与评估的五个实操教训
5.1 解压与路径类问题
坑一:压缩包解压到一半报错,或者解压后目录缺文件。现象是unzip -t报invalid zip archive: could not find eocd,或者解压后统计图片数量比预期少几个数量级。原因是下载中断、FTP 传输模式错误、或者硬盘空间不足导致写入失败。解决方法是先跑unzip -t校验再解压,解压时用df -h确认剩余空间,Windows 上把包挪到短路径下用 7-Zip 解压。这个坑看似基础,但项目组里最终至少会翻一次车,而且往往是在训练环境里解压到一半才被发现。
坑二:解压后多出__MACOSX目录和一堆._开头的文件。现象是从 macOS 上压缩、到 Linux 服务器解压后,目录里散布着元数据文件,Python 的rglob("*.jpg")不会匹配它们,所以影响不大,但如果你用find全量扫描会看到大量无关文件。原因是 macOS 归档工具会把扩展属性单独打包。解决方法是解压后加一步清理:rm -rf __MACOSX,并且以后自己打包时用zip -r命令而不是图形界面拖拽,避免再次污染。
5.2 标注与训练类问题
坑三:类别 ID 从 1 开始读,结果模型预测全部偏移一位。现象是训练正常、loss 正常,但推理时把 call 猜成 dislike、把 ok 猜成 palm,整体错位。原因是 HaGRID 的类别 ID 是 0~17,而网上不少教程和表格把序号从 1 开始标,照抄就偏了。解决方法是转换脚本里输出任意一张图的标签跑head -1看看第一列数字,再对照第 2 章的类别表验证;如果发现标签文件里出现了 18,说明某个环节加了 1,回去改转换脚本而不是强行改数据。
坑四:像素坐标直接当 YOLO 坐标用。现象是训练时 loss 不收敛,或者 loss 收敛但推理框全部偏到图像角落。原因是 HaGRID 原始标注是像素坐标 x1y1x2y2,YOLO 要的是归一化的 cx/cy/w/h,很多人省了除以宽高这一步。解决方法是严格按第 3 章的脚本转换,并且转换后抽一个文件手工核对:4 0.5013 0.6229 0.3453 0.7042这种格式,cx/cy 都在 0~1 之间,宽高也都小于 1。
坑五:随机划分导致同一个人出现在训练集和验证集,评估指标虚高。现象是本地验证 mAP 接近 0.9,拿到现场测只有 0.6。原因是同一人的几十张图高度相似,随机划分后相似帧被同时分到 train 和 val,模型相当于“见过”验证集,评估结果自然好看。解决方法是按人物 ID 分组划分,或直接采用官方给出的划分方案,再拿划分后验证集算指标。
6. 用一组自拍图验证HaGRID的成色:迁移与部署的最后一公里
模型在 HaGRID 验证集上分数再高,也只代表它在 HaGRID 的采集条件下表现好。部署前我一般会做一套自建验证集:手机或笔记本摄像头对着自己拍 50 张,覆盖正手、手背、不同距离、不同光照,甚至故意加一点遮挡,然后批量喂给模型推理,统计错误模式。这一步不是求精度数字,而是看模型在“没见过的人和环境”下是否崩,以及崩在哪里。
自拍验证里最容易暴露的问题就是手背方向的手势识别。HaGRID 里 inverted 类别专门覆盖了手背视角,但训练时如果开了水平翻转增广,like 和 dislike、two_up 和 two_up_inverted 会被翻转混淆,模型在真实场景里就会把“竖大拇指”认成“竖大拇指但手背朝外”,语义直接反转。处理方法是训练时关闭水平翻转,或者在数据层面把正反手明确区分,不要指望翻转增强来补充手背样本。
如果要在自己的产品场景里用,我的做法是拿 HaGRID 训练出的权重当预训练底座,再采 100 到 500 张目标场景数据微调。这个量级下,模型能把光照、肤色、摄像头位置的差异拉回来,而不会丢掉 HaGRID 学到的 18 类手势基础。最后验收时别只看 mAP,还要看单帧推理延迟和连续帧的稳定性,手势识别这种交互场景里,一帧识别错没事,连续帧抖动才致命。
我在这里吃过一次亏:早期做演示时图省事,把 like 和 dislike 当成一个类别去训,现场演示时观众竖起大拇指,屏幕上的反馈却是“踩”,翻车翻得非常彻底。后来我给自己定了一条规矩:训练前画框抽查、训练后看混淆矩阵、部署前自拍验证,三件事一件都不能省,这也是我能给到的最实在的建议。希望帮到你。
本文还有配套的精品资源,点击获取