最近在做图像语义分割方面的学习,前期已经在本地电脑完整地训练了ADEChallengeData2016数据,下一阶段准备使用自己的数据集完成一些任务。下面整理一下我在使用labelme进行标注,实现自己数据集的制作。主要分为以下五部分:
图像标注使用专门的图像标签制作软件labelme。
该软件的WINDOWS操作系统下的安装包我已经上传,无偿奉送,点击链接自取。
界面简洁易上手,一般使用红框里的这四个按钮。下面简单介绍:
【Open Dir】打开文件夹,将该文件下的所有文件路径均会以列表形式显示在界面右下角的“File List”容器内,标注且保存过的图片其路径前的复选框会被勾选;
【Save】保存标注文件,个人建议和原图放在同一个文件夹下,这样即使没有标注完该文件夹下的所有文件,关闭后再次打开被标注的图片其名称前依旧会被checked。
【Create Polygons】绘制多边形,可以在绘图区用鼠标绘制闭合的区域,当鼠标最后一个点接近第一个点图形会自动闭合,并弹出窗体提示用户输入该多边形的标签(半角英文标签哦!),点击“Save”按钮保存到指定路径下,标注的文件扩展名为.json。
【Next Image】显示下一个未标注的图片,继续画多边形,输入标签,保存,就这样像木得感情的机器循环往复如此而已。
温馨提示: 此过程需要耗费大量的时间和精力,标注时一定要睁大眼睛,眼药水备用,尽量保证标注尽可能的完美,不然会影响接下来的训练结果,注意眼部休息
标注好的标签文件,除扩展名为json外名,其余原图保持一致。
有了json文件就离我们的标签图片进了一大步,接下来就是研究怎么把json转换成图片格式(这里转为png)。我们先来看看人家的数据集长什么样。
我在网上查找到了相关代码,但只不过是逐个进行转换,需要手动修改每个图片的路径,着实麻烦,我就根据自己的需求对文件夹下的所有json文件进行了批量转换。代码如下:
import json
import os
import os.path as osp
import warnings
import PIL.Image
import yaml
from labelme import utils
import base64
def JSON_to_IMG(json_file, save_file):
"""
将指定目录下的json标注文件全部转换为以下文件:①jpg原图文件、 ②png标签文件
③其他相关说明文件 。存储在指定路径下
:param json_file: Json格式所在文件夹
:param save_file: 转换后的文件存储文件夹
"""
#如果存储路径不存在就创建文件目录
if not osp.exists(save_file):
os.mkdir(save_file)
#文件目录下的所有json文件名称
count = os.listdir(json_file)
#遍历目录下的所有json文件
for i in range(0, len(count)):
#获取json文件全路径名称
path = os.path.join(json_file, count[i])
#如果是imagedata格式文件进行读取
if os.path.isfile(path):
#打开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)
#将图片中背景赋值为0
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)
#获取json文件名并将.json改为_json用作文件夹目录
#out_dir = osp.basename(count[i]).replace('.json', '.png')
out_pic_name = str(i) + ".jpg"#原图
out_label_name = str(i) + ".png"#标签图
out_label_viz_name = str(i) + "_label_viz.png"#带标注的图片
out_labeltxt_name = str(i) + "_label_names.txt"#标签名对应值
out_info_name = str(i) + "_info.yaml"
#在目标文件夹下保存原始图片
PIL.Image.fromarray(img).save(osp.join(save_file, out_pic_name))
#保存标签图片
utils.lblsave(osp.join(save_file, out_label_name), lbl)
#保存带标注的可视化图片
PIL.Image.fromarray(lbl_viz).save(osp.join(save_file, out_label_viz_name))
with open(osp.join(save_file, out_labeltxt_name), '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(save_file, out_info_name), 'w') as f:
yaml.safe_dump(info, f, default_flow_style=False)
print('完成了对Json文件: %s的IMG格式转换!' % osp.basename(count[i]))
print("指定目录下的所有JSON文件完成转换!")
#程序主入口
if __name__ == '__main__':
json_file = r"C:\Users\梦了个梦\Desktop\打标签\json文件汇总"#这里json文件所在文件夹的全路径
save_file = r"C:\Users\梦了个梦\Desktop\打标签\JSON_PNG"#这里填写转换后文件保存的文件夹全路径
JSON_to_IMG(json_file, save_file)#调用上方定义的转换函数完成批量转换
提示:运行该脚本文件需要安装python编译器,导入相关库,在运行过程中,某些条件下系统一直提示如下错误:
去网上查找相关解释,是说安装的labelme库下没有这些脚本,可能是高版本(我一开始是4.0+版本),将labelme的版本降到3.16.2,就不再报错了。
代码成功运行完毕,每个json文件会生成下图所示的五个文件。
我只需要用到前两个图片,倒数第二个txt文件会指示最后一个图片标签中1所代表的具体实体语义,yaml我也没用到也不清楚就不管了。
运行到此,我以为成功近在咫尺,所以直接将目前的数据输入分割网络,但提示错误,说他们需要深度为1的标签图片,而到目前为止实现的标签图片是四个通道,这就需要进行通道数转换,转成只有一个通道的灰度图。怎么转灰度图呢,废话不多说直接上代码。
#批量图片转换:将RGB模式或P模式转换为L灰度拉伸值(可以自定义拉伸大小)
import numpy as np
from PIL import Image
import os
def Mode_P_to_L(img_file,stretch_value):
"""
将当前文件下的所有图片进行灰度值转换
:param img_file: 图片文件存放目录
:param stretch_value: 拉伸值, 图片原始值*stretch_value的结果理论上应该小于255
"""
##获取目录下所有文件名
file_name_list = os.listdir(img_file)
#遍历所有图片文件
for file in file_name_list:
#获取某个图片的全路径
img_path = os.path.join(img_file, file)
#打开图片
image = Image.open(img_path)
print(image.mode) #p模式
img_arry = Image.fromarray(np.uint8(image))
print(img_arry.mode)#L模式
img_L = img_arry.convert("L")
print(img_L.mode)
#灰度拉伸
img_end = Image.fromarray(np.uint8(img_L) * stretch_value)
print(img_end.mode)
#保存图片,并覆盖原图
img_end.save(img_path)
#提示
print("完成对图片:",file," 的转换!")
print("所有图片均已完成转换!")
#程序主入口
if __name__ == "__main__":
# 需要转换的图片所在文件目录
img_file = r""#自己输入上一步生成的png格式的标签图
stretch_value = 1#自定义拉伸值,但要注意,图片标签值*stretch_value的结果理论上应该小于255
Mode_P_to_L(img_file, stretch_value)#调用自定义的转换方法
由于数据集数量较少,而有监督的语义分割机器学习需要大量的训练数据集,图片扩充就呼之欲出,
废话不多说直接上代码(这里有90度、180度、270度顺时针旋转和水平,垂直镜像种扩充形式)
from PIL import Image
import os
def Expand_trainset_pic(img_file):
"""
对指定目录下的图片进行简单数据扩充,包括对原图
进行90°、180°,270°顺时针旋转
和水平镜像以及垂直镜像
:param img_file: 需要批量扩充的图片所在文件夹
"""
#获取文件目录下的所有文件名
file_name_list = os.listdir(img_file)
for file in file_name_list:
#获取图片文件全路径
img_path = os.path.join(img_file, file)
#打开图片
image = Image.open(img_path)
#获取文件名
filename= os.path.splitext(file)[0]
#获取扩展名
file_ext=os.path.splitext(file)[1]
#90、180、270顺时针旋转
img1 = image.transpose(Image.ROTATE_90) # 引用固定的常量值
img1_name=filename+"_90"+file_ext
img1_save_path=os.path.join(img_file, img1_name)
img1.save(img1_save_path)
img2 = image.transpose(Image.ROTATE_180) # 引用固定的常量值
img2_name=filename+"_180"+file_ext
img2_save_path=os.path.join(img_file, img2_name)
img2.save(img2_save_path)
img3 = image.transpose(Image.ROTATE_270) # 引用固定的常量值
img3_name=filename+"_270"+file_ext
img3_save_path=os.path.join(img_file, img3_name)
img3.save(img3_save_path)
img4 = image.transpose(Image.FLIP_LEFT_RIGHT) # 引用固定的常量值
img4_name=filename+"_x"+file_ext
img4_save_path=os.path.join(img_file, img4_name)
img4.save(img4_save_path)
img5 = image.transpose(Image.FLIP_TOP_BOTTOM) # 引用固定的常量值
img5_name=filename+"_y"+file_ext
img5_save_path=os.path.join(img_file, img5_name)
img5.save(img5_save_path)
print("完成对图片:", file, " 的扩充")
print("完成对所有图片的扩充!")
#程序主入口
if __name__ == "__main__":
img_file = r' ' #在此输入需要转换的图片所在文件夹
Expand_trainset_pic(img_file)#调用自定义扩充函数
FBI警告!
该扩充操作只能针对于训练集,否则会造成数据泄露,导致虽然最后训练集以及验证集的精度都很完美但实际上很垃圾。
使用过ADEchallenge数据集及其相关代码的同学请自行学习read_MITSceneParsingData.py脚本文件的read_dataset函数以及create_image_lists函数,我之前的帖子有关于该代码报错的解决说明。