使用labelme软件标注并制作自己的数据集

最近在做图像语义分割方面的学习,前期已经在本地电脑完整地训练了ADEChallengeData2016数据,下一阶段准备使用自己的数据集完成一些任务。下面整理一下我在使用labelme进行标注,实现自己数据集的制作。主要分为以下五部分:

目录

  • 图像手动标注
  • JSON文件批量转换
  • 标签转为单通道灰度图
  • 训练集批量扩充
  • pickle文件(以字典形式保存数据集路径)制作

图像手动标注

图像标注使用专门的图像标签制作软件labelme。
该软件的WINDOWS操作系统下的安装包我已经上传,无偿奉送,点击链接自取。
使用labelme软件标注并制作自己的数据集_第1张图片
界面简洁易上手,一般使用红框里的这四个按钮。下面简单介绍:

【Open Dir】打开文件夹,将该文件下的所有文件路径均会以列表形式显示在界面右下角的“File List”容器内,标注且保存过的图片其路径前的复选框会被勾选;
【Save】保存标注文件,个人建议和原图放在同一个文件夹下,这样即使没有标注完该文件夹下的所有文件,关闭后再次打开被标注的图片其名称前依旧会被checked。
【Create Polygons】绘制多边形,可以在绘图区用鼠标绘制闭合的区域,当鼠标最后一个点接近第一个点图形会自动闭合,并弹出窗体提示用户输入该多边形的标签(半角英文标签哦!),点击“Save”按钮保存到指定路径下,标注的文件扩展名为.json。
【Next Image】显示下一个未标注的图片,继续画多边形,输入标签,保存,就这样像木得感情的机器循环往复如此而已。

温馨提示: 此过程需要耗费大量的时间和精力,标注时一定要睁大眼睛,眼药水备用,尽量保证标注尽可能的完美,不然会影响接下来的训练结果,注意眼部休息
使用labelme软件标注并制作自己的数据集_第2张图片
标注好的标签文件,除扩展名为json外名,其余原图保持一致。

JSON文件批量转换

有了json文件就离我们的标签图片进了一大步,接下来就是研究怎么把json转换成图片格式(这里转为png)。我们先来看看人家的数据集长什么样。
使用labelme软件标注并制作自己的数据集_第3张图片
我在网上查找到了相关代码,但只不过是逐个进行转换,需要手动修改每个图片的路径,着实麻烦,我就根据自己的需求对文件夹下的所有json文件进行了批量转换。代码如下:

