简介:面向目标检测与手势识别任务的数据集,采用YOLO与VOC两种标注格式,涵盖fist、no_gesture、like、ok、palm五个手势类别,共2400张图片,适合用于YOLOv5至YOLOv10、Faster RCNN、SSD等模型的训练与算法对比。资源内除图片外,还提供txt标签、xml标签和指定类别信息的yaml文件,且图片与txt标签已按训练集、验证集、测试集划分,可直接导入模型训练流程,省略自行标注、格式转换和数据拆分的环节。资源包含2000个文件,以txt标注文件为主,另有1个yaml类别配置文件,压缩包约479.2MB,目录结构与命名规范,便于按类别和划分批次检索。对于目标检测入门练习、YOLO系列迁移学习或手势识别应用开发,都是一份可直接使用的标注数据基础。目前已有300人浏览学习,适合需要快速获得高质量手势识别训练数据、比较不同检测算法效果的深度学习开发者。
1. 手势识别数据集:2400张五类数据,为什么我建议直接拿它当YOLO入门第一课
做手势识别目标检测这几年,我拆过不下十套公开数据集,最头疼的不是模型调参,而是数据本身:要么类别标注不全,要么训练集和验证集混在一起、还得自己重写划分脚本。这套手势识别数据集一共2400张图片,覆盖 fist(握拳)、no_gesture(无手势)、like(竖大拇指)、ok、palm(手掌)五个类别,同时附带 YOLO 格式的 txt 标签、VOC 格式的 xml 标签,以及一份指定类别信息的 yaml 配置文件。最实用的一点是,图片和 txt 标签已经划分好了训练集、验证集和测试集,下载解压后改一下 yaml 里的路径就能直接开训,不用再碰数据切分的脏活。适合刚接触目标检测、想快速跑通 YOLOv5 到 YOLOv10 全流程的从业者,也适合需要一个小规模基准数据来验证检测算法是否改对了的熟手。
2. 数据集解剖:五类手势、三种标注格式,先搞清楚再动手
很多人拿到数据集的第一反应是直接解压丢进训练脚本,结果要么类别数对不上,要么坐标全乱。这套数据虽然能开箱即用,但我还是建议先花十分钟把它的结构看清楚,后面至少能少踩三个坑。
2.1 类别定义与标注格式对应关系
五个类别里,fist、like、ok、palm 是四种明确的手势,no_gesture 是“无手势”背景类。这个背景类非常关键,它决定了模型除了学会识别四种手势之外,还得学会区分“空手”和“手势”。如果你要做的业务是“有手势就触发,没手势就不触发”,那 no_gesture 样本不足会让模型疯狂误检,我后面会专门讲这个问题。
从文件列表看,每个样本的命名像是No_gesture_0_Ok_4_xxx_jpg.rf.xxxx.txt,这种是 Roboflow 导出时留下的命名痕迹:前面几段是图片原始标签里的多标签信息,.rf.后面是导出的唯一标识。也就是说,一张图片可能同时包含“No_gesture”和“Ok”两个标签对象,这也是这数据集的一个细节——每个 txt 里可能不止一个标注框。
三种标注格式对应关系如下:
| 格式 | 文件后缀 | 内容示例 | 适用框架 |
|---|---|---|---|
| YOLO txt | .txt | 3 0.5 0.5 0.2 0.3 | YOLOv5-v10、Ultralytics |
| VOC xml | .xml | <name>ok</name><bndbox> | Faster RCNN、SSD、mmdetection |
| YAML 配置 | .yaml | names: ['fist', ...] | YOLO 系列直接读取 |
这里有个容易搞混的点:YOLO 的 txt 里第一列是类别索引,从 0 开始,顺序必须和 yaml 里的names列表严格一致。假设 yaml 里写的是names: ['fist', 'no_gesture', 'like', 'ok', 'palm'],那 txt 里0就是 fist,1是 no_gesture,以此类推。如果你拿到的 txt 和 yaml 是分开下载的,第一步就要核对这个对应关系,我见过有人把类别顺序搞反,训练出来的模型四个手势全部错位。
2.2 图片与 txt 的目录划分逻辑
这套数据已经按训练集、验证集、测试集分好目录。常见做法是类似这样的结构:
gesture_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── data.yaml └── annotations/ # xml 标签所在目录训练前先跑一条 find 命令,确认每个目录下的图片数量和你预期一致:
find images/train -name "*.jpg" | wc -l find labels/train -name "*.txt" | wc -l这两条命令分别统计训练集图片数量和 txt 标签数量。如果两个数字不一致,说明有图片漏标或者标签文件缺失,直接训练会让数据加载器报AssertionError: Label class x is greater than number of classes。正常的划分比例大概是 70% 训练、20% 验证、10% 测试,2400 张图片对应下来训练集约 1600 多张。你要是发现自己的数据集划分比例差很多,先别急着训练,回头看看下载源是否把划分说明写清楚了。
还有一点要确认:图片文件的 basename 和 txt 文件的 basename 必须完全一致。这里的命名里既有jpg又有rf信息,万一有人重命名过图片但没同步改 txt,训练时就会报Image not found。我一般会写个两行脚本校验一遍:
import os img_dir = "images/train" label_dir = "labels/train" img_names = set(os.path.splitext(f)[0] for f in os.listdir(img_dir)) label_names = set(os.path.splitext(f)[0] for f in os.listdir(label_dir)) print("缺标签的图片:", img_names - label_names) print("缺图片的标签:", label_names - img_names)这段逻辑很简单:把两边的文件名去掉扩展名后求差集。差集为空说明图片和标签完全对得上;只要有输出,对应的文件名就是问题样本,先删掉或补齐再训练。
2.3 标签文件里到底写了什么
打开一个 txt 文件,例如Ok_4_No_gesture_0_xxx_jpg.rf.xxxx.txt,里面的每一行代表一个目标框:
3 0.516667 0.631944 0.083333 0.141667 0 0.255556 0.706944 0.061111 0.113889第一行第一个数字3是类别索引,对应 yaml 里第 4 个类别即 ok。后面四个数字分别是归一化后的中心点 x 坐标、中心点 y 坐标、框宽度、框高度,取值范围都在 0 到 1 之间,是用像素坐标除以图片原始宽高得到的。第二行类别0是 fist,坐标数值不同,说明这张图里同时有两个手势框。看明白这个格式,后面你想做数据清洗或者转成别的格式就都不会慌。
xml 标签的内容则是另一个样子,里面存的是像素坐标和类别名:
<annotation> <filename>Ok_4_No_gesture_0_xxx_jpg.rf.xxxx.jpg</filename> <object> <name>ok</name> <bndbox> <xmin>310</xmin> <ymin>182</ymin> <xmax>360</xmax> <ymax>223</ymax> </bndbox> </object> </annotation>如果你要训 Faster RCNN 这类需要 VOC 格式的模型,直接用这套 xml 就行;如果要训 YOLO,就用 txt。两种格式都齐是这套数据最省心的地方,比那些只有单一格式的数据集强不少。但要注意,xml 里的坐标是原始像素值,不是归一化的,做转换时千万别把这两套坐标混着用。
3. 把数据集跑进 YOLOv8:从 yaml 配置到第一次训练
拿到数据集的第三十分钟,大多数人应该已经想跑起来了。YOLOv8 是现在最稳的版本,环境配置不再赘述,重点讲清楚 yaml 怎么改、训练命令怎么填,以及第一次跑完的结果怎么看。
3.1 先改 yaml 文件,把路径落到绝对路径
数据集自带的 yaml 内容大概长这样:
train: ../gesture_dataset/images/train val: ../gesture_dataset/images/val test: ../gesture_dataset/images/test nc: 5 names: ['fist', 'no_gesture', 'like', 'ok', 'palm']这里最大的坑是train和val的路径。自带的相对路径../是相对于你执行训练命令那个目录来算的,如果你把数据集放到了别的位置,或者训练时工作目录不在预期位置,路径就会失效,报Dataset not found。我一般会直接把这三行改成绝对路径:
train: /home/yourname/datasets/gesture_dataset/images/train val: /home/yourname/datasets/gesture_dataset/images/val test: /home/yourname/datasets/gesture_dataset/images/test nc: 5 names: 0: fist 1: no_gesture 2: like 3: ok 4: palm注意nc是类别总数,必须等于 5。names的索引顺序必须和 txt 标签里的数字一致。这里我特意写成了键值对的形式,能更明确地看到索引和类名的对应关系。特别提醒:自带的 yaml 如果只有val没有test,YOLO 也能训,因为训练时只用 val 做验证;但如果你想在全部训练完后用测试集做一次独立评估,就必须在 yaml 里写test字段。
另外,palm这个词在这个领域里有点特殊。有些公开数据集把“手掌”单独作为一类,但分类标准和角度不同;这里 palm 是五指张开的整个手掌,和医学影像里的 palm 数据集不是一回事,别混用。
3.2 YOLOv8 训练命令与参数说明
用 Ultralytics 的 YOLOv8 训练,命令很简单:
yolo detect train \ data=/home/yourname/datasets/gesture_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ project=gesture_yolo \ name=run1逐段说明一下:
data=指向刚才改好的 yaml 文件,必须是可访问的绝对路径。model=yolov8n.pt是预训练权重。n 是 nano 版本,最快但精度稍低;如果你的显卡显存够用,可以换成yolov8s.pt或yolov8m.pt。第一次跑建议用 nano,先把流程跑通,后期再换大模型。epochs=100是训练轮数。2400 张的数据集不算大,100 轮足够看到收敛趋势,但早停通常发生在 60 轮左右。imgsz=640是输入图片缩放到 640x640。这取决于原图分辨率,如果原图本身就是 800x600,缩放过程中目标会变小,小手势框可能丢失。可以改成imgsz=416试试,速度更快但精度可能下降。batch=16是批次大小。显存不够时报CUDA out of memory,解决方法不是硬调 batch,而是改用半精度推理,或把 imgsz 调小。patience=20是早停耐心值,验证集指标连续 20 轮不提升就停止训练。这个参数对中小数据集很有用,防止后期过拟合。
训练过程中,终端会打印每个 epoch 的 box_loss、cls_loss、dfl_loss 以及验证集的 mAP50、mAP50-95。如果你看到 mAP50 一直停在 0.9 以上,说明模型基本学出来了;如果 mAP50 反复在 0.2 到 0.5 之间震荡,大概率是数据或参数有问题,先别急着继续训。
3.3 验证集输出怎么看
训练结束后,gesture_yolo/run1/目录下会生成weights/best.pt和last.pt,还有一批验证图片。best.pt是在验证集上表现最好的权重,部署时用这个。
验证集图片里,YOLO 会把预测框画在图上,并在框上方标注类名 置信度。这时候我会重点看两类图片:一是误检图,比如把 no_gesture 框成了 like;二是漏检图,比如真实有个 ok 但没框出来。如果误检集中在 no_gesture 这一类,说明数据集里的背景样本不够多样;如果漏检集中在 fist,说明 fist 类别的样本量太少,模型没学好这类特征。
想要更精确地评估,单独跑一次验证命令:
yolo detect val \ model=gesture_yolo/run1/weights/best.pt \ data=/home/yourname/datasets/gesture_dataset/data.yaml \ batch=32命令里的val会输出一个confusion_matrix.png,这个图很有用。横轴是真实类别,纵轴是预测类别,对角线越亮越好。你一眼就能看出 no_gesture 被误分成哪一类,再针对性补数据。我第一次跑这套数据时,发现 no_gesture 和 fist 在混淆矩阵里互串了 20% 左右,后来发现是因为 fist 类的样本里有大量拳头放在身体前的照片,而 no_gesture 里也有类似的握拳动作,只是没明确标注。这就是类别定义边界模糊的问题,属于数据本身的坑。
4. 避坑指南:标注错位、类别不平衡、过拟合的四个排查点
数据标注类项目,训练失败永远有自己的道理。我把自己在类似数据集上踩过的坑和排查路径整理成四个现象,你可以直接对号入座。
4.1 现象:训练时报 “Label class x is greater than number of classes”
报错原因很简单:txt 标签里某一行的第一个数字,超出了 yaml 里nc定义的范围。比如 yaml 里nc: 5,但某个 txt 里出现了5,对应第 6 类,自然就越界了。
这类情况通常不是数据集原作者手滑,而是下载或者合并文件时混入了别的类别的标签。解决方法是写个脚本扫描所有 txt,找出所有类别索引的最大值:
find labels -name "*.txt" | xargs cat | awk '{print $1}' | sort -n | uniq -c这条命令会把所有 txt 第一列的数字做统计。如果输出里出现6或更大的数字,说明有越界标签。定位到具体文件后,要么删掉那行,要么改成正确的类别索引。我处理过一个案例:某个文件名里带Ok_4的图片,txt 里却写了个6,原因是原作者把不同批次的标签文件合并时串了。
4.2 现象:训练集 mAP 很高,验证集 mAP 很低
这是典型的过拟合,而且在小数据集上很容易发生。2400 张图片对目标检测来说只是入门量级,模型很容易背住训练集的细节,而不是学到泛化特征。
解决路径有三个,按优先级排序:
- 先把
patience调小到 10,让早停更敏感,避免在过拟合区域继续训练。 - 开启数据增强,YOLOv8 默认有一套增强策略,但你可以把
hsv_h=0.02、hsv_s=0.8、flipud=0.5这类参数在命令里叠加,增加多样性。 - 换更小参数的模型,从
yolov8n换到yolov8pico或者直接用yolov8s但加dropout。
如果验证集 mAP50 和训练集相差超过 15 个百分点,也不要急着加数据,先看看是不是标注框本身有问题。可视化几个训练样本,用annotations目录里的 xml 画出真实框,确认框是否准确贴合手势边缘。有的数据集标注框是正方形的截图框,手势只占框的一半,这种情况模型永远学不好。
4.3 现象:txt 和 xml 坐标对不上
这套数据同时提供两种格式,但它们有可能来自同一次标注的不同导出批次。txt 里的坐标是归一化的浮点数,xml 里是整数像素。如果你把 txt 转成像素坐标再和 xml 对比,发现两个坐标不一致,先别急着怀疑数据坏了,先确认图片分辨率是否一致。
同一个样本的 txt 写的是0.5 0.5 0.2 0.3,在 640x480 的图上对应中心点 (320, 240),宽 128,高 144;但 xml 里写的可能是 (300, 250)。这种偏差多数来自转码时四舍五入丢失精度,几个像素的偏差对训练影响不大。但如果偏差超过 30 个像素,说明 txt 和 xml 来自不同时期的标注版本,这时候以 txt 为准——因为 YOLO 训练读的是 txt。不需要纠结 xml 是否精确,但如果你要训 Faster RCNN,必须用 txt 重新生成 xml,而不是直接用自带的那个。
4.4 现象:模型把 no_gesture 误判成其他手势
这是我在这类数据集上花时间最多的问题。no_gesture 作为背景类,它的定义是“没有明确手势”,但图片里往往有手在画面中,只是没做手势。模型会把这些手误判为 fist 或 like,因为特征上太像了。
解决思路有两个方向。第一是数据角度,单独读一下训练集里 no_gesture 类别的图片数量。五类中如果 no_gesture 的样本占比只有 10%,而其他四类各占 20% 以上,那模型对背景类学习不充分。做法是给 no_gesture 类别的 loss 加大权重,YOLOv8 里可以在 yaml 中加loss_gain或者自定义每个类的权重,但更简单的做法是从其他数据集里抽样一些“空手”图片补充进来。第二是后处理角度,在推理阶段对下发的置信度阈值做差异化处理。比如检测到 fist 时,如果置信度低于 0.6 就丢弃,因为设备上误触发的代价比漏检更高。
从数据分布角度,我建议你训练前先做一次类别统计:
import os from collections import Counter counts = Counter() for f in os.listdir("labels/train"): with open(os.path.join("labels/train", f)) as fp: counts.update(line.split()[0] for line in fp if line.strip()) print(counts)这段代码统计每个类别的框数量。重点看 no_gesture 是不是明显偏少。如果偏少,一个实用的临时办法是复制 no_gesture 样本并做轻微平移、旋转,生成增强样本。注意增强时别改变标签坐标,用imgaug或者albumentations的同步变换。
5. 从 YOLO 到 Faster RCNN:这套数据的多框架兼容玩法
这套数据自带 VOC xml,意味着它不止能喂给 YOLO。但实际使用时,xml 和 txt 未必完全对应,所以更稳的做法是自己维护一份标准格式,再按需转换。
5.1 为什么说它兼容多框架
YOLO 系列的训练读取 txt,Faster RCNN、SSD 的常见实现读取 VOC xml,mmdetection 则可以两者都吃。这套数据两种都给了,理论上你可以在 YOLOv8 上快速出基准结果,再切到 mmdetection 里复现一次,对比不同框架对同一批数据的表现。这个流程在论文复现或算法选型时很有用,因为你不需要换数据集,只换加载器。
但兼容不只是文件格式的问题,还有目录结构。Faster RCNN 的 VOC 数据集标准目录长这样:
VOCdevkit/ ├── VOC2007/ │ ├── ImageSets/Main/train.txt │ ├── JPEGImages/*.jpg │ └── Annotations/*.xml自带的 xml 文件夹可能没有按这个结构组织,你需要写脚本生成三个 txt 文件分别记录训练、验证、测试图片的列表,并把 xml 和 jpg 放进对应目录。这里不展开,因为每套数据集的组织方式都略有差异,但核心做法是把图片名列表导出成train.txt,每行写一个不带扩展名的文件名。
5.2 自己写脚本把 txt 转成 VOC xml
有时候你需要更干净的 xml,比如自带的 xml 里缺了difficult字段,或者类别名大小写不统一。常见做法是用 Python 脚本从 txt 反推 xml,这样能保证 xml 和 YOLO 训练用的标签严格一致。
import os import xml.etree.ElementTree as ET class_names = ['fist', 'no_gesture', 'like', 'ok', 'palm'] def convert_txt_to_xml(txt_path, img_width, img_height, xml_path): with open(txt_path) as f: lines = f.readlines() annotation = ET.Element("annotation") size = ET.SubElement(annotation, "size") ET.SubElement(size, "width").text = str(img_width) ET.SubElement(size, "height").text = str(img_height) for line in lines: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) x_center = float(parts[1]) * img_width y_center = float(parts[2]) * img_height width = float(parts[3]) * img_width height = float(parts[4]) * img_height obj = ET.SubElement(annotation, "object") ET.SubElement(obj, "name").text = class_names[cls_id] bndbox = ET.SubElement(obj, "bndbox") ET.SubElement(bndbox, "xmin").text = str(int(x_center - width / 2)) ET.SubElement(bndbox, "ymin").text = str(int(y_center - height / 2)) ET.SubElement(bndbox, "xmax").text = str(int(x_center + width / 2)) ET.SubElement(bndbox, "ymax").text = str(int(y_center + height / 2)) tree = ET.ElementTree(annotation) tree.write(xml_path)这段逻辑是两个格式互换的核心:先把归一化坐标乘以图片宽高得到像素中心,再加减宽高的一半得到左上角和右下角。两个容易翻车的点:一是图片宽高必须从原图读取,不能写死;二是xmax和ymax用 int 转换时可能因为浮点误差产生 1 像素偏差,对训练几乎无影响,但如果你的评估脚本严格比对坐标,建议用round而不是int。我一般在转换脚本里读取图片尺寸用的是 PIL:
from PIL import Image img = Image.open("xxx.jpg") w, h = img.size写脚本的好处是,下一次你从另一个数据集拿到 txt 但想要 xml,或者反过来,都不必再手改。这套数据只是你手里的第一套,不是你唯一一套。
5.3 数据增强的实战补充
2400 张图片不够大,增强是绕不开的。YOLOv8 训练时自带的 mosaic 增强在最后十几个 epoch 会自动关闭,这是默认行为,不用额外干预。但如果你想在训练前离线扩充出一批样本,建议只做轻度的几何变换。
我常用的方案是albumentations:
import albumentations as A import cv2 transform = A.Compose([ A.HorizontalFlip(p=0.5), A.RandomBrightnessContrast(p=0.3), A.Rotate(limit=15, border_mode=cv2.BORDER_CONSTANT, value=0), ], bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"]))这个配置里,旋转角度限制在 ±15 度以内,避免手势变形太夸张;边框模式用常数填充,避免旋转后出现大块黑边干扰。bbox_params指定了标签格式是 YOLO 的归一化坐标,增强后返回的框会自动跟着目标旋转,不需要你手动换算。注意水平翻转之前,要确认你的业务是否允许左右手互换——如果手势本身有左右含义(比如“左边”“右边”),翻转会破坏语义。这套数据的手势不区分左右手,所以翻转是安全的。
增强后的图片可以用cv2.imwrite保存,同时把对应的新标签同步写到另一目录。这里别省事,不要原地覆盖原图,因为你可能之后还要对比原版数据训练的效果。
6. 把模型部署到摄像头前的最后一步:帧率与误检的取舍
训练好模型只是第一步,真正磨人的是把best.pt跑进摄像头实时检测。我先说一个反直觉的经验:2400 张图片训练出来的模型,在摄像头场景下,误检率比你想的高。原因很简单,训练集图片大多是手机拍的静态照片,摄像头画面里有运动模糊、曝光变化、手臂局部出画,这些在数据集里占比很小。
部署时我习惯先做一个“置信度阈值扫描”,而不是直接设 0.5。写一段脚本,用验证集从 0.3 到 0.9 每隔 0.05 测一次 mAP,找出误检和漏检平衡点的阈值。常见结果是 no_gesture 类别的误检阈值要 0.7,而 fist 只要 0.45——因为 fist 的类内差异大。所以实际推理代码里,我不用单个阈值,而是按类别传不同阈值:
from ultralytics import YOLO model = YOLO("best.pt") thresholds = {"fist": 0.45, "no_gesture": 0.7, "like": 0.55, "ok": 0.6, "palm": 0.5} results = model.predict(frame, conf=0.4, verbose=False) for box in results[0].boxes: cls_name = model.names[int(box.cls)] conf = float(box.conf) if conf >= thresholds[cls_name]: draw_box(frame, box.xyxy, cls_name, conf)这段逻辑的要点是:conf参数设为全局最低 0.4,保证各类别都有输出,然后逐个框用类别专属阈值过滤。这样做的代价是 no_gesture 的框可能被过滤得多一点,换来的是误触发下降。如果你在做一个手势控制器,误触发一次可能意味着执行一个错误动作,值不值得就看你这边的业务容忍度了。
最后一层优化是推理帧率。如果摄像头是 30 帧,YOLOv8n 在显卡上跑到 100 帧没问题,但在 CPU 上可能只有 10 帧。这时我会把imgsz从 640 降到 480,同时把模型转成 TensorRT 的 engine 格式。转换在 Ultralytics 里一句话:
yolo export model=best.pt format=engine imgsz=480 half=True这条命令导出 TensorRT engine,half=True开启 FP16 推理,显存占用和耗电都会降低。注意imgsz=480必须和训练时保持同一长宽比,否则会因缩放比例不对导致精度崩掉。我踩过一个大跟头:训练用 640,导出用 416,模型输出框的位置整体偏移了二十多个像素,排查了大半天才发现是缩放比例不一致。
从那以后,我每次换部署设备都强制自己先跑一遍验证命令,对比 engine 格式和 pt 格式在验证集上的 mAP,差超过 0.02 就绝不直接上线。这套手势识别数据虽然规模不大,但胜在格式齐全、划分清晰,拿来做 YOLO 入门、多框架对比、部署验证都够用。如果你正要找一个能快速跑通、又带点真实挑战的数据集,这一套不会让你白忙。希望帮到你。
本文还有配套的精品资源,点击获取