最前面的说明
这里keypoint 没有带上connection 直接检测关键点的
keypoint detection
标注
CVAT
(该工具 需要手动补充visible) 按照cvat 绿色字体 先走一遍关键点检测流程 标注直接按照coco annotator进行标注 结尾 coco annotator标注过程
步骤:
Project—create a new project—Name-Add label—submit&open—create a new task
Name—Submit&continue—打开创建的task—点击Job—开始标注
确定点数—shape——keypoints & bbox(除了关键点还需要绘制bbox)——格式导出 xml文件
标注导出格式:coco keypoint 1.0(但不起作用)还是选择用CVAT for images1.1 再转换格式
(转换代码在github中 CVAT_to_cocoKeypoints.py 更改xml 及需要输出的路径即可)
补充:coco key point格式: classnum + bbox(yolo)+ points [ x y v(visible flag) ]
注:cvat 不支持 关键点是否可见 即 v没有 (我选择 手动在txt中里面添加 2 全部都可见)
(需要使用另外的标注工具 coco annotator)
直接运行会出现如下报错 labels require 29 columns each (1 + 4 + 8*3) 但实际上cvat只有xy
文件结构
yolov8根目录 F:\want-fish\pose-detection-keypoints-estimation-yolov8-origin
data/images/train val data/labels/train val 按照比例划分train val文件夹
class.names NAMES文件 按照自己的关键点修改class.names文件
config.yaml 修改地址 关键点数量 类别名称 filp_idx
train.py 修改train.py epochs
到官网 在根目录下下载yolov8n.pt yolov8n-pose.pt
代码
-
config.yaml
其中 keypoints 的 flip_idx参数解释:当图像水平翻转时,索引该如何翻转
同一个图片 数据增强-水平翻转 涉及到右耳变左耳 因此该参数列表 将涉及到左右的进行互换,其余保持不动
(我的数据集只考虑一边的话 是不需要区分左右的 默认左右一致)[0,1,2,3,4,5,6,7]
-
train.py
from ultralytics import YOLO
model = YOLO('yolo8n-pose.pt')
model.train(data='config.yaml', epochs=500 ,imgsz=540, device=0)
说明
训练完毕,查看损失函数图像,重点关注与姿势相关的损失函数 损失函数模型可以优化吗 最近有新的pose网络和损失函数吗
-
inference.py
from ultralytics import YOLO
import cv2
model_path = '/media/veracrypt2/computer_vision/47_pose_detection_yolov8/code/runs/pose/train9/weights/last.pt'
image_path = './samples/wolf.jpg'
img = cv2.imread(image_path)
model = YOLO(model_path)
results = model(image_path)[0]
for result in results:
for keypoint_indx, keypoint in enumerate(result.keypoints.tolist()):
cv2.putText(img, str(keypoint_indx), (int(keypoint[0]), int(keypoint[1])),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
inference.py出现了一点报错
由于生成了两个错误的bbox框 且两个框的keypoints的位置大概一致,暂时将inference.py的代码改为如下,直接去掉最外面的for 循环 只取第一维的结果 后续使用自己的数据集 再将for循环加上
from ultralytics import YOLO
import cv2
model_path = 'F:/want-fish/pose-detection-keypoints-estimation-yolov8-origin/runs/pose/train22/weights/last.pt'
image_path = './samples_data/cow.jpg'
img = cv2.imread(image_path)
model = YOLO(model_path)
results = model(image_path)[0]
for keypoint_indx, keypoint in enumerate(results.keypoints.xy.tolist()[0]):
cv2.putText(img, str(keypoint_indx), (int(keypoint[0]), int(keypoint[1])),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
暂时得到下图: (列表维度 真的不熟练 5555555555)
加上外圈for循环: (results.keypoints.balabala 不应该放在内层result里面)
for result in results.keypoints.xy.tolist():
for keypoint_indx, keypoint in enumerate(result):
me:prompt利用yolo终端进行predict bbox预测有问题(查看原视频评论区发现作者标注文件中的bbox有点问题)
yolo task=pose mode=predict source='./samples_data/cow.jpg' model='F:/want-fish/pose-detection-keypoints-estimation-yolov8-origin/runs/pose/train22/weights/last.pt'
keypoints测试效果说明
原up的四足动物 数据集 train 8000多 val 1000多 关键点还算比较清楚
me: 训练了2次 两个数据集 每个数据集中只有一个类别 图像数量:54(40+14) 73(59+13)
还是偏了一点点 ,所以还需要补充大量的数据集进行标注(卑微.jpg)
tracking
测试代码
说明:该代码使用cv2窗口查看测试结果;
me: Roboflow标注 + prompt终端
训练:
yolo task=detect mode=train model=yolov8n.pt data=datasets/data.yaml batch=16 epochs=5000 imgsz=640 device=0
测试:使用训练后的detect模型进行测试并保存视频追踪文件
yolo track model=F:\want-fish\ultralytics-main\runs\detect\train3\weights\best.pt source='2030fish.mp4' save=True
from ultralytics import YOLO
import cv2
# load yolov8 model
model = YOLO('yolov8n.pt')
# load video
video_path = './test.mp4'
cap = cv2.VideoCapture(video_path)
ret = True
# read frames
while ret:
ret, frame = cap.read()
if ret:
# detect objects
# track objects
results = model.track(frame, persist=True)
# plot results
# cv2.rectangle
# cv2.putText
frame_ = results[0].plot() # 因为我们只在1帧中进行追踪
# visualize
cv2.imshow('frame', frame_)
if cv2.waitKey(25) & 0xFF == ord('q'): #等待25ms 按q退出
break
- read方法 返回一个布尔值 且返回一个新帧
- ret, frame = cap.read() # 到视频最后一帧时,ret返回值False frame返回值None
- yolov8使用一句话同时进行detect & track
- model.track(frame, persist=True)
-
启用目标持久性意味着目标追踪器会尝试在连续的帧中持续追踪目标,即使在某些帧中目标被遮挡或移出视野。这对于处理短暂的遮挡或目标出现/消失的情况非常有用。
-
-
两种绘图方式 cv2.rectangle cv2.putText
frame_ = results[0].plot()
Keypoint pose 训练
本地4060
补充说明:GPU 几乎0%
但实际上GPU还是有在使用的
我设置device=‘0’ (device=0同) 但是任务管理器查看的gpu & nvidia-smi 如下
搜了半天:Pytorch使用cuda后,任务管理器GPU的利用率还是为0?
其中的法一 将任务管理器-性能-GPU的3D 换成CUDA进行查看 别的帖子说设置要重启电脑 暂时无法确认 (图形设置——更改默认图形设置——关闭 硬件加速gpu计划)
其中法二 我的Volatile GPU-Util显示80% 表明GPU还是在用的 瞬时再查看 可以到100%
中断后继续训练
yolo task=pose mode=train model=F:\want-fish\pose-detection-keypoints-estimation-yolov8-origin\runs\pose\train\weights\last.pt data=config.yaml epochs=100 batch=16 save=True resume=True
Google Colab+ Drive 云端训练 (还没有尝试)
Drive上传数据
其他标注工具说明:
1.labelme
无法标注关键点是否可见,默认为1.00000,后续将其全部更改为2.00000
2.coco annotator
补充上一行中原up没有说到的:
- 到网页里图片集都scan出来之后,点击一张图像进入标注页面
- 开始标注时没有+号 显示No categories have been enabled for this image. 要在datasets的添加默认类别
- 一个图像文件夹export coco 导出一个json文件 用以下代码转换为coco keypoint的格式(注意txt中的坐标需要normalize 有个憨憨一开始没有注意到)
import json import os def json_to_yolo(json_path, output_folder, image_folder): with open(json_path, 'r') as json_file: data = json.load(json_file) for image_info in data['images']: image_id = image_info['id'] annotations = [anno for anno in data['annotations'] if anno['image_id'] == image_id] if annotations: image_name = image_info['file_name'] image_path = os.path.join(image_folder, image_name) image_width, image_height = get_image_size(image_path) txt_content = coco_to_yolo(annotations, image_width, image_height) txt_path = os.path.join(output_folder, f"{os.path.splitext(image_name)[0]}.txt") with open(txt_path, 'w') as txt_file: txt_file.write(txt_content) def get_image_size(image_path): # 使用 OpenCV 获取图像的宽度和高度 import cv2 image = cv2.imread(image_path) return image.shape[1], image.shape[0] def coco_to_yolo(annotations, image_width, image_height): txt_content = "" for annotation in annotations: category_id = annotation['category_id'] - 1 # 将类别 ID 减去 1 bbox = annotation['bbox'] # Convert COCO format bbox to YOLO format yolo_bbox = coco_bbox_to_yolo(bbox, image_width, image_height) keypoints = annotation['keypoints'] keypoints_normalized = normalize_keypoints(keypoints, image_width, image_height) # Create YOLO format txt content txt_content += f"{category_id} {' '.join(map(str, yolo_bbox))} {' '.join(map(str, keypoints_normalized))}\n" return txt_content def coco_bbox_to_yolo(bbox, image_width, image_height): x, y, width, height = bbox center_x = x + width / 2 center_y = y + height / 2 yolo_x = center_x / image_width yolo_y = center_y / image_height yolo_width = width / image_width yolo_height = height / image_height return yolo_x, yolo_y, yolo_width, yolo_height def normalize_keypoints(keypoints, image_width, image_height): normalized_keypoints = [] for i in range(0, len(keypoints), 3): x = keypoints[i] y = keypoints[i + 1] v = keypoints[i + 2] normalized_x = x / image_width normalized_y = y / image_height normalized_keypoints.extend([normalized_x, normalized_y, v]) return normalized_keypoints json_path = "E:\\下载\\fish01-1.json" # 替换为你的JSON文件路径 output_folder = "E:\\桌面\\labels" # 替换为你要保存TXT文件的文件夹路径 image_folder = "F:\\want-fish\\coco-annotator\\datasets\\fish01" # 替换为你的图像文件夹路径 if not os.path.exists(output_folder): os.makedirs(output_folder) json_to_yolo(json_path, output_folder, image_folder)
PS 最后的最后:1 tracking部分 没有用github中的代码 直接 终端 yolo
2 google drive自己跑的时候 有报错 好像还要搞一下环境 没做