Labelme与YOLO标签格式互转,含实例分割和目标检测,轻松实现数据扩充

最近在进行目标检测和实例分割任务时,对无聊的标注工作十分苦恼。由于Labelme标注的数据格式是json格式,但YOLO使用的格式是txt格式,于是需要格式转换,但无奈转换工作太繁琐,而且txt格式无法直接被labelme软件使用,再加上YOLO目标检测和实例分割使用的txt标签方式不完全相同,于是下定决心实现格式互转。

通过这个方法,先用Labelme标注少量图片,再用YOLO训练得到一个初步的检测模型,然后把结果保存为txt,再将结果转化为Labelme可编辑的json格式,对标注进行微调即可,从而避免了大量重复标注带来的麻烦。

下面是具体代码:

一、Labelme标签转YOLO目标检测标签

import json
import os

def labelme2yolo_det(class_name, json_dir, labels_dir):

    list_labels = []  # 存放json文件的列表
    # 0.创建保存转换结果的文件夹
    if (not os.path.exists(labels_dir)):
        os.mkdir(labels_dir)

    # 1.获取目录下所有的labelme标注好的Json文件,存入列表中
    for files in os.listdir(json_dir):  # 遍历json文件夹下的所有json文件
        file = os.path.join(json_dir, files)  # 获取一个json文件
        list_labels.append(file)  # 将json文件名加入到列表中

    for labels in list_labels:  # 遍历所有json文件
        with open(labels, "r") as f:
            file_in = json.load(f)
            shapes = file_in["shapes"]

        txt_filename = os.path.basename(labels).replace(".json", ".txt")
        txt_path = os.path.join(labels_dir, txt_filename)  # 使用labels_dir变量指定保存路径

        with open(txt_path, "w+") as file_handle:
            for shape in shapes:
                line_content = []  # 初始化一个空列表来存储每个形状的坐标信息
                line_content.append(str(class_name.index(shape['label'])))  # 添加类别索引
                [[x1, y1], [x2, y2]] = shape['points']
                x1, x2 = x1 / file_in['imageWidth'], x2 / file_in['imageWidth']
                y1, y2 = y1 / file_in['imageHeight'], y2 / file_in['imageHeight']
                cx, cy = (x1 + x2) / 2, (y1 + y2) / 2  # 中心点归一化的x坐标和y坐标
                wi, hi = abs(x2 - x1), abs(y2 - y1)  # 归一化的目标框宽度w,高度h
                line_content.append(str(cx))
                line_content.append(str(cy))
                line_content.append(str(wi))
                line_content.append(str(hi))
                # 使用空格连接列表中的所有元素,并写入文件
                file_handle.write(" ".join(line_content) + "\n")

        print("转换完成:", txt_filename)

二、Labelme标签转YOLO实例分割标签

import json
import os


def labelme2yolo_seg(class_name, json_dir, labels_dir):
    """
        此函数用来将labelme软件标注好的json格式转换为yolov_seg中使用的txt格式
        :param json_dir: labelme标注好的*.json文件所在文件夹
        :param labels_dir: 转换好后的*.txt保存文件夹
        :param class_name: 数据集中的类别标签
        :return:
    """
    list_labels = []  # 存放json文件的列表

    # 0.创建保存转换结果的文件夹
    if (not os.path.exists(labels_dir)):
        os.mkdir(labels_dir)

    # 1.获取目录下所有的labelme标注好的Json文件,存入列表中
    for files in os.listdir(json_dir):  # 遍历json文件夹下的所有json文件
        file = os.path.join(json_dir, files)  # 获取一个json文件
        list_labels.append(file)  # 将json文件名加入到列表中

    for labels in list_labels:  # 遍历所有json文件
        with open(labels, "r") as f:
            file_in = json.load(f)
            shapes = file_in["shapes"]
            print(labels)

        txt_filename = os.path.basename(labels).replace(".json", ".txt")
        txt_path = os.path.join(labels_dir, txt_filename)  # 使用labels_dir变量指定保存路径

        with open(txt_path, "w+") as file_handle:
            for shape in shapes:
                line_content = []  # 初始化一个空列表来存储每个形状的坐标信息
                line_content.append(str(class_name.index(shape['label'])))  # 添加类别索引
                # 添加坐标信息
                for point in shape["points"]:
                    x = point[0] / file_in["imageWidth"]
                    y = point[1] / file_in["imageHeight"]
                    line_content.append(str(x))
                    line_content.append(str(y))
                # 使用空格连接列表中的所有元素,并写入文件
                file_handle.write(" ".join(line_content) + "\n")

