前言

决定使用Yolov8-pose来做这一个跌倒检测,主要是看重他的关键点识别

标注

这个项目采用labelme标注工具,不用labelimg的原因其一就在于labelimg不能标注关键点

安装labelme很简单,像你安装别的第三方包一样,在终端中键入

pip install labelme

然后在终端中直接输入labelme便可打开

打开后选择你的数据集进行标注

第一步将人物用矩形框框起来,命名为person,并且将group id设置为0

接下来依次标注人体关键点,要严格按照一个自定义的顺序来标注,要不然未来转换文件格式的时候会很麻烦

笔者这里采用了 头-右肩-左肩-身体-右手-左手-右腿-左腿-右脚-左脚 的顺序进行标注,并且将group id设置为1

值得注意的是,标注的过程中也许会遇到没有关键部位的情况,这种情况分为两种,第一种是被遮挡但仍然在画面内,这种情况下我们选择估算他的大概位置,然后进行标注,同时将group id设为2

另一种就是压根不在画面内,就像例图中的腿部与脚部,这种我们直接跳过标注即可,但是切记要按照顺序来标注。

YOLOv8训练需要txt格式的文件,而labelme标注得到的是json格式,因此我们要对其进行格式转换

import json
import os

json_path = r''  # json文件路径
txt_path = r''  # txt文本文件路径

# 关键点数组
pointsArr = ['head', 'rightshoulder', 'leftshoulder', 'body', 'righthand', 'lefthand', 'rightleg', 'leftleg',
             'rightfoot', 'leftfoot']


# 坐标归一化,返回中心点坐标和宽高
def coordinates2yolo(xmin, ymin, xmax, ymax, img_w, img_h):
    x = abs(xmin + xmax) / (2.0 * img_w)
    y = abs(ymin + ymax) / (2.0 * img_h)
    w = abs(xmax - xmin) / (1.0 * img_w)
    h = abs(ymax - ymin) / (1.0 * img_h)
    return x, y, w, h


def writeJson(rootpath, rootpath1, filename):
    path = os.path.join(rootpath, filename + '.json')
    count = 0  # 记录一张图片中人数的多少
    index = 0  # data索引,用于区分json文件中的label值

    with open(path) as f:
        # 读取json格式文件并获取相应信息
        data = json.load(f)
        imageHeight = data['imageHeight']
        imageWidth = data['imageWidth']
        data = data['shapes']
        length = len(data)
        print('length', length)

    # 遍历json文件,用变量count记录 data[i]['label']=’类别名‘ 的次数,以此说明图片中有几个人
    for i in range(0, length):
        # 类别名换成自己的类别,当有多个类别时,用关键字or进行连接
        # if data[i]['label'] == '类别名1' or data[i]['label'] == '类别名2'
        if data[i]['label'] == 'person':
            count += 1

    # 将json文件信息写入txt文本文件中
    file = open(os.path.join(rootpath1, filename + '.txt'), mode='w')
    for j in range(0, count):
        # 在txt文本文件中写入类别id、目标框中心坐标以及图片宽高
        file.write(str(data[index]['group_id']))
        file.write(" ")
        points = data[index]['points']
        print(points)
        xmin = points[0][0]
        print(xmin)
        ymin = points[0][1]
        print(ymin)
        xmax = points[1][0]
        print(xmax)
        ymax = points[1][1]
        print(ymax)
        x, y, w, h = coordinates2yolo(xmin, ymin, xmax, ymax, imageWidth, imageHeight)
        file.write(str(round(x, 6)))
        file.write(" ")
        file.write(str(round(y, 6)))
        file.write(" ")
        file.write(str(round(w, 6)))
        file.write(" ")
        file.write(str(round(h, 6)))
        file.write(" ")
        index += 1
        # 在txt文本文件中写入关键点坐标与对应id值
        for point in pointsArr:
            # print(index)
            # print(data)
            if index < length:
                if data[index]['label'] == point:
                    point = data[index]['points']  # 获取关键点的坐标值
                    file.write(str(round(point[0][0] / imageWidth, 6)))
                    file.write(" ")
                    file.write(str(round(point[0][1] / imageHeight, 6)))
                    file.write(" ")
                    # data[index]['group_id'] == 2,表名为被遮挡的关键点,在txt文档中写入1
                    if data[index]['group_id'] == 2:
                        file.write('1.000000')
                        file.write(" ")
                    # data[index]['group_id'] != 2,表名为正常标记的关键点,在txt文档中写入2
                    else:
                        file.write('2.000000')
                        file.write(" ")
                    index += 1
                # 若data[index]['label'] != point,则写入(0, 0, 0),前两个代表坐标,最后一个‘0’代表此关键点未被标记
                else:
                    file.write('0.000000')
                    file.write(" ")
                    file.write('0.000000')
                    file.write(" ")
                    file.write('0.000000')
                    file.write(" ")
            else:
                file.write('0.000000')
                file.write(" ")
                file.write('0.000000')
                file.write(" ")
                file.write('0.000000')
                file.write(" ")
        file.write('\n')


# 读取path路径中的文件
filenames = os.listdir(json_path)
for item in filenames:
    # 以'.'为标志分割获取文件名
    filename = item.split('.')[0]
    print(filename)
    writeJson(json_path, txt_path, filename)

训练采用yolov8n-pose.pt模型

yolo pose train data=pose.yaml model=yolov8n-pose.pt project= name= epochs=50 batch=16 device=cpu

pose.yaml内部格式

# 数据集的根目录路径。
path:
# 训练集的相对路径
train: train
# 验证集的相对路径
val: val
test: val
# 关键点的形状,表示有10个关键点,每个关键点有3个维度(通常是x坐标、y坐标和一个表示可见性的标记)。
kpt_shape: [10, 3]
# 类别名称。
names:
  0: person