import json
import os
import os.path as osp
import warnings
import PIL.Image
import yaml
from labelme import utils
import base64
def JSON_to_IMG(json_file, save_file):
    """
    将指定目录下的json标注文件全部转换为以下文件:①jpg原图文件、 ②png标签文件
    ③其他相关说明文件 。存储在指定路径下
    :param json_file: Json格式所在文件夹
    :param save_file: 转换后的文件存储文件夹
    """
    #如果存储路径不存在就创建文件目录
    if not osp.exists(save_file):
        os.mkdir(save_file)
    #文件目录下的所有json文件名称
    count = os.listdir(json_file)
    #遍历目录下的所有json文件
    for i in range(0, len(count)):
        #获取json文件全路径名称
        path = os.path.join(json_file, count[i])
        #如果是imagedata格式文件进行读取
        if os.path.isfile(path):
            #打开json文件
            data = json.load(open(path))
            if data['imageData']:
                imageData = data['imageData']
            else:
                imagePath = os.path.join(os.path.dirname(path), data['imagePath'])
                with open(imagePath, 'rb') as f:
                    imageData = f.read()
                    imageData = base64.b64encode(imageData).decode('utf-8')
            img = utils.img_b64_to_arr(imageData)
            #将图片中背景赋值为0
            label_name_to_value = {'_background_': 0}
            for shape in data['shapes']:
                label_name = shape['label']
                if label_name in label_name_to_value:
                    label_value = label_name_to_value[label_name]
                else:
                    label_value = len(label_name_to_value)
                    label_name_to_value[label_name] = label_value

            # label_values must be dense
            label_values, label_names = [], []
            for ln, lv in sorted(label_name_to_value.items(), key=lambda x: x[1]):
                label_values.append(lv)
                label_names.append(ln)
            assert label_values == list(range(len(label_values)))

            lbl = utils.shapes_to_label(img.shape, data['shapes'], label_name_to_value)

            captions = ['{}: {}'.format(lv, ln)
                        for ln, lv in label_name_to_value.items()]
            lbl_viz = utils.draw_label(lbl, img, captions)
            #获取json文件名并将.json改为_json用作文件夹目录
            #out_dir = osp.basename(count[i]).replace('.json', '.png')
            out_pic_name = str(i) + ".jpg"#原图
            out_label_name = str(i) + ".png"#标签图
            out_label_viz_name = str(i) + "_label_viz.png"#带标注的图片
            out_labeltxt_name = str(i) + "_label_names.txt"#标签名对应值
            out_info_name = str(i) + "_info.yaml"
            #在目标文件夹下保存原始图片
            PIL.Image.fromarray(img).save(osp.join(save_file, out_pic_name))
            #保存标签图片
            utils.lblsave(osp.join(save_file, out_label_name), lbl)
            #保存带标注的可视化图片
            PIL.Image.fromarray(lbl_viz).save(osp.join(save_file, out_label_viz_name))
            with open(osp.join(save_file, out_labeltxt_name), 'w') as f:
                for lbl_name in label_names:
                    f.write(lbl_name + '\n')

            warnings.warn('info.yaml is being replaced by label_names.txt')
            info = dict(label_names=label_names)
            with open(osp.join(save_file, out_info_name), 'w') as f:
                yaml.safe_dump(info, f, default_flow_style=False)

            print('完成了对Json文件: %s的IMG格式转换!' % osp.basename(count[i]))
    print("指定目录下的所有JSON文件完成转换!")

#程序主入口
if __name__ == '__main__':
    json_file = r"C:\Users\梦了个梦\Desktop\打标签\json文件汇总"#这里json文件所在文件夹的全路径
    save_file = r"C:\Users\梦了个梦\Desktop\打标签\JSON_PNG"#这里填写转换后文件保存的文件夹全路径
    JSON_to_IMG(json_file, save_file)#调用上方定义的转换函数完成批量转换

提示:运行该脚本文件需要安装python编译器,导入相关库,在运行过程中,某些条件下系统一直提示如下错误:
报错
去网上查找相关解释,是说安装的labelme库下没有这些脚本,可能是高版本(我一开始是4.0+版本),将labelme的版本降到3.16.2,就不再报错了。

代码成功运行完毕,每个json文件会生成下图所示的五个文件。
使用labelme软件标注并制作自己的数据集_第4张图片
我只需要用到前两个图片,倒数第二个txt文件会指示最后一个图片标签中1所代表的具体实体语义,yaml我也没用到也不清楚就不管了。

标签转为单通道灰度图

运行到此,我以为成功近在咫尺,所以直接将目前的数据输入分割网络,但提示错误,说他们需要深度为1的标签图片,而到目前为止实现的标签图片是四个通道,这就需要进行通道数转换,转成只有一个通道的灰度图。怎么转灰度图呢,废话不多说直接上代码。

#批量图片转换:将RGB模式或P模式转换为L灰度拉伸值(可以自定义拉伸大小)
import numpy as np
from PIL import Image
import os
def Mode_P_to_L(img_file,stretch_value):
    """
将当前文件下的所有图片进行灰度值转换
    :param img_file: 图片文件存放目录
    :param stretch_value: 拉伸值, 图片原始值*stretch_value的结果理论上应该小于255
    """
    ##获取目录下所有文件名
    file_name_list = os.listdir(img_file)
    #遍历所有图片文件
    for file in file_name_list:
        #获取某个图片的全路径
        img_path = os.path.join(img_file, file)
        #打开图片
        image = Image.open(img_path)
        print(image.mode) #p模式
        img_arry = Image.fromarray(np.uint8(image))
        print(img_arry.mode)#L模式
        img_L = img_arry.convert("L")
        print(img_L.mode)
        #灰度拉伸
        img_end = Image.fromarray(np.uint8(img_L) * stretch_value)
        print(img_end.mode)
        #保存图片,并覆盖原图
        img_end.save(img_path)
        #提示
        print("完成对图片:",file," 的转换!")
    print("所有图片均已完成转换!")

