ARTICLE DETAIL

资讯详情

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

手势识别目标检测实战:2400张YOLO数据集全解析

手势识别目标检测实战:2400张YOLO数据集全解析 简介面向目标检测与手势识别任务的数据集采用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.txt3 0.5 0.5 0.2 0.3YOLOv5-v10、UltralyticsVOC xml.xmlnameok/namebndboxFaster RCNN、SSD、mmdetectionYAML 配置.yamlnames: [fist, ...]YOLO 系列直接读取这里有个容易搞混的点YOLO 的 txt 里第一列是类别索引从 0 开始顺序必须和 yaml 里的names列表严格一致。假设 yaml 里写的是names: [fist, no_gesture, like, ok, palm]那 txt 里0就是 fist1是 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 filenameOk_4_No_gesture_0_xxx_jpg.rf.xxxx.jpg/filename object nameok/name bndbox xmin310/xmin ymin182/ymin xmax360/xmax ymax223/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没有testYOLO 也能训因为训练时只用 val 做验证但如果你想在全部训练完后用测试集做一次独立评估就必须在 yaml 里写test字段。另外palm这个词在这个领域里有点特殊。有些公开数据集把“手掌”单独作为一类但分类标准和角度不同这里 palm 是五指张开的整个手掌和医学影像里的 palm 数据集不是一回事别混用。3.2 YOLOv8 训练命令与参数说明用 Ultralytics 的 YOLOv8 训练命令很简单yolo detect train \ data/home/yourname/datasets/gesture_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectgesture_yolo \ namerun1逐段说明一下data指向刚才改好的 yaml 文件必须是可访问的绝对路径。modelyolov8n.pt是预训练权重。n 是 nano 版本最快但精度稍低如果你的显卡显存够用可以换成yolov8s.pt或yolov8m.pt。第一次跑建议用 nano先把流程跑通后期再换大模型。epochs100是训练轮数。2400 张的数据集不算大100 轮足够看到收敛趋势但早停通常发生在 60 轮左右。imgsz640是输入图片缩放到 640x640。这取决于原图分辨率如果原图本身就是 800x600缩放过程中目标会变小小手势框可能丢失。可以改成imgsz416试试速度更快但精度可能下降。batch16是批次大小。显存不够时报CUDA out of memory解决方法不是硬调 batch而是改用半精度推理或把 imgsz 调小。patience20是早停耐心值验证集指标连续 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 \ modelgesture_yolo/run1/weights/best.pt \ data/home/yourname/datasets/gesture_dataset/data.yaml \ batch32命令里的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_h0.02、hsv_s0.8、flipud0.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 系列的训练读取 txtFaster RCNN、SSD 的常见实现读取 VOC xmlmmdetection 则可以两者都吃。这套数据两种都给了理论上你可以在 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。我一般在转换脚本里读取图片尺寸用的是 PILfrom PIL import Image img Image.open(xxx.jpg) w, h img.size写脚本的好处是下一次你从另一个数据集拿到 txt 但想要 xml或者反过来都不必再手改。这套数据只是你手里的第一套不是你唯一一套。5.3 数据增强的实战补充2400 张图片不够大增强是绕不开的。YOLOv8 训练时自带的 mosaic 增强在最后十几个 epoch 会自动关闭这是默认行为不用额外干预。但如果你想在训练前离线扩充出一批样本建议只做轻度的几何变换。我常用的方案是albumentationsimport albumentations as A import cv2 transform A.Compose([ A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(p0.3), A.Rotate(limit15, border_modecv2.BORDER_CONSTANT, value0), ], bbox_paramsA.BboxParams(formatyolo, 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, conf0.4, verboseFalse) 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 modelbest.pt formatengine imgsz480 halfTrue这条命令导出 TensorRT enginehalfTrue开启 FP16 推理显存占用和耗电都会降低。注意imgsz480必须和训练时保持同一长宽比否则会因缩放比例不对导致精度崩掉。我踩过一个大跟头训练用 640导出用 416模型输出框的位置整体偏移了二十多个像素排查了大半天才发现是缩放比例不一致。从那以后我每次换部署设备都强制自己先跑一遍验证命令对比 engine 格式和 pt 格式在验证集上的 mAP差超过 0.02 就绝不直接上线。这套手势识别数据虽然规模不大但胜在格式齐全、划分清晰拿来做 YOLO 入门、多框架对比、部署验证都够用。如果你正要找一个能快速跑通、又带点真实挑战的数据集这一套不会让你白忙。希望帮到你。本文还有配套的精品资源点击获取
返回列表