手势识别模型

1. 数据

1.1 标注

用于识别原始数据(图片、文本文件、视频等)并添加一个或多个有意义的信息标签以提供上下文,从而使机器学习模型能够从中学习
每张图片生成对应标签文件
标签文件仅含有一个表示手势种类的标签
多个标签可能导致训练过程中标签文件读取错误,也可以处理只读取手势类别的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import os

#手势与标签的映射
class_mapping = {
'01_palm': 0,
'02_l': 1,
'03_fist': 2,
'04_fist_moved': 3,
'05_thumb': 4,
'06_index': 5,
'07_ok': 6,
'08_palm_moved': 7,
'09_c': 8,
'10_down': 9,
}

data_path = 'leapGestRecog'

#遍历图片,根据文件夹名称对应手势种类,标注图片
for group in os.listdir(data_path):
group_path = os.path.join(data_path, group)
if not os.path.isdir(group_path):
continue

for gesture in os.listdir(group_path):
gesture_path = os.path.join(group_path, gesture)
if not os.path.isdir(gesture_path):
continue

if gesture not in class_mapping:
continue
class_id = class_mapping[gesture]

for img_file in os.listdir(gesture_path):
if img_file.endswith('.png'):
img_path = os.path.join(gesture_path, img_file)
label_file = img_path.replace('.png', '.txt')

with open(label_file, 'w') as f:
f.write(f"{class_id}\n")

print("标签文件更新完成。")

1.2 划分数据集

这里划分了训练集和测试集,校准集从训练集和测试集中随机抽取

划分训练集和测试集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import os
import shutil
from sklearn.model_selection import train_test_split

data_path = 'leapGestRecog'
#划分比例8:2
train_ratio = 0.8
test_ratio = 0.2

train_data_path = 'dataset/train'
test_data_path = 'dataset/test'

os.makedirs(train_data_path, exist_ok=True)
os.makedirs(test_data_path, exist_ok=True)

def split_data(class_dir, dest_train_dir, dest_test_dir):
files = [f for f in os.listdir(class_dir) if f.endswith('.png')]
labels = [f.replace('.png', '.txt') for f in files]

train_files, test_files = train_test_split(files, test_size=test_ratio, random_state=42)

for file in train_files:
#原图片,标签文件复制到目标文件位置
src_img = os.path.join(class_dir, file)
src_label = os.path.join(class_dir, file.replace('.png', '.txt'))

dst_img = os.path.join(dest_train_dir, file)
dst_label = os.path.join(dest_train_dir, file.replace('.png', '.txt'))

shutil.copy(src_img, dst_img)
shutil.copy(src_label, dst_label)

for file in test_files:
src_img = os.path.join(class_dir, file)
src_label = os.path.join(class_dir, file.replace('.png', '.txt'))

dst_img = os.path.join(dest_test_dir, file)
dst_label = os.path.join(dest_test_dir, file.replace('.png', '.txt'))

shutil.copy(src_img, dst_img)
shutil.copy(src_label, dst_label)

for group in os.listdir(data_path):
group_path = os.path.join(data_path, group)
if not os.path.isdir(group_path):
continue

for gesture in os.listdir(group_path):
gesture_path = os.path.join(group_path, gesture)
if not os.path.isdir(gesture_path):
continue

dest_train_dir = os.path.join(train_data_path, group, gesture)
dest_test_dir = os.path.join(test_data_path, group, gesture)
os.makedirs(dest_train_dir, exist_ok=True)
os.makedirs(dest_test_dir, exist_ok=True)

#每一组的每种手势都按照比例划分训练集和测试集
split_data(gesture_path, dest_train_dir, dest_test_dir)

print("数据集划分完成!")

划分校准集并存储为.pkl文件

由于后续量化参考教程划分校准数据集,保存为.pkl,其实可以直接划分出校准数据集,保存到dataset/image目录,后续对校准数据集的预处理也较为方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import os
import pickle
import shutil
from sklearn.model_selection import train_test_split

train_data_path = 'dataset/train'
test_data_path = 'dataset/test'
calib_data_path = 'dataset/calib'

os.makedirs(calib_data_path, exist_ok=True)

def get_files_from_dir(data_path):
files = []
for group in os.listdir(data_path):
group_path = os.path.join(data_path, group)
if not os.path.isdir(group_path):
continue

for gesture in os.listdir(group_path):
gesture_path = os.path.join(group_path, gesture)
if not os.path.isdir(gesture_path):
continue

for file in os.listdir(gesture_path):
if file.endswith('.png'):
img_path = os.path.join(gesture_path, file)
label_path = os.path.join(gesture_path, file.replace('.png', '.txt'))
#将每个图像文件路径和对应的标签文件路径作为一个元组 (img_path, label_path),并将这个元组添加到 files 列表中
files.append((img_path, label_path))
return files

train_files = get_files_from_dir(train_data_path)
test_files = get_files_from_dir(test_data_path)

# 设定提取比例
ts = 0.3 # 从训练集中和测试集中各提取 30% 的数据作为校准集的一部分

_, calib_train_files = train_test_split(train_files, test_size=ts, random_state=42)
_, calib_test_files = train_test_split(test_files, test_size=ts, random_state=42)

# 合并校准数据集
calib_files = calib_train_files + calib_test_files

# 将校准数据集复制到新的目录
for img_path, label_path in calib_files:
calib_img_path = img_path.replace('dataset/train', 'dataset/calib').replace('dataset/test', 'dataset/calib')
calib_label_path = label_path.replace('dataset/train', 'dataset/calib').replace('dataset/test', 'dataset/calib')

os.makedirs(os.path.dirname(calib_img_path), exist_ok=True)
os.makedirs(os.path.dirname(calib_label_path), exist_ok=True)

shutil.copy(img_path, calib_img_path)
shutil.copy(label_path, calib_label_path)

print("校准数据集提取完成!")

# 保存校准数据集为.pkl文件
calib_images = []
calib_labels = []

for img_path, label_path in calib_files:
#使用二进制模式 ('rb') 打开文件并读取其内容,将其作为字节流数据添加到 calib_images 列表中。
with open(img_path, 'rb') as f:
calib_images.append(f.read()) #加载图片

with open(label_path, 'r') as f:
calib_labels.append(f.read())

# 以二进制写模式 ('wb') 打开(或创建)一个文件
with open('X_cal.pkl', 'wb') as f:
#将 calib_images 列表序列化并写入文件 f
pickle.dump(calib_images, f)

with open('y_cal.pkl', 'wb') as f:
pickle.dump(calib_labels, f)

print("校准数据集已保存为.pkl文件!")

查阅.pkl文件
序列化数据:.pkl 文件使用 Python 的 pickle 模块保存,pickle 可以将 Python 对象序列化为字节流,这样可以方便地将数据对象(如列表、字典、NumPy 数组等)存储到文件中。
快速加载:相较于原始的文件格式(如文本文件或图像文件),pickle 可以更快地加载数据,因为它是直接将二进制数据加载回内存,而不需要进行文本解析或图像解码。
保存复杂数据结构:pickle 可以直接保存复杂的数据结构,例如包含多种类型的 Python 对象的列表或字典,而不需要进行特殊的转换。
便于后续使用:机器学习和数据科学中的很多工作流需要频繁加载和保存数据集,使用 .pkl 文件格式可以更高效地保存数据对象的状态,并在以后使用时方便地加载。

1.3 数据预处理(包含在模型训练脚本中)