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('文件不存在或已删除')
代码写的很冗余,继续学习。