语义分割中单类别和多类别图片数据标注,以及灰度类别转换

                                                                                                     点击上方码农的后花园”,选择星标” 公众号

                                                                                                                     精选文章,第一时间送达

上期讲解了语义分割模型的基本架构和常用数据集,这期就讲解一下语义分割数据集的制作,追下去吧~

 

制作总体步骤:

1. 使用lableme对图片数据进行标注,生成对应图片的x.json文件。

2. 执行lableme下的内置函数labelme_json_to_dataset,依次手动生成图片对应的x_json文件(或者使用代码一次性处理生成)。

3. 对第二步生成文件夹中的文件进行处理,生成语义图片label.png。

4. 将语义图片转换为类别灰度图图片-最终训练标签文件。

 

一、文件目录结构:

 

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第1张图片

 

 

二、正式开始制作

第一步:标注软件的安装

1.Anaconda Prompt中创建一个环境

conda create --name=labelImg python=3.6

2.激活进入刚建立的新环境,

conda activate labelImg

3.安装界面支持pyqt5包

pip install pyqt5 -i https://pypi.douban.com/simple/

4.下载安装labelme

pip install labelme -i https://pypi.douban.com/simple/

5.输入命令labelme,就可以启动程序进行数据标注

 

 

第二步:进行标注

A.单类别标注 - 即每一张图片上只有一个目标

【1】在命令行中输入命令 labelme,打开标注界面,然后打开要标注的图片所在的文件夹进行标注

Opendir “”Test_Image“” ->Create  polygons ->Save->Next Image

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第2张图片

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第3张图片

 

【2】所有图片标注完之后,标注文件以x.json形式文件进行保存,制作完成后放在目录的before文件夹下。

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第4张图片

用VS2017可查看Json文件内容,包含信息为我们标注区域内每一个像素点的数据

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第5张图片

 

【3】利用labelme的自带函数labelme_json_to_dataset手动依次将每个json文件格式转换为语义图片的数据。

1.cd 到json文件所在的地方:cmd D:Test_Image

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第6张图片

 

2.查看当前labelme安装所在的环境label并激活

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第7张图片

 

3.利用labelme_json_to_dataset手动依次对每张图片的标注文件x.json格式依次进行处理, 生成得到x_json文件即每张图片的对应的语义图片  - 局部类别标签(这个过程需要手动对每一张图片的x.json文件进行处理,比较麻烦,可以写代码直接一次性转换)

命令:labelme_json_to_data [ x.json ] -> labelme标注生成的json文件名

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第8张图片

得到每张图片的x.json文件对应的局部类别标签保存在每个x_json文件中,如下所示:

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第9张图片

每张图片局部类别标签内容 x_json文件中的内容:

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第10张图片

注解:

img.png: 对应的局部的jpg原图文件,训练时要用

label.png: 标注语义图像,训练时要用

label_names.txt: 在这张图像上目标分类名称

label_viz.png:  标签可视化,用于让我们确认是否标记正确

 

代码一次性转换:json_to_dataset.py

会生成和上述x_json文件下一模一样的文件内容

import argparse
import json
import os
import os.path as osp
import warnings
 
import PIL.Image
import yaml
 
from labelme import utils
import base64
 
def main():
    count = os.listdir("./before/") 
    for i in range(0, len(count)):
        path = os.path.join("./before", count[i])

        # 找到before文件中以json为结尾的文件并且打开
        if os.path.isfile(path) and path.endswith('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)
            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)
            out_dir = osp.basename(count[i]).replace('.', '_')
            out_dir = osp.join(osp.dirname(count[i]), out_dir)
            out_dir = osp.join("output",out_dir)
    
            if not osp.exists(out_dir):
                os.mkdir(out_dir)
 
            PIL.Image.fromarray(img).save(osp.join(out_dir, 'img.png'))

            utils.lblsave(osp.join(out_dir, 'label.png'), lbl)
            PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, 'label_viz.png'))
 
            with open(osp.join(out_dir, 'label_names.txt'), '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(out_dir, 'info.yaml'), 'w') as f:
                yaml.safe_dump(info, f, default_flow_style=False)
 
            print('Saved to: %s' % out_dir)

if __name__ == '__main__':
    main()

 

我们得到了每张图片所对应的局部语义类别标签之后(每一张图片,灰度类别转换后,属于标记目标,如猫,其所属区域像素点的值均为1,不属于标记目标的图片区域(背景)像素点值为0)。

如果数据集新增图片,这张图片上也只有一个目标狗,为了将目标狗这个类别加入到训练集中,则需要将狗这个局部类别标签进行转换为全局类别标签,即在训练时,背景的像素点值为0,猫的像素点值为1,狗的像素点值为2,依次类推)

 

【4】局部类别标签—>全局类别标签(类别灰度转换)

将得到的每张图片所对应的局部训练标签文件label.png转换为全局训练标签文件,放在了png文件夹下,这个过程中还涉及了类别灰度化操作。

