标签制作与读取
这里说的标签制作并非yolov3所需要的标签,而是一般的没有处理的标签
一、标签的制作
1、制作工具及制作结果
制作工具使用的是:labelImg
用labelimg制作完成后的标签
二、xml标签的读取方法及实现
1、数据文件结构
2、定义xml文件解析器类,用于获取xml文件中的标注信息
3、测试xml文件解析类功能
4、xml文件解析器类实现代码
import numpy as np
from xml.etree.ElementTree import parse # xml解析器模块
class PascalVocXmlParser(object): # 定义xml解析器类
def __init__(self):
pass
# 1、依据xml文件的地址获取标注图片名称
def get_fname(self, annotation_file):
'''
依据xml文件的地址获取标注图片名称
:param annotation_file: xml文件的地址
:return: 标注图片名称
'''
root = self._root_tag(annotation_file)
return root.find("filename").text
# 2、依据xml文件的地址获取标注图片的宽
def get_width(self, annotation_file):
'''
依据xml文件的地址获取标注图片的宽
:param annotation_file: xml文件的地址
:return: 标注图片的宽
'''
tree = self._tree(annotation_file)
for elem in tree.iter():
if 'width' in elem.tag:
return int(elem.text)
# 3、依据xml文件的地址获取标注图片的高
def get_height(self, annotation_file):
'''
依据xml文件的地址获取标注图片的高
:param annotation_file: xml文件的地址
:return: 标注图片的高
'''
tree = self._tree(annotation_file)
for elem in tree.iter():
if 'height' in elem.tag:
return int(elem.text)
# 4、依据xml文件的地址获取标注图片的所有标注框的分类类别
def get_labels(self, annotation_file):
'''
依据xml文件的地址获取标注图片的所有标注框的分类类别
:param annotation_file: xml文件的地址
:return: 所有标注框的分类类别列表
'''
root = self._root_tag(annotation_file)
labels = []
obj_tags = root.findall("object")
for t in obj_tags:
labels.append(t.find("name").text)
return labels
# 5、依据xml文件的地址获取标注图片的所有标注框的坐标(左上右下形式的坐标)
def get_boxes(self, annotation_file):
'''
依据xml文件的地址获取标注图片的所有标注框的坐标(左上右下形式的坐标)
:param annotation_file: xml文件的地址
:return: 依据xml文件的地址获取标注图片的所有标注框的坐标(左上右下形式的坐标)列表
'''
root = self._root_tag(annotation_file)
bbs = []
obj_tags = root.findall("object")
for t in obj_tags:
box_tag = t.find("bndbox")
x1 = box_tag.find("xmin").text
y1 = box_tag.find("ymin").text
x2 = box_tag.find("xmax").text
y2 = box_tag.find("ymax").text
box = np.array([int(float(x1)), int(float(y1)), int(float(x2)), int(float(y2))])
bbs.append(box)
bbs = np.array(bbs)
return bbs
# 6、依据xml文件的地址获取xml文件信息
def _root_tag(self, fname):
'''
依据xml文件的地址获取xml文件信息
:param fname: xml文件的地址
:return: xml内容对象
'''
tree = parse(fname)
root = tree.getroot()
return root
# 7、依据xml文件的地址获取xml文件解析器
def _tree(self, fname):
'''
依据xml文件的地址获取xml文件解析器
:param fname: xml文件的地址
:return: xml解析器
'''
tree = parse(fname)
return tree
xmlpath = "D:\\pythonproject\\yolov3\\基于yolov3的门牌号识别\\data\\ann\\2.xml"
p = PascalVocXmlParser()
print(p.get_fname(xmlpath))
print(p.get_width(xmlpath))
print(p.get_height(xmlpath))
print(p.get_labels(xmlpath))
print(p.get_boxes(xmlpath))
三、简单的目录操作
# 1、获取当前目录
import os
PROJECT_ROOT = os.path.dirname(__file__) # D:/pythonproject/yolov3/基于yolov3的门牌号识别
# 2、组合字符串
ann_dir = os.path.join(PROJECT_ROOT, "data", "ann",
"*.xml") # 组合字符串 得到'D:/pythonproject/yolov3/基于yolov3的门牌号识别\\data\\ann\\*.xml'
img_dir = os.path.join(PROJECT_ROOT, "data", "img") # 组合字符串 得到'D:/pythonproject/yolov3/基于yolov3的门牌号识别\\data\\img'
train_ann_fnames = glob.glob(ann_dir) # 以列表形式获取到当前目录下符合字符串的所有文件的绝对地址,D:\pythonproject\yolov3\基于yolov3的门牌号识别下的data下的img下的所有xml文件的绝对地址
# 形如 ['D:/pythonproject/yolov3/基于yolov3的门牌号识别\\data\\ann\\1.xml', 'D:/pythonproject/yolov3/基于yolov3的门牌号识别\\data\\ann\\10.xml', 'D:/pythonproject/yolov3/基于yolov3的门牌号识别\\data\\ann\\11.xml']
四、制作标签列表
将一个标签信息都整合到一个自定义类中
1、定义一个类,用于存放标签信息
另外还有就是计算当前的xml文件对应的图片的绝对路径
class Annotation(object):
def __init__(self, filename):
self.fname = filename # xml文件对应的图片的地址
self.labels = [] # 存放标注框的分类名称
self.coded_labels = [] # 存放标注框的分类编码
self.boxes = None # 存放一张图上的多个标注框信息的矩阵
def add_object(self, x1, y1, x2, y2, name, code):
'''
:param x1: 标注框的左上坐标x值
:param y1: 标注框的左上坐标y值
:param x2: 标注框的右下坐标x值
:param y2: 标注框的右下坐标y值
:param name: 标注框的分类名称
:param code: 标注框的分类编码 属于(0,1,...,n)中的一个
:return:
'''
self.labels.append(name) # 将标注框的分类名称添加到labels列表中
self.coded_labels.append(code) # 将标注框的分类编码添加到coded_labels列表中
if self.boxes is None: # 如果boxes 为空,则将本次标注框信息转化成矩阵格式添加到boxes中
self.boxes = np.array([x1, y1, x2, y2]).reshape(-1,4)
else:
box = np.array([x1, y1, x2, y2]).reshape(-1, 4) # 将坐标值转换成 矩阵格式 形状为 [x1, y1, x2, y2]
self.boxes = np.concatenate([self.boxes, box]) # 如果boxes不为空 利用矩阵拼接函数将self.boxes和box拼接到一起,
2、将数据中的所有xml文件信息整合到Annotation类中
def parse_annotation(ann_fname, img_dir, labels_naming=[]):
'''
:param ann_fname: xml文件的地址
:param img_dir: 存放所有图片的文件夹地址
:param labels_naming: 标签列表即分类种类 ['分类名1','分类名2',...,'分类名n']
:return: [图片地址] [[图片中第1个标注框坐标]...[图片中第n个标注框坐标]],[[图片中第1个标注框坐标的分类标签]...[图片中第n个标注框坐标的分类标签]
例如:D:/pythonproject/yolov3/基于yolov3的门牌号识别\data\img\2.png [[ 99 5 113 28][114 8 122 31][121 6 133 29]] [2, 1, 0]
'''
parser = PascalVocXmlParser()
fname = parser.get_fname(ann_fname) # 依据xml文件的地址,获取到xml文件对应的标注图片名称
# os.path.join(img_dir, fname)获取到xml文件对应的图片的地址
annotation = Annotation(os.path.join(img_dir, fname)) # 创建
labels = parser.get_labels(ann_fname) # 依据xml文件的地址,获取到xml文件对应的标注图片的所有标注框的分类标签
boxes = parser.get_boxes(ann_fname) # 依据xml文件的地址,获取到xml文件对应的标注图片的所有标注框的左上右下坐标
for label, box in zip(labels, boxes): # 一起遍历 一张图片上的标注框分类标签和标注框
x1, y1, x2, y2 = box # 获取到标注框的四个坐标值
if label in labels_naming: # 如果标注框分类标签在目标分类种类中 即 ['分类名1','分类名2',...,'分类名n']中
# labels_naming.index(label) 计算label在['分类名1','分类名2',...,'分类名n']中的下标,作为分类的编码
annotation.add_object(x1, y1, x2, y2, name=label, code=labels_naming.index(label)) # 将标注框信息添加到annotation类的boxes属性中
return annotation.fname, annotation.boxes, annotation.coded_labels # 返回一张图片的所有标注框的分类名称,标注框信息,编码标签
五、总结
通过函数parse_annotation()可以读取到一个xml文件的信息,并能过获取到xml文件对应的图片的地址。
例如:
D:/pythonproject/yolov3/基于yolov3的门牌号识别\data\img\2.png --xml文件对应的图片的地址。
[[ 99 5 113 28][114 8 122 31][121 6 133 29]] --图片上的anchor的左上右下坐标
[2, 1, 0] --每个anchor的分类