Mutual conversion between data set annotation files when training different models of the yolo series
Article directory
- Mutual conversion between data set annotation files when training different models of the yolo series
-
- 1. Convert the json file marked by the annotation wizard into the txt format required for yolov5 training. Each json file needs to be traversed in turn.
- 2. To convert the txt format of yolov5 into the xml of yolovX, each txt file needs to be traversed in sequence.
- 3. Convert the yolo format annotation file .txt to the coco format annotation file .json
- 4. Write the paths to the txt files to store the training set and validation set images respectively.
- 5. Convert labelme’s json to yolo’s txt
1. Convert the json file marked by the annotation wizard into the txt format required for yolov5 training. Each json file needs to be traversed in turn.
def json2txt():
from tqdm import tqdm, trange
''' 标注精灵的json(x1,y1,x2,y2)文件转训练所用txt(x0,y0,w,h)'''
name2307 = {'pedes': 0, 'car': 1, 'bus': 2, 'bigtru': 3, 'bike': 4, 'elec': 5, 'tricycle': 6, 'coni': 7, 'warm': 8,
'polic': 1, 'tralight': 9, 'ambu': 10, 'fireen': 10,
'pedes ': 0, 'car ': 1, ' car': 1, ' car': 1, ' ca r': 1, 'suv': 1, 'van': 1,
'mpv': 1, 'pickup': 1, 'ar': 1, 'pedesd': 0, 'coach ': 2, 'car ': 1,
'suv ': 1, 'coach': 2,'trailer': 3, 'crane': 3, 'smatru': 3, 'tank': 3, 'escort': 10,
'engine': 10, 'bull': 10, 'excava': 10, 'military': 10, 'elec ': 5, 'moto': 5, 'motopoli': 5}
#name2307 = {#pedes: 0,#car: 1,#bus: 2,#truck: 3,#bike: 4,#elec: 5,#tricycle: 6,#coni: 7,#warm: 8,#tralight: 9,#special_vehicles: 10,}
# 如下参数需根据情况进行修改
namell = name2307
pathimg = r'D:\yolov5train\datasTrain3_More\images\val'
pathjson = r'E:\datasTrainALL\OrdinaryRoad00_outputs44285'
despathtxt = r'D:\yolov5train\datasTrain3_More\labels\val'
files = os.listdir(pathimg)
print(len(files))
# files = [x for x in files if x[-4:] == '.jpg']
ic = 0
ycfile = []
# for f in files[:]:
for i in tqdm(range(len(files)), desc='进度:'):
f = files[i]
ic = ic + 1
# print(ic, ': ', f)
if (f[-4:] != '.jpg'):
continue
imgShape = cv2.imread(os.path.join(pathimg, f)).shape
imgw, imgh = imgShape[1], imgShape[0]
json_path_name = os.path.join(pathjson, f[:-4] + '.json')
with open(json_path_name, 'r', encoding='utf-8') as rf:
rf = json.load(rf)
try:
objs = rf['outputs']['object']
except:
ycfile.append(f)
print(ycfile)
# break
continue
boxs = []
for o in objs:
try:
id = o['name']
if id not in namell.keys():
continue
id = str(namell[id])
xmin = o['bndbox']['xmin']
ymin = o['bndbox']['ymin']
xmax = o['bndbox']['xmax']
ymax = o['bndbox']['ymax']
ymax = imgh if (ymax > imgh or (imgh - ymax<5)) else ymax
ymin = 0 if ymin < 5 else ymin
xmax = imgw if (xmax > imgw or (imgw - xmax < 5)) else xmax
xmin = 0 if xmin < 5 else xmin
x = (xmin + xmax) / 2 / imgw
y = (ymin + ymax) / 2 / imgh
w = (xmax - xmin) / imgw
h = (ymax - ymin) / imgh
x = str(x) if len(str(x)) <= 7 else str(x)[:7]
y = str(y) if len(str(y)) <= 7 else str(y)[:7]
w = str(w) if len(str(w)) <= 7 else str(w)[:7]
h = str(h) if len(str(h)) <= 7 else str(h)[:7]
box = id + ' ' + x + ' ' + y + ' ' + w + ' ' + h + '\n'
boxs.append(box)
except:
continue
if boxs == []:
continue
boxs[-1] = boxs[-1].replace('\n', '')
txt_path_name = os.path.join(despathtxt, f[:-4] + '.txt')
with open(txt_path_name, 'w', encoding='utf-8') as wf:
wf.writelines(boxs)
print(ycfile)
2. To convert the txt format of yolov5 into the xml of yolovX, each txt file needs to be traversed in sequence.
ref :https://zhuanlan.zhihu.com/p/525950939
def txt2xml():
"""
for YOLOX
将yolov5的txt格式转化为yolovX的xml
"""
# ref :https://zhuanlan.zhihu.com/p/525950939
from xml.dom.minidom import Document
import os
import cv2
"""
此函数用于将yolov5格式txt标注文件转换为voc格式xml标注文件
在自己的标注图片文件夹下建三个子文件夹
"""
dic = {'0': "peses", # 创建字典用来对类型进行转换
'1': "car", # 此处的字典要与自己的classes.txt文件中的类对应,且顺序要一致
'2': "bus",
'3': "truck",
'4': "bike",
'5': "elec",
'6': "tricycle",
'7': "coni",
'8': "warm",
'9': "tralight",
'10': "special_vehicle"
}
picPath = "D:/yolov5train/datasTrain3_More/images/train/" # 图片所在文件夹路径,后面的/一定要带上
txtPath = "D:/yolov5train/datasTrain3_More/labels/train/" # txt所在文件夹路径,后面的/一定要带上
xmlPath = "D:/yolov5train/datasTrain3_More/annotations/train/" # xml文件保存路径,后面的/一定要带上
files = os.listdir(txtPath)
for i, name in enumerate(files):
xmlBuilder = Document()
annotation = xmlBuilder.createElement("annotation") # 创建annotation标签
xmlBuilder.appendChild(annotation)
txtFile = open(txtPath + name)
txtList = txtFile.readlines()
img = cv2.imread(picPath + name[0:-4] + ".jpg")
Pheight, Pwidth, Pdepth = img.shape
folder = xmlBuilder.createElement("folder") # folder标签
foldercontent = xmlBuilder.createTextNode("driving_annotation_dataset")
folder.appendChild(foldercontent)
annotation.appendChild(folder) # folder标签结束
filename = xmlBuilder.createElement("filename") # filename标签
filenamecontent = xmlBuilder.createTextNode(name[0:-4] + ".jpg")
filename.appendChild(filenamecontent)
annotation.appendChild(filename) # filename标签结束
size = xmlBuilder.createElement("size") # size标签
width = xmlBuilder.createElement("width") # size子标签width
widthcontent = xmlBuilder.createTextNode(str(Pwidth))
width.appendChild(widthcontent)
size.appendChild(width) # size子标签width结束
height = xmlBuilder.createElement("height") # size子标签height
heightcontent = xmlBuilder.createTextNode(str(Pheight))
height.appendChild(heightcontent)
size.appendChild(height) # size子标签height结束
depth = xmlBuilder.createElement("depth") # size子标签depth
depthcontent = xmlBuilder.createTextNode(str(Pdepth))
depth.appendChild(depthcontent)
size.appendChild(depth) # size子标签depth结束
annotation.appendChild(size) # size标签结束
for j in txtList:
oneline = j.strip().split(" ")
object = xmlBuilder.createElement("object") # object 标签
picname = xmlBuilder.createElement("name") # name标签
namecontent = xmlBuilder.createTextNode(dic[oneline[0]])
picname.appendChild(namecontent)
object.appendChild(picname) # name标签结束
pose = xmlBuilder.createElement("pose") # pose标签
posecontent = xmlBuilder.createTextNode("Unspecified")
pose.appendChild(posecontent)
object.appendChild(pose) # pose标签结束
truncated = xmlBuilder.createElement("truncated") # truncated标签
truncatedContent = xmlBuilder.createTextNode("0")
truncated.appendChild(truncatedContent)
object.appendChild(truncated) # truncated标签结束
difficult = xmlBuilder.createElement("difficult") # difficult标签
difficultcontent = xmlBuilder.createTextNode("0")
difficult.appendChild(difficultcontent)
object.appendChild(difficult) # difficult标签结束
bndbox = xmlBuilder.createElement("bndbox") # bndbox标签
xmin = xmlBuilder.createElement("xmin") # xmin标签
mathData = int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)
xminContent = xmlBuilder.createTextNode(str(mathData))
xmin.appendChild(xminContent)
bndbox.appendChild(xmin) # xmin标签结束
ymin = xmlBuilder.createElement("ymin") # ymin标签
mathData = int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)
yminContent = xmlBuilder.createTextNode(str(mathData))
ymin.appendChild(yminContent)
bndbox.appendChild(ymin) # ymin标签结束
xmax = xmlBuilder.createElement("xmax") # xmax标签
mathData = int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)
xmaxContent = xmlBuilder.createTextNode(str(mathData))
xmax.appendChild(xmaxContent)
bndbox.appendChild(xmax) # xmax标签结束
ymax = xmlBuilder.createElement("ymax") # ymax标签
mathData = int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)
ymaxContent = xmlBuilder.createTextNode(str(mathData))
ymax.appendChild(ymaxContent)
bndbox.appendChild(ymax) # ymax标签结束
object.appendChild(bndbox) # bndbox标签结束
annotation.appendChild(object) # object标签结束
f = open(xmlPath + name[0:-4] + ".xml", 'w')
xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
f.close()
# break
def ImageSetsMain():
"""
for YOLOX:将每一个数据文件名称保存在txt文件中
"""
imgPath = r'D:\yolov5train\datasTrain3_More\images\train'
xmlPath = r'D:\yolov5train\datasTrain3_More\labels\train'
train_val_txtPath = r'D:\yolov5train\datasTrain3_More\ImageSets\Main\train.txt'
imgs = os.listdir(imgPath)
with open(train_val_txtPath, 'w') as wf:
for i in range(len(imgs)):
text = imgs[i].split(".jpg")[0] +'\n'
wf.write(text)
3. Convert the yolo format annotation file .txt to the coco format annotation file .json
import argparse
import json
import os
import sys
import shutil
from datetime import datetime
import cv2
coco = dict()
coco['images'] = []
coco['type'] = 'instances'
coco['annotations'] = []
coco['categories'] = []
category_set = dict()
image_set = set()
image_id = 000000
annotation_id = 0
def addCatItem(category_dict):
for k, v in category_dict.items():
category_item = dict()
category_item['supercategory'] = 'none'
category_item['id'] = int(k)
category_item['name'] = v
coco['categories'].append(category_item)
def addImgItem(file_name, size):
global image_id
image_id += 1
image_item = dict()
image_item['id'] = image_id
image_item['file_name'] = file_name
image_item['width'] = size[1]
image_item['height'] = size[0]
image_item['license'] = None
image_item['flickr_url'] = None
image_item['coco_url'] = None
image_item['date_captured'] = str(datetime.today())
coco['images'].append(image_item)
image_set.add(file_name)
return image_id
def addAnnoItem(object_name, image_id, category_id, bbox):
global annotation_id
annotation_item = dict()
annotation_item['segmentation'] = []
seg = []
# bbox[] is x,y,w,h
# left_top
seg.append(bbox[0])
seg.append(bbox[1])
# left_bottom
seg.append(bbox[0])
seg.append(bbox[1] + bbox[3])
# right_bottom
seg.append(bbox[0] + bbox[2])
seg.append(bbox[1] + bbox[3])
# right_top
seg.append(bbox[0] + bbox[2])
seg.append(bbox[1])
annotation_item['segmentation'].append(seg)
annotation_item['area'] = bbox[2] * bbox[3]
annotation_item['iscrowd'] = 0
annotation_item['ignore'] = 0
annotation_item['image_id'] = image_id
annotation_item['bbox'] = bbox
annotation_item['category_id'] = category_id
annotation_id += 1
annotation_item['id'] = annotation_id
coco['annotations'].append(annotation_item)
def xywhn2xywh(bbox, size):
bbox = list(map(float, bbox))
size = list(map(float, size))
xmin = (bbox[0] - bbox[2] / 2.) * size[1]
ymin = (bbox[1] - bbox[3] / 2.) * size[0]
w = bbox[2] * size[1]
h = bbox[3] * size[0]
box = (xmin, ymin, w, h)
return list(map(int, box))
def parseXmlFilse(image_path, anno_path, save_path, json_name='train.json'):
assert os.path.exists(image_path), "ERROR {} dose not exists".format(image_path)
assert os.path.exists(anno_path), "ERROR {} dose not exists".format(anno_path)
if os.path.exists(save_path):
pass
# shutil.rmtree(save_path)
os.makedirs(save_path)
json_path = os.path.join(save_path, json_name)
category_set = []
with open(anno_path + '/classes.txt', 'r') as f:
for i in f.readlines():
category_set.append(i.strip())
category_id = dict((k, v) for k, v in enumerate(category_set))
addCatItem(category_id)
images = [os.path.join(image_path, i) for i in os.listdir(image_path)]
files = [os.path.join(anno_path, i) for i in os.listdir(anno_path)]
images_index = dict((v.split(os.sep)[-1][:-4], k) for k, v in enumerate(images))
for file in files:
if os.path.splitext(file)[-1] != '.txt' or 'classes' in file.split(os.sep)[-1]:
continue
if file.split(os.sep)[-1][:-4] in images_index:
index = images_index[file.split(os.sep)[-1][:-4]]
img = cv2.imread(images[index])
shape = img.shape
filename = images[index].split(os.sep)[-1]
current_image_id = addImgItem(filename, shape)
else:
continue
with open(file, 'r') as fid:
for i in fid.readlines():
i = i.strip().split()
category = int(i[0])
category_name = category_id[category]
bbox = xywhn2xywh((i[1], i[2], i[3], i[4]), shape)
addAnnoItem(category_name, current_image_id, category, bbox)
json.dump(coco, open(json_path, 'w'))
print("class nums:{}".format(len(coco['categories'])))
print("image nums:{}".format(len(coco['images'])))
print("bbox nums:{}".format(len(coco['annotations'])))
def MultTxt2SingleJson():
"""
脚本说明:
本脚本用于将yolo格式的标注文件.txt转换为coco格式的标注文件.json
参数说明:
anno_path:标注文件txt存储路径
save_path:json文件输出的文件夹
image_path:图片路径
json_name:json文件名字
"""
parser = argparse.ArgumentParser()
parser.add_argument('-ap', '--anno-path', type=str, default=r'D:\yolov5train\datasTrain3_More\labels\val', help='yolo txt path')
parser.add_argument('-s', '--save-path', type=str, default=r'D:\yolov5train\datasTrain3_More\images\Annotations', help='json save path')
parser.add_argument('--image-path', default=r'D:\yolov5train\datasTrain3_More\images\val')
parser.add_argument('--json-name', default='val.json')
opt = parser.parse_args()
if len(sys.argv) > 1:
print(opt)
parseXmlFilse(**vars(opt))
else:
anno_path = r'D:\yolov5train\datasTrain3_More\labels\val'
save_path = r'D:\yolov5train\datasTrain3_More\images\Annotations'
image_path = r'D:\yolov5train\datasTrain3_More\images\val'
json_name = 'val.json'
parseXmlFilse(image_path, anno_path, save_path, json_name)
pass
4. Write the paths to the txt files to store the training set and validation set images respectively.
def writeImgPath_forYolov7Datas():
'''
因为yolov7训练的时候,在数据集根目录下,需要有txt文件分别存放训练集和验证集图片的路径
'''
from tqdm import tqdm
import os
imgpath = r'D:/yolov5train/datasTrain3_More/images/train'
txt_path_name = r'D:/yolov5train/datasTrain3_More/train_list.txt'
files = os.listdir(imgpath)
imgspath = []
for i in tqdm(range(len(files)), desc='进度 '):
f = files[i]
try:
name = os.path.join(imgpath, f)
name = name + '\n'
imgspath.append(name)
except Exception as e:
print(f, ":", e)
continue
imgspath[-1] = imgspath[-1].replace('\n', '')
with open(txt_path_name, 'w', encoding='utf-8') as wf:
wf.writelines(imgspath)
5. Convert labelme’s json to yolo’s txt
def labelmejson2yolotxt():
import os
import numpy as np
from tqdm import tqdm
imgPath = r' '
jsonPath = r' '
savetxtPath = r' '
clsStatic, clsStaticFlag= {
}, True
# 先统计标签有哪些再保存txt,根据统计的标签 好确定 namell
savetxtPath, savetxtPathFlag = savetxtPath, True
# name15label = [pedes0, car1, bus2, truck3, bike4, elec5, tricycle6, coni7, warm8, tralight9, polic10, ambu11,
# fireen12, suv13, mpv14]
name15 = {
'car': 1, 'suv': 13, 'mpv': 14, 'van': 14, 'polic': 10, 'smatru': 3, 'elec': 5,
'pedes': 0, 'coni': 7, 'tank': 3, 'moto': 5, 'bigtru': 3, 'bus': 2, 'engine': 3,
'tricycle': 6, 'pickup': 1, 'coach': 2, 'bike': 4, 'pedesr': 0, 'crane': 3, 'trailer': 3,
'excava': 3, 'warm': 8, 'tralight': 9, 'truck': 3, 'bigtrur': 3, 'vcar': 1, 'escort': 3 }
namell = name15
files = os.listdir(imgPath)
print('len(imgPath): ', len(files))
# files = [x for x in files if x[-4:] == '.jpg']
for i in tqdm(range(len(files)), desc='进度:'):
f = files[i]
if (f[-4:] != '.jpg'):
continue
imgShape = cv2.imread(os.path.join(imgPath, f)).shape
imgw, imgh = imgShape[1], imgShape[0]
json_path_name = os.path.join(jsonPath, f[:-4] + '.json')
with open(json_path_name, 'r', encoding='utf-8') as rf:
rf = json.load(rf)
try:
objs = rf['shapes']
except:
continue
boxs = []
for o in objs:
try:
id = o['label']
if clsStaticFlag:
if id in clsStatic.keys():
clsStatic[id] += 1
else:
clsStatic[id] = 1
if savetxtPathFlag:
if id not in namell.keys():
continue
id = str(namell[id])
xmin = o['points'][0][0]
ymin = o['points'][0][1]
xmax = o['points'][1][0]
ymax = o['points'][1][1]
ymax = imgh if (ymax > imgh or (imgh - ymax < 5)) else ymax
ymin = 0 if ymin < 5 else ymin
xmax = imgw if (xmax > imgw or (imgw - xmax < 5)) else xmax
xmin = 0 if xmin < 5 else xmin
x = (xmin + xmax) / 2 / imgw
y = (ymin + ymax) / 2 / imgh
w = (xmax - xmin) / imgw
h = (ymax - ymin) / imgh
x = str(x) if len(str(x)) <= 7 else str(x)[:7]
y = str(y) if len(str(y)) <= 7 else str(y)[:7]
w = str(w) if len(str(w)) <= 7 else str(w)[:7]
h = str(h) if len(str(h)) <= 7 else str(h)[:7]
box = id + ' ' + x + ' ' + y + ' ' + w + ' ' + h + '\n'
boxs.append(box)
except Exception as e:
print('jie Xi Json Error: ', e)
continue
if boxs == [] and savetxtPathFlag == True:
continue
if savetxtPathFlag:
boxs[-1] = boxs[-1].replace('\n', '')
txt_path_name = os.path.join(savetxtPath, f[:-4] + '.txt')
with open(txt_path_name, 'w', encoding='utf-8') as wf:
wf.writelines(boxs)
if clsStaticFlag:
print('clsStatic: ', clsStatic)
# break