三、YOLO实例分割标签转Labelme标签

import os
import glob
import numpy as np
import cv2
import json
# 可以将yolov8实例分割生成的txt格式的标注转为json,可以使用labelme查看标注
# 该方法可以用于辅助数据标注
def convert_txt_to_labelme_json(txt_path, image_path, output_dir, class_name, image_fmt='.jpg' ):
    """
    将文本文件转换为LabelMe格式的JSON文件。
    此函数处理文本文件中的数据,将其转换成LabelMe标注工具使用的JSON格式。包括读取图像,
    解析文本文件中的标注信息,并生成相应的JSON文件。
    :param txt_path: 文本文件所在的路径
    :param image_path: 图像文件所在的路径
    :param output_dir: 输出JSON文件的目录
    :param class_name: 类别名称列表,索引对应类别ID
    :param image_fmt: 图像文件格式,默认为'.jpg'
    :return:
    """
    # 获取所有文本文件路径
    txts = glob.glob(os.path.join(txt_path, "*.txt"))
    for txt in txts:
        # 初始化LabelMe JSON结构
        labelme_json = {
            'version': '5.5.0',  # labelme版本号
            'flags': {},
            'shapes': [],
            'imagePath': None,
            'imageData': None,
            'imageHeight': None,
            'imageWidth': None,
        }
        # 获取文本文件名
        txt_name = os.path.basename(txt)
        # 根据文本文件名生成对应的图像文件名
        image_name = txt_name.split(".")[0] + image_fmt
        labelme_json['imagePath'] = image_name
        # 构造完整图像路径
        image_name = os.path.join(image_path, image_name)
        # 检查图像文件是否存在,如果不存在则抛出异常
        if not os.path.exists(image_name):
            raise Exception('txt 文件={},找不到对应的图像={}'.format(txt, image_name))
        # 读取图像
        image = cv2.imdecode(np.fromfile(image_name, dtype=np.uint8), cv2.IMREAD_COLOR)
        # 获取图像高度和宽度
        h, w = image.shape[:2]
        labelme_json['imageHeight'] = h
        labelme_json['imageWidth'] = w
        # 读取文本文件内容
        with open(txt, 'r') as t:
            lines = t.readlines()
            for line in lines:
                point_list = []
                content = line.split(' ')
                # 根据类别ID获取标签名称
                label = class_name[int(content[0])]  # 标签
                # 解析点坐标
                for index in range(1, len(content)):
                    if index % 2 == 1:  # 下标为奇数,对应横坐标
                        x = (float(content[index])) * w
                        point_list.append(x)
                    else:  # 下标为偶数,对应纵坐标
                        y = (float(content[index])) * h
                        point_list.append(y)
                # 将点列表转换为二维列表,每两个值表示一个点
                point_list = [point_list[i:i+2] for i in range(0, len(point_list), 2)]
                # 构造shape字典
                shape = {
                    'label': label,
                    'points': point_list,
                    'group_id': None,
                    'description': None,
                    'shape_type': 'polygon',
                    'flags': {},
                    'mask': None
                }
                labelme_json['shapes'].append(shape)
            # 生成JSON文件名
            json_name = txt_name.split('.')[0] + '.json'
            json_name_path = os.path.join(output_dir, json_name)
            # 写入JSON文件
            fd = open(json_name_path, 'w')
            json.dump(labelme_json, fd, indent=2)
            fd.close()
            # 输出保存信息
            print("save json={}".format(json_name_path))

if __name__ == '__main__':
    txt_path = r'../test_json'
    image_path = r'../test_json'
    output_dir = r'../test_json'
    # 标签列表
    class_name = ['box_top', 'tape', 'tape_defect', 'box_bottom']  # 标签类别名
    convert_txt_to_labelme_json(txt_path, image_path, output_dir, class_name)

