目标检测标签转换txt2json、xml2txt

这里写自定义目录标题

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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值