草庐IT

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

qq_43718758 2023-08-15 原文

文章目录

一、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的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  9. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  10. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

随机推荐