数据格式转换(labelme、labelimg、yolo格式相互转换)

👨‍💻个人简介: 深度学习图像领域工作者
🎉总结链接:
             链接中主要是个人工作的总结,每个链接都是一些常用demo,代码直接复制运行即可。包括:
                    📌1.工作中常用深度学习脚本
                    📌2.torch、numpy等常用函数详解
                    📌3.opencv 图片、视频等操作
                    📌4.个人工作中的项目总结(纯干活)
              链接: https://blog.csdn.net/qq_28949847/article/details/128552785
🎉视频讲解: 以上记录,通过B站等平台进行了视频讲解使用,可搜索 ‘Python图像识别’ 进行观看
              B站:Python图像识别
              抖音:Python图像识别
              西瓜视频:Python图像识别


1. 目标检测矩形框转换

(1)labelme标注的数据json文件转为labelimg格式的xml文件

目前只能支持 json中 ‘rectangle’ 和 ‘polygon’ 两种模式的转换,其中 ‘polygon’ 的转换方式为,替换成最小外接矩形的左上角和右下角坐标。

输入:由labelme的rectangle或这polygon方式标注生成的json文件;

输出:类似于labelImg的rectangle方式标注生成的xml文件,是左上角和右下角的坐标。

个人只需修改相应的路径。

# -*- coding: utf-8 -*-
import numpy as np
import json
from lxml import etree
import os
from tqdm import tqdm


class ReadJson(object):
    '''
    读取json文件,获取相应的标签信息
    '''

    def __init__(self, json_path):
        self.json_data = json.load(open(json_path, encoding="utf-8"))
        self.filename = self.json_data['imagePath']
        self.width = self.json_data['imageWidth']
        self.height = self.json_data['imageHeight']

        self.coordis = []
        # 构建坐标
        self.process_shapes()

    def process_shapes(self):
        for single_shape in self.json_data['shapes']:
            if single_shape['shape_type'] == "rectangle":
                bbox_class = single_shape['label']
                xmin = single_shape['points'][0][0]
                ymin = single_shape['points'][0][1]
                xmax = single_shape['points'][1][0]
                ymax = single_shape['points'][1][1]
                self.coordis.append([xmin, ymin, xmax, ymax, bbox_class])
            elif single_shape['shape_type'] == 'polygon':
                bbox_class = single_shape['label']
                temp_points = single_shape['points']
                temp_points = np.array(temp_points)
                xmin, ymin = temp_points.min(axis=0)
                xmax, ymax = temp_points.max(axis=0)
                self.coordis.append([xmin, ymin, xmax, ymax, bbox_class])
            else:
                print("shape type error, shape_type not in ['rectangle', 'polygon']")

    def get_width_height(self):
        return self.width, self.height

    def get_filename(self):
        return self.filename

    def get_coordis(self):
        return self.coordis


class labelimg_Annotations_xml:
    def __init__(self, folder_name, filename, path, database="Unknown"):
        self.root = etree.Element("annotation")
        child1 = etree.SubElement(self.root, "folder")
        child1.text = folder_name
        child2 = etree.SubElement(self.root, "filename")
        child2.text = filename
        child3 = etree.SubElement(self.root, "path")
        child3.text = path
        child4 = etree.SubElement(self.root, "source")
        child5 = etree.SubElement(child4, "database")
        child5.text = database

    def set_size(self, width, height, channel):
        size = etree.SubElement(self.root, "size")
        widthn = etree.SubElement(size, "width")
        widthn.text = str(width)
        heightn = etree.SubElement(size, "height")
        heightn.text = str(height)
        channeln = etree.SubElement(size, "channel")
        channeln.text = str(channel)

    def set_segmented(self, seg_data=0):
        segmented = etree.SubElement(self.root, "segmented")
        segmented.text = str(seg_data)

    def set_object(self, label, x_min, y_min, x_max, y_max,
                   pose='Unspecified', truncated=0, difficult=0):
        object = etree.SubElement(self.root, "object")
        namen = etree.SubElement(object, "name")
        namen.text = label
        posen = etree.SubElement(object, "pose")
        posen.text = pose
        truncatedn = etree.SubElement(object, "truncated")
        truncatedn.text = str(truncated)
        difficultn = etree.SubElement(object, "difficult")
        difficultn.text = str(difficult)
        bndbox = etree.SubElement(object, "bndbox")
        xminn = etree.SubElement(bndbox, "xmin")
        xminn.text = str(x_min)
        yminn = etree.SubElement(bndbox, "ymin")
        yminn.text = str(y_min)
        xmaxn = etree.SubElement(bndbox, "xmax")
        xmaxn.text = str(x_max)
        ymaxn = etree.SubElement(bndbox, "ymax")
        ymaxn.text = str(y_max)

    def savefile(self, filename):
        tree = etree.ElementTree(self.root)
        tree.write(filename, pretty_print=True, xml_declaration=False, encoding='utf-8')


