代码实现如何将yolov5数据格式转换为coco格式

很多训练算法使用coco格式,而原版的数据集可能采用yolov5的数据格式,故写个简单的教程;

yolov5数据集的目录格式:

 images存放的图像,例如 1.jpg,2.jpg.

labels存放的是对应图片的标注信息,例如 1.txt,2.txt.

txt 中信息是这样的:

(框高)每一行对应一个bbox框信息,分别是class_id ,xc(框的中心x坐标),yc(框的中心x坐标),w(框宽),h (框高)

coco数据集的目录如下:

instances_test2017.json的格式如下:

 


#COCO总体结构如下
{
  "info": info,
  "licenses": [license],
  "categories": [categories],
  "images": [image],
  "annotations": [annotation],
}

#其中info,license,categories,image,annotation 的说明如下-----

info = {
    "year": int,               #年份
    "version": str,            #数据集版本
    "description": str,        #数据集描述
    "contributor": str,        #数据集的提供者
    "url": str,                #数据集的下载地址
    "date_created": datetime,  #数据集的创建日期
}

categories ={
    "id": int, #类别id
    "name": str,#类别名称
    "supercategory": str,##大类名
 }

license = {
    "id": int,
    "name": str,
    "url": str,
}
image = {
    "id": int, #图像的索引id,自己指定
    "width": int,#图像的宽
    "height": int,#图像的高
    "file_name": str,#图像的文件名
    "license": int,
    "flickr_url": str,
    "coco_url": str,
    "date_captured": datetime,
}
annotation = {
    "id": int, #boudingbox的索引id,自己指定
    "image_id": int,#对应所在图像的索引id;
    "category_id": int,#所属类别的id;
    "segmentation": RLE or [polygon],#分割的点集序列;
    "area": float,#bbox的面积
    "bbox": [x,y,width,height],#重要,左上角点的坐标,bbox的宽高;
    "iscrowd": 0 or 1, ##是否拥挤
# }

完成的转换代码如下:

import json
import os
import shutil

import cv2

# info ,license,categories 结构初始化;
# 在train.json,val.json,test.json里面信息是一致的;

# info,license暂时用不到
info = {
    "year": 2022,
    "version": '1.0',
    "date_created": 2022 - 10 - 15
}

licenses = {
    "id": 1,
    "name": "null",
    "url": "null",
}

#自己的标签类别,跟yolov5的要对应好;
categories = [
    {
        "id": 0,
        "name": 'class_1',
        "supercategory": 'lines',
    },
    {
        "id": 1,
        "name": 'class_2',
        "supercategory": 'lines',
    }
]

#初始化train,test数据字典
# info licenses categories 在train和test里面都是一致的;
train_data = {'info': info, 'licenses': licenses, 'categories': categories, 'images': [], 'annotations': []}
test_data = {'info': info, 'licenses': licenses, 'categories': categories, 'images': [], 'annotations': []}

# image_path 对应yolov5的图像路径,比如images/train;
# label_path 对应yolov5的label路径,比如labels/train 跟images要对应;
def v5_covert_coco_format(image_path, label_path):
    images = []
    annotations = []
    for index, img_file in enumerate(os.listdir(image_path)):
        if img_file.endswith('.jpg'):
            image_info = {}
            img = cv2.imread(os.path.join(image_path, img_file))
            height, width, channel = img.shape
            image_info['id'] = index
            image_info['file_name'] = img_file
            image_info['width'], image_info['height'] = width, height
        else:
            continue
        if image_info != {}:
            images.append(image_info)
        # 处理label信息-------
        label_file = os.path.join(label_path, img_file.replace('.jpg', '.txt'))
        with open(label_file, 'r') as f:
            for idx, line in enumerate(f.readlines()):
                info_annotation = {}
                class_num, xs, ys, ws, hs = line.strip().split(' ')
                class_id, xc, yc, w, h = int(class_num), float(xs), float(ys), float(ws), float(hs)
                xmin = (xc - w / 2) * width
                ymin = (yc - h / 2) * height
                xmax = (xc + w / 2) * width
                ymax = (yc + h / 2) * height
                bbox_w = int(width * w)
                bbox_h = int(height * h)
                img_copy = img[int(ymin):int(ymax),int(xmin):int(xmax)].copy()

                info_annotation["category_id"] = class_id  # 类别的id
                info_annotation['bbox'] = [xmin, ymin, bbox_w, bbox_h]  ## bbox的坐标
                info_annotation['area'] = bbox_h * bbox_w ###area
                info_annotation['image_id'] = index # bbox的id
                info_annotation['id'] = index * 100 + idx  # bbox的id
                # cv2.imwrite(f"./temp/{info_annotation['id']}.jpg", img_copy)
                info_annotation['segmentation'] = [[xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax]]  # 四个点的坐标
                info_annotation['iscrowd'] = 0  # 单例
                annotations.append(info_annotation)
    return images, annotations

# key == train,test,val
# 对应要生成的json文件,比如instances_train2017.json,instances_test2017.json,instances_val2017.json
# 只是为了不重复写代码。。。。。
def gen_json_file(yolov5_data_path, coco_format_path, key):
    # json path
    json_path = os.path.join(coco_format_path, f'annotations/instances_{key}2017.json')
    dst_path = os.path.join(coco_format_path, f'{key}2017')
    if not os.path.exists(os.path.dirname(json_path)):
        os.makedirs(os.path.dirname(json_path), exist_ok=True)
    data_path = os.path.join(yolov5_data_path, f'images/{key}')
    label_path = os.path.join(yolov5_data_path, f'labels/{key}')
    images, anns = v5_covert_coco_format(data_path, label_path)
    if key == 'train':
        train_data['images'] = images
        train_data['annotations'] = anns
        with open(json_path, 'w') as f:
            json.dump(train_data, f, indent=2)
        # shutil.copy(data_path,'')
    elif key == 'test':
        test_data['images'] = images
        test_data['annotations'] = anns
        with open(json_path, 'w') as f:
            json.dump(test_data, f, indent=2)
    else:
        print(f'key is {key}')
    print(f'generate {key} json success!')
    return

if __name__ == '__main__':

    yolov5_data_path = '/your/yolov5/datasets/path'
    coco_format_path = '/your/coco/datasets/path'
    gen_json_file(yolov5_data_path, coco_format_path,key='train')
    gen_json_file(yolov5_data_path, coco_format_path,key='test')

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年9月12日
下一篇 2023年9月12日

相关推荐