240718_使用Labelme制作自己的图像分割数据集
从目标检测入门的朋友们可能更熟悉的是LabelImg,这里要注意做好区分,LabelImg和Labelme不是一个东西,如下经典图:
(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就可以启动该工具。
,
点击左上角打开,打开我们的图像,同样可以采取打开目录进行批量打开。点击上方创建多边形,就可以开始画框框了,用过ps钢笔工具或者套索工具的同学应该比较熟练。
拉一个多边形框的最后一步点击初始点就可以闭合,然后会有一个弹窗让我们输入标签名称,此时我输入bottle。
同理完成其他图像的标注。
本来这个杯子我是想先把外围整个框好,再去找删除选区工具把他把手包含的空白删掉的,但好像没有这个工具,只能一笔拉好,产生了路线重叠,见谅。
然后点击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
在使用该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可执行文件直接进行演示
直接使用py脚本进行转换需要进入虚拟环境,不然会报缺失包,多了一步比较麻烦,如果要用py脚本,可参考:
可以看到,以上两种方法都可以执行,但都有警告说这个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
折腾来折腾去我们终于在根目录看到了一个同名文件夹,里面有四个东西,分别是分别是原图png文件、标签png文件、标签名txt文件、掩膜png文件(带有类型标注),到此处也算是回收封面了。
以上处理方法已经成功将一个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)
以下是修改部分的代码:
# 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()
此处使用的数据是我随便找的几张素描图,打了简陋的标
在命令行中执行
python multiple_export_json.py D:\l_139\桌面\test
在根目录下得到四个文件夹
至此就实现了批量处理,但是还没有实现批量存放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))
以下是效果:
以下是完整代码:
# 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博客