四、YOLO目标检测标签转Labelme标签

import os
import glob
import numpy as np
import cv2
import json
# 可以将yolov8目标检测生成的txt格式的标注转为json,可以使用labelme查看标注
# 该方法可以用于辅助数据标注
def convert_txt_to_labelme_json(txt_path, image_path, output_dir, class_name, image_fmt='.jpg' ):
    """
    将文本文件转换为LabelMe格式的JSON文件。
    此函数处理文本文件中的数据,将其转换成LabelMe标注工具使用的JSON格式。包括读取图像,
    解析文本文件中的标注信息,并生成相应的JSON文件。
    :param txt_path: 文本文件所在的路径
    :param image_path: 图像文件所在的路径
    :param output_dir: 输出JSON文件的目录
    :param class_name: 类别名称列表,索引对应类别ID
    :param image_fmt: 图像文件格式,默认为'.jpg'
    :return:
    """
    # 获取所有文本文件路径
    txts = glob.glob(os.path.join(txt_path, "*.txt"))
    for txt in txts:
        # 初始化LabelMe JSON结构
        labelme_json = {
            'version': '5.5.0',
            'flags': {},
            'shapes': [],
            'imagePath': None,
            'imageData': None,
            'imageHeight': None,
            'imageWidth': None,
        }
        # 获取文本文件名
        txt_name = os.path.basename(txt)
        # 根据文本文件名生成对应的图像文件名
        image_name = txt_name.split(".")[0] + image_fmt
        labelme_json['imagePath'] = image_name
        # 构造完整图像路径
        image_name = os.path.join(image_path, image_name)
        # 检查图像文件是否存在,如果不存在则抛出异常
        if not os.path.exists(image_name):
            raise Exception('txt 文件={},找不到对应的图像={}'.format(txt, image_name))
        # 读取图像
        image = cv2.imdecode(np.fromfile(image_name, dtype=np.uint8), cv2.IMREAD_COLOR)
        # 获取图像高度和宽度
        h, w = image.shape[:2]
        labelme_json['imageHeight'] = h
        labelme_json['imageWidth'] = w
        # 读取文本文件内容
        with open(txt, 'r') as t:
            lines = t.readlines()
            for line in lines:
                point_list = []
                content = line.split(' ')
                # 根据类别ID获取标签名称
                label = class_name[int(content[0])]  # 标签
                # 解析点坐标
                for index in range(1, len(content)):
                    if index == 1:  # 中心点归一化的x坐标
                        cx = float(content[index])
                    if index == 2:  # 中心点归一化的y坐标
                        cy = float(content[index])
                    if index == 3:  # 归一化的目标框宽度
                        wi = float(content[index])
                    if index == 4:  # 归一化的目标框高度
                        hi = float(content[index])
                x1 = (2 * cx * w - w * wi) / 2
                x2 = (w * wi + 2 * cx * w) / 2
                y1 = (2 * cy * h - h * hi) / 2
                y2 = (h * hi + 2 * cy * h) / 2
                point_list.append(x1)
                point_list.append(y1)
                point_list.append(x2)
                point_list.append(y2)
                # 将点列表转换为二维列表,每两个值表示一个点
                point_list = [point_list[i:i+2] for i in range(0, len(point_list), 2)]
                # 构造shape字典
                shape = {
                    'label': label,
                    'points': point_list,
                    'group_id': None,
                    'description': None,
                    'shape_type': 'rectangle',
                    'flags': {},
                    'mask': None
                }
                labelme_json['shapes'].append(shape)
            # 生成JSON文件名
            json_name = txt_name.split('.')[0] + '.json'
            json_name_path = os.path.join(output_dir, json_name)
            # 写入JSON文件
            fd = open(json_name_path, 'w')
            json.dump(labelme_json, fd, indent=2)
            fd.close()
            # 输出保存信息
            print("save json={}".format(json_name_path))

if __name__ == '__main__':
    txt_path = r'../test_json'
    image_path = r'../test_json'
    output_dir = r'../test_json'
    # 标签列表
    class_name = ['logo', 'spec', 'anniversary_logo']  # 标签类别名
    convert_txt_to_labelme_json(txt_path, image_path, output_dir, class_name)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值