CVAT标注平台导出格式转YOLOv8格式

文章介绍了如何在CVAT平台上处理半自动标注,遇到的YOLO格式导出问题,以及如何将COCO格式中的RLE标注转换为普通像素点格式的过程,包括使用Python脚本进行数据转换和清理无标注图片。
摘要由CSDN通过智能技术生成

CVAT是一个非常方便的标注平台,可以实现半自动标注,导出的格式也是非常全面,基本的图像检测分割算法需要的标注信息格式它都涵盖。

使用官方的YOLOv8代码正常来说可以直接转成YOLO格式供使用。

但是我这里的YOLO格式导出后txt里面没有标注内容,不知道为什么,因此采用了先转COCO格式,再手动代码转YOLO格式。而在CVAT标注中如果使用了Draw new mask这个按点标注的功能的话,在导出的COCO的Json文件中会出现类似与这种格式

这其实是RLE格式的标注信息,可以使用如下代码转化为普通的segmentation像素点格式。

####RLE格式标签###转化
import numpy as np
import matplotlib.pyplot as plt
rle = [339224, 5, 1, 2, 591, 10, 589, 11, 588, 12, 588, 12, 587, 13, 587, 12, 589, 6, 594, 6, 594, 5, 594, 5, 594, 5, 594, 6, 593, 6, 593, 6, 594, 5, 594, 5, 594, 5, 595, 5, 594, 5, 595, 4, 596, 4, 595, 5, 595, 5, 594, 5, 595, 5, 594, 5, 595, 5, 596, 3, 603986]
assert sum(rle) == 600*1600
M = np.zeros(600*1600)
N = len(rle)
n = 0
val = 1
for pos in range(N):
    val = not val
    for c in range(rle[pos]):
        M[n] = val
        n += 1
# np.savetxt('test.txt',M)
GEMFIELD = M.reshape(([600, 1600]), order='F')
print(GEMFIELD.shape[1])
count = 0
ans = []
for i in range(GEMFIELD.shape[0]):
    for j in range(GEMFIELD.shape[1]):
        if GEMFIELD[i][j] != 0:

            print('数值',GEMFIELD[i][j])
            count += 1
            ans.append(j)
            ans.append(i)
print(count)
print(ans)

最后总体转化的代码为

import os
import json
from tqdm import tqdm
import argparse
import numpy as np

parser = argparse.ArgumentParser()
parser.add_argument('--json_path', default='instances_default.json', type=str,#CVAT导出的coco格式的json文件
                    help="input: coco format(json)")
parser.add_argument('--save_path', default='datasets/coco/labels/train2017', type=str,
                    help="specify where to save the output dir of labels")
arg = parser.parse_args()




if __name__ == '__main__':
    json_file = arg.json_path  # COCO Object Instance 类型的标注
    ana_txt_save_path = arg.save_path  # 保存的路径

    data = json.load(open(json_file, 'r',encoding='utf-8'))
    if not os.path.exists(ana_txt_save_path):
        os.makedirs(ana_txt_save_path)

    id_map = {}  # coco数据集的id不连续!重新映射一下再输出!
    for i, category in enumerate(data['categories']):
        id_map[category['id']] = i

    # 通过事先建表来降低时间复杂度
    max_id = 0
    for img in data['images']:
        max_id = max(max_id, img['id'])
    # 注意这里不能写作 [[]]*(max_id+1),否则列表内的空列表共享地址
    img_ann_dict = [[] for i in range(max_id + 1)]
    for i, ann in enumerate(data['annotations']):
        img_ann_dict[ann['image_id']].append(i)

    for img in tqdm(data['images']):
        filename = img["file_name"]
        img_width = img["width"]
        img_height = img["height"]
        img_id = img["id"]
        head, tail = os.path.splitext(filename)
        ana_txt_name = head + ".txt"  # 对应的txt名字,与jpg一致
        f_txt = open(os.path.join(ana_txt_save_path, ana_txt_name), 'w')
        '''for ann in data['annotations']:
            if ann['image_id'] == img_id:
                box = convert((img_width, img_height), ann["bbox"])
                f_txt.write("%s %s %s %s %s\n" % (id_map[ann["category_id"]], box[0], box[1], box[2], box[3]))'''
        # 这里可以直接查表而无需重复遍历
        for ann_id in img_ann_dict[img_id]:
            ann = data['annotations'][ann_id]


            #普通的seg格式len为1,如果为RLE格式长度为2
            if len(ann["segmentation"]) == 1:
                #归一化
                for i in range(len(ann["segmentation"][0])):
                    if i % 2 == 0:
                        ann["segmentation"][0][i] /= 1600
                    else:
                        ann["segmentation"][0][i] /= 600
                ann["segmentation"][0].insert(0,id_map[ann["category_id"]])

                line = ' '.join(map(str,ann["segmentation"][0])).strip(' ')

                f_txt.write(line+'\n')
            if len(ann["segmentation"]) == 2:

                rle = ann["segmentation"]["counts"]

                assert sum(rle) == 600 * 1600
                M = np.zeros(600 * 1600)
                N = len(rle)
                n = 0
                val = 1
                for pos in range(N):
                    val = not val
                    for c in range(rle[pos]):
                        M[n] = val
                        n += 1
                # np.savetxt('test.txt',M)
                GEMFIELD = M.reshape(([600, 1600]), order='F')

                count = 0
                ans = []
                for i in range(GEMFIELD.shape[0]):
                    for j in range(GEMFIELD.shape[1]):
                        if GEMFIELD[i][j] != 0:
                            # print('数值', GEMFIELD[i][j])
                            count += 1
                            ans.append(j)
                            ans.append(i)
                for i in range(len(ans)):
                    if i % 2 == 0:
                        ans[i] /= 1600
                    else:
                        ans[i] /= 600

                ans.insert(0,id_map[ann["category_id"]])
                line = ' '.join(map(str, ans)).strip(' ')
                f_txt.write(line+'\n')
        f_txt.close()

    #没标的图片标注信息的txt是空的,删掉
    yolo_path = 'datasets/coco/labels/train2017/'#之前转化的YOLO格式的txt标签都存在了这个目录下,下面的代码会删除没标注的
    coco_img_path = 'datasets/coco/images/train2017/'#先把所有的导出的图片都放在这个目录下,下面的代码会删除没标注的图片
    for item in os.listdir(yolo_path):
        if os.path.exists(yolo_path+item) and os.path.getsize(yolo_path+item) == 0:
            os.remove(yolo_path+item)
            img_item = item.split(".")[0]+".bmp"
            os.remove(coco_img_path+img_item)
            print('txt已删除')
            print('图片已删除')
        else:
            print('文件不存在或已删除')

代码写的很冗余,继续学习。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值