Yolopose关键点检测:自己标注数据集,制作数据集

Posted 锅sir

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Yolopose关键点检测:自己标注数据集,制作数据集相关的知识,希望对你有一定的参考价值。

前言

最近实习需要搞的工程涉及到姿态估计,对这2天的学习做一下总结归纳,第一步就是准备数据集,查资料后发现window环境下使用的标注软件就是labelme(coco-annotator好像就可以直接输出coco格式的JSON文件,后期继续学习),labelme产出的JSON文件需要转换到coco格式的JSON文件,然后将JSON文件转成Yolo需要的txt文件,才能送工程进行训练,转来转去的个人感觉非常麻烦。
本博文主要内容是分享关键点的labelme2coco——JSON文件转换,cocoJSON2cocotxt的转换,以及coco_kpts文件夹的内容布局。

一、labelme标注关键点的使用:

用labelme标注关键点与后续的labelme2coco转换代码息息相关,假设标注对象是人体,关键点有17个:“nose”,“left_eye”, “right_eye”,“left_ear”, “right_ear”,“left_shoulder”, “right_shoulder”,“left_elbow”, “right_elbow”,“left_wrist”, “right_wrist”,“left_hip”, “right_hip”,“left_knee”, “right_knee”,“left_ankle”, “right_ankle”,label编号从1依次排到17,然后再把人用rectangle框出来label编号为bbox(Yolopose是需要画框的,并且numclass已经固定好了为person),所有的Group id都不用写,JSON文件会自动补上null但不影响我们后续操作,填写label及Group id的界面如下图所示:

选取一张图片,把17个关键点都标注出来,再把人框出来,如果对象显示半个身体,不满17个关键点,也无所谓,没有哪个部位就把那个部位标注数值空出来就可以了,一张图片操作结束后,就如下图所示:

coco图片的格式是12位数字,标注完后的JSON文件用相同的名称命名即可。

二、labelme2coco_keypoints.py

CSDN上的其他转换代码,我都没法直接跑,也没有相关的讲解,于是我就拿了现有的代码修改了一些,并记录了我的学习过程。先介绍一下哪些部分需要修改的,然后在附上整个代码,以17个人体关键点标注好的JSON文件为例。

"keypoints":#这是固定的,要是其他的关键点标注,则需要修改内容并且与前面的label编号一一对应。
                    [
                        "nose",
                        "left_eye", "right_eye",
                        "left_ear", "right_ear",
                        "left_shoulder", "right_shoulder",
                        "left_elbow", "right_elbow",
                        "left_wrist", "right_wrist",
                        "left_hip", "right_hip",
                        "left_knee", "right_knee",
                        "left_ankle", "right_ankle"
                    ],
                "skeleton": [
                    [16, 14],
                    [14, 12],
                    [17, 15],
                    [15, 13],
                    [12, 13],
                    [6, 12],
                    [7, 13],
                    [6, 7],
                    [6, 8],
                    [7, 9],
                    [8, 10],
                    [9, 11],
                    [2, 3],
                    [1, 2],
                    [1, 3],
                    [2, 4],
                    [3, 5],
                    [4, 6],
                    [5, 7]
                ]
json_file[-17:-5]#这是为了取文件的名称,比如000000000001,如果文件名称变了,则需要修改
json_file[-17:-4]#这是为了取文件的名称,比如000000000001.(多一个.)

keypoints 里面放的是(x,y,v):x,y是坐标值,v一般取2,0代表没有该点,1代表该点存在但是被遮挡了,2代表该点存在且没有被遮挡。(具体可以参考coco数据集的格式,本文主要介绍如何应用)

keypoints = [0] * 3 * 17  #这里是我们标注的关节点个数  如有改动,需要修改
json_path = r'E:\\val2017\\Json'   #存放需要转换的json文件夹,变动需要修改
c = tococo(json_path, save_path=r'E:\\val2017\\annotations\\val.json', a=1)  #输出的地址及名称,变动需要修改

