前言:最近这两天因为数据集的问题,进行了一些简单的标注工作,使用labelme标注完成之后会生成json格式的文件(每张图对应一个json文件)。在获得json文件之后需要将其转换为txt格式。
在标注完成之后,你可以自己划分文件夹得到两个文件夹:一个只包含图片,一个只包含json文件
以下是json转txt的具体步骤:
(1)首先将json标签转VOC格式的xml标签
# -*- coding: utf-8 -*-
"""
Created on Sun May 31 10:19:23 2020
@author: ywx
#TODO:json转xml文件
#TODO:需要改动的地方为两个路径:①19行改为json格式所在路径;②21行整个数据集所在的路径;
#TODO:最后会在数据集路径下自动创建一个xmls路径用来存放xml的文件路径
"""
import os
from typing import List, Any
import numpy as np
import codecs
import json
from glob import glob
import cv2
import shutil
from sklearn.model_selection import train_test_split
# 1.标签路径
labelme_path = r"D:\datasets\small-object-detection-datasets\Solar_1200\json/"#这个路径是存放每个图片的json的路径
# 原始labelme标注数据路径
saved_path = r"D:\datasets\small-object-detection-datasets\Solar_1200/"#这个路径是这个数据集的路径
# 2.获取待处理文件
files = glob(labelme_path + "*.json")
files = [i.replace("\\","/").split("/")[-1].split(".json")[0] for i in files]
print(files)
# 4.读取标注信息并写入 xml
for json_file_ in files:
json_filename = labelme_path + json_file_ + ".json"
json_file = json.load(open(json_filename, "r", encoding="utf-8"))
height, width, channels = cv2.imread(saved_path + "JPEGImages/" + json_file_ + ".tiff").shape
# with codecs.open(saved_path + "labels/" + json_file_ + ".xml", "w", "utf-8") as xml:
with codecs.open(saved_path + "xml/" + json_file_ + ".xml", "w", "utf-8") as xml:
xml.write('<annotation>\n')
xml.write('\t<folder>' + 'Solar' + '</folder>\n')
xml.write('\t<filename>' + json_file_ + ".tiff" + '</filename>\n')
xml.write('\t<source>\n')
xml.write('\t\t<database>Unknown</database>\n')
xml.write('\t</source>\n')
xml.write('\t<size>\n')
xml.write('\t\t<width>' + str(width) + '</width>\n')
xml.write('\t\t<height>' + str(height) + '</height>\n')
xml.write('\t\t<depth>' + str(channels) + '</depth>\n')
xml.write('\t</size>\n')
xml.write('\t\t<segmented>0</segmented>\n')
for multi in json_file["shapes"]:
points = np.array(multi["points"])
labelName = multi["label"]
xmin = min(points[:, 0])
xmax = max(points[:, 0])
ymin = min(points[:, 1])
ymax = max(points[:, 1])
label = multi["label"]
if xmax <= xmin:
pass
elif ymax <= ymin:
pass
else:
xml.write('\t<object>\n')
xml.write('\t\t<name>' + labelName + '</name>\n')
xml.write('\t\t<pose>Unspecified</pose>\n')
xml.write('\t\t<truncated>1</truncated>\n')
xml.write('\t\t<difficult>0</difficult>\n')
xml.write('\t\t<bndbox>\n')
xml.write('\t\t\t<xmin>' + str(int(xmin)) + '</xmin>\n')
xml.write('\t\t\t<ymin>' + str(int(ymin)) + '</ymin>\n')
xml.write('\t\t\t<xmax>' + str(int(xmax)) + '</xmax>\n')
xml.write('\t\t\t<ymax>' + str(int(ymax)) + '</ymax>\n')
xml.write('\t\t</bndbox>\n')
xml.write('\t</object>\n')
print(json_filename, xmin, ymin, xmax, ymax, label)
xml.write('</annotation>')
(2)生成一个image_id.txt文件
这个txt文件的内容就是图片名的前缀,如下图我的图片名命名为000001~001200共1200张图,所要得到的txt文件就包含1200行
(3)用xml文件生成yolo格式的txt标签文件(脚本如下,使用之前先看TODO后面的提示)
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd
'''使用这个脚本需要修改的代码为:
①13行classes修改成数据集对应的类别;②34行存储xml文本的路径;③36行、66行、67行存储txt文本的路径;④第68行的image_ids,要创建一个包含图片id的txt纯数字文件;
⑤第69行:创建一个文件夹(过程性的,不用管);⑥72行图片存储的路径(图片格式不是tiff的话要对应修改)。
'''
# sets = ['train', 'val', 'test']
sets = ['image_id']
classes = ['bl', 'ce', 'ch', 'cr', 'hi', 'ir', 'oi', 'pe'] # 改成自己的类别
# abs_path = os.getcwd()
# print(abs_path)
def convert(size, box):
dw = 1. / (size[0])
dh = 1. / (size[1])
x = (box[0] + box[1]) / 2.0 - 1
y = (box[2] + box[3]) / 2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return x, y, w, h
def convert_annotation(image_id):
#修改为xml文件存放的路径
in_file = open('D:\datasets\small-object-detection-datasets\Solar_1200/xml/%s.xml' % (image_id), encoding='UTF-8')
#修改为txt文件存放的路径
out_file = open('D:\datasets\small-object-detection-datasets\Solar_1200/txt/%s.txt' % (image_id), 'w')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
# difficult = obj.find('Difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
b1, b2, b3, b4 = b
# 标注越界修正
if b2 > w:
b2 = w
if b4 > h:
b4 = h
b = (b1, b2, b3, b4)
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
for image_set in sets:
#存放在txt的标签路径
if not os.path.exists('D:\datasets\small-object-detection-datasets\Solar_1200/txt/'):
os.makedirs('D:\datasets\small-object-detection-datasets\Solar_1200/txt/')
image_ids = open('D:\datasets\small-object-detection-datasets\Solar_1200/%s.txt' % (image_set)).read().strip().split()
list_file = open('D:\datasets\small-object-detection-datasets\Solar_1200/0/%s.txt' % (image_set), 'w')
for image_id in image_ids:
# print(abs_path)
list_file.write('D:\datasets\small-object-detection-datasets\Solar_1107\JPEGImages/%s.tiff\n' % (image_id))
convert_annotation(image_id)
list_file.close()
这样之后就获得了yolo格式的txt文件夹,yolo格式转COCO格式要生成小目标的APs指标可以参考我这一篇小目标的检测指标APs怎么获得?------数据集Yolo格式生成json文件-CSDN博客的步骤,只需要使用图片文件夹和这篇博客生成的txt文件夹就可以了。