想要标注自己的语义分割数据集,我们常借助的工具是labelme。

·下载Labelme

首先,我们下载labelme:打开网站https://github.com/wkentaro/labelme,进入labelme的github主页,然后下划,在右侧找到“Releases”并点击下方版本,之后选择对应自己系统的版本下载即可。

ADE20K语义分割色彩参考表格_python

·运行Labelme并完成图片标注

下载并安装之后,打开labelme。首先点击“File”,取消勾选“Save With Image Data”选项,并勾选“Save Automatically”选项,这样可以减少文件大小,并且可以保证可以自动保存文件,避免丢失。

之后点击“Open Dir”,打开需要标注的图片所在的文件夹(注意:需要提前将图片保存在同一个文件夹中).在“Edit”中,你可以选择不同的标注类型,如:多段线,矩形,圆,点等等。选择对应的标注工具,将不同事物的轮廓“画”出来,要尽量保证轮廓的平滑。“画”完之后对其命名。如果在标注过程中有失误或者需要修改的地方,你可以点击左侧的“Edit Polygons”对其进行修正,确认无误后,按“Ctrl+S”进行保存。之后,你在本地就可以找到一个json格式的文件,这就是labelme格式的json标注文件。

如果你觉得人工标注过程中轮廓“画”的没有那么完美,你可以选择“Edit”中的“AI-Polygon”,它可以自动帮我们标注,比如你想标注一块草地,你只需要点击这块草地,系统可以自动帮我们把草地标注出来,在这个过程中,你也可以多点几个区域使标注更合理,之后按回车键或者双击即可确定。

在标注完成一张图片之后,你可以按d进入下一张图片。

注意,点开一个json文件之后,你可以看到每一个标注的名称,标注工具类型,标注点坐标等信息。下划到最下方,可以看到这张图片的路径,这里的路径只能是图片名字本身,不能存在其他路径或符号。

现在,我们就标注好了json文件,但是labelme的json文件并不能直接用于语义分割模型训练,我们需要把json文件转换未整数掩膜mask格式,在像素层面上作类别标注,并且划分出训练几何测试集,才能用于后续的训练。

·代码实现labelme的json格式转mask格式

准备工作:打开网站github.com/TommyZihao/Label2Everything ,找到“labelme2mask”并打开,里面包含了分别对单张图像,训练集以及划分训练测试集的代码,全部下载到本地。

我们以处理单张图像为例,介绍如何将labelme格式的json文件转换为mask格式。

我们首先导入工具包:

i

mport os
import json
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
之后载入我们刚刚标注的jpeg图片:
img_path=”***.jpeg”
# ***为你刚才自己设置的图片名称
之后创立一张与原图一样大小的空白图像:
img_mask = np.zeros(img_brg.shape[:21])
plt.imshow(img_mask)
plt.show()
之后载入labelme格式的json标注文件:
labelme_json_path = “***.json”
# ***同上
with open(labelme_json_path,”r”,encoding=”utf-8”) as f:
labelme = json.load(f)
labelme.keys()
dict_keys([“version” , ”flags” , “shapes” , “imagePath” , “imageData” , “imageHeight” , “imageWidth”])
每个类别的信息及画mask的顺序(按照由大到小,由粗到精的顺序)
# 0-背景,从 1 开始class_info = [
    {'label':'sky', 'type':'polygon', 'color':1},                    # polygon 多段线
    {'label':'road', 'type':'polygon', 'color':2},
    {'label':'building', 'type':'polygon', 'color':3},
    {'label':'tower','type':'polygon','color':4},
    {'label':'bus','type':'polygon','color':5},
    {'label':'car','type':'polygon','color':6},
    {'label':'tree','type':'polygon','color':7},
    {'label':'fence','type':'polygon','color':8},
    {'label':'wall','type':'polygon','color':9},
    {'label':'person','type':'polygon','color':10},
    {'label':'clock', 'type':'circle', 'color':11, 'thickness':-1},   # circle 圆形,-1表示填充
    {'label':'lane', 'type':'line', 'color':12, 'thickness':5},       # line 两点线段,填充线宽
    {'label':'sign', 'type':'linestrip', 'color':13, 'thickness':3}   # linestrip 多段线,填充线宽]
之后按顺序将mask画在空白图上面:
for one_class in class_info: # 按顺序遍历每一个类别
    for each in labelme['shapes']: # 遍历所有标注,找到属于当前类别的标注
        if each['label'] == one_class['label']:
            if one_class['type'] == 'polygon': # polygon 多段线标注
                
                # 获取点的坐标
                points = [np.array(each['points'], dtype=np.int32).reshape((-1, 1, 2))]
                
                # 在空白图上画 mask(闭合区域)
                img_mask = cv2.fillPoly(img_mask, points, color=one_class['color'])
                
            elif one_class['type'] == 'line' or one_class['type'] == 'linestrip': # line 或者 linestrip 线段标注
                
                # 获取点的坐标
                points = [np.array(each['points'], dtype=np.int32).reshape((-1, 1, 2))]
                
                # 在空白图上画 mask(非闭合区域)
                img_mask = cv2.polylines(img_mask, points, isClosed=False, color=one_class['color'], thickness=one_class['thickness']) 
            
            elif one_class['type'] == 'circle': # circle 圆形标注
                
                points = np.array(each['points'], dtype=np.int32)
                
                center_x, center_y = points[0][0], points[0][1] # 圆心点坐标
                
                edge_x, edge_y = points[1][0], points[1][1]     # 圆周点坐标
                
                radius = np.linalg.norm(np.array([center_x, center_y] - np.array([edge_x, edge_y]))).astype('uint32') # 半径
                
                img_mask = cv2.circle(img_mask, (center_x, center_y), radius, one_class['color'], one_class['thickness'])
            
            else:
                print('未知标注类型', one_class['type'])
plt.imshow(img_mask)
plt.show()
然后保存mask标注图像(png格式):
img_mask.shape
mask_path = img_path.split('.')[0] + '.png'
cv2.imwrite(mask_path, img_mask)
最后,载入mask标注图像:
mask_img = cv2.imread('uk1.png')
mask_img.shape
np.unique(mask_img)
plt.imshow(mask_img[:,:,0])
plt.show()

现在我们就完成了abelme格式的json文件到mask格式的转换。其他类型的转换也大同小异,你可参考github.com/TommyZihao/Label2Everything里面的教程一步步的进行。

最后,本教程参考了同济自豪兄和OpenMMLab的相关教程,非常感谢OpenMMLab提供的平台。向所有开源工作者致敬。