240718_使用Labelme制作自己的图像分割数据集

240718_使用Labelme制作自己的图像分割数据集

从目标检测入门的朋友们可能更熟悉的是LabelImg,这里要注意做好区分,LabelImg和Labelme不是一个东西,如下经典图:

image-20240718171255970

(a)图像分类(目标检测):一张图像中是否含某种物体
(b)物体定位(目标检测与目标识别):确定目标位置和所属类别。
(c)语义分割(目标分割和目标分类):对图像进行像素级分类,预测每个像素属于的类别,不区分个体;(所有的CUBE一个颜色)
(d)实例分割(目标分割和目标识别):定位图中每个物体,并进行像素级标注,区分不同个体;(CUBE都是不同颜色)

LabelImg做的是b图的工作,拉框框把目标标注出来,通常用于目标检测和识别任务,其输出为.xml或者.txt。

Labelme可以完成图中所有工作,与LabelImg对比,Labelme可以拉多边形的框,准确的把轮廓标注出来,常用于分割,输出为json。

我们在完成图像识别的任务,例如使用YOLO v5时,可能用到的是LabelImg,但如果需要完成分割的任务,例如使用经典的Unet,我们就需要使用Labelme。

在使用该工具开始工作之前,肯定要先安装工具。

安装Labelme

首先新建一个专属于Labelme的虚拟环境,因为该工具是基于PyQt实现的,所以我们也要在该虚拟环境中安装PyQt。

conda create -n Labelme python=3.9
conda activate Labelme
pip install PyQt5
pip install labelme

使用Labelme进行打标

安装完成后直接在该虚拟环境中输入labelme就可以启动该工具。

image-20240718173454466

image-20240718173520497

点击左上角打开,打开我们的图像,同样可以采取打开目录进行批量打开。点击上方创建多边形,就可以开始画框框了,用过ps钢笔工具或者套索工具的同学应该比较熟练。

image-20240718174218754

拉一个多边形框的最后一步点击初始点就可以闭合,然后会有一个弹窗让我们输入标签名称,此时我输入bottle。

image-20240718174449390

同理完成其他图像的标注。

image-20240718175309354

本来这个杯子我是想先把外围整个框好,再去找删除选区工具把他把手包含的空白删掉的,但好像没有这个工具,只能一笔拉好,产生了路线重叠,见谅。

然后点击CTRL+S保存,就可以保存一份json文件。

P:如果没有目标,比如我们做裂缝检测任务,图像中没有裂缝,我们可以不进行标注,直接CTRL+S保存,也可以生成一份json文件,用作正样本。

json转png

但在我们实际投入神经网络下训练的过程中,通常不使用json格式的标签,而是使用png格式,所以此时我们就需要进行格式转换。

针对这个问题,labelme官方给了解决方案,该工具包含一个json_to_dataset.py文件,在自己的虚拟环境目录下,例如我这里就是

D:\SoftWares\Codes\Anaconda\envs\Labelme\Lib\site-packages\labelme\cli

image-20240719122736702

在使用该py脚本进行格式转换时需要在命令行键入(例如我们的json文件路径为D:\l_139\桌面\Snipaste_2024-07-18_17-38-25.json)(需要进入虚拟环境,往后看看)

python json_to_dataset.py D:\l_139\桌面\Snipaste_2024-07-18_17-38-25.json

当然,官方也给我们准备了可执行的exe文件labelme_json_to_dataset.exe,位于虚拟环境目录下的Scripts文件夹下,我这里的路径为

D:\SoftWares\Codes\Anaconda\envs\Labelme\Scripts

在命令行键入以下命令即可实现转换

labelme_json_to_dataset D:\l_139\桌面\Snipaste_2024-07-18_17-38-25.json

注意哦,以上命令都必须在目标路径下执行哦,直接在桌面上开个命令行窗口可没用,要么就cd到py脚本或exe可执行文件所在目录,要么直接在该目录打开命令行。此处使用exe可执行文件直接进行演示

image-20240719123754550

直接使用py脚本进行转换需要进入虚拟环境,不然会报缺失包,多了一步比较麻烦,如果要用py脚本,可参考:

image-20240719125253569

可以看到,以上两种方法都可以执行,但都有警告说这个py脚本即将弃用,取而代之的是labelme_export_json,那我们就顺应大势所趋,使用人家官方建议你用的呗,不然到时候人家弃用了,咱用前朝的剑斩本朝的官当然是斩不掉咯。(此处提供使用labelme_export_json.exe的代码,以下代码在命令行中逐行输入)

# 如果是在目标路径直接打开命令行则无需以下两步操作,路径更换成自己的
D:
cd D:\SoftWares\Codes\Anaconda\envs\Labelme\Scripts
labelme_export_json D:\l_139\桌面\Snipaste_2024-07-18_17-38-25.json

image-20240719130441697

折腾来折腾去我们终于在根目录看到了一个同名文件夹,里面有四个东西,分别是分别是原图png文件、标签png文件、标签名txt文件、掩膜png文件(带有类型标注),到此处也算是回收封面了。

image-20240719130545211

