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即可,或者双击python安装目录下的Scripts目录下的labelme.exe。

选择图片文件夹或者图片文件,这里演示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 编码或为null。

"imageHeight": 图像的高度,这里是1280。

"imageWidth": 图像的宽度,这里是1706。

3. Json文件生成标签图像

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

以下是每个文件的作用:

draw_json.py: 这个文件用于通过命令行绘制标注并保存为JSON文件的工具。它允许您手动绘制标注形状并将其保存为JSON格式的标注文件。

draw_label_png.py: 这个文件用于通过命令行绘制标注并将其保存为PNG图像文件的工具。它允许您手动绘制标注形状并将其保存为带有标签的PNG图像文件。

export_json.py: 这个文件用于将标注数据导出为其他格式(如COCO、VOC等)的工具。它可以将labelme标注的JSON文件转换为其他标注格式的文件。

json_to_dataset.py: 这个文件用于将标注数据转换为可用于训练机器学习模型的数据集的工具。它可以将labelme标注的JSON文件转换为图像和标签的数据集。

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: 标签可视化, 只是方便确认标记对了没有

原文件名.png:这个使用命令运行会得到,图中未给出,是不带伪彩色的单通道灰度图标签图像。

说明:label.png为伪彩色图像或者说是使用了调色板,使用label.png或pic.png都可以用于训练,只不过label.png有伪彩色,人容易看到前景。label.png如果使用cv2去读取并打印其像素值集合,会将彩色,比如上图红色像素值打印出来了。关于生成标签掩码,伪彩色标签掩码,相互转换,通道数,标注重叠等系列问题,具体请看下面的链接:

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如下:

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

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

如果使用上述代码替换了原来的labelme_json_to_dataset.py文件后,也可以使用命令:labelme_json_to_dataset.exe json文件夹路径来批量生成标签等数据,如果json文件夹只有json文件没有图片,则需要在标注时候,保留imageData字段,不能为null,否则会因为没有图像信息报错。或者使用labelme_json_to_dataset.exe json与原始图片放一起的文件夹路径,这样也可以批量生成标签等数据。此时,imageData字段有无都不影响生成标签数据。由于imageData是图像的base64编码,会导致json文件相对于imageData为null而言,文件大了很多。

小结:除了可以按照本文的方式标注,还可以预先设置txt的标签类别,使用命令:labelme 图片文件夹 --labels 标签.txt 或者labelme --labels 标签.txt启动,这样标注好后,标签选择就可以了,不需要输入标签名。

参考资料:

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