接上文https://blog.csdn.net/Hu_helloworld/article/details/118610897?spm=1001.2014.3001.5501
我们已经可以通过labelme工具标注语义分割数据,然后根据labelme提供的labelme_json_to_dataset脚本,将所有json文件批量生成如下的训练数据:
然后我们通过方法
1、moveSrcMasksImage()分别将原图(img.png)和掩膜图像(label.png)移动整理到JPEGImages和SegmentationClass文件夹。
2、通过couple_img_masks()方法制作、划分数据集。
这样划分出的数据集是一个原图img.png对应一个根据类别颜色分割的掩膜图像label.png。
采用VOC格式的数据集,img.png 和 label.png分别放入JPEGImages和SegmentationClass文件夹中,划分数据集将文件名写入train.txt和val.txt。训练时可选择resnet、mobilenet等多个backbone
用这样的数据集训练deeplab系列模型没问题。具体使用参见官方repo:https://github.com/jfzhang95/pytorch-deeplab-xception
但这些数据在bisenetv2训练时出现“RuntimeError: copy_if failed to synchronize: cudaErrorIllegalAddress: an illegal memory access was encountered”的报错,原因是bisenetv2要求输入的标签是单通道的灰度图。而我们的mask图像仍然是RGB图
我们知道RGB图像可以用颜色区分类别,而灰度图每个像素只有0-255的灰度值,如何用单通道的灰度图表示多种类别呢?
解决思路巧妙的将每个类别的rgb颜色值用单通道的0,1…255灰度值置换。
其中我们用labelme脚本labelme_json_to_dataset生成的__json文件夹中:
我们得到RGB的label.png(局部语义类别标签)后,还需要进行灰度类别转换成单通道的标签图像:
灰度类别转换后,属于背景区域的像素点的值全是0,属于标记目标的像素点按照所有类别名的顺序1,2…255排列。如属于cat的像素点值全是1。若再加一个dog像素点值就全是2
'''
TODO:
提取__json目录下的原图、并将label.png转换为最终的单通道标签。
param:
__jsondir: labelme生成的__json文件夹目录
class_txt: 所有像素的类别(包括背景0)
image_path: 提取的原图路径
label_path: 提取并转换的单通道全局标签路径
输出转换的标签数量、原图和标签的路径
Note:
__json文件夹已全部生成,并将img.png,label.png提取整理到不同路径
'''
def label2singlechannel(__jsondir, class_txt='class_name.txt', image_path=None, label_path=None):
class_txt = open(class_txt, "r", encoding='utf-8')
class_names = class_txt.read().splitlines() # 全局类别
if image_path==None and label_path==None:
image_path = os.path.join(__jsondir.rsplit('/', 2)[0], 'JPEGImages')
label_path = os.path.join(__jsondir.rsplit('/', 2)[0], 'SegmentationClass')
if not os.path.exists(image_path):
os.mkdir(image_path)
if not os.path.exists(label_path):
os.mkdir(label_path)
for json_dir in os.listdir(__jsondir):
src_path = os.path.join(__jsondir, json_dir, 'img.png')
file_path = os.path.join(__jsondir, json_dir, 'label.png')
if not os.path.exists(src_path) or not os.path.exists(file_path): # 文件夹为空 img不存在
continue
label_names = os.path.join(__jsondir, json_dir, 'label_names.txt')
src = Image.open(src_path)
src.save(os.path.join(image_path, json_dir+'.png'))
img = Image.open(file_path)
with open(label_names, "r") as f:
names = f.read().splitlines() # x_json文件里面存在的类 局部的类
new = Image.new("RGB", [np.shape(img)[1], np.shape(img)[0]])
# 找到局部的类在全局中的类的序号
for name in names:
index_json = names.index(name) # 局部类
index_all = class_names.index(name) # 全局类别
# 将局部类转换成为全局类
# 将原图img中像素点的值为index_json的像素点乘以其在全局中的像素点的所对应的类的序号 得到 其实际在数据集中像素点的值
# 比如dog,在局部类(output/x_json/label_names)中它的序号为1,dog在原图中的像素点的值也为1.
# 但是在全局的类(before/classes.txt)中其对应的序号为2,所以在新的图片中要将局部类的像素点的值*全局类的序号,从而得到标签文件
new = new + np.expand_dims(index_all * (np.array(img) == index_json), -1)
new = Image.fromarray(np.uint8(new))
# 将转变后的得到的新的最终的标签图片保存到make_dataset/png文件夹下
new.save(os.path.join(label_path, json_dir + '.png'))
print(f"save: - {
os.path.join(label_path, json_dir + '.png')}")
# 找到新的标签文件中像素点值的最大值和最小值,最大值为像素点对应的类在class_name.txt中的序号,最小值为背景,即0
print(np.max(new), np.min(new))
print(f"\nJPEGImages: {
image_path}\nSegmentationClass: {
label_path}")
由于像素点灰度值0 1 2亮度值很小,最终生成参与训练的全局标签文件是这种全黑的。
用原图 + 单通道的标签图像即可训练BiseNetv2模型。
具体BiseNetv2的训练方式可参考官方repo:https://github.com/CoinCheung/BiSeNet或其他教程
件是这种全黑的。
用原图 + 单通道的标签图像即可训练BiseNetv2模型。
具体BiseNetv2的训练方式可参考官方repo:https://github.com/CoinCheung/BiSeNet或其他教程