整个代码如下,可以做到一个文件夹下所有的JSON文件一起转换并输出一个coco格式的JSON文件

import numpy as np
import json
import glob
import codecs
import os

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        else:
            return super(MyEncoder, self).default(obj)


class tococo(object):
    def __init__(self, jsonfile, save_path, a):
        self.images = []
        self.categories = [
            
                "supercategory": "person",
                "id": 1,
                "name": "person",
                "keypoints":
                    [
                        "nose",
                        "left_eye", "right_eye",
                        "left_ear", "right_ear",
                        "left_shoulder", "right_shoulder",
                        "left_elbow", "right_elbow",
                        "left_wrist", "right_wrist",
                        "left_hip", "right_hip",
                        "left_knee", "right_knee",
                        "left_ankle", "right_ankle"
                    ],
                "skeleton": [
                    [16, 14],
                    [14, 12],
                    [17, 15],
                    [15, 13],
                    [12, 13],
                    [6, 12],
                    [7, 13],
                    [6, 7],
                    [6, 8],
                    [7, 9],
                    [8, 10],
                    [9, 11],
                    [2, 3],
                    [1, 2],
                    [1, 3],
                    [2, 4],
                    [3, 5],
                    [4, 6],
                    [5, 7]
                ]
            
        ]
        self.annotations = []
        self.jsonfile = os.listdir(jsonfile)
        self.save_path = save_path  # 保存json的路径
        self.class_id = a  # class  我们的类别只有一个 person
        self.coco = 
        self.path = jsonfile

    def labelme_to_coco(self):
        for num, json_file in enumerate(self.jsonfile):
            json_file = os.path.join(self.path, json_file)
            data = codecs.open(json_file, 'r')
            data = json.load(data)
            self.images.append(self.get_images(json_file[-17:-4] + 'jpg', data["imageHeight"], data["imageWidth"]))
            shapes = data["shapes"]
            annotation =   # 一个annotation代表一张图片中的所有samples
            num_keypoints = 0
            keypoints = [0] * 3 * 17  #这里是我们标注的关节点个数  如有改动,需要修改
            flag = 0
            for shape in shapes:
                if shape['shape_type'] == 'rectangle' or shape["label"] == 'bbox':
                    bbox = []
                    temp = shape["points"]
                    try:
                        x_min = min(temp[0][0], temp[1][0])
                    except IndexError as e:
                        print('class: , image: '.format(self.class_id, int(json_file[-17:-5])))

                    x_max = max(temp[0][0], temp[1][0])
                    y_min = min(temp[0][1], temp[1][1])
                    y_max = max(temp[0][1], temp[1][1])
                    bbox.append(x_min)
                    bbox.append(y_min)
                    w = x_max - x_min + 1
                    h = y_max - y_min + 1
                    bbox.append(w)
                    bbox.append(h)
                    annotation['bbox'] = bbox
                    flag = flag + 1
                else:
                    idx = int(shape['label'])

                    try:
                        keypoints[(idx - 1) * 3 + 0] = shape['points'][0][0]
                        keypoints[(idx - 1) * 3 + 1] = shape['points'][0][1]
                        keypoints[(idx - 1) * 3 + 2] = 2
                        num_keypoints = num_keypoints + 1
                    except IndexError as e:
                        print('class: , image: '.format(self.class_id, int(json_file[-17:-5])))

            if flag == 0:
                print('\\\\ does not contain bbox\\n'.format(self.class_id, json_file))
            annotation['segmentation'] = [[]]
            annotation['num_keypoints'] = num_keypoints
            annotation['iscrowd'] = 0
            annotation['keypoints'] = keypoints
            annotation['image_id'] = int(json_file[-17:-5])  # 对应的图片ID
            if 'bbox' not in annotation:
                annotation['bbox'] = [0, 0, data['imageWidth'], data['imageHeight']]
                annotation['area'] = 0
            else:
                annotation['area'] = int(bbox[2] * bbox[3])
            annotation['category_id'] = 1
            annotation['id'] = int(json_file[-17:-5])  # 对象id
            self.annotations.append(annotation)
            self.image_id = int(json_file[-17:-5])

        self.coco["images"] = self.images
        self.coco["categories"] = self.categories
        self.coco["annotations"] = self.annotations



    def get_images(self, filename, height, width):
        image = 
        image["height"] = height
        image['width'] = width
        image["id"] = int(filename[-16:-4])
        image["file_name"] = filename
        return image

    def get_categories(self, name, class_id):
        category = 
        category["supercategory"] = "person"
        category['id'] = class_id
        category['name'] = name
        return category

    def save_json(self):
        self.labelme_to_coco()
        coco_data = self.coco
        # 保存json文件
        json.dump(coco_data, open(self.save_path, 'w'), indent=4, cls=MyEncoder)  # indent=4 更加美观显示
        return self.image_id


