labelme的json文件转换为VOC数据集的png

Posted qq_43718758

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了labelme的json文件转换为VOC数据集的png相关的知识,希望对你有一定的参考价值。

文章目录

一、json转png

打开anaconda prompt,激活labelme的环境
输入labelme_json_to_dataset F:\\PROJECT\\xxx\\New_data\\410.json可以直接转换
生成了1个文件夹,有5个文件:

注:我测试的json数据中,有两个版本分别为3.16.7和5.0.1
使用2个版本的labelme环境对两个版本的json都可以进行转换
故,可以理解为:不需要考虑版本不匹配的问题

但是当我们生成了多个png后,发现了一个问题:

但是,在这两张照片中,可以看到对于不同的标签,颜色竟然也是一样的,这显然是不对的。

同样的,在同一个标签中。
如下图所示,当我们把左图中红色的标签删去,可以发现,另外一个标签变成了红色,这个标签在之前是绿色的

因此,我们推断,标签的颜色,是根据出现的顺序了分配的。

为了更加规范我们的数据集,我们需要把json数据转换成VOC格式的png数据
因为在voc的语义分割数据集中,我们可以发现,同一类的物体,在所有图片中的颜色都是相同的。

二、json转换为VOC数据

1、在GitHub下载官方工具,对官方数据进行转换

在github上链接:https://github.com/zhong110020/labelme/tree/master/examples/semantic_segmentation
文件路径:labelme/examples/semantic_segmentation/

在pycharm中打开,选择conda打开。

labelme5.0.1


可以看到,我选择了labelme版本是5.0.1,并且把data_annotated、labelme2voc.py、labels.txt放到同一文件夹下
修改参数:data_annotated data_dataset_voc --labels labels.txt

运行:

