参考:同济子豪兄:GitHub - TommyZihao/Label2Everything: 常见计算机视觉标注格式相互转换
import os
import json
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 加载图像
img_path = 'uk1.jpeg'
img_bgr = cv2.imread(img_path)
#创建背景
img_mask = np.zeros(img_bgr.shape[:2])
#载入该图像labelme格式的json标注文件
labelme_json_path = 'uk1.json'
with open(labelme_json_path, 'r', encoding='utf-8') as f:
labelme = json.load(f)
# 查看该图中的所有标注信息
for each in labelme['shapes']:
print(each['label'], each['shape_type'])
#每个类别的信息及画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格式)
mask_path = img_path.split('.')[0] + '.png'
cv2.imwrite(mask_path, img_mask)
# 载入mask标注图像
mask_img = cv2.imread('uk1.png')
批量操作
# 查看数据集目录结构
import seedir as sd
sd.seedir('Watermelon87_Semantic_Seg_Labelme', style='emoji', depthlimit=1)
📁 Watermelon87_Semantic_Seg_Labelme/
├─📁 images/
└─📁 labelme_jsons/
删除系统自动生成的多余文件
查看待删除的多余文件
!find . -iname '__MACOSX'
!find . -iname '.DS_Store'
!find . -iname '.ipynb_checkpoints'
./.ipynb_checkpoints
删除多余文件
!for i in `find . -iname '__MACOSX'`; do rm -rf $i;done
!for i in `find . -iname '.DS_Store'`; do rm -rf $i;done
!for i in `find . -iname '.ipynb_checkpoints'`; do rm -rf $i;done
验证多余文件已删除
!find . -iname '__MACOSX'
!find . -iname '.DS_Store'
!find . -iname '.ipynb_checkpoints'
进入数据集目录
import os
import json
import numpy as np
import cv2
import shutil
from tqdm import tqdm
Dataset_Path = 'Watermelon87_Semantic_Seg_Labelme'
每个类别的信息及画mask的顺序(按照由大到小,由粗到精的顺序)
# 0-背景,从 1 开始
class_info = [
{'label':'red', 'type':'polygon', 'color':1}, # polygon 多段线
{'label':'green', 'type':'polygon', 'color':2},
{'label':'white', 'type':'polygon', 'color':3},
{'label':'seed-black','type':'polygon','color':4},
{'label':'seed-white','type':'polygon','color':5}
]
单张图像labelme转mask函数
def labelme2mask_single_img(img_path, labelme_json_path):
'''
输入原始图像路径和labelme标注路径,输出 mask
'''
img_bgr = cv2.imread(img_path)
img_mask = np.zeros(img_bgr.shape[:2]) # 创建空白图像 0-背景
with open(labelme_json_path, 'r', encoding='utf-8') as f:
labelme = json.load(f)
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'])
return img_mask
labelme转mask-批量
os.chdir(Dataset_Path)
os.mkdir('masks')
os.chdir('images')
for img_path in tqdm(os.listdir()):
try:
labelme_json_path = os.path.join('../', 'labelme_jsons', '.'.join(img_path.split('.')[:-1])+'.json')
img_mask = labelme2mask_single_img(img_path, labelme_json_path)
mask_path = img_path.split('.')[0] + '.png'
cv2.imwrite(os.path.join('../','masks',mask_path), img_mask)
except Exception as E:
print(img_path, '转换失败', E)
转换之后的mask保存在masks
文件夹中
重命名和删除文件夹
os.chdir('../')
shutil.move('images', 'img_dir')
shutil.move('masks', 'ann_dir')
!rm -rf labelme_jsons
os.chdir('../')
得到最终的语义分割数据集
查看数据集目录结构
import seedir as sd
sd.seedir('Watermelon87_Semantic_Seg_Labelme', style='emoji', depthlimit=1)
📁 Watermelon87_Semantic_Seg_Labelme/ ├─📁 img_dir/ └─📁 ann_dir/