labelme的json标签和图像改变分辨率,再将json转换为YOLO的txt格式进行实例分割

最近在做一个分割数据集,训练数据时由于图像数据太大一直爆显存,然后就找了找同时resize图像和json的脚本,然后转换为YOLO格式一直出问题,标签和目标位置对不上,也是困扰了好久,终于解决,记录一下。

首先是resize图像和json,下面是找到的一个脚本,他可以自定义宽高其中一个,另一个根据比例改变,参考的这个链接对labelme已经标注的图片和json文件做resize操作_Yee_Ko的博客-CSDN博客

这个src_dir和 dst_dir是处理前后的路径,src_dir里要同时放图像和json,w_new = “”这里改你想要的大小

import cv2
import os
import glob
import json
import collections
import numpy as np
from labelme import utils
#这个文件可以更改图像和json文件的分辨率,但是是宽高改一个,另一个比例改,改后的json文件图像的宽高有问题
#需要用到json_hw更改
if __name__ == "__main__":
    src_dir = r'E:\seg_resize\before'
    dst_dir = r'E:\seg_resize\after'

    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    # 先收集一下文件夹中图片的格式列表,例如 ['.jpg', '.JPG']
    exts = dict()
    filesnames = os.listdir(src_dir)
    for filename in filesnames:
        name, ext = filename.split('.')
        if ext != 'json':
            if exts.__contains__(ext):
                exts[ext] += 1
            else:
                exts[ext] = 1

    anno = collections.OrderedDict()  # 这个可以保证保存的字典顺序和读取出来的是一样的,直接使用dict()的话顺序会很乱(小细节哦)
    for key in exts.keys():
        for img_file in glob.glob(os.path.join(src_dir, '*.' + key)):
            file_name = os.path.basename(img_file)
            print(f"Processing {file_name}")
            img = cv2.imread(img_file)
            (h, w, c) = img.shape  # 统计了一下,所有图片的宽度里面,1344是占比较多的宽度中最小的那个,因此
            # 都等比例地将宽resize为1344(这里可以自己修改)
            w_new = 1440
            h_new = int(h / w * w_new)  # 高度等比例缩放
            ratio = w_new / w  # 标注文件里的坐标乘以这个比例便可以得到新的坐标值
            img_resize = cv2.resize(img, (w_new, h_new))  # resize中的目标尺寸参数为(width, height)
            cv2.imwrite(os.path.join(dst_dir, file_name), img_resize)

            # 接下来处理标注文件json中的标注点的resize
            json_file = os.path.join(src_dir, file_name.split('.')[0] + '.json')
            save_to = open(os.path.join(dst_dir, file_name.split('.')[0] + '.json'), 'w')
            with open(json_file, 'rb') as f:
                anno = json.load(f)
                for shape in anno["shapes"]:
                    points = shape["points"]
                    points = (np.array(points) * ratio).astype(int).tolist()
                    shape["points"] = points

                # 注意下面的img_resize编码加密之前要记得将通道顺序由BGR变回RGB
                anno['imageData'] = str(utils.img_arr_to_b64(img_resize[..., (2, 1, 0)]), encoding='utf-8')
                json.dump(anno, save_to, indent=4)
    print("Done")

但是用这个脚本处理后,我想使用YOLOv8做实例分割,后来发现怎么也转换不对,经过别人提醒,原因是json转txt的代码中获取的是json中的图像长宽计算的,这个脚本并没有改变json中记录的原图像长宽,所以写了一个文件根据resize后的图像长宽去改json中的值,命名为json_hw.py,下边代码中的json_dir和image_dir要放你的json和resize后图像的路径,到这步就改完了json文件了。

import json
import os
from PIL import Image
#这是一个调整图像分辨率后,但是json文件记录的高宽没有改变,使用这个脚本可以将json文件中的图像高宽改为实际图像高宽