进行get_jpg_and_png.py操作即可得到每张图片的全局类别标签文件和类别灰度转换,即一张图片中背景的像素点的值为0,若目标是猫,则其像素点的值为1,若目标是狗,则其像素点的值为2。

这里最终的全局训练标签是灰度图,是单通道的,像素点的取值范围为0~255,这里全黑是因为,像素点值为1和2,在灰度图中,值比较小,区别比较小,所以显示都是黑的。

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第11张图片

 

类别灰度图图片:

我们可以看到,猫这张图片对应的类别灰度图图片所在区域的像素点的值均为 1,而背景所对应的像素点的值均为0

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第12张图片

 

1. 这里我们需要首先新建一个包含全局类名称的class_name.txt

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第13张图片

 

2. 借助class_name.txt文件,将图片局部类别标签文件转换成全局类别标签文件,转换代码:get_jpg_and_png.py

import os
from PIL import Image
import numpy as np
def main():
    # 读取原文件夹
    count = os.listdir("./before/") 
    for i in range(0, len(count)):
        # 如果里的文件以jpg结尾
        # 则寻找它对应的png
        if count[i].endswith("jpg"):
            path = os.path.join("./before", count[i])
            img = Image.open(path)
            img.save(os.path.join("./jpg", count[i]))


            # 找到对应的png
            path = "./output/" + count[i].split(".")[0] + "_json/label.png"
            img = Image.open(path)


            # 找到全局的类
            class_txt = open("./before/class_name.txt","r")
            class_name = class_txt.read().splitlines()
            # ["bk","cat","dog"] 全局的类
            # 打开x_json文件里面存在的类,称其为局部类
            with open("./output/" + count[i].split(".")[0] + "_json/label_names.txt","r") as f:
                names = f.read().splitlines()
                # ["bk","dog"] 局部的类
                # 新建一张空白图片
                new = Image.new("RGB",[np.shape(img)[1],np.shape(img)[0]])


                # 找到局部的类在全局中的类的序号
                for name in names:
                    # index_json是x_json文件里存在的类label_names.txt,局部类
                    index_json = names.index(name)
                    # index_all是全局的类,
                    index_all = class_name.index(name)


                    # 将局部类转换成为全局类
                    # 将原图img中像素点的值为index_json的像素点乘以其在全局中的像素点的所对应的类的序号 得到 其实际在数据集中像素点的值
                    # 比如dog,在局部类(output/x_json/label_names)中它的序号为1,dog在原图中的像素点的值也为1.
                    # 但是在全局的类(before/classes.txt)中其对应的序号为2,所以在新的图片中要将局部类的像素点的值*全局类的序号,从而得到标签文件
                    new = new + np.expand_dims(index_all*(np.array(img) == index_json),-1)


            new = Image.fromarray(np.uint8(new))
            # 将转变后的得到的新的最终的标签图片保存到make_dataset/png文件夹下
            new.save(os.path.join("./png", count[i].replace("jpg","png")))
            # 找到新的标签文件中像素点值的最大值和最小值,最大值为像素点对应的类在class_name.txt中的序号,最小值为背景,即0
            print(np.max(new),np.min(new))




if __name__ == '__main__':
    main()

‍‍‍‍‍‍‍‍3. 最后我们利用get_train_txt.py对上述得到的jpg文件下和png文件下的内容进行数据集原图和标签的一一对应的关系,将其train_data.txt中

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第14张图片

【5】最终训练模型数据集的样式:jpg文件夹(原图)+png文件夹(全局灰度类别标签) + train_data.txt(原图和标签的对应关系),放入模型当中即可训练了,下期讲解基于MobileNet的SegNet的语义分割模型。

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第15张图片

 

B.多类别标注- 即一张图片上有多个目标

标注过程和上述类似,对图片上的多目标依次进行标注,生成的x_json文件内容和单目标标注一样。

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第16张图片

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第17张图片

 

新建一个包含全局类名称的class_name.txt,然后类别灰度转换, 利用get_jpg_and_png.py进行转换,我们可以看到猫对应的区域像素点的值为1,对应类别1,同理狗对应区域的像素点的值为2,对应类别2,而背景区域像素点的值为0

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第18张图片

 

进行类别灰度转换之后,就可以进行模型训练了。

最终训练模型数据集的样式:jpg文件夹(原图)+png文件夹(全局灰度类别标签) + train_data.txt(原图和标签的对应关系),放入模型当中即可训练了,下期讲解基于MobileNet的SegNet的语义分割模型。

语义分割中单类别和多类别图片数据标注,以及灰度类别转换_第19张图片

好啦,数据标注的部分到这里就结束了,所有处理文件代码,回复关键字:项目实战,即可获取。

 

精彩推荐:

新教程之图像分割系列

图像分割系列 <-> 语义分割

更多优质内容?等你点在看

 

你可能感兴趣的:(python,深度学习,人工智能,计算机视觉,java)