D:\\Anaconda3\\envs\\labelme5.0.1\\python.exe F:/VOC/Json_Voc/labelme2voc.py data_annotated data_dataset_voc --labels labels.txt
Creating dataset: data_dataset_voc
class_names: ('_background_', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor')
Saved class_names: data_dataset_voc\\class_names.txt
Traceback (most recent call last):
  File "F:/VOC/Json_Voc/labelme2voc.py", line 95, in <module>
    main()
  File "F:/VOC/Json_Voc/labelme2voc.py", line 56, in main
    colormap = labelme.utils.label_colormap(255)
AttributeError: module 'labelme.utils' has no attribute 'label_colormap'

Process finished with exit code 1

问题原因:版本不匹配

labelme3.16.7

解决办法:我更换了环境,pytorch01,这里安装的labelme版本为3.16.7

再次运行:

C:\\Users\\zhw\\.conda\\envs\\pytorch01\\python.exe F:/VOC/Json_Voc/labelme2voc.py data_annotated data_dataset_voc --labels labels.txt
Output directory already exists: data_dataset_voc

Process finished with exit code 1

解决:我们只需要把刚刚生成的data_dataset_voc删除即可(运行失败但也会生成文件夹)(在之后的运行中,每次报错修改后都需要删除文件夹后再运行!)
再次运行:

C:\\Users\\zhw\\.conda\\envs\\pytorch01\\python.exe F:/VOC/Json_Voc/labelme2voc.py data_annotated data_dataset_voc --labels labels.txt
Creating dataset: data_dataset_voc
class_names: ('_background_', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor')
Saved class_names: data_dataset_voc\\class_names.txt
Generating dataset from: data_annotated\\2011_000003.json
Generating dataset from: data_annotated\\2011_000006.json
Generating dataset from: data_annotated\\2011_000025.json

Process finished with exit code 0

2、使用官方py文件转换自己的数据集

将自己的数据放到文件夹下

运行:

Traceback (most recent call last):
  File "F:/VOC/Json_Voc/labelme2voc.py", line 96, in <module>
    main()
  File "F:/VOC/Json_Voc/labelme2voc.py", line 78, in main
    img = np.asarray(PIL.Image.open(img_file))
  File "C:\\Users\\zhw\\.conda\\envs\\pytorch01\\lib\\site-packages\\PIL\\Image.py", line 3092, in open
    fp = builtins.open(filename, "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'data_annotated\\\\2011_000003.jpg'

Process finished with exit code 1

报错原因:在标注时,是用图片.bmp标注的,所以在json中的imagePath值为图片.bmp,但是文件夹中只有jpg,所以报错。
因为在实验过程中,标注实验的人可能使用了jpg,也可能使用bmp。
所以我修改代码:

 			img_file = osp.join(osp.dirname(label_file), data['imagePath'])
            
            if os.path.exists(img_file)==0:
                print(img_file,"不存在!","查看是否有jpg格式的图片")
                img_file=img_file[:-3]+"jpg"
                if os.path.exists(img_file)==1:
                    print(img_file,"已在文件中找到!")
                print("img_file:",img_file)#这句话之后删掉了

            img = np.asarray(PIL.Image.open(img_file))


运行
报错:

Traceback (most recent call last):
  File "F:/VOC/Json_Voc/labelme2voc.py", line 101, in <module>
    main()
  File "F:/VOC/Json_Voc/labelme2voc.py", line 89, in main
    label_name_to_value=class_name_to_id,
  File "C:\\Users\\zhw\\.conda\\envs\\pytorch01\\lib\\site-packages\\labelme\\utils\\shape.py", line 67, in shapes_to_label
    cls_id = label_name_to_value[cls_name]
KeyError: 'O-Around the characteristics 8 dry'

Process finished with exit code 1

需要修改labels.txt,标签改为自己数据集的名称

再次运行:成功!

C:\\Users\\zhw\\.conda\\envs\\pytorch01\\python.exe F:/VOC/Json_Voc/labelme2voc.py data_annotated data_dataset_voc --labels labels.txt
Creating dataset: data_dataset_voc
class_names: ('_background_', 'O-Around the characteristics 8 dry', 'O-Surrounding characteristics 9 desquamation', 'H-Wound bed', 'O-Surrounding characteristics 6 pigmentation', 'H-Wound bed yellow 3 (75%)')
Saved class_names: data_dataset_voc\\class_names.txt
Generating dataset from: data_annotated\\250.json
data_annotated\\250.bmp 不存在! 查看是否有jpg格式的图片
data_annotated\\250.jpg 已在文件中找到!
Generating dataset from: data_annotated\\251.json
data_annotated\\251.bmp 不存在! 查看是否有jpg格式的图片
data_annotated\\251.jpg 已在文件中找到!
Generating dataset from: data_annotated\\252.json
data_annotated\\252.bmp 不存在! 查看是否有jpg格式的图片
data_annotated\\252.jpg 已在文件中找到!
Generating dataset from: data_annotated\\316.json
data_annotated\\316.BMP 不存在! 查看是否有jpg格式的图片
data_annotated\\316.jpg 已在文件中找到!

Process finished with exit code 0



可以看到,解决了我们在第一节中提到的:相同类别颜色却不同的这一问题!

注:在之前部署U-net模型时,其实转换还有其他的方法

labelme2voc.py源码(没有进行过修改的)

from __future__ import print_function

import argparse
import glob
import json
import os
import os.path as osp
import sys

import numpy as np
import PIL.Image

import labelme


def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser.add_argument('input_dir', help='input annotated directory')
    parser.add_argument('output_dir', help='output dataset directory')
    parser.add_argument('--labels', help='labels file', required=True)
    args = parser.parse_args()

    if osp.exists(args.output_dir):
        print('Output directory already exists:', args.output_dir)
        sys.exit(1)
    os.makedirs(args.output_dir)
    os.makedirs(osp.join(args.output_dir, 'JPEGImages'))
    os.makedirs(osp.join(args.output_dir, 'SegmentationClass'))
    os.makedirs(osp.join(args.output_dir, 'SegmentationClassPNG'))
    os.makedirs(osp.join(args.output_dir, 'SegmentationClassVisualization'))
    print('Creating dataset:', args.output_dir)

    class_names = []
    class_name_to_id = 
    for i, line in enumerate(open(args.labels).readlines()):
        class_id = i - 1  # starts with -1
        class_name = line.strip()
        class_name_to_id[class_name] = class_id
        if class_id == -1:
            assert class_name == '__ignore__'
            continue
        elif class_id == 0:
            assert class_name == '_background_'
        class_names.append(class_name)
    class_names = tuple(class_names)
    print('class_names:', class_names)
    out_class_names_file = osp.join(args.output_dir, 'class_names.txt')
    with open(out_class_names_file, 'w') as f:
        f.writelines('\\n'.join(class_names))
    print('Saved class_names:', out_class_names_file)

    colormap = labelme.utils.label_colormap(255)

    for label_file in glob.glob(osp.join(args.input_dir, '*.json')):
        print('Generating dataset from:', label_file)
        with open(label_file) as f:
            base = osp.splitext(osp.basename(label_file))[0]
            out_img_file = osp.join(
                args.output_dir, 'JPEGImages', base + '.jpg')
            out_lbl_file = osp.join(
                args.output_dir, 'SegmentationClass', base + '.npy')
            out_png_file = osp.join(
                args.output_dir, 'SegmentationClassPNG', base + '.png')
            out_viz_file = osp.join(
                args.output_dir,
                'SegmentationClassVisualization',
                base + '.jpg',
            )

            data = json.load(f)

            img_file = osp.join(osp.dirname(label_file), data['imagePath'])
            img = np.asarray(PIL.Image.open(img_file))
            PIL.Image.fromarray(img).save(out_img_file)

            lbl = labelme.utils.shapes_to_label(
                img_shape=img.shape,
                shapes=data['shapes'],
                label_name_to_value=class_name_to_id,
            )
            labelme.utils.lblsave(out_png_file, lbl)

            np.save(out_lbl_file, lbl)

            viz = labelme.utils.draw_label(
                lbl, img, class_names, colormap=colormap)
            PIL.Image.fromarray(viz).save(out_viz_file)


if __name__ == '__main__':
    main()

2.3 对数据集进行 训练/验证 的划分

主要工作就是生成了4个txt的文档,并且路径是按照voc的数据集修改的

注意:目前还没有对代码进行优化,因此在每次运行时,都需要把ImageSets全部删去再运行。

voc_annotation.py源码如下:若要使用该代码,若需要修改,则主要修改路径即可

import os
import random

import numpy as np
from PIL import Image
from tqdm import tqdm

#-------------------------------------------------------#
#   想要增加测试集修改trainval_percent 
#   修改train_percent用于改变验证集的比例 9:1
#   
#   当前该库将测试集当作验证集使用,不单独划分测试集
#-------------------------------------------------------#
trainval_percent    = 1
train_percent       = 0.8
#	训练集占 训练+验证的 80%
#-------------------------------------------------------#
#   指向VOC数据集所在的文件夹
#   默认指向根目录下的VOC数据集
#-------------------------------------------------------#
VOCdevkit_path      = 'data_dataset_voc'

if __name__ == "__main__":
    random.seed(0)
    print("Generate txt in ImageSets.")
    #
    #键文件夹,如果再次运行需要删掉已经建好的,目前还没有进行优化
    os.mkdir('data_dataset_voc/ImageSets')
    os.mkdir('data_dataset_voc/ImageSets/Segmentation')

    #segfilepath     = os.path.join(VOCdevkit_path, 'SegmentationClass')
    segfilepath = os.path.join(VOCdevkit_path, 'SegmentationClassPNG')
    #获取语义分割png格式数据集的路径,但是在这里需要改为SegmentationClassPng
    saveBasePath    = os.path.join(VOCdevkit_path, 'ImageSets/Segmentation')
    #获取txt的存储路径

    temp_seg = os.listdir(segfilepath)
    #temp_seg 为语义分割数据集名字列表,一会会对这个列表进行筛选
    total_seg = []
    #筛选后的图片列表
    for seg in temp_seg:
        if seg.endswith(".png"):
            total_seg.append(seg)
    #只把png格式的数据加入到列表中

    num     = len(total_seg)
    #存储一共有多少个数据
    list    = range(num)
    #可迭代的一个列表0---最后一个值 用来标识所有的数据
    print("list:",list,"type(list):",type(list))
    tv      = int(num*trainval_percent)
    #tv 可以理解为 训练+验证 总数
    tr      = int(tv*train_percent)
    #tr 训练集总数
    trainval= random.sample(list,tv)
    #生成 训练+验证 数据集 索引列表(类似数组)
    train   = random.sample(trainval,tr)
    #生成 训练集的 索引列表
    print('trainval',type(trainval),trainval)
    #random.sample(list, k) 从list样本或集合中随机抽取K个不重复的元素形成新的序列
    
    print("train and val size",tv)
    print("train size",tr)
    ftrainval   = open(os.path.join(saveBasePath,'trainval.txt'), 'w')  
    ftest       = open(os.path.join(saveBasePath,'test.txt'), 'w')  
    ftrain      = open(os.path.join(saveBasePath,'train.txt'), 'w')  
    fval        = open(os.path.join(saveBasePath,'val.txt'), 'w')  
    
    for i in list:  
        name = total_seg[i][:-4]+'\\n'
        #   写入,去掉“ .png",并且加入换行
        if i in trainval:  
            ftrainval.write(name)  
            if i in train:  
                ftrain.write(name)
                #   在trainval中,且在train中,写入
            else:  
                fval.write(name)
                #   对于trainval中的数据,如果不在train中,那就把他归为验证集
        else:  
            ftest.write(name)  
    
    ftrainval.close()  
    ftrain.close()  
    fval.close()  
    ftest.close()
    print("Generate txt in ImageSets done.")

###################################################################
#########这里下面的代码可以省略
    print("Check datasets format, this may take a while.")
    print("检查数据集格式是否符合要求,这可能需要一段时间。")
    classes_nums        = np.zeros([256], np.int)
    for i in tqdm(list):
        name            = total_seg[i]
        png_file_name   = os.path.join(segfilepath, name)
        if not os.path.exists(png_file_name):
            raise ValueError("未检测到标签图片%s,请查看具体路径下文件是否存在以及后缀是否为png。"%(png_file_name))
        
        png             = np.array(Image.open(png_file_name), np.uint8)
        if len(np.shape(png)) > 2:
            print("标签图片%s的shape为%s,不属于灰度图或者八位彩图,请仔细检查数据集格式。"%(name, str(np.shape(png))))
            print("标签图片需要为灰度图或者八位彩图,标签的每个像素点的值就是这个像素点所属的种类。"%(name, str(np.shape(png))))

        classes_nums += np.bincount(np.reshape(png, [-1]), minlength=256)
            
    print("打印像素点的值与数量。")
    print('-' * 37)
    print("| %15s | %15s |"%("Key", "Value"))
    print('-' * 37)
    for i in range(256):
        if classes_nums[i] > 0:
            print("| %15s | %15s |"%(str(i), str(classes_nums[i])))
            print('-' * 37)
    
    if classes_nums[255] > 0 and classes_nums[0] > 0 and np.sum(classes_nums[1:255]) == 0:
        print("检测到标签中像素点的值仅包含0与255,数据格式有误。")
        print("二分类问题需要将标签修改为背景的像素点值为0,目标的像素点值为1。")
    elif classes_nums[0] > 0 and np.sum(classes_nums[1:]) == 0:
        print("检测到标签中仅仅包含背景像素点,数据格式有误,请仔细检查数据集格式。")

    print("JPEGImages中的图片应当为.jpg文件、SegmentationClass中的图片应当为.png文件。")
    print("如果格式有误,参考:")
    print("https://github.com/bubbliiiing/segmentation-format-fix")
    

以上是关于labelme的json文件转换为VOC数据集的png的主要内容,如果未能解决你的问题,请参考以下文章

MMsegmentation训练自己的voc数据集

[技术杂谈][原创]voc数据集,labelme数据集,coco数据集之间相互转换方法

coco标注信息与labelme标注信息的详解相互转换及可视化

使用Python将DOTA数据集的格式转换成VOC2007数据集的格式

使用Python将DOTA数据集的格式转换成VOC2007数据集的格式

使用Python将DOTA数据集的格式转换成VOC2007数据集的的格式