(一)环境安装配置
在之前的文章中已经介绍了Anaconda环境的配置,因此这里我们直接从数据集制作的环境开始说起。那么我自己使用的数据及制作则是如标题所示,语义分割使用labelme,目标检测使用labelimg。首先创建一个新环境“label”,并在其中进行软件环境的下载。
conda create -n label python=3.7
激活环境备用,并添加kernel。
#激活新环境
activate label
#在jupyter中添加kernel
pip install ipykernel
#然后添加新的kernel
python -m ipykernel install --name label
)
(二)labelme
1. labelme下载
下载labelme
pip install labelme
2. labelme使用(以多分类进行举例)
在对应环境输入labelme打开标注窗口。
选择标注文件路径,并打开。
点击Edit–>Create Plygons,围绕目标周围进行标注,输入分类名称。可以点击File–>Save Automatically自动保存。
标注完成后,会生成.json文件。
3. 代码转化
3.1 二分类(便于做二值分割的使用)
import json
import os
import numpy as np
from PIL import Image
from labelme import utils
def json2mask_multi(json_path, save_path):
if not os.path.exists(save_path):
os.makedirs(save_path)
for json_name in os.listdir(json_path):
data = json.load(open(os.path.join(json_path, json_name)))
json_name = json_name.split('.')[0]
# 根据imageData字段的字符可以得到原图像
img = utils.img_b64_to_arr(data['imageData'])
# lbl为label图像(用类别名对应的数字来标,背景为0)
# lbl_names为label名和数字的对应关系字典
lbl, lbl_names = utils.labelme_shapes_to_label(img.shape, data['shapes'])
mask = Image.fromarray(lbl).convert('L')
# putpalette给对象加上调色板,相当于上色:R,G,B
# 三个数一组,对应于RGB通道,可以自己定义标签颜色
mask.putpalette([0, 0, 0,
255, 255, 255,
255, 255, 255,
128, 128, 128])
mask.save(os.path.join(save_path, json_name +".png"))
#json路径与目标路径
json_path = "E:\\jupyter\\bao\\image_programming\\labelme\\seg\\val\\json"
save_path = "E:\\jupyter\\bao\\image_programming\\labelme\\seg\\val\\png"
json2mask_multi(json_path,save_path)
结果展示
3.2 多分类
import argparse
import base64
import json
import os
import os.path as osp
import imgviz
import PIL.Image
from labelme.logger import logger
from labelme import utils
import glob
# 最前面加入导包
import yaml
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."
)
parser = argparse.ArgumentParser()
###############################################json文件位置##############################
# parser.add_argument("json_file")
parser.add_argument("-json_dir",default="E:\\jupyter\\bao\\image_programming\\labelme\\seg\\val\\json")
#######################################mask输出文件位置##################################
parser.add_argument("-out", default="E:\\jupyter\\bao\\image_programming\\labelme\\seg\\val\\png")
args = parser.parse_args(args=[])
assert args.json_dir is not None and len(args.json_dir) > 0
# json_file = args.json_file
json_dir = args.json_dir
if osp.isfile(json_dir):
json_list = [json_dir] if json_dir.endswith('.json') else []
else:
json_list = glob.glob(os.path.join(json_dir, '*.json'))
for json_file in json_list:
json_name = osp.basename(json_file).rsplit('.',1)[0]
out_dir = args.out if (args.out is not None) else osp.join(osp.dirname(json_file), json_name)
if not osp.exists(out_dir):
os.makedirs(out_dir)
data = json.load(open(json_file))
imageData = data.get("imageData")
if not imageData:
imagePath = os.path.join(os.path.dirname(json_file), 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)
######################################种类修改##################################
label_name_to_value = {"_background_": 0,"Deep grinding mark": 1,"Scratch": 2,"Pit": 3,"Adhesion": 4,"Bulge": 5}
for shape in sorted(data["shapes"], key=lambda x: x["label"]):
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
lbl, _ = utils.shapes_to_label(
img.shape, data["shapes"], label_name_to_value
)
label_names = [None] * (max(label_name_to_value.values()) + 1)
for name, value in label_name_to_value.items():
label_names[value] = name
lbl_viz = imgviz.label2rgb(
lbl, imgviz.asgray(img), label_names=label_names, loc="rb"
)
PIL.Image.fromarray(img).save(osp.join(out_dir, json_name+"img.png"))
utils.lblsave(osp.join(out_dir, json_name+"label.png"), lbl)
PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, json_name+"label_viz.png"))
with open(osp.join(out_dir,json_name+ "label_names.txt"), "w") as f:
for lbl_name in label_names:
f.write(lbl_name + "\n")
logger.info("Saved to: {}".format(out_dir))
#######
#增加了yaml生成部分
logger.warning('info.yaml is being replaced by label_names.txt')
info = dict(label_names=label_names)
with open(osp.join(out_dir, 'info.yaml'), 'w') as f:
yaml.safe_dump(info, f, default_flow_style=False)
logger.info('Saved to: {}'.format(out_dir))
if __name__ == "__main__":
main()
结果展示
3.3 多分类x_label更名与原文件同名
自己的数据集读取习惯是原图与label名称一致,所以进行更名。
import os
class BatchRename():
'''
批量重命名文件夹中的图片文件
'''
def __init__(self):
self.path = 'E:\\jupyter\\bao\\image_programming\\labelme\\seg\\val\\png' #表示需要命名处理的文件夹
def rename(self):
filelist = os.listdir(self.path) #获取文件路径
total_num = len(filelist) #获取文件长度(个数)
for item in filelist:
#print(item)
if item.endswith('label.png'): #初始的图片的格式为png格式的(或者其他格式,后面的转换格式就可以调整为自己需要的格式即可)
src = os.path.join(os.path.abspath(self.path), item)#当前文件中图片的地址
image_name = item.split('_')[0]
dst = os.path.join(self.path+ "\\"+image_name + '.png')#处理后文件的地址和名称,可以自己按照自己的要求改进
os.rename(src, dst)
print ('Finished')
if __name__ == '__main__':
demo = BatchRename()
demo.rename()
结果展示
之后按照数据集的分布进行处理即可,部分被、内容可参考这里面的(一)
(三)labelimg
1. labelimg下载
在环境中输入
pip install labelimg
2. labelimg使用
与上述类似,不多赘述,直接上图。注意保存的标签路径。
3. 代码转化
不同的数据集类型输入的标签有所差异,此处提供xml_to_txt。
xml_to_txt
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
from tqdm import tqdm
from os import getcwd
import os
import random
import argparse
import shutil
sets = ['train']
#修改分类
classes = ['Deep grinding mark', 'Scratch', 'Pit', 'Adhesion', 'Bulge','Burn']
#image,xml,txt的路径
image_path = 'E:/jupyter/bao/image_programming/labelme/seg/val/image'
xml_path = 'E:/jupyter/bao/image_programming/labelme/seg/val/xml'
txt_path = 'E:/jupyter/bao/image_programming/labelme/seg/val/txt'
parser = argparse.ArgumentParser()
parser.add_argument('--xml_path', default = xml_path, type=str, help='input xml label path')
parser.add_argument('--txt_path', default = txt_path, type=str, help='output txt label path')
opt = parser.parse_args([])
'''
num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)
'''
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
os.makedirs(txtsavepath)
num = len(total_xml)
train = range(0,num)
file_train = open(txtsavepath + '/train.txt', 'w')
for i in train:
name = total_xml[i][:-4] + '\n'
if i in train:
file_train.write(name)
file_train.close()
def convert(size, box):
dw = 1. / (size[0])
dh = 1. / (size[1])
x = (box[0] + box[1]) / 2.0 - 1
y = (box[2] + box[3]) / 2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return x, y, w, h
def convert_annotation(image_id):
# try:
in_file = open(xml_path + '/%s.xml' % (image_id), encoding='utf-8')
out_file = open(txt_path + '/%s.txt' % (image_id), 'w', encoding='utf-8')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
b1, b2, b3, b4 = b
# 标注越界修正
if b2 > w:
b2 = w
if b4 > h:
b4 = h
b = (b1, b2, b3, b4)
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " +
" ".join([str(a) for a in bb]) + '\n')
# except Exception as e:
# print(e, image_id)
wd = getcwd()
for image_set in sets:
if not os.path.exists('txt_path'):
os.makedirs('txt_path')
image_ids = open(txt_path +'/train.txt').read().strip().split()
list_file = open(txt_path + '/train.txt', 'w')
for image_id in tqdm(image_ids):
list_file.write(image_path + '/%s.jpg\n' % (image_id))
convert_annotation(image_id)
list_file.close()
#删除多余的train.txt文件
if os.path.exists(txt_path +'/train.txt'):
os.remove(txt_path +'/train.txt')
结果展示
转换完成后,可以根据代码特色进行数据集划分,至此,本篇结束,感谢批评与指正!