json_path = r'E:\\val2017\\Json_old'   #保存json的文件夹路径
c = tococo(json_path, save_path=r'E:\\val2017\\annotations_old\\val.json', a=1)  #我们将我们的左右json文件合成为一个json文件,这是最后json文件的名称
image_id = c.save_json()

本人将5个JSON文件放在同一个文件夹下运行转换代码,最后val.json通过VS2022(其他软件也可以)打开后内容如下所示:

三、coco api来检查coco格式的JSON文件是否有问题

# !/usr/bin python3
# encoding    : utf-8 -*-
# @author     :   liangjian
# @software   : PyCharm
# @file       :   0408.py
# @Time       :   2021/4/8 21:51
import skimage.io as io
import pylab
import time as time
import json
import numpy as np
from collections import defaultdict
import itertools
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection


def _isArrayLike(obj):
    return hasattr(obj, '__iter__') and hasattr(obj, '__len__')


class COCO:
    def __init__(self, annotation_file=None):
        """
        Constructor of Microsoft COCO helper class for reading and visualizing annotations.
        :param annotation_file (str): location of annotation file
        :param image_folder (str): location to the folder that hosts images.
        :return:
        """
        # load dataset
        self.dataset, self.anns, self.cats, self.imgs = dict(), dict(), dict(), dict()
        self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list)
        if not annotation_file == None:
            print('loading annotations into memory...')
            tic = time.time()
            dataset = json.load(open(annotation_file, 'r'))
            assert type(dataset) == dict, 'annotation file format  not supported'.format(type(dataset))
            print('Done (t=:0.2fs)'.format(time.time() - tic))
            self.dataset = dataset
            self.createIndex()

    def createIndex(self):
        # create index
        print('creating index...')
        anns, cats, imgs = , , 
        imgToAnns, catToImgs = defaultdict(list), defaultdict(list)
        if 'annotations' in self.dataset:
            for ann in self.dataset['annotations']:
                imgToAnns[ann['image_id']].append(ann)
                anns[ann['id']] = ann

        if 'images' in self.dataset:
            for img in self.dataset['images']:
                imgs[img['id']] = img

        if 'categories' in self.dataset:
            for cat in self.dataset['categories']:
                cats[cat['id']] = cat

        if 'annotations' in self.dataset and 'categories' in self.dataset:
            for ann in self.dataset['annotations']:
                catToImgs[ann['category_id']].append(ann['image_id'])

        print('index created!')

        # create class members
        self.anns = anns
        self.imgToAnns = imgToAnns
        self.catToImgs = catToImgs
        self.imgs = imgs
        self.cats = cats

    def getCatIds(self, catNms=[], supNms=[], catIds=[]):
        """
        filtering parameters. default skips that filter.
        :param catNms (str array)  : get cats for given cat names
        :param YOLO-Darknet实战:使用LabelImg制作目标检测数据集

YOLO-Darknet实战:使用LabelImg制作目标检测数据集

YOLO-Darknet实战:使用LabelImg制作目标检测数据集

Pytorch:YOLO-v5目标检测(下)

COCO_02 二值分割数据集制作为COCO数据集

COCO_02 二值分割数据集制作为COCO数据集