def json_transform_xml(json_path, xml_path):
    json_anno = ReadJson(json_path)
    width, height = json_anno.get_width_height()
    channel = 3
    filename = json_anno.get_filename()
    coordis = json_anno.get_coordis()

    anno = labelimg_Annotations_xml('JPEGImages', filename, 'JPEGImages')
    anno.set_size(width, height, channel)
    anno.set_segmented()
    for data in coordis:
        x_min, y_min, x_max, y_max, label = data
        anno.set_object(label, int(x_min), int(y_min), int(x_max), int(y_max))
    anno.savefile(xml_path)


if __name__ == "__main__":
    '''
        目前只能支持 json中 rectangle 和 polygon 两种模式的转换,其中 polygon 的转换方式为,替换成最小外接矩形的左上角和右下角坐标
    '''
    root_json_dir = r"C:\Users\JoelYang\Desktop\111111\bbox_20230417_gjx"
    # root_save_xml_dir = r"\\SHARE\public\Time_Plus\traindata\bbox\tiaoshui3m\20230418_zxl"
    root_save_xml_dir = root_json_dir
    for json_filename in tqdm(os.listdir(root_json_dir)):
        if not json_filename.endswith(".json"):
            continue
        json_path = os.path.join(root_json_dir, json_filename)
        save_xml_path = os.path.join(root_save_xml_dir, json_filename.replace(".json", ".xml"))
        json_transform_xml(json_path, save_xml_path)

效果如下图:

json文件:
在这里插入图片描述

xml文件:
在这里插入图片描述

(2)labelimg xml文件转 labelme json文件

import xml.etree.ElementTree as ET
import os
import json


def xml_transform_json(xml_path, file, save_path):
    print(os.path.join(xml_path, file))
    # 读取xml文件
    path_file_xml = os.path.join(xml_path, file)
    # 解析读取xml函数
    root = ET.parse(path_file_xml)
    folder = root.find('folder').text
    filename = root.find('filename').text
    path = root.find('path').text
    sz = root.find('size')
    width = int(sz[0].text)
    height = int(sz[1].text)

    # 构建json数据
    data = {}
    data['flags'] = {}
    data['version'] = "4.5.6"
    data["shapes"] = []
    for child in root.findall('object'):  # 找到图片中的所有框
        sub = child.find('bndbox')  # 找到框的标注值并进行读取
        xmin = float(sub[0].text)
        ymin = float(sub[1].text)
        xmax = float(sub[2].text)
        ymax = float(sub[3].text)
        points = [[xmin, ymin], [xmax, ymax]]
        itemData = {'points': []}
        itemData['points'].extend(points)
        name = child.find("name").text
        itemData["flag"] = {}
        itemData["group_id"] = None
        itemData["shape_type"] = "rectangle"
        itemData["label"] = name
        data["shapes"].append(itemData)
    data['imageWidth'] = width
    data['imageHeight'] = height
    data['imageData'] = None
    data['imagePath'] = filename

    filename, extension = os.path.splitext(file)
    jsonName = ".".join([filename, "json"])
    # 写入json
    json_path = os.path.join(save_path, jsonName)
    with open(json_path, "w") as f:
        json.dump(data, f)
    print(json_path, "加载入文件完成...")


if __name__ == '__main__':
    xml_path = r"C:\Users\JoelYang\Desktop\111111\bbox_20230417_gjx"
    # save_path = r"C:\Users\JoelYang\Desktop\111111\bbox_20230417_gjx"
    save_path = xml_path
    for root, dirs, files in os.walk(xml_path):
        for file in files:
            if not file.endswith(".xml"):
                continue
            xml_transform_json(root, file, save_path)

