feat(pose): 添加姿态估计模型训练和检测脚本- 新增 YOLO 姿态估计模型训练脚本 (14.训练模型-姿态估计.py)
- 新增 YOLO 姿态估计模型检测脚本 (15.模型检测-姿态估计.py)- 添加 LabelMe 标注文件转 YOLO 格式的转换脚本 (labelme_to_yolo.py) - 添加 LabelMe 姿态标注转 YOLO 姿态格式的转换脚本 (labelme_to_yolo_pose.py)- 新增训练数据集配置文件 (dataset1/train.yaml, dataset1/data.yaml) - 添加训练样本标签文件 (dataset1/train/labels/5_png.rf.xxx.txt 等) - 配置手部关键点检测参数,支持11个关键点- 设置图像尺寸为640,批次大小为32,训练轮数为30 - 支持CPU设备训练,预留GPU训练接口 - 实现关键点标签转换,包括归一化坐标处理 - 添加类别名称和关键点顺序定义 - 集成Roboflow数据集配置信息 - 支持模型推理视频和图片文件夹输入-保存检测结果到本地文件 - 提供模型加载和参数配置说明- 支持best.pt和last.pt模型权重加载 - 实现关键点可见性标注 (2 表示可见)- 添加关键点标签映射和排序逻辑 - 创建输出目录自动检查和创建机制- 输出转换完成提示信息 - 定义关键点标签顺序 (f_1_1, f_1_2, ..., f)
|
|
@ -0,0 +1,15 @@
|
||||||
|
from ultralytics import YOLO
|
||||||
|
|
||||||
|
# 加载预训练模型
|
||||||
|
yolo = YOLO('models/yolo11n-pose.pt')
|
||||||
|
# yolo = YOLO('models/yolo11x-pose.pt')
|
||||||
|
|
||||||
|
# 训练模型
|
||||||
|
# data:数据集路径
|
||||||
|
# epochs:训练轮数
|
||||||
|
# imgsz: 图片大小
|
||||||
|
# batch: 批次大小
|
||||||
|
# device: 使用设备 0:GPU 'cpu':CPU
|
||||||
|
|
||||||
|
yolo.train(data='./dataset1/train.yaml', epochs=30, imgsz=640, batch=32, device='cpu')
|
||||||
|
print("训练完成")
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from ultralytics import YOLO
|
||||||
|
|
||||||
|
# 加载训练好的模型
|
||||||
|
# best.pt: 最佳模型,适用于生产
|
||||||
|
# last.pt: 最后一轮训练的模型,适用于继续训练
|
||||||
|
yolo = YOLO('runs/pose/train2/weights/best.pt')
|
||||||
|
# yolo = YOLO('models/yolo11n-pose.pt')
|
||||||
|
# yolo('resources/input.mp4', show=True, save=True)
|
||||||
|
yolo('resources/姿态估计-自定义', show=True, save=True)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
train: ../train/images
|
||||||
|
val: ../valid/images
|
||||||
|
test: ../test/images
|
||||||
|
|
||||||
|
kpt_shape: [11, 3]
|
||||||
|
flip_idx: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
|
nc: 1
|
||||||
|
names: ['hand']
|
||||||
|
|
||||||
|
roboflow:
|
||||||
|
workspace: crabapples
|
||||||
|
project: pose-label-qi4gq
|
||||||
|
version: 4
|
||||||
|
license: CC BY 4.0
|
||||||
|
url: https://universe.roboflow.com/crabapples/pose-label-qi4gq/dataset/4
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
path: ./dataset1 # 数据集根目录
|
||||||
|
train: train/images # 训练集目录名
|
||||||
|
val: valid/images # 验证集目录名
|
||||||
|
nc: 1 # 类别数
|
||||||
|
names: [ 'hand' ] # 类别名称
|
||||||
|
kpt_shape: [11, 3] # 关键点数量, 维度数量 (2 表示 x,y 或 3 表示 x,y,可见性)
|
||||||
|
flip_idx: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 关键点
|
||||||
|
#roboflow:
|
||||||
|
# workspace: crabapples
|
||||||
|
# project: pose-label-qi4gq
|
||||||
|
# version: 1
|
||||||
|
# license: CC BY 4.0
|
||||||
|
# url: https://universe.r
|
||||||
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.4980106469002696 0.5210340336134454 0.9595187331536388 0.7952501050420168 0.31628382749326145 0.15887090336134455 2 0.49280539083557956 0.3138540966386555 2 0.6320774258760108 0.5677090336134453 2 0.31614238544474393 0.40554527310924365 2 0.1032300539083558 0.31149474789915965 2 0.31880424528301887 0.5359605042016806 2 0.04661044474393531 0.5335344537815127 2 0.34387304582210243 0.6485216386554622 2 0.091125 0.7194095588235294 2 0.40198315363881404 0.7709457983193276 2 0.22757722371967656 0.8904413865546218 2
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.521675468483816 0.5048226765799256 0.8860931856899489 0.9360530669144981 0.13079139693356048 0.6075380111524163 2 0.3619626916524702 0.7667338289962825 2 0.5848917376490631 0.7973117100371747 2 0.4717445485519591 0.45636013011152415 2 0.41038918228279386 0.15159275092936803 2 0.5887324531516185 0.42722407063197027 2 0.5821443781942078 0.08329042750929369 2 0.6753246235782011 0.45754726916000155 2 0.7351526405451448 0.14876626394052045 2 0.7731171209540034 0.5452516728624535 2 0.932955877342419 0.34004916356877324 2
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0 0.31119766627771295 0.4018844506517691 0.42091645274212364 0.7651220670391061 0.1250828471411902 0.3437918994413408 2 0.15997456242707117 0.4997692737430168 2 0.2824428821470245 0.6896988826815642 2 0.2559768961493582 0.33362635009310987 2 0.2565303967327888 0.0508830540037244 2 0.3240253792298716 0.3400173184357542 2 0.31313162193698946 0.061950279329608934 2 0.3841848626286199 0.36324192201343036 2 0.4163682030338389 0.08204785847299814 2 0.4366808634772462 0.4498942271880819 2 0.5070864644107351 0.24957290502793295 2
|
||||||
|
0 0.7588829638273046 0.4356913407821229 0.4290819136522754 0.6692012104283054 0.942162252042007 0.5224180633147114 2 0.8413381563593932 0.6512546554934823 2 0.6957051341890315 0.7228277467411546 2 0.7854238623103851 0.4522063314711359 2 0.8205078179696617 0.1698008379888268 2 0.7087509334889148 0.43044608938547485 2 0.6952493582263711 0.12026340782122906 2 0.6450586931155192 0.4419185288640596 2 0.621041015169195 0.15973640595903166 2 0.5924945740956826 0.4828463687150838 2 0.563469544924154 0.2698160148975791 2
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.5116909190371991 0.5103464203233257 0.9214625820568928 0.9355762124711315 0.7077192560175055 0.7290727482678984 2 0.47908183807439825 0.8937125866050808 2 0.3129327133479213 0.8123004618937644 2 0.47407625820568927 0.6644008083140877 2 0.9567908096280088 0.3453039260969977 2 0.4309589715536105 0.5227428406466512 2 0.8209161925601751 0.1121919168591224 2 0.3286816192560175 0.4369609699769053 2 0.671258533916849 0.08105127020785219 2 0.1860917943107221 0.4227184757505774 2 0.44097035010940916 0.1309676674364896 2
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
0 0.237708533916849 0.4413739197530864 0.4057931072210066 0.8759524691358025 0.38839091903719913 0.562329012345679 2 0.29891706783369804 0.7599103395061728 2 0.0704477024070022 0.8311961419753087 2 0.27909442013129104 0.5735907407407408 2 0.4108089715536105 0.14452253086419753 2 0.21449015317286654 0.4787239197530864 2 0.3647879649890591 0.06194212962962963 2 0.13330361050328227 0.4352554012345679 2 0.263048249452954 0.05245046296296297 2 0.07826487964989058 0.42285370370370373 2 0.14125284463894966 0.15948333333333334 2
|
||||||
|
0 0.7253844638949671 0.5215375 0.5492310722100656 0.913316049382716 0.520664989059081 0.6375057098765432 2 0.6906377461706783 0.7938762345679011 2 0.910586761487965 0.8017950617283951 2 0.675085010940919 0.5425566358024692 2 0.4784204595185996 0.23187052469135805 2 0.7411031982848176 0.4784855650122625 2 0.5631542669584244 0.11600925925925927 2 0.8206215016687666 0.4754104267988527 2 0.673181181619256 0.10558055555555557 2 0.907315317286652 0.4800390432098765 2 0.8391327133479213 0.18670864197530865 2
|
||||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.5 0.5139559633027523 1 0.9717500917431192 0.23999856194690264 0.25721348623853213 2 0.10956393805309735 0.5475046788990825 2 0.22414026548672566 0.8682677064220183 2 0.4551941371681416 0.4291499082568807 2 0.6244196902654867 0.055982752293577985 2 0.5889128318584071 0.47038311926605497 2 0.8739679203539823 0.1098356880733945 2 0.6822233407079646 0.5905195412844036 2 0.9602350663716814 0.22233366972477064 2 0.7657805309734512 0.7057052293577982 2 0.9813836283185841 0.44794908256880733 2
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.5150173850574712 0.38422231947483587 0.7927554597701149 0.6767075492341357 0.1701617816091954 0.39178424507658643 2 0.37381580459770114 0.5174552516411378 2 0.5782625 0.6283656455142231 2 0.4658104885057471 0.3583204595185996 2 0.41665962643678156 0.12851301969365425 2 0.5853130747126437 0.3540252735229759 2 0.5842818965517241 0.08341378555798687 2 0.6711130747126437 0.3756934354485777 2 0.7037633620689655 0.13914945295404813 2 0.7507885057471265 0.4276787746170678 2 0.8816913793103448 0.2640832603938731 2
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0 0.47921510791366906 0.5048761224489796 0.5155844124700241 0.8138338775510203 0.6748226618705035 0.5235385714285714 2 0.5472028776978417 0.7444142857142857 2 0.3504914868105516 0.7438351020408163 2 0.5531387290167866 0.5306761224489795 2 0.7279676258992805 0.17757755102040818 2 0.4361697841726619 0.4416508163265306 2 0.6429606714628298 0.1937818367346939 2 0.3815884892086331 0.40159448979591833 2 0.5656037170263789 0.1929634693877551 2 0.30450635491606715 0.41855877551020404 2 0.45534508393285367 0.18808469387755103 2
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def labelme_to_yolo(json_file, output_dir):
|
||||||
|
with open(json_file, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
# 自动提取所有类别并建立索引
|
||||||
|
classes = list(set(shape['label'] for shape in data['shapes']))
|
||||||
|
classes.sort() # 按字母排序保持一致性
|
||||||
|
|
||||||
|
img_width = data['imageWidth']
|
||||||
|
img_height = data['imageHeight']
|
||||||
|
|
||||||
|
yolo_lines = []
|
||||||
|
for shape in data['shapes']:
|
||||||
|
class_name = shape['label']
|
||||||
|
class_id = classes.index(class_name)
|
||||||
|
|
||||||
|
points = shape['points']
|
||||||
|
x_coords = [p[0] for p in points]
|
||||||
|
y_coords = [p[1] for p in points]
|
||||||
|
|
||||||
|
x_min, x_max = min(x_coords), max(x_coords)
|
||||||
|
y_min, y_max = min(y_coords), max(y_coords)
|
||||||
|
|
||||||
|
# 归一化坐标
|
||||||
|
x_center = (x_min + x_max) / 2 / img_width
|
||||||
|
y_center = (y_min + y_max) / 2 / img_height
|
||||||
|
width = (x_max - x_min) / img_width
|
||||||
|
height = (y_max - y_min) / img_height
|
||||||
|
|
||||||
|
yolo_lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}")
|
||||||
|
|
||||||
|
# 保存标签文件
|
||||||
|
txt_filename = os.path.splitext(os.path.basename(json_file))[0] + '.txt'
|
||||||
|
txt_path = os.path.join(output_dir, txt_filename)
|
||||||
|
|
||||||
|
with open(txt_path, 'w') as f:
|
||||||
|
f.write('\n'.join(yolo_lines))
|
||||||
|
|
||||||
|
# 保存类别文件
|
||||||
|
with open(os.path.join(output_dir, 'classes.txt'), 'w') as f:
|
||||||
|
f.write('\n'.join(classes))
|
||||||
|
|
||||||
|
return classes
|
||||||
|
|
||||||
|
|
||||||
|
# 使用
|
||||||
|
json_file = "./dataset1/json/1.json"
|
||||||
|
output_dir = "./dataset1/labels"
|
||||||
|
classes = labelme_to_yolo(json_file, output_dir)
|
||||||
|
print("检测到的类别:", classes)
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def labelme_to_yolo_pose(json_file, output_dir):
|
||||||
|
with open(json_file, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
img_width = data['imageWidth']
|
||||||
|
img_height = data['imageHeight']
|
||||||
|
|
||||||
|
# 提取手部边界框和关键点
|
||||||
|
bbox = None
|
||||||
|
keypoints = {}
|
||||||
|
|
||||||
|
for shape in data['shapes']:
|
||||||
|
if shape['shape_type'] == 'rectangle' and shape['label'] == 'hand':
|
||||||
|
points = shape['points']
|
||||||
|
x_coords = [p[0] for p in points]
|
||||||
|
y_coords = [p[1] for p in points]
|
||||||
|
x_min, x_max = min(x_coords), max(x_coords)
|
||||||
|
y_min, y_max = min(y_coords), max(y_coords)
|
||||||
|
|
||||||
|
x_center = (x_min + x_max) / 2 / img_width
|
||||||
|
y_center = (y_min + y_max) / 2 / img_height
|
||||||
|
width = (x_max - x_min) / img_width
|
||||||
|
height = (y_max - y_min) / img_height
|
||||||
|
bbox = (x_center, y_center, width, height)
|
||||||
|
|
||||||
|
elif shape['shape_type'] == 'point':
|
||||||
|
label = shape['label']
|
||||||
|
x, y = shape['points'][0]
|
||||||
|
keypoints[label] = (x / img_width, y / img_height)
|
||||||
|
|
||||||
|
# 关键点顺序(按JSON中的标签)
|
||||||
|
keypoint_order = ['f_1_1', 'f_1_2', 'f_2_1', 'f_2_2', 'f_3_2', 'f_3_1',
|
||||||
|
'f_4_1', 'f_4_2', 'f_5_2', 'f_5_1', 'f']
|
||||||
|
|
||||||
|
if bbox:
|
||||||
|
yolo_line = f"0 {bbox[0]:.6f} {bbox[1]:.6f} {bbox[2]:.6f} {bbox[3]:.6f}"
|
||||||
|
|
||||||
|
for kp_name in keypoint_order:
|
||||||
|
if kp_name in keypoints:
|
||||||
|
kx, ky = keypoints[kp_name]
|
||||||
|
yolo_line += f" {kx:.6f} {ky:.6f} 2"
|
||||||
|
else:
|
||||||
|
yolo_line += " 0 0 0"
|
||||||
|
|
||||||
|
# 保存为txt文件
|
||||||
|
txt_filename = os.path.splitext(os.path.basename(json_file))[0] + '.txt'
|
||||||
|
txt_path = os.path.join(output_dir, txt_filename)
|
||||||
|
|
||||||
|
with open(txt_path, 'w') as f:
|
||||||
|
f.write(yolo_line)
|
||||||
|
|
||||||
|
print(f"转换完成: {txt_filename}")
|
||||||
|
|
||||||
|
|
||||||
|
# 使用
|
||||||
|
json_file = "./dataset1/json/1.json"
|
||||||
|
output_dir = "./dataset1/labels"
|
||||||
|
|
||||||
|
if not os.path.exists(output_dir):
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
labelme_to_yolo_pose(json_file, output_dir)
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 534 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
After Width: | Height: | Size: 699 KiB |