前言
在进行手部姿态估计、手势识别时,需要先检测出手的位置。本文对网上公开的手部数据集进行获取、清洗、处理与数据增强,用于YOLO等目标检测网络的训练。
1.手部检测数据集概览
1.1 HaGRID手势识别数据集
如下图,可以用来做手势识别的数据集,共19个类别(下图18个类+1个no_gesture类)
原数据组成:详细介绍在项目地址里。官方提供了高清(723GB)和压缩(26.4GB)两个版本,共554,800张图片(train+val+test)。压缩版把图片较小的一边减小到512像素。
数据集特点:该数据集较简单,每类200张,用YOLOv8进行手势识别训练,在验证集上就可以取得99%的准确率,可以使用数据增强(马赛克增强+随机裁剪)减少训练数据量。
数据集划分:测试集每类有5000张,验证集每类有3000张。所以,用测试集9W张图片作为训练集,用验证集54000张作为测试集,这样可以简化划分,同时减少训练数据量。
1.2 COCO-hand和TV-hand
原数据组成:COCO-Hand是对COCO中含人的图片进行手部标注(27000多张有标签),TV-Hand是对电影里含人的截图进行手部标注(4000多张)。
数据集特点:官方是用人体手腕关键点和手部关键点两个模型自动标注的,所以标注质量很低(COCO-Hand有手工标注,相对好一些),需要数据清洗(筛选+打码)。
数据集划分:COCO-hand共获取6000张图片(5000训练,1000测试),TV-hand共获取553张正样本+5000张负样本(450+4000训练,83+1000测试)。
处理好后的COCO-Hand数据如下:
1.3 VOC-hand
原数据组成:人体含手部标注的应该有几百张。
数据集特点:官方样图看着标注挺好,实际标注质量还是不高,错标、漏标不少,还是需要数据清洗(筛选)。
数据集划分:共获取594张图片(训练420张,测试174张)。
1.4 100_day_of_hand
原数据组成:详细介绍见项目地址,几乎涵盖了所有场景的手部检测。
数据集特点:标注质量高,只需将原标签转换成YOLO格式。
数据集划分:按原数据集划分trainval有89916张用于训练,test有9983张用于测试。
1.5 hand_det_ump
原数据组成:旋转框标注,.mat格式,且bbox点不是 (x, y),而是 (y, x),转格式需要注意。
数据集特点:标注质量较高(也存在漏标),而且是旋转框,标签转换后YOLO的框会略大。
数据集划分:按原数据集划分trainval有4096+738张用于训练,test有821张用于测试。
1.6 hand_keypoint_26k
原数据组成:数据集组合了三个部分(v1.干净灰背景;v2.真人拍摄;v3.纯白背景)。原数据集用于手部关键点检测,也含有手部检测框。
数据集特点:v1简单只含手部,存在少量标注错误(需要数据清洗);v2含真人,存在约10%的标注错误(需要数据清洗);v3简单只含手部,无标注错误。同时,v1和v3方向单一,需要增加旋转,进行数据增强。
数据集划分:训练集(v1:1544张,v1旋转:1544张,v2:10585张,v3:9487张,v3旋转:9487张),验证集(v1:171张,v1旋转:171张,v2:1186张,v3:1054张,v3旋转:1054张)
1.7 handpose_v2
如下图,该数据集只有手部关键点,但是检测框bbox可以通过关键点获取。该数据集的检测框在图片中的位置是固定的(约为[0.5, 0.5, 0.6, 0.6]),所以需要进行随机裁剪,来修改检测框数值。同时,每张图片只标注了一只手,所以实在没数据时再使用。
具体处理细节在后续手部关键点检测中给出。
2.手部检测数据集处理
2.1 HaGRID马赛克增强
原数据集可以直接使用,但数据规模过大,训练非常耗时,而数据集本身又很简单,因此,可以使用马赛克增强。
如下图,对图片进行拼接,因为原图至少有一边大小为512(另一边更大),因此可以按相同边进行连接后,统一缩放(减少图片压缩导致的信息丢失)。
如下图红框,对YOLOv8中的马赛克增强进行改进:将填充区域使用负样本图片进行填充(下图使用COCO数据集中不含人的图片填充)
可以设置图片组合比例,如:{1:1, 2:1, 3:1, 4:1, 6:1, 8:1, 9:2, 12:5, 15:4, 16:4, 20:4, 24:4},再指定形状:{1: (1, 1), 2: (1, 2), 3: (1, 3), 4: (2, 2), 6: (2, 3), 8: (2, 4), 9: (3, 3), 12: (3, 4), 15: (3, 5), 16: (4, 4), 20: (4, 5), 24: (4, 6)}。
将每张图片含有原始图片数的期望值控制在11,这样原本55W张图片,将生成5W张。训练、推理的数据量直接减为原始数据集的9%。这样操作,在训练时需要关闭马赛克增强,同时要增加随机裁剪的概率。
2.2 COCO-hand和TV-hand清洗与打码
数据集生成方法:
1. 确定预测的手腕位置,称为“w_pred”。
2. 计算预测的手部关键点的平均值,称为“h_avg”。
3. 将“h_avg”-“w_pred”视为手的方向,确定与此方向对齐且包含预测手腕和所有手部关键点的最小边界矩形。
4. 计算与手部方向平行的矩形边的长度 L。
5. 计算预测的手腕位置 wpred 与最近的注释手腕位置 w_gt 之间的误差,E = ||w_pred − w_gt||。
6. 如果误差(相对于手的大小)大于 0.2(根据经验选择),则丢弃检测到的手 - 即,如果 E/L > 0.2,则丢弃检测。
COCO-hand Annotation format: [image_name, xmin, xmax, ymin, ymax, x1, y1, x2, y2, x3, y3, x4, y4]
TV-hand 逆天README: 写的是 [image_name, xmin, xmax, ymin, ymax, x1, y1, x2, y2, x3, y3, x4, y4],结果前面4个坐标实际是 [xmin, ymin, xmax, ymax]。
原数据集标注很不准,需要手筛(工作量较大,可以先打码除去负样本,减少筛选量)。
2.3 VOC-hand 预处理
2.4 100_day_of_hand 直接使用
2.5 hand_det_ump 坐标转换
boxes: [[array([[(array([[277.17641528, 392.31264669]]),
array([[296.94449976, 410.70348234]]),
array([[316.85334049, 389.30371276]]),
array([[297.08525601, 370.9128771 ]]),
array(['L'], dtype='<U1'), array([], shape=(0, 0), dtype=uint8))]],
dtype=[('a', 'O'), ('b', 'O'), ('c', 'O'), ('d', 'O'),
('handtype', 'O'), ('truncated', 'O')])
array([[(array([[172.31843252, 216.90699639]]),
array([[166.50836487, 232.88332619]]),
array([[189.7926786 , 241.35106813]]),
array([[195.60274624, 225.37473832]]),
array(['R'], dtype='<U1'), array([], shape=(0, 0), dtype=uint8))]],
dtype=[('a', 'O'), ('b', 'O'), ('c', 'O'), ('d', 'O'),
('handtype', 'O'), ('truncated', 'O')])
2.6 hand_keypoint_26k 筛选和旋转
v2只需要进行筛选,一万多张筛起来,工作量有点大。v3只需要旋转。v2需要筛选(错误标注图片较少)和旋转。
v1和v3进行旋转,如下图,顺时针旋转90度,只需将原归一化的xy坐标修改为(y, 1-x)。同理,逆时针90度为(1-y,x),旋转180度为(1-y, 1-x)。也可以旋转其他角度, 但计算起来很麻烦,还会使检测框变大(假设原本检测框刚好有目标区域,框的增大会导致无效信息的增加),所以没必要,旋转一次90度就好。
2.7 handpose_v2 预处理
手部关键点处理中补充。
3.获取负样本数据集
3.1 不含人的图片
如下图,像COCO中不含人的图片作为负样本。
3.2 HaGRID打码
HaGRID数据集比较干净,且标注准确,直接在训练集上随机抽取几万张图片打码来作为负样本。如下图,使用高斯噪声覆盖检测框区域,相比于纯色,高斯噪声更容易产生一定梯度。
3.3 无标签图片打码
已经有yolov8-pose姿态预训练模型和手部检测模型。
步骤1:对全图使用手部检测,对检测结果打码。
步骤2:用yolov8-pose获取腕关节和手臂关键点,两点反向延长获取手部框,然后打码。同时,对人体检测框内进行手部检测,然后打码。
获取的负样本图如下:
4.最终用于训练的数据集
训练集总共36W张(224793张正样本,135208张负样本);
验证集总共80754张(69865张正样本,10889张负样本):
YOLOv8的data.yaml:
# path: /path/to/datasets
train: ["./datasets/hagrid/yolo_det/test/images",
"./datasets/hand_detection/COCO_hand_det/train/images",
"./datasets/hand_detection/TV_hand_det/train/images",
"./datasets/hand_detection/TV_hand_det/train/images_neg",
"./datasets/VOC/yolo_hand_det/train/images",
"./datasets/hand_detection/100_day_of_hand/yolo_det/trainval/images",
"./datasets/hand_detection/hand_det_ump/train/images",
"./datasets/hand_detection/hand_det_ump/val/images",
"./datasets/hand_keypoint_26k/yolo_det/train/images",
"./datasets/hand_detection/hand_08_08/train/images",
"./datasets/negative_dataset/neg_hand/train"]
val: ["./datasets/hagrid/yolo_det/val/images",
"./datasets/hand_detection/COCO_hand_det/val/images",
"./datasets/hand_detection/TV_hand_det/val/images",
"./datasets/hand_detection/TV_hand_det/val/images_neg",
"./datasets/VOC/yolo_hand_det/val/images",
"./datasets/hand_detection/100_day_of_hand/yolo_det/test/images",
"./datasets/hand_detection/hand_det_ump/test/images",
"./datasets/hand_keypoint_26k/yolo_det/val/images",
"./datasets/hand_detection/hand_08_08/val/images",
"./datasets/negative_dataset/neg_hand/val"]
# Add negative image ----------------------------------------------------------
negative_setting:
neg_ratio: 0 # 小于等于0时,按原始官方配置训练,大于0时,控制正负样本。
use_extra_neg: True
extra_neg_sources: {"./datasets/hagrid/yolo_pose_neg/train/images": 42413,
# "./datasets/negative_dataset/neg_hand/train": 50000
}
fix_dataset_length: 0 # 是否自定义每轮参与训练的图片数量
# number of classes -----------------------------------------------------------
nc: 1
# Classes ---------------------------------------------------------------------
names:
0: hand