def update_image_resolution(json_dir, image_dir):
    json_files = os.listdir(json_dir)

    for json_file in json_files:
        json_path = os.path.join(json_dir, json_file)
        image_path = os.path.join(image_dir, os.path.splitext(json_file)[0] + '.jpg')

        # 读取JSON文件
        with open(json_path, 'r') as f:
            data = json.load(f)

        # 获取图像的实际大小
        image = Image.open(image_path)
        width, height = image.size

        # 更新JSON文件中的分辨率字段
        data['imageHeight'] = height
        data['imageWidth'] = width

        # 保存更新后的JSON文件
        with open(json_path, 'w') as f:
            json.dump(data, f, indent=4)


if __name__ == "__main__":
    json_dir = r'E:\yolov5-master\mydata\json'
    image_dir = r'E:\yolov5-master\mydata\images'

    update_image_resolution(json_dir, image_dir)

然后随便找了一个json转YOLO的txt格式的脚本,这个一搜很多,我这里随便放一个,json-dir是你json文件的保存路径,save-dir是txt文件的保存路径,classes是你的数据集中的类别,我这代码中写了三类,分别是bud,inflorescence,branch,这三类的顺序和你YOLOv5或者v8的数据集的yaml文件对应好顺序就行,v5和v8都适用,我是用的下面这位博主提供的脚本,这个就是获取json中的图像长宽,也是resize后一直出问题的原因:h, w = json_dict['imageHeight'], json_dict['imageWidth']

YOLOv8实例分割训练自己的数据集保姆级教程_dg68668的博客-CSDN博客

# -*- coding: utf-8 -*-
import json
import os
import argparse
from tqdm import tqdm
from PIL import Image

#这是一个将json文件转换为YOLO格式的txt文件的脚本
def convert_label_json(json_dir, save_dir, classes):
    json_paths = os.listdir(json_dir)
    classes = classes.split(',')


    for json_path in tqdm(json_paths):
        # for json_path in json_paths:
        path = os.path.join(json_dir, json_path)
        with open(path, 'r') as load_f:
            json_dict = json.load(load_f)

        h, w = json_dict['imageHeight'], json_dict['imageWidth']

        # save txt path
        txt_path = os.path.join(save_dir, json_path.replace('json', 'txt'))
        txt_file = open(txt_path, 'w')

        for shape_dict in json_dict['shapes']:
            label = shape_dict['label']
            label_index = classes.index(label)
            points = shape_dict['points']

            points_nor_list = []

            for point in points:
                points_nor_list.append(point[0] / w)
                points_nor_list.append(point[1] / h)

            points_nor_list = list(map(lambda x: str(x), points_nor_list))
            points_nor_str = ' '.join(points_nor_list)

            label_str = str(label_index) + ' ' + points_nor_str + '\n'
            txt_file.writelines(label_str)


if __name__ == "__main__":
    """
    python json2txt_nomalize.py --json-dir my_datasets/color_rings/jsons --save-dir my_datasets/color_rings/txts --classes "cat,dogs"
    """
    parser = argparse.ArgumentParser(description='json convert to txt params')
    parser.add_argument('--json-dir', type=str, default=r'E:\ultralytics-main\ultralytics\mydata\json', help='json path dir')
    parser.add_argument('--save-dir', type=str, default=r'E:\ultralytics-main\ultralytics\mydata\txt', help='txt save dir')
    parser.add_argument('--classes', type=str, default='bud,inflorescence,branch', help='classes')
    args = parser.parse_args()
    json_dir = args.json_dir
    save_dir = args.save_dir
    classes = args.classes
    convert_label_json(json_dir, save_dir, classes)

这个一整套下来就很轻松地将labelme的标签转换成YOLOv5(v5要7.0版本)或者v8需要的分割数据集了,各位加油,如果有帮助就给点个赞吧!!!

猜你喜欢

转载自blog.csdn.net/love_respect/article/details/131529790
今日推荐