介绍
自己写了一个简易的代码来实现从labelme语义分割标注的json文件转为labelimg目标检测的XML文件,思路十分简单,通过将原分割区域的最小外接矩形转化为检测的目标框实现。
调用
import json
import os
from xml.etree.ElementTree import Element, SubElement, ElementTree
寻找最小外接矩形
由于我的数据集是分部件进行标注的,所以存放在不同的文件夹下
file_path="D:\\0datasets\\oil"
#读取文件列表
file_list = os.listdir(file_path)
#遍历文件列表中的文件夹
for file in file_list:
# 如果文件是目录
if os.path.isdir(file_path+"\\"+file):
# 获取文件夹路径
dir_path = file_path+"\\"+file
# 获取文件夹中的文件列表
dir_list = os.listdir(dir_path)
# 遍历文件夹中的文件
for dir_file in dir_list:
if dir_file.endswith('.json'):
files = dir_path + "\\" + dir_file
with open(files, 'r', encoding='utf-8') as f:
data = json.load(f)
shapes = data['shapes']
w = data['imageWidth']
h = data['imageHeight']
sizes = []
for i in range(len(shapes)):
op = shapes[i]
# 在这里设置你原来的标签
if op['label'] == "oil":
map = op['points']
xmax, ymax = 0, 0
xmin, ymin = w, h
# 获取每个标注的最小外接矩形
for m in range(len(map)):
x = map[m][0]
y = map[m][1]
if x > xmax:
xmax = x
if x < xmin:
xmin = x
if y > ymax:
ymax = y
if y < ymin:
ymin = y
cc = [int(xmin), int(ymin), int(xmax), int(ymax)]
sizes.append(cc)
# 假如存在标注,就会将json文件转为xml文件
if sizes:
json_xml(dir_file, dir_path, w, h, sizes)
os.remove(files)
else:
# 删除文件
os.remove(files)
os.remove(files.replace("json", "jpg"))
XML格式定义
由于我不太懂XML文件的操作,所以自己写了一遍labelimg的XML格式
def json_xml(fname,file_path,w,h,sizes):
root = Element('annotation')
folder = SubElement(root, 'folder')
folder.text="op"
filename=SubElement(root, 'filename')
filename.text=fname
path = SubElement(root, 'path')
path.text=file_path+"\\"+fname.replace("json","jpg")
source = SubElement(root, 'source')
database = SubElement(source, 'database')
database.text = 'Unknown'
size = SubElement(root, 'size')
width = SubElement(size, 'width')
width.text = str(w)
height = SubElement(size, 'height')
height.text = str(h)
depth = SubElement(size, 'depth')
depth.text = '3'
for i in range(len(sizes)):
xmi, ymi, xma, yma = sizes[i]
object = SubElement(root, 'object')
name = SubElement(object, 'name')
#这里这里设置你的标签名称
name.text = 'labelname'
pose = SubElement(object, 'pose')
pose.text = 'Unspecified'
truncated = SubElement(object, 'truncated')
truncated.text = '1'
difficult = SubElement(object, 'difficult')
difficult.text = '0'
bndbox = SubElement(object, 'bndbox')
xmin = SubElement(bndbox, 'xmin')
xmin.text = str(xmi)
ymin = SubElement(bndbox, 'ymin')
ymin.text = str(ymi)
xmax = SubElement(bndbox, 'xmax')
xmax.text = str(xma)
ymax = SubElement(bndbox, 'ymax')
ymax.text = str(yma)
pretty_xml(root, "\t", "\n")
tree = ElementTree(root)
fname = path.text.replace(".jpg", ".xml")
tree.write(fname, encoding="utf-8")
XML格式美化
调整换行和缩进使其符合labelimg生成的形式
def pretty_xml(element, indent, newline, level=0): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行
if element is not None: # 判断element是否有子元素
if (element.text is None) or element.text.isspace(): # 如果element的text没有内容
element.text = newline + indent * (level + 1)
# else: # 此处两行如果把注释去掉,Element的text也会另起一行
# element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * level
temp = list(element) # 将element转成list
for subelement in temp:
if temp.index(subelement) < (len(temp) - 1): # 如果不是list的最后一个元素,说明下一个行是同级别元素的起始,缩进应一致
subelement.tail = newline + indent * (level + 1)
else: # 如果是list的最后一个元素, 说明下一行是母元素的结束,缩进应该少一个
subelement.tail = newline + indent * level
pretty_xml(subelement, indent, newline, level=level + 1) # 对子元素进行递归操作
结语
希望能对大家的工作起到一点点帮助。