已标注数据的可视化有两种方式:
1.将图片和标注文件(json\xml)放在同一文件夹中,然后用labelme\labelimg打开此文件夹即可查看;
2.解析标注文件,用opencv进行可视化。
显然,本文要讲的是第二种!!!
一、labelimg标注的xml文件可视化
# 单张图片可视化
import cv2
import matplotlib.pyplot as plt
import xml.etree.ElementTree as ET
# 载入图像
img_path = '3.jpg'
img_bgr = cv2.imread(img_path)
# 可视化
# plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
# plt.show()
# 载入labelimg格式的xml标注文件
xml_path = '3.xml'
xml_inf = open(xml_path)
tree = ET.parse(xml_inf)
# print(tree)
root = tree.getroot()
# print(root)
# 框可视化配置
bbox_color = (255, 129, 0) # 框的颜色
bbox_thickness = 2 # 框的线宽
# 框类别文字
bbox_labelstr = {
'font_size':1, # 字体大小
'font_thickness':2, # 字体粗细
'offset_x':0, # X 方向,文字偏移距离,向右为正
'offset_y':-10, # Y 方向,文字偏移距离,向下为正
}
# 画框
for obj in root.iter('object'): # 一个object代表一个标注物体
# 框的类别
bbox_label = obj.find('name').text
# 框的两点坐标
# 左上角坐标
bbox_top_left_x = int(obj.find('bndbox').find('xmin').text)
bbox_top_left_y = int(obj.find('bndbox').find('ymin').text)
# 右下角坐标
bbox_bottom_right_x = int(obj.find('bndbox').find('xmax').text)
bbox_bottom_right_y = int(obj.find('bndbox').find('ymax').text)
# 画矩形:画框
img_bgr = cv2.rectangle(img_bgr, (bbox_top_left_x, bbox_top_left_y), (bbox_bottom_right_x, bbox_bottom_right_y),
bbox_color, bbox_thickness)
# 写框类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
img_bgr = cv2.putText(img_bgr, bbox_label, (
bbox_top_left_x + bbox_labelstr['offset_x'],
bbox_top_left_y + bbox_labelstr['offset_y']),
cv2.FONT_HERSHEY_SIMPLEX, bbox_labelstr['font_size'], bbox_color,
bbox_labelstr['font_thickness'])
# 可视化
plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
plt.show()
# 当前目录下保存可视化结果
cv2.imwrite('visualize3.jpg', img_bgr)
# 数据集可视化
import cv2
import os
import matplotlib.pyplot as plt
import xml.etree.ElementTree as ET
data_path = os.path.join("VOCdevkit", "VOC2007")
imgs_path = os.path.join(data_path, 'JPEGImages')
anns_path = os.path.join(data_path, 'Annotations')
# img_names = set([i for i in os.listdir(imgs_path)])
# ann_names = set([i for i in os.listdir(anns_path)])
img_names = set([os.path.splitext(i)[0] for i in os.listdir(imgs_path)])
ann_names = set([os.path.splitext(i)[0] for i in os.listdir(anns_path)])
img_names = list(img_names)
ann_names = list(ann_names)
# print(img_names)
# print(ann_names)
# print(img_names[0])
# print(ann_names[0])
# print(len(img_names))
# print(len(ann_names))
for i in range(len(img_names)):
img_path = os.path.join(imgs_path, img_names[i]+".jpg")
img_bgr = cv2.imread(img_path)
xml_path = os.path.join(anns_path, ann_names[i] + ".xml")
xml_inf = open(xml_path)
tree = ET.parse(xml_inf)
root = tree.getroot()
# 框可视化配置
bbox_color = (255, 129, 0) # 框的颜色
bbox_thickness = 2 # 框的线宽
# 框类别文字
bbox_labelstr = {
'font_size': 1, # 字体大小
'font_thickness': 2, # 字体粗细
'offset_x': 0, # X 方向,文字偏移距离,向右为正
'offset_y': -10, # Y 方向,文字偏移距离,向下为正
}
# 画框
for obj in root.iter('object'): # 一个object代表一个标注物体
# 框的类别
bbox_label = obj.find('name').text
# 框的两点坐标
# 左上角坐标
bbox_top_left_x = int(obj.find('bndbox').find('xmin').text)
bbox_top_left_y = int(obj.find('bndbox').find('ymin').text)
# 右下角坐标
bbox_bottom_right_x = int(obj.find('bndbox').find('xmax').text)
bbox_bottom_right_y = int(obj.find('bndbox').find('ymax').text)
# 画矩形:画框
img_bgr = cv2.rectangle(img_bgr, (bbox_top_left_x, bbox_top_left_y), (bbox_bottom_right_x, bbox_bottom_right_y),
bbox_color, bbox_thickness)
# 写框类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
img_bgr = cv2.putText(img_bgr, bbox_label, (
bbox_top_left_x + bbox_labelstr['offset_x'],
bbox_top_left_y + bbox_labelstr['offset_y']),
cv2.FONT_HERSHEY_SIMPLEX, bbox_labelstr['font_size'], bbox_color,
bbox_labelstr['font_thickness'])
cv2.imwrite("D:\cnn\Segmentation\CCBANet-main\VOCdevkit\VOC2007/result//{}.jpg".format(img_names[i]), img_bgr)
# 提取包含某些特定类别的图片和xml文件
import os
import shutil
import xml.etree.ElementTree as ET
data_path = os.path.join("D:\cnn\Segmentation\CCBANet-main\VOCdevkit", "VOC2007")
imgs_path = os.path.join(data_path, 'JPEGImages')
anns_path = os.path.join(data_path, 'Annotations')
img_names = set([os.path.splitext(i)[0] for i in os.listdir(imgs_path)])
ann_names = set([os.path.splitext(i)[0] for i in os.listdir(anns_path)])
img_names = list(img_names)
print(len(img_names))
ann_names = list(ann_names)
for i in range(len(img_names)):
img_path = os.path.join(imgs_path, img_names[i]+".png")
xml_path = os.path.join(anns_path, ann_names[i] + ".xml")
xml_inf = open(xml_path)
tree = ET.parse(xml_inf)
root = tree.getroot()
result_path = os.path.join(data_path, 'SD')
# 分别单独运行每一个类别
for obj in root.iter('object'): # 一个object代表一个标注物体
# 框的类别
bbox_label = obj.find('name').text
# if bbox_label == "GL":
# shutil.copy(img_path, result_path)
# shutil.copy(xml_path, result_path)
# if bbox_label == "PG":
# shutil.copy(img_path, result_path)
# shutil.copy(xml_path, result_path)
if bbox_label == "SD":
shutil.move(img_path, result_path)
shutil.copy(xml_path, result_path)
二、labelme标注的json文件可视化
# 导入工具包
import cv2
import numpy as np
import json
import matplotlib.pyplot as plt
# matplotlib inline
# 载入图像
img_path = '1.jpg'
img_bgr = cv2.imread(img_path)
# 可视化
# plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
# plt.show()
# 载入labelme格式的json标注文件
labelme_path = '1_labelme.json'
with open(labelme_path, 'r', encoding='utf-8') as f:
labelme = json.load(f)
# 查看标注信息 rectangle:矩形 point:点 polygon:多边形
# print(labelme.keys())
# dict_keys(['version', 'flags', 'shapes', 'imagePath', 'imageData', 'imageHeight', 'imageWidth'])
# print(labelme['shapes'])
# <<<<<<<<<<<<<<<<<<可视化框(rectangle)标注>>>>>>>>>>>>>>>>>>>>>
# 框可视化配置
bbox_color = (255, 129, 0) # 框的颜色
bbox_thickness = 5 # 框的线宽
# 框类别文字
bbox_labelstr = {
'font_size':6, # 字体大小
'font_thickness':14, # 字体粗细
'offset_x':0, # X 方向,文字偏移距离,向右为正
'offset_y':-80, # Y 方向,文字偏移距离,向下为正
}
# 画框
for each_ann in labelme['shapes']: # 遍历每一个标注
if each_ann['shape_type'] == 'rectangle': # 筛选出框标注
# 框的类别
bbox_label = each_ann['label']
# 框的两点坐标
bbox_keypoints = each_ann['points']
bbox_keypoint_A_xy = bbox_keypoints[0]
bbox_keypoint_B_xy = bbox_keypoints[1]
# 左上角坐标
bbox_top_left_x = int(min(bbox_keypoint_A_xy[0], bbox_keypoint_B_xy[0]))
bbox_top_left_y = int(min(bbox_keypoint_A_xy[1], bbox_keypoint_B_xy[1]))
# 右下角坐标
bbox_bottom_right_x = int(max(bbox_keypoint_A_xy[0], bbox_keypoint_B_xy[0]))
bbox_bottom_right_y = int(max(bbox_keypoint_A_xy[1], bbox_keypoint_B_xy[1]))
# 画矩形:画框
img_bgr = cv2.rectangle(img_bgr, (bbox_top_left_x, bbox_top_left_y), (bbox_bottom_right_x, bbox_bottom_right_y),
bbox_color, bbox_thickness)
# 写框类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
img_bgr = cv2.putText(img_bgr, bbox_label, (
bbox_top_left_x + bbox_labelstr['offset_x'],
bbox_top_left_y + bbox_labelstr['offset_y']),
cv2.FONT_HERSHEY_SIMPLEX, bbox_labelstr['font_size'], bbox_color,
bbox_labelstr['font_thickness'])
# # 可视化
# plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
# plt.show()
# <<<<<<<<<<<<<<<<<<可视化关键点(keypoint)标注>>>>>>>>>>>>>>>>>>>>>
# 关键点的可视化配置
# 关键点配色
kpt_color_map = {
'angle_30':{'id':0, 'color':[255,0,0], 'radius':30, 'thickness':-1},
'angle_60':{'id':1, 'color':[0,255,0], 'radius':30, 'thickness':-1},
'angle_90':{'id':2, 'color':[0,0,255], 'radius':30, 'thickness':-1}
}
# 点类别文字
kpt_labelstr = {
'font_size':4, # 字体大小
'font_thickness':12, # 字体粗细
'offset_x':30, # X 方向,文字偏移距离,向右为正
'offset_y':100, # Y 方向,文字偏移距离,向下为正
}
# 画点
for each_ann in labelme['shapes']: # 遍历每一个标注
if each_ann['shape_type'] == 'point': # 筛选出关键点标注
kpt_label = each_ann['label'] # 该点的类别
# 该点的 XY 坐标
kpt_xy = each_ann['points'][0]
kpt_x, kpt_y = int(kpt_xy[0]), int(kpt_xy[1])
# 该点的可视化配置
kpt_color = kpt_color_map[kpt_label]['color'] # 颜色
kpt_radius = kpt_color_map[kpt_label]['radius'] # 半径
kpt_thickness = kpt_color_map[kpt_label]['thickness'] # 线宽(-1代表填充)
# 画圆:画该关键点
img_bgr = cv2.circle(img_bgr, (kpt_x, kpt_y), kpt_radius, kpt_color, kpt_thickness)
# 写该点类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
img_bgr = cv2.putText(img_bgr, kpt_label, (kpt_x + kpt_labelstr['offset_x'], kpt_y + kpt_labelstr['offset_y']),
cv2.FONT_HERSHEY_SIMPLEX, kpt_labelstr['font_size'], kpt_color,
kpt_labelstr['font_thickness'])
# 可视化
# plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
# plt.show()
# <<<<<<<<<<<<<<<<<<可视化多段线(polygon)标注>>>>>>>>>>>>>>>>>>>>>
# 多段线的可视化配置
poly_color = (151, 57, 224)
poly_thickness = 3
poly_labelstr = {
'font_size':4, # 字体大小
'font_thickness':12, # 字体粗细
'offset_x':-200, # X 方向,文字偏移距离,向右为正
'offset_y':0, # Y 方向,文字偏移距离,向下为正
}
# 画多段线
img_mask = np.ones(img_bgr.shape, np.uint8) #创建一个和img_bgr一样大小的黑色mask
for each_ann in labelme['shapes']: # 遍历每一个标注
if each_ann['shape_type'] == 'polygon': # 筛选出多段线(polygon)标注
poly_label = each_ann['label'] # 该多段线的类别
poly_points = [np.array(each_ann['points'], np.int32).reshape((-1, 1, 2))] #reshape后增加一个维度
# 该多段线平均 XY 坐标,用于放置多段线类别文字
x_mean = int(np.mean(poly_points[0][:, 0, :][:, 0])) #取出所有点的x坐标并求平均值
y_mean = int(np.mean(poly_points[0][:, 0, :][:, 1])) #取出所有点的y坐标并求平均值
# 画该多段线轮廓
img_bgr = cv2.polylines(img_bgr, poly_points, isClosed=True, color=poly_color, thickness=poly_thickness)
# 画该多段线内部填充
img_mask = cv2.fillPoly(img_mask, poly_points, color=poly_color) #填充的颜色为color=poly_color
# 写该多段线类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
img_bgr = cv2.putText(img_bgr, poly_label,
(x_mean + poly_labelstr['offset_x'], y_mean + poly_labelstr['offset_y']),
cv2.FONT_HERSHEY_SIMPLEX, poly_labelstr['font_size'], poly_color,
poly_labelstr['font_thickness'])
opacity = 0.8 # 透明度,越大越接近原图
img_bgr = cv2.addWeighted(img_bgr, opacity, img_mask, 1-opacity, 0)
# 可视化
plt.imshow(img_bgr[:,:,::-1]) # 将bgr通道转换成rgb通道
plt.show()
# 可视化多段线填充效果
# plt.imshow(img_mask[:, :, ::-1]) # 将bgr通道转换成rgb通道
# plt.show()
# 当前目录下保存可视化结果
cv2.imwrite('visualize1.jpg', img_bgr)
# 可视化json标注文件,并将其转化为mask掩码图像
import base64
import json
import os
import os.path as osp
from labelme.logger import logger
from labelme import utils
def main():
logger.warning(
"This script is aimed to demonstrate how to convert the "
"JSON file to a single image dataset."
)
logger.warning(
"It won't handle multiple JSON files to generate a "
"real-use dataset."
)
# json_file是标注完之后生成的json文件的目录。out_dir是输出目录,即数据处理完之后文件保存的路径
json_file = r"D:\cnn\deeplabv3-plus-pytorch-main_YUANBAN\datasets\before"
out_dir = r"D:\cnn\deeplabv3-plus-pytorch-main_YUANBAN\datasets\SegmentationClass345-onlySB"
# 如果输出的路径不存在,则自动创建这个路径
if not osp.exists(out_dir):
os.mkdir(out_dir)
# 将类别名称转换成数值,以便于计算,此处先定义好每个(部分)类别对应的像素值
label_name_to_value = {"_background_": 0, "SB": 1}
for file_name in os.listdir(json_file):
# 遍历json_file里面所有的文件,并判断这个文件是不是以.json结尾
if file_name.endswith(".json"):
path = os.path.join(json_file, file_name)
if os.path.isfile(path):
data = json.load(open(path))
# 获取json里面的图片数据,也就是二进制数据
imageData = data.get("imageData")
# 如果通过data.get获取到的数据为空,就重新读取图片数据
if not imageData:
imagePath = os.path.join(json_file, data["imagePath"])
with open(imagePath, "rb") as f:
imageData = f.read()
imageData = base64.b64encode(imageData).decode("utf-8")
# 将二进制数据转变成numpy格式的数据
img = utils.img_b64_to_arr(imageData)
for shape in sorted(data["shapes"], key=lambda x: x["label"]):
label_name = shape["label"]
# print(label_name)
if label_name in label_name_to_value:
label_value = label_name_to_value[label_name]
else: #如果想排除某个类别,就把该类别的label_value置为0
# label_value = len(label_name_to_value)
label_value = 0
label_name_to_value[label_name] = label_value
lbl, _ = utils.shapes_to_label(img.shape, data["shapes"], label_name_to_value)
# print(label_name_to_value) #{'_background_': 0, 'SB': 1, 'PI': 0}
label_names = [None] * (max(label_name_to_value.values()) + 1)
for name, value in label_name_to_value.items():
label_names[value] = name
# 将输出结果保存
utils.lblsave(osp.join(out_dir, "%s.png" % file_name.split(".")[0]), lbl)
logger.info("Saved to: {}".format(out_dir))
print("label:", label_name_to_value)
if __name__ == "__main__":
main()
import base64
import json
import os
import os.path as osp
import numpy as np
from labelme.logger import logger
from labelme import utils
def main():
logger.warning(
"This script is aimed to demonstrate how to convert the "
"JSON file to a single image dataset."
)
logger.warning(
"It won't handle multiple JSON files to generate a "
"real-use dataset."
)
# json_file是标注完之后生成的json文件的目录。out_dir是输出目录,即数据处理完之后文件保存的路径
json_file = r"D:\cnn\deeplabv3-plus-pytorch-main_YUANBAN\datasets\before"
out_dir = r"D:\cnn\deeplabv3-plus-pytorch-main_YUANBAN\datasets\SegmentationClass-onlyPI"
# 如果输出的路径不存在,则自动创建这个路径
if not osp.exists(out_dir):
os.mkdir(out_dir)
# 将类别名称转换成数值,以便于计算,此处先定义好每个(部分)类别对应的像素值
label_name_to_value = {"_background_": 0, "PI": 1}
for file_name in os.listdir(json_file):
# 遍历json_file里面所有的文件,并判断这个文件是不是以.json结尾
if file_name.endswith(".json"):
path = os.path.join(json_file, file_name)
if os.path.isfile(path):
data = json.load(open(path))
# 获取json里面的图片数据,也就是二进制数据
imageData = data.get("imageData")
# 如果通过data.get获取到的数据为空,就重新读取图片数据
if not imageData:
imagePath = os.path.join(json_file, data["imagePath"])
with open(imagePath, "rb") as f:
imageData = f.read()
imageData = base64.b64encode(imageData).decode("utf-8")
# 将二进制数据转变成numpy格式的数据
img = utils.img_b64_to_arr(imageData)
for shape in sorted(data["shapes"], key=lambda x: x["label"]):
label_name = shape["label"]
# print(label_name)
if label_name in label_name_to_value:
label_value = label_name_to_value[label_name]
else: #如果想排除某个类别,就把该类别的label_value置为0
# label_value = len(label_name_to_value)
label_value = 0
label_name_to_value[label_name] = label_value
lbl, _ = utils.shapes_to_label(img.shape, data["shapes"], label_name_to_value) #lbl即为对应的mask掩码图
# print(label_name_to_value) #{'_background_': 0, 'SB': 1, 'PI': 0}
# print(lbl)
label_names = [None] * (max(label_name_to_value.values()) + 1)
for name, value in label_name_to_value.items():
label_names[value] = name
mask_array = np.array(lbl)
# print(np.unique(mask_array))
print(np.max(mask_array))
if np.max(mask_array) == 1: #判断lbl中的像素最大值是否为1,否则为无标注图
# 将输出结果保存
utils.lblsave(osp.join(out_dir, "%s.png" % file_name.split(".")[0]), lbl)
logger.info("Saved to: {}".format(out_dir))
print("label:", label_name_to_value)
if __name__ == "__main__":
main()
reference
【标注关键点检测数据集】 https://www.bilibili.com/video/BV19g4y1777q/?share_source=copy_web&vd_source=95705b32f23f70b32dfa1721628d5874