txt2json
class_names是类别名称,image_path源图片路径,label_path txt标签路径,save_path json标签保存路径。label_path此路径确保全是txt文件以及对应的图片要存在,否则就会报错。
# -*- coding:utf8 -*-
"""
date:2021-09-01
"""
import os
import json
from PIL import Image, ImageDraw, ImageFont
from loguru import logger
import base64
import re
class XmlMaker:
def __init__(self, txtpath):
self.txtPath = txtpath
def get_imgData(self, img_name):
with open(img_name, "rb")as f:
base64_data = str(base64.b64encode(f.read()))
match_pattern = re.compile(r'b\'(.*)\'')
base64_data = match_pattern.match(base64_data).group(1)
return base64_data
def yolov5txt_to_json(self, save_path, W=800, H=2048, img_path=None):
if not os.path.exists(save_path):
os.makedirs(save_path)
# '建立json文件'
data = {}
data['version'] = "1.0.0"
data['flags'] = {}
data['shapes'] = []
name = self.txtPath.strip().split('/')[-1]
filename = name[:-4] + '.jpg'
if save_path:
try:
emg = Image.open(os.path.join(img_path, filename))
except FileNotFoundError as e:
os.remove(img_path.replace('jpg', 'txt'))
W, H = emg.size[0], emg.size[1]
assert name[-4:] == '.txt', "only read .txt"
data['imagePath'] = filename
# data['imageData'] = "null"
data['imageData'] = self.get_imgData(os.path.join(img_path, filename))
txtfile = open(self.txtPath, 'r')
txtList = txtfile.readlines()
for i in txtList:
print(i)
x1 = float(i.strip().split(" ")[1])
y1 = float(i.strip().split(" ")[2])
x2 = float(i.strip().split(" ")[3])
y2 = float(i.strip().split(" ")[4])
label = {}
label['points'] = []
label['group_id'] = None
label['shape_type'] = "rectangle"
label['flags'] = {}
label['label'] = class_names[int(i.strip().split(" ")[0])]
label['points'].append([float(x1 * W - x2 * W / 2), float(y1 * H - y2 * H / 2)])
label['points'].append([float(x1 * W + x2 * W / 2), float(y1 * H + y2 * H / 2)])
# up['points']['xmin'] = int(x1 * W - x2 * W / 2)
# up['points']['ymin'] = int(y1 * H - y2 * H / 2)
# up['points']['xmax'] = int(x1 * W + x2 * W / 2)
# up['points']['ymax'] = int(y1 * H + y2 * H / 2)
data['shapes'].append(label)
data['imageHeight'] = H
data['imageWidth'] = W
# data['time_labeled'] = str(100)
# data['labeled'] = 'true'
# A3 = {} # {"width":3400,"height":587,"depth":3}
# A3['width'], A3['height'], A3['depth'] = W, H, str(3)
# data['size'] = A3
article = json.dumps(data, ensure_ascii=False)
# logger.debug(article)
f = open(
os.path.join(save_path, name[:-4] + '.json'), 'w')
f.write(article)
def yolov5json_to_txt(self, save_path, areas=False, select=False, img_path=None):
WF = open(os.path.join(self.txtPath), encoding='UTF-8')
ZTXT = json.load(WF) # 加载json文件内容
logger.debug(ZTXT)
filename = ZTXT["path"] # 获取json文件里面path所对应的东西
filename_V5 = filename.strip().split('\\') # 按\\将路径单独分开
jpg = filename_V5[-1] # 只取出照片的名字**.png
emg = Image.open(os.path.join(img_path, jpg))
W_1, H_1 = emg.size[0], emg.size[1]
LABE = os.path.join(save_path, '{}'.format(jpg[:-4] + '.txt'))
LABE_file = open(LABE, "w")
objects = ZTXT['outputs']['object'] # 取出json文件里面outputs下object里面的东西
logger.debug(objects)
W = int(ZTXT['size']['width'])
H = int(ZTXT['size']['height']) # 读取图片的宽度与高度
for i, object in enumerate(objects): # enumerate既遍历索引又遍历元素
if object['name'] not in class_names:
logger.debug('未知类别--{}--'.format(object['name']))
continue
cla = class_names.index(str(object['name']))
xmin = int(float(object['bndbox']['xmin']))
ymin = int(float(object['bndbox']['ymin']))
xmax = int(float(object['bndbox']['xmax']))
ymax = int(float(object['bndbox']['ymax']))
x_1 = (xmin + xmax) / float(2 * W)
y_1 = (ymin + ymax) / float(2 * H)
x_w = float(xmax - xmin)
x_h = float(ymax - ymin)
# '做统计用'
area = x_w, x_h
areas.append(area) # 将area不动追加到sareas末尾
LABE_file.write(
"{} {} {} {} {}\n".format(cla, x_1, y_1, (xmax - xmin) / float(W), (ymax - ymin) / float(H)))
if __name__ == "__main__":
class_names = ['digger', 'directional drilling', 'Exploration drilling', 'pile driver', 'rotary', 'truck']
image_path = r'your_img_path'
label_path = r'your_label_path'
save_path = r'your_json_lab_save_path'
for i in os.listdir(label_path):
read = XmlMaker(os.path.join(label_path, i))
# read.yolov5txt_to_json(save_path, img_path=r'C:\Users\ZQ\Desktop\RSDDs dataset\Type-I RSDDs dataset\luo\img')
read.yolov5txt_to_json(save_path, img_path=image_path)```
xml2txt
本脚本有两个功能:
1.将voc数据集标注信息(.xml)转为yolo标注格式(.txt),并将图像文件复制到相应文件夹
2.根据json标签文件,生成对应names标签(my_data_label.names)
import os
from tqdm import tqdm
from lxml import etree
import json
import shutil
# voc数据集根目录以及版本
voc_root = r"C:\Users\numim\Desktop\VOC2020"
voc_version = "VOC2020"
# 转换的训练集以及验证集对应txt文件
train_txt = "train.txt"
val_txt = "val.txt"
# 转换后的文件保存目录
save_file_root = r"C:\Users\numim\Desktop\CSDN_fire"
# label标签对应json文件
label_json_path = r'C:\Users\numim\Desktop\yolov3_spp\data\data.json'
# 拼接出voc的images目录,xml目录,txt目录
voc_images_path = os.path.join(voc_root, voc_version, "JPEGImages")
voc_xml_path = os.path.join(voc_root, voc_version, "Annotations")
train_txt_path = os.path.join(voc_root, voc_version, "ImageSets", "Main", train_txt)
val_txt_path = os.path.join(voc_root, voc_version, "ImageSets", "Main", val_txt)
# 检查文件/文件夹都是否存在
print(label_json_path)
assert os.path.exists(voc_images_path), "VOC images path not exist..."
assert os.path.exists(voc_xml_path), "VOC xml path not exist..."
assert os.path.exists(train_txt_path), "VOC train txt file not exist..."
assert os.path.exists(val_txt_path), "VOC val txt file not exist..."
assert os.path.exists(label_json_path), "label_json_path does not exist..."
if os.path.exists(save_file_root) is False:
os.makedirs(save_file_root)
def parse_xml_to_dict(xml):
"""
将xml文件解析成字典形式,参考tensorflow的recursive_parse_xml_to_dict
Args:
xml: xml tree obtained by parsing XML file contents using lxml.etree
Returns:
Python dictionary holding XML contents.
"""
if len(xml) == 0: # 遍历到底层,直接返回tag对应的信息
return {xml.tag: xml.text}
result = {}
for child in xml:
child_result = parse_xml_to_dict(child) # 递归遍历标签信息
if child.tag != 'object':
result[child.tag] = child_result[child.tag]
else:
if child.tag not in result: # 因为object可能有多个,所以需要放入列表里
result[child.tag] = []
result[child.tag].append(child_result[child.tag])
return {xml.tag: result}
def translate_info(file_names: list, save_root: str, class_dict: dict, train_val='train'):
"""
将对应xml文件信息转为yolo中使用的txt文件信息
:param file_names:
:param save_root:
:param class_dict:
:param train_val:
:return:
"""
save_txt_path = os.path.join(save_root, train_val, "labels")
if os.path.exists(save_txt_path) is False:
os.makedirs(save_txt_path)
save_images_path = os.path.join(save_root, train_val, "images")
if os.path.exists(save_images_path) is False:
os.makedirs(save_images_path)
for file in tqdm(file_names, desc="translate {} file...".format(train_val)):
# 检查下图像文件是否存在
img_path = os.path.join(voc_images_path, file + ".jpg")
assert os.path.exists(img_path), "file:{} not exist...".format(img_path)
# 检查xml文件是否存在
xml_path = os.path.join(voc_xml_path, file + ".xml")
assert os.path.exists(xml_path), "file:{} not exist...".format(xml_path)
# read xml
with open(xml_path) as fid:
xml_str = fid.read()
xml = etree.fromstring(xml_str)
data = parse_xml_to_dict(xml)["annotation"]
img_height = int(data["size"]["height"])
img_width = int(data["size"]["width"])
# write object info into txt
with open(os.path.join(save_txt_path, file + ".txt"), "w") as f:
assert "object" in data.keys(), "file: '{}' lack of object key.".format(xml_path)
for index, obj in enumerate(data["object"]):
# 获取每个object的box信息
xmin = float(obj["bndbox"]["xmin"])
xmax = float(obj["bndbox"]["xmax"])
ymin = float(obj["bndbox"]["ymin"])
ymax = float(obj["bndbox"]["ymax"])
class_name = obj["name"]
class_index = class_dict[class_name] - 1 # 目标id从0开始
# 进一步检查数据,有的标注信息中可能有w或h为0的情况,这样的数据会导致计算回归loss为nan
if xmax <= xmin or ymax <= ymin:
print("Warning: in '{}' xml, there are some bbox w/h <=0".format(xml_path))
continue
# 将box信息转换到yolo格式
xcenter = xmin + (xmax - xmin) / 2
ycenter = ymin + (ymax - ymin) / 2
w = xmax - xmin
h = ymax - ymin
# 绝对坐标转相对坐标,保存6位小数
xcenter = round(xcenter / img_width, 6)
ycenter = round(ycenter / img_height, 6)
w = round(w / img_width, 6)
h = round(h / img_height, 6)
info = [str(i) for i in [class_index, xcenter, ycenter, w, h]]
if index == 0:
f.write(" ".join(info))
else:
f.write("\n" + " ".join(info))
# copy image into save_images_path
shutil.copyfile(img_path, os.path.join(save_images_path, img_path.split(os.sep)[-1]))
def create_class_names(class_dict: dict):
keys = class_dict.keys()
with open("./data/my_data_label.names", "w") as w:
for index, k in enumerate(keys):
if index + 1 == len(keys):
w.write(k)
else:
w.write(k + "\n")
def main():
# read class_indict
json_file = open(label_json_path, 'r', encoding='utf8')
class_dict = json.load(json_file)
# 读取train.txt中的所有行信息,删除空行
with open(train_txt_path, "r") as r:
train_file_names = [i for i in r.read().splitlines() if len(i.strip()) > 0]
# voc信息转yolo,并将图像文件复制到相应文件夹
translate_info(train_file_names, save_file_root, class_dict, "train")
# 读取val.txt中的所有行信息,删除空行
with open(val_txt_path, "r") as r:
val_file_names = [i for i in r.read().splitlines() if len(i.strip()) > 0]
# voc信息转yolo,并将图像文件复制到相应文件夹
translate_info(val_file_names, save_file_root, class_dict, "val")
# 创建my_data_label.names文件
create_class_names(class_dict)
if __name__ == "__main__":
main()