#程序主入口
if __name__ == "__main__":
    # 需要转换的图片所在文件目录
    img_file = r""#自己输入上一步生成的png格式的标签图
    stretch_value = 1#自定义拉伸值,但要注意,图片标签值*stretch_value的结果理论上应该小于255
    Mode_P_to_L(img_file, stretch_value)#调用自定义的转换方法

下图即为标签图的原图(左)和转换后的灰度标签图(右)对比:
使用labelme软件标注并制作自己的数据集_第5张图片

训练集批量扩充

由于数据集数量较少,而有监督的语义分割机器学习需要大量的训练数据集,图片扩充就呼之欲出,
废话不多说直接上代码(这里有90度、180度、270度顺时针旋转和水平,垂直镜像种扩充形式)

from PIL import Image
import os

def Expand_trainset_pic(img_file):
    """
    对指定目录下的图片进行简单数据扩充,包括对原图
    进行90°、180°,270°顺时针旋转
    和水平镜像以及垂直镜像
    :param img_file: 需要批量扩充的图片所在文件夹
    """
    #获取文件目录下的所有文件名
    file_name_list = os.listdir(img_file)
    for file in file_name_list:
        #获取图片文件全路径
        img_path = os.path.join(img_file, file)
        #打开图片
        image = Image.open(img_path)
        #获取文件名
        filename= os.path.splitext(file)[0]
        #获取扩展名
        file_ext=os.path.splitext(file)[1]
        #90、180、270顺时针旋转
        img1 = image.transpose(Image.ROTATE_90)   # 引用固定的常量值
        img1_name=filename+"_90"+file_ext
        img1_save_path=os.path.join(img_file, img1_name)
        img1.save(img1_save_path)

        img2 = image.transpose(Image.ROTATE_180)   # 引用固定的常量值
        img2_name=filename+"_180"+file_ext
        img2_save_path=os.path.join(img_file, img2_name)
        img2.save(img2_save_path)

        img3 = image.transpose(Image.ROTATE_270)   # 引用固定的常量值
        img3_name=filename+"_270"+file_ext
        img3_save_path=os.path.join(img_file, img3_name)
        img3.save(img3_save_path)

        img4 = image.transpose(Image.FLIP_LEFT_RIGHT)   # 引用固定的常量值
        img4_name=filename+"_x"+file_ext
        img4_save_path=os.path.join(img_file, img4_name)
        img4.save(img4_save_path)

        img5 = image.transpose(Image.FLIP_TOP_BOTTOM)   # 引用固定的常量值
        img5_name=filename+"_y"+file_ext
        img5_save_path=os.path.join(img_file, img5_name)
        img5.save(img5_save_path)
        print("完成对图片:", file, " 的扩充")
    print("完成对所有图片的扩充!")
    
#程序主入口
if __name__ == "__main__":
    img_file = r'     ' #在此输入需要转换的图片所在文件夹
    Expand_trainset_pic(img_file)#调用自定义扩充函数

FBI警告!

该扩充操作只能针对于训练集,否则会造成数据泄露,导致虽然最后训练集以及验证集的精度都很完美但实际上很垃圾。

pickle文件(以字典形式保存数据集路径)制作

使用过ADEchallenge数据集及其相关代码的同学请自行学习read_MITSceneParsingData.py脚本文件的read_dataset函数以及create_image_lists函数,我之前的帖子有关于该代码报错的解决说明。

你可能感兴趣的:(FCN,tensorflow,深度学习,labelme,语义分割)