以上处理方法已经成功将一个json转换为png文件,但在实际应用中,我们往往需要的是把成千上万个json文件转换成png标签,并且我们所需要的也只是上图中的label.png,如果每个json都生成这么一个文件夹,我们在处理过程中会有更多的麻烦。

json批量转为png

原来的py脚本中只转换一次是因为代码只执行了一次,我们要批量转换,最简单的不就是在原本的代码上加上一层循环,实现批量转换嘛,把export_json.py复制一份,更名为multiple_export_json.py并打开(我的在D:\SoftWares\Codes\Anaconda\envs\Labelme\Lib\site-packages\labelme\cli目录下,参考一下,别又找不到了)

首先我们来嵌套循环并修改路径读取代码,修改的没多少,可以用PyCharm的对比看一眼,对比是左侧目录中用Ctrl同时选中两个文件,然后右键,会有一个比较文件(或者选中后直接Ctrl+D)

image-20240719140108320

以下是修改部分的代码:

# multiple_export_json.py

import glob
def main():
    parser = argparse.ArgumentParser()
    # 修改变量名为文件夹变量名
    parser.add_argument("json_dir_file")
    parser.add_argument("-o", "--out", default=None)
    args = parser.parse_args()

    # 使用glob模块查找所有的json文件
    json_file = glob.glob(os.path.join(args.json_dir_file, '*.json'))

    # 添加循环,其下代码整体缩进
    for json_file in json_file:
        '''处理逻辑不变'''


if __name__ == "__main__":
    main()

此处使用的数据是我随便找的几张素描图,打了简陋的标

image-20240719140456192

在命令行中执行

python multiple_export_json.py D:\l_139\桌面\test

image-20240719140644335

在根目录下得到四个文件夹

image-20240719140704662

至此就实现了批量处理,但是还没有实现批量存放label.png,继续改进。

在代码最后保存部分进行修改,在根目录新建一个mask文件夹,把标签文件改名后都存放到mask文件夹中,以下是部分修改代码

		# PIL.Image.fromarray(img).save(osp.join(out_dir, "img.png"))
        # 对label.png更名后进行统一存放
        mask_name = osp.splitext(osp.basename(json_file))[0]
        mask_dir = osp.join( osp.dirname(json_file),'mask')
        if not osp.exists(mask_dir):
            os.mkdir(mask_dir)
        utils.lblsave(osp.join(mask_dir, mask_name+".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")

        logger.info("Saved to: {}".format(out_dir))

image-20240719153800649

以下是效果:

image-20240719153842254

以下是完整代码:

# multiple_export_json.py
import argparse
import base64
import json
import os
import os.path as osp

import imgviz
import PIL.Image

from labelme import utils
from labelme.logger import logger

import glob
def main():
    parser = argparse.ArgumentParser()
    # 修改变量名为文件夹变量名
    parser.add_argument("json_dir_file")
    parser.add_argument("-o", "--out", default=None)
    # args = parser.parse_args()
    args = parser

    args.json_dir_file="D:\\l_139\\桌面\\test"
    args.out=None
    # 使用glob模块查找所有的json文件
    json_file = glob.glob(os.path.join(args.json_dir_file, '*.json'))

    # 添加循环,其下代码整体缩进
    for json_file in json_file:
        if args.out is None:
            out_dir = osp.splitext(osp.basename(json_file))[0]
            out_dir = osp.join(osp.dirname(json_file), out_dir)
        else:
            out_dir = args.out
        # if not osp.exists(out_dir):
        #     os.mkdir(out_dir)

        data = json.load(open(json_file))
        imageData = data.get("imageData")

        if not imageData:
            imagePath = os.path.join(os.path.dirname(json_file), 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 sorted(data["shapes"], key=lambda x: x["label"]):
            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
        lbl, _ = utils.shapes_to_label(img.shape, data["shapes"], label_name_to_value)

        label_names = [None] * (max(label_name_to_value.values()) + 1)
        for name, value in label_name_to_value.items():
            label_names[value] = name

        lbl_viz = imgviz.label2rgb(
            lbl, imgviz.asgray(img), label_names=label_names, loc="rb"
        )

        # PIL.Image.fromarray(img).save(osp.join(out_dir, "img.png"))
        # 对label.png更名后进行统一存放
        mask_name = osp.splitext(osp.basename(json_file))[0]
        mask_dir = osp.join( osp.dirname(json_file),'mask')
        if not osp.exists(mask_dir):
            os.mkdir(mask_dir)
        utils.lblsave(osp.join(mask_dir, mask_name+".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")

        logger.info("Saved to: {}".format(out_dir))


if __name__ == "__main__":
    main()

如果做的是一个二分类任务,可能会涉及需要将label的像素值归一化为黑白0-1。具体参考240719_图像二分类任务中图像像素值的转换-[0,255]-[0,1]-CSDN博客

参考博客:

labelImg和labelme的区别、安装和基本使用_labelme和labelimg区别-CSDN博客

Labelme做数据标签、批量处理json文件转换为png方法_labelme json文件转换-CSDN博客

  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值