labelme语义分割标注与批量图片转换

发布时间 2023-11-07 16:27:00作者: wancy

  labelme是一种常用的开源图像标注工具,特别适用于语义分割标注。它提供了直观的用户界面,可以方便地标注每个像素的类别。

1.安装

  pip install -i https://pypi.tuna.tsinghua.edu.cn/simple labelme

  也可以从github获取安装https://github.com/wkentaro/labelme

  安装过程中如果报错,可能需要安装pyqt5、pillow等包,根据提示安装就可以了。

2.标注步骤

  • 绘制标注区域:使用Labelme提供的绘图工具,在图像上绘制每个语义类别的区域。可以选择绘制多边形、矩形或自由绘制等形状。
  • 标注类别:为每个绘制的区域分配相应的语义类别标签。可以在Labelme的界面上设置类别标签并为每个区域选择相应的标签。
  • 保存标注结果:保存标注结果,Labelme会将标注结果保存为JSON文件,其中包含了每个区域的坐标和类别信息。

  通过这些步骤,可以使用Labelme进行语义分割标注,并生成像素级别的语义分割标签。

  先启动labelme,在cmd中输入labelme即可。

  选择图片文件夹或者图片文件,这里演示open,选择图片文件。

 

  选择create Polygons,绘制多边形。标注好后,回车就会出现对话框,输入自己的标签名。

  最后点击保存,保存的是json文件。保存的json数据如下:

                                                             

  • "version": 表示标注工具的版本号,这里是 "5.3.1"。
  • "flags": 用于存储一些标志信息,这里是一个空对象。
  • "shapes": 包含了一个形状的数组,每个形状都有以下属性:
    • "label": 形状的标签,这里是 "bottle"。
    • "points": 形状的顶点坐标,这里是一个多边形的四个顶点坐标。
    • "group_id": 形状所属的组ID,这里是null。
    • "description": 形状的描述信息,这里是空字符串。
    • "shape_type": 形状的类型,这里是 "polygon" 表示多边形。
    • "flags": 用于存储一些标志信息,这里是一个空对象。
  • "imagePath": 图像文件的路径,这里是 "blue.jpg"。
  • "imageData" 参数用于存储图像的像素数据,以便在标注工具中显示图像。它的值是一个字符串,可以是图像的 base64 编码或者是图像文件的路径。
  • "imageHeight": 图像的高度,这里是1280。
  • "imageWidth": 图像的宽度,这里是1706。

3. Json文件生成标签图像

  在安装好的labelme包中,有个cli文件夹,里面内容如下:

  以下是每个文件的作用:

  1. draw_json.py: 这个文件用于通过命令行绘制标注并保存为JSON文件的工具。它允许您手动绘制标注形状并将其保存为JSON格式的标注文件。
  2. draw_label_png.py: 这个文件用于通过命令行绘制标注并将其保存为PNG图像文件的工具。它允许您手动绘制标注形状并将其保存为带有标签的PNG图像文件。
  3. export_json.py: 这个文件用于将标注数据导出为其他格式(如COCO、VOC等)的工具。它可以将labelme标注的JSON文件转换为其他标注格式的文件。
  4. json_to_dataset.py: 这个文件用于将标注数据转换为可用于训练机器学习模型的数据集的工具。它可以将labelme标注的JSON文件转换为图像和标签的数据集。
  5. on_docker.py: 这个文件用于在Docker容器中运行labelme的命令行接口的工具。它提供了在Docker容器环境中使用labelme的便捷方法。

  我们可以使用json_to_dataset.py来将json转换成png图片(具体没有操作过),也可以使用labelme_json_to_dataset.exe(本文使用这个)来生成我们训练时需要的图片。安装好labelme后,会在python的Scripts目录下看到有labelme_json_to_dataset.exe(如果是虚拟环境,就在虚拟环境中找)。

   准备好自己使用labelme标注好的json文件。

  在cmd中输入命令labelme_json_to_dataset F:\json_data\blue.json

  blue_json文件夹里面的内容如下:

 

  • img.png: 原始图像, 训练时需要
  • label.png: 标签图像, 训练时需要
  • label_names.txt: 在这张图像中目标的类别名称.
  • label_viz.png: 标签可视化, 只是方便确认标记对了没有

4. 批量的json文件转label.png

  修改labelme_json_to_dataset.py代码为下面代码,原来代码最好也备份一份。

  将json_to_dataset.py中的代码用下面的完全替换掉:

import argparse
import base64
import json
import os
import os.path as osp
import imgviz
import PIL.Image
from labelme.logger import logger
from labelme import utils
import numpy as np

def main():
    # 打印警告信息
    logger.warning("This script is aimed to demonstrate how to convert the JSON file to a single image dataset.")
    logger.warning("It won't handle multiple JSON files to generate a real-use dataset.")

    parser = argparse.ArgumentParser()
    parser.add_argument("json_dirname")  # 输入JSON文件目录
    parser.add_argument("-o", "--out", default=None)  # 输出文件夹路径
    args = parser.parse_args()

    # 查找目录下所有的json文件
    json_files = []
    for root, dirs, files in os.walk(args.json_dirname):
        for file in files:
            if file.endswith(".json"):
                json_files.append(os.path.join(root, file))

    # json_file = args.json_dirname
    for json_file in json_files:
        # 确定输出文件夹路径
        if args.out is None:
            out_dir = osp.basename(json_file).replace(".", "_")
            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)
        else:
            continue

        # 读取JSON文件内容
        data = json.load(open(json_file))
        imageData = data.get("imageData")

        # 如果JSON文件中包含图像数据,则直接读取图像数据
        # 如果不包含图像数据,则寻找该目录下的图片
        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"))
        utils.lblsave(osp.join(out_dir, "label.png"), lbl)
        PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, "label_viz.png"))
        PIL.Image.fromarray(lbl.astype(np.uint8)).save(osp.join(out_dir, osp.basename(json_file).replace(".json",".png")))

        # 保存标签名称到文本文件
        with open(osp.join(out_dir, "label_names.txt"), "w") as f:
            f.write(
                "img.png: Original image \n"
                "label.png: Semantic map with pixel values between 0 and 255 \n"
                "label_viz.png: Overlay the semantic map and the original map \n" +
                osp.basename(json_file).replace(".json",".png") + ": The pixel value is a grayscale image of the label sequence number\n\n"
            )
            for name, value in label_name_to_value.items():
                f.write("class: " + name + "\t\tvalue: " + str(value) + "\n")

        # 输出保存路径
        logger.info("Saved to: {}".format(out_dir))
        print(f"Saved to: {out_dir}")

if __name__ == "__main__":
    main()

  现在可以批量转换json数据为标签图片了,标注的批量json如下:

   输入以下命令,注意自己的路径不能是中文:

  然后生成了批量的标注的标签图片。

 小结:除了可以按照本文的方式标注,还可以预先设置txt的标签类别,使用命令:label   图片文件夹   标签.txt启动,这样标注好后,标签选择就可以了,不需要输入标签名。另外,label.png的位深度为8,表明该图是单通道图。关于颜射设置问题后面再补充。

 

参考资料:

https://zhuanlan.zhihu.com/p/649269470