效果如下:

xml文件
在这里插入图片描述
转后的json文件

在这里插入图片描述

(3)VOC xml格式转yolo txt格式

import os
import xml.etree.ElementTree as ET

# VOC数据集路径
voc_ann_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\chepai_OCR\data\traindata\xml"
voc_img_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\chepai_OCR\data\traindata\VOC\images\train\images\train"
# YOLO数据集路径
yolo_out_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\chepai_OCR\data\traindata\txt"
# VOC类别名称和对应的编号
classes = {"blue": 0, "green": 1}  # 根据实际情况修改
# 遍历VOC数据集文件夹
for filename in os.listdir(voc_ann_path):
    # 解析XML文件
    tree = ET.parse(os.path.join(voc_ann_path, filename))
    root = tree.getroot()
    # 获取图片尺寸
    size = root.find("size")
    width = int(size.find("width").text)
    height = int(size.find("height").text)
    # 创建YOLO标注文件
    yolo_filename = filename.replace(".xml", ".txt")
    yolo_file = open(os.path.join(yolo_out_path, yolo_filename), "w")
    # 遍历XML文件中的所有目标
    for obj in root.findall("object"):
        # 获取目标类别名称和边界框坐标
        name = obj.find("name").text
        xmin = int(obj.find("bndbox").find("xmin").text)
        ymin = int(obj.find("bndbox").find("ymin").text)
        xmax = int(obj.find("bndbox").find("xmax").text)
        ymax = int(obj.find("bndbox").find("ymax").text)
        # 计算边界框中心点坐标和宽高
        x = (xmin + xmax) / 2 / width
        y = (ymin + ymax) / 2 / height
        w = (xmax - xmin) / width
        h = (ymax - ymin) / height
        # 将目标写入YOLO标注文件
        class_id = classes[name]
        yolo_file.write(f"{class_id} {x} {y} {w} {h}\n")
    yolo_file.close()

划分为 train、val、test且生成为yolo训练集的文件夹结构

import os
import random
import shutil
import xml.etree.ElementTree as ET


########################################################################
# 只需修改voc_ann_path、voc_img_path、yolo_out_path三个路径,自动生成为yolo
# 训练格式的数据集
########################################################################
# VOC数据集路径
voc_ann_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\datasets\VOC2012\VOCtrainval_11-May-2012\VOCdevkit\VOC2012\Annotations"
voc_img_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\datasets\VOC2012\VOCtrainval_11-May-2012\VOCdevkit\VOC2012\JPEGImages"
# YOLO数据集路径
yolo_out_path = r"D:\lg\BaiduSyncdisk\project\person_code\project_self\25_person_detect\data\yolo"

train_img_path = os.path.join(yolo_out_path, 'train', 'images', 'train')
train_labels_path = train_img_path.replace('images', 'labels')
test_img_path = os.path.join(yolo_out_path, 'test', 'images', 'test')
test_labels_path = test_img_path.replace('images', 'labels')
val_img_path = os.path.join(yolo_out_path, 'val', 'images', 'val')
val_labels_path = val_img_path.replace('images', 'labels')

# 类别,只需填写自己需要的类别,会自动过滤不需要的类别
classes = {"person": 0}  # 根据实际情况修改

# 随机划分比例
train_ratio = 0.8
val_ratio = 0.1
test_ratio = 0.1

# 获取所有文件名
all_files = os.listdir(voc_ann_path)
# 随机打乱文件顺序
random.shuffle(all_files)

# 计算划分的索引位置
train_index = int(len(all_files) * train_ratio)
val_index = train_index + int(len(all_files) * val_ratio)

# 创建保存txt文件的文件夹
os.makedirs(train_labels_path, exist_ok=True)
os.makedirs(test_labels_path, exist_ok=True)
os.makedirs(val_labels_path, exist_ok=True)

# 创建保存图片的文件夹
os.makedirs(train_img_path, exist_ok=True)
os.makedirs(test_img_path, exist_ok=True)
os.makedirs(val_img_path, exist_ok=True)

# 遍历VOC数据集文件夹
for i, filename in enumerate(all_files):
    # 解析XML文件
    tree = ET.parse(os.path.join(voc_ann_path, filename))
    root = tree.getroot()
    # 获取图片尺寸
    size = root.find("size")
    width = int(size.find("width").text)
    height = int(size.find("height").text)
    # 创建YOLO标注文件
    yolo_filename = filename.replace(".xml", ".txt")
    if i < train_index:
        yolo_file_path = os.path.join(train_labels_path, yolo_filename)
        yolo_file = open(yolo_file_path, "w")
        img_dest_path = train_img_path

    elif i < val_index:
        yolo_file_path = os.path.join(val_labels_path, yolo_filename)
        yolo_file = open(yolo_file_path, "w")
        img_dest_path = val_img_path
    else:
        yolo_file_path = os.path.join(test_labels_path, yolo_filename)
        yolo_file = open(yolo_file_path, "w")
        img_dest_path = test_img_path

    # 遍历XML文件中的所有目标
    for obj in root.findall("object"):
        # 获取目标类别名称和边界框坐标
        name = obj.find("name").text
        xmin = int(float(obj.find("bndbox").find("xmin").text))
        ymin = int(float(obj.find("bndbox").find("ymin").text))
        xmax = int(float(obj.find("bndbox").find("xmax").text))
        ymax = int(float(obj.find("bndbox").find("ymax").text))
        # 计算边界框中心点坐标和宽高
        x = (xmin + xmax) / 2 / width
        y = (ymin + ymax) / 2 / height
        w = (xmax - xmin) / width
        h = (ymax - ymin) / height
        # 过滤不需要的类别
        if name not in classes.keys():
            continue
        # 将目标写入YOLO标注文件
        class_id = classes[name]
        yolo_file.write(f"{class_id} {x} {y} {w} {h}\n")

    yolo_file.close()
    # 删除生成的txt文件,不然也是空的
    if os.path.getsize(yolo_file_path) == 0:
        os.remove(yolo_file_path)
        continue
    # 复制图片到对应的划分文件夹
    img_filename = filename.replace(".xml", ".jpg")
    img_src_path = os.path.join(voc_img_path, img_filename)
    shutil.copy(img_src_path, img_dest_path)




(4) txt转xml

import os
import cv2


def txt_to_xml(txt_path, img_path, xml_path):
    # 1.字典对标签中的类别进行转换
    dict = {'0': "pothole",
            '1': "alligator cracking",
            '2': "lateral cracking",
            '3': "longitudinal cracking"}
    # 2.找到txt标签文件夹
    files = os.listdir(txt_path)
    # 用于存储 "老图"
    pre_img_name = ''
    # 3.遍历文件夹
    for i, name in enumerate(files):
        image_name, ext = os.path.splitext(name)
        if ext != '.txt' or name == 'classes.txt':
            continue
        print(name)
        # 4.打开txt
        txtFile = open(txt_path + name, encoding='utf')
        # 读取所有内容
        txtList = txtFile.readlines()
        # 读取图片名称
        img_name = name.split(".")[0]
        pic = cv2.imread(img_path + img_name + ".jpeg")
        # 获取图像大小信息
        Pheight, Pwidth, Pdepth = pic.shape
        # 5.遍历txt文件中每行内容
        for row in txtList:
            # 按' '分割txt的一行的内容
            oneline = row.strip().split(" ")
            # 遇到的是一张新图片
            if img_name != pre_img_name:
                # 6.新建xml文件
                xml_file = open((xml_path + img_name + '.xml'), 'w')
                xml_file.write('<annotation>\n')
                xml_file.write('    <folder>VOC2007</folder>\n')
                xml_file.write('    <filename>' + img_name + '.jpg' + '</filename>\n')
                xml_file.write('<source>\n')
                xml_file.write('<database>orgaquant</database>\n')
                xml_file.write('<annotation>organoids</annotation>\n')
                xml_file.write('</source>\n')
                xml_file.write('    <size>\n')
                xml_file.write('        <width>' + str(Pwidth) + '</width>\n')
                xml_file.write('        <height>' + str(Pheight) + '</height>\n')
                xml_file.write('        <depth>' + str(Pdepth) + '</depth>\n')
                xml_file.write('    </size>\n')
                xml_file.write('    <object>\n')
                xml_file.write('<name>' + dict[oneline[0]] + '</name>\n')
                xml_file.write('        <bndbox>\n')
                xml_file.write('            <xmin>' + str(
                    int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)) + '</xmin>\n')
                xml_file.write('            <ymin>' + str(
                    int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)) + '</ymin>\n')
                xml_file.write('            <xmax>' + str(
                    int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)) + '</xmax>\n')
                xml_file.write('            <ymax>' + str(
                    int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)) + '</ymax>\n')
                xml_file.write('        </bndbox>\n')
                xml_file.write('    </object>\n')
                xml_file.close()
                pre_img_name = img_name  # 将其设为"老"图
            else:  # 不是新图而是"老图"
                # 7.同一张图片,只需要追加写入object
                xml_file = open((xml_path + img_name + '.xml'), 'a')
                xml_file.write('    <object>\n')
                xml_file.write('<name>' + dict[oneline[0]] + '</name>\n')
                '''  按需添加这里和上面
                xml_file.write('        <pose>Unspecified</pose>\n')
                xml_file.write('        <truncated>0</truncated>\n')
                xml_file.write('        <difficult>0</difficult>\n')
                '''
                xml_file.write('        <bndbox>\n')
                xml_file.write('            <xmin>' + str(
                    int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)) + '</xmin>\n')
                xml_file.write('            <ymin>' + str(
                    int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)) + '</ymin>\n')
                xml_file.write('            <xmax>' + str(
                    int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)) + '</xmax>\n')
                xml_file.write('            <ymax>' + str(
                    int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)) + '</ymax>\n')
                xml_file.write('        </bndbox>\n')
                xml_file.write('    </object>\n')
                xml_file.close()

        # 8.读完txt文件最后写入</annotation>
        xml_file1 = open((xml_path + pre_img_name + '.xml'), 'a')
        xml_file1.write('</annotation>')
        xml_file1.close()
    print("Done !")


# 修改成自己的文件夹 注意文件夹最后要加上/
if __name__ == '__main__':
    input_txt_dir = r'D:\lg\BaiduSyncdisk\project\person_code\project_self\datasets\road\archive\\'
    image_dir = r'D:\lg\BaiduSyncdisk\project\person_code\project_self\datasets\road\archive\\'
    output_xml_dir = r'C:\Users\hzy\Downloads\111\\'

    txt_to_xml(input_txt_dir, image_dir, output_xml_dir)

5. 划分分类数据集

import os
import shutil
import random

# 数据集根目录
dataset_dir = r'D:\lg\BaiduSyncdisk\project\person_code\project_self\19_corn_disease\data\corn'
output_dir = r'D:\lg\BaiduSyncdisk\project\person_code\project_self\19_corn_disease\data\traindata'
# 划分比例
train_ratio = 0.8  # 训练集比例
test_ratio = 0.1  # 测试集比例
val_ratio = 0.1  # 验证集比例

# 创建目标文件夹
train_dir = os.path.join(output_dir, 'train')
test_dir = os.path.join(output_dir, 'test')
val_dir = os.path.join(output_dir, 'val')

os.makedirs(train_dir, exist_ok=True)
os.makedirs(test_dir, exist_ok=True)
os.makedirs(val_dir, exist_ok=True)

# 遍历数据集文件夹
for category in os.listdir(dataset_dir):
    category_dir = os.path.join(dataset_dir, category)
    if os.path.isdir(category_dir):
        # 创建目标类别文件夹
        train_category_dir = os.path.join(train_dir, category)
        test_category_dir = os.path.join(test_dir, category)
        val_category_dir = os.path.join(val_dir, category)
        os.makedirs(train_category_dir, exist_ok=True)
        os.makedirs(test_category_dir, exist_ok=True)
        os.makedirs(val_category_dir, exist_ok=True)

        # 获取类别下的所有文件
        files = os.listdir(category_dir)
        random.shuffle(files)

        # 计算划分的索引
        train_split = int(train_ratio * len(files))
        test_split = int((train_ratio + test_ratio) * len(files))

        # 将文件复制到对应的目标文件夹
        for i, file in enumerate(files):
            file_path = os.path.join(category_dir, file)
            if i < train_split:
                shutil.copy(file_path, os.path.join(train_category_dir, file))
            elif i < test_split:
                shutil.copy(file_path, os.path.join(test_category_dir, file))
            else:
                shutil.copy(file_path, os.path.join(val_category_dir, file))

  • 2
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Python图像识别

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值