目标检测一般利用标记工具生成xml后缀的文件,当进行图片增强或者其他操作的时候需要对xml文件进行相应的修改,本文将讲述如何在已经标记好的图片上修改xml文件以及部分数据增强操作,同时利用训练好的模型计算每个类别的MAP
xml文件的修改和生成
首先将xml文件中的字段保存到字典,之后修改字典对应的key,之后将修改好的字典转换成xml文件。
-
xml文件转换成字典形式
def xml2dict(file_path): file_object = open(file_path,encoding = 'utf-8') try: all_the_xmlStr = file_object.read() finally: file_object.close() #xml To dict convertedDict = xmltodict.parse(all_the_xmlStr) return convertedDict
-
将字典转换成xml文件
def jsontoxml(jsonstr, save_path): #xmltodict库的unparse()json转xml xmlstr = xmltodict.unparse(jsonstr) dom = parseString(xmlstr) prettyxml = dom.toprettyxml(indent=' ') f = open(save_path, 'w', encoding='utf-8') f.write(prettyxml) f.close() return dom
图片进行数据增强后相应变化xml文件
from PIL import Image, ImageEnhance
import os
import random
import xmltodict
from xml.dom.minidom import parseString
import xmltodict
from xml.dom.minidom import parseString
def xml2dict(xml_path):
file_object = open(xml_path,encoding = 'utf-8')
try:
all_the_xmlStr = file_object.read()
finally:
file_object.close()
#xml To dict
convertedDict = xmltodict.parse(all_the_xmlStr)
return convertedDict
def jsontoxml(jsonstr, save_path):
#xmltodict库的unparse()json转xml
xmlstr = xmltodict.unparse(jsonstr)
dom = parseString(xmlstr)
prettyxml = dom.toprettyxml(indent=' ')
f = open(save_path, 'w', encoding='utf-8')
f.write(prettyxml)
f.close()
return dom
class ImgPre:
def __init__(self, rootPath, export_path_base, xml_path):
self.rootPath = rootPath # 图像完整路径
self.export_path_base = export_path_base
self.xml_path = xml_path
# 创建输出根目录
if not os.path.exists(export_path_base):
os.mkdir(export_path_base)
def get_savename(self, operate):
"""
:param export_path_base: 图像输出路径
:param operate: 脸部区域名
:return: 返回图像存储名
"""
import time
# 获取时间戳,用于区分图像
now = time.time()
tail_time = str(round(now * 1000000))[-4:] # 时间戳尾数
head_time = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
# 时间标签
label = str(head_time + tail_time)
# 输出文件夹
export_path_base = self.export_path_base
# 子文件夹以“操作operate”命名
out_path = export_path_base
# 创建子文件夹
# if not os.path.exists(out_path):
# os.mkdir(out_path)
# 存储完整路径
name = operate + '_' + label
savename = out_path + operate + '_' + label + ".jpg"
savexml = out_path + operate + '_' + label + ".xml"
# 日志
return name, savename, savexml
def lightness(self):
rootPath = self.rootPath
xml_path = self.xml_path
xml_dict = xml2dict(xml_path)
with Image.open(rootPath) as image:
operate = 'Color'
num = 2 * random.random()
im_Color = ImageEnhance.Color(image).enhance(num)
im_Color = im_Color.convert('RGB')
name, savename, savexml = self.get_savename(operate)
# 图像存储
im_Color.save(savename)
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
jsontoxml(xml_dict, savexml)
num = 2 * random.random()
operate = 'Brightness'
im_Brightness = ImageEnhance.Brightness(image).enhance(num)
im_Brightness = im_Brightness.convert('RGB')
name, savename, savexml = self.get_savename(operate)
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
jsontoxml(xml_dict, savexml)
# 图像存储
im_Brightness.save(savename)
operate = 'Contrast'
num = 2 * random.random()
im_Contrast = ImageEnhance.Contrast(image).enhance(num)
im_Contrast = im_Contrast.convert('RGB')
name, savename, savexml = self.get_savename(operate)
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
jsontoxml(xml_dict, savexml)
# 图像存储
im_Contrast.save(savename)
def transpose(self):
"""图像左右翻转操作."""
operate = 'transpose'
# 图像完整路径
rootPath = self.rootPath
xml_path = self.xml_path
xml_dict = xml2dict(xml_path)
with Image.open(rootPath) as image:
# 图像左右翻转
out = image.transpose(Image.FLIP_LEFT_RIGHT)
out = out.convert('RGB')
w, h = image.size
# 重命名
name, savename, savexml = self.get_savename(operate)
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
for ele in xml_dict['annotation']['object']:
xmin = ele['bndbox']['xmin']
xmax = ele['bndbox']['xmax']
ele['bndbox']['xmin'] = int(float(w) - float(ele['bndbox']['xmax']))
ele['bndbox']['xmax'] = int(int(w) - int(xmin))
jsontoxml(xml_dict, savexml)
# 图像存储
out.save(savename, quality=100) # quality=100
def deform(self):
"""图像拉伸."""
operate = 'deform'
# 图像完整路径
rootPath = self.rootPath
with Image.open(rootPath) as image:
w, h = image.size
w = int(w)
h = int(h)
# 拉伸成宽为w的正方形
a = random.randint(0, 1)
if a == 0:
out_ww = image.resize((int(w), int(w)))
name, savename, savexml = self.get_savename(operate + '_ww')
out_ww.save(savename, quality=100)
# 拉伸成宽为h的正方形
else:
out_ww = image.resize((int(h), int(h)))
savename = self.get_savename(operate + '_hh')
out_ww.save(savename, quality=100)
def crop(self):
"""提取四个角落和中心区域."""
operate = 'crop'
# 图像完整路径
rootPath = self.rootPath
xml_path = self.xml_path
xml_dict = xml2dict(xml_path)
with Image.open(rootPath) as image:
w, h = image.size
# 切割后尺寸
rand = random.random()
while rand < 0.6:
rand = random.random()
scale = rand
# 切割后长宽
ww = int(w * scale)
hh = int(h * scale)
# 图像起点,左上角坐标
x = y = 0
a = random.randint(0, 4)
# 切割左上角
if a == 0:
x_lu = x
y_lu = y
out_lu = image.crop((x_lu, y_lu, ww, hh))
out_lu = out_lu.convert('RGB')
name, savename, savexml = self.get_savename(operate + '_lu')
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
jsontoxml(xml_dict, savexml)
out_lu.save(savename, quality=100)
# logger.info(operate + '_lu')
elif a == 1:
# 切割左下角
x_ld = int(x)
y_ld = int(y + (h - hh))
out_ld = image.crop((x_ld, y_ld, ww, hh))
out_ld = out_ld.convert('RGB')
name, savename, savexml = self.get_savename(operate + '_ld')
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
for ele in xml_dict['annotation']['object']:
ele['bndbox']['ymin'] = int(float(ele['bndbox']['ymin']) - float(h - hh))
ele['bndbox']['ymax'] = int(float(ele['bndbox']['ymax']) - float(h - hh))
jsontoxml(xml_dict, savexml)
out_ld.save(savename, quality=100)
# logger.info(operate + '_ld')
elif a == 2:
# 切割右上角
x_ru = int(x + (w - ww))
y_ru = int(y)
out_ru = image.crop((x_ru, y_ru, w, hh))
name, savename, savexml = self.get_savename(operate + '_ru')
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
for ele in xml_dict['annotation']['object']:
ele['bndbox']['xmin'] = int(float(ele['bndbox']['xmin']) - float(w - ww))
ele['bndbox']['xmax'] = int(float(ele['bndbox']['xmax']) - float(w - ww))
jsontoxml(xml_dict, savexml)
out_ru = out_ru.convert('RGB')
out_ru.save(savename, quality=100)
# logger.info(operate + '_ru')
elif a ==3:
# 切割右下角
x_rd = int(x + (w - ww))
y_rd = int(y + (h - hh))
out_rd = image.crop((x_rd, y_rd, w, h))
out_rd = out_rd.convert('RGB')
name, savename, savexml = self.get_savename(operate + '_rd')
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
for ele in xml_dict['annotation']['object']:
ele['bndbox']['xmin'] = int(float(ele['bndbox']['xmin']) - float(w - ww))
ele['bndbox']['xmax'] = int(float(ele['bndbox']['xmax']) - float(w - ww))
ele['bndbox']['ymin'] = int(float(ele['bndbox']['ymin']) - float(h - hh))
ele['bndbox']['ymax'] = int(float(ele['bndbox']['ymax']) - float(h - hh))
jsontoxml(xml_dict, savexml)
out_rd.save(savename, quality=100)
# logger.info(operate + '_rd')
else:
# 切割中心
x_c = int(x + (w - ww) / 2)
y_c = int(y + (h - hh) / 2)
out_c = image.crop((x_c, y_c, ww, hh))
out_c = out_c.convert('RGB')
name, savename, savexml = self.get_savename(operate + '_c')
xml_dict["annotation"]["filename"] = name + '.jpg'
xml_dict["annotation"]["path"] = savename
for ele in xml_dict['annotation']['object']:
ele['bndbox']['xmin'] = int(float(ele['bndbox']['xmin']) - float((w - ww) / 2))
ele['bndbox']['xmax'] = int(float(ele['bndbox']['xmax']) - float((w - ww) / 2))
ele['bndbox']['ymin'] = int(float(ele['bndbox']['ymin']) - float((h - hh) / 2))
ele['bndbox']['ymax'] = int(float(ele['bndbox']['ymax']) - float((h - hh) / 2))
jsontoxml(xml_dict, savexml)
out_c.save(savename, quality=100)
# logger.info('提取中心')
def test():
# 源地址和输出地址
path = 'D:/fms/sendi/project_excavator/construction/non_box'
filenames = os.listdir(path)
for filename in filenames:
file_name = filename.split('.')[0]
rootPath = path + '/' + filename
xml_path = 'D:/fms/sendi/project_excavator/construction/gongdi-label/' + file_name + '.xml'
export_path_base = 'D:/fms/sendi/project_excavator/data_ enhancement/'
# 声明类对象
imgPre = ImgPre(rootPath, export_path_base, xml_path)
methold_choice = random.randint(0, 4)
if methold_choice ==0:
imgPre.transpose()
elif methold_choice ==1:
imgPre.crop()
else:
imgPre.lightness()
if __name__ == '__main__':
import datetime
print('start...')
# 计时
start_time = datetime.datetime.now()
test()
end_time = datetime.datetime.now()
time_consume = (end_time - start_time).microseconds / 1000000
按比例剪裁图片保存对应的xml文件
def crop_image(img, h_crop, xml_path):
size = img.shape
half_w = int(size[0]/2)
half_h = int(size[1]/h_crop)
for i in range(h_crop):
img1_name = 'crop1' + str(i) + '_' + str(time.time())
save_img1 = 'D:/fms/sendi/project_excavator/all_img/' + img1_name + '.jpg'
save_xml1 = 'D:/fms/sendi/project_excavator/all_xml/' + img1_name + '.xml'
img2_name = 'crop2' + str(i) + '_' + str(time.time())
save_img2 = 'D:/fms/sendi/project_excavator/all_img/' + img2_name + '.jpg'
save_xml2 = 'D:/fms/sendi/project_excavator/all_xml/' + img2_name + '.xml'
crop_img1 = img[0:half_w, int(i * half_h):int((i + 1) * half_h), :]
print("crop_size", int(i * half_h), int((i + 1) * half_h))
size_1 = crop_img1.shape
xml_info = xml2dict(xml_path)
print("xml_info", xml_info['annotation']['object'])
xml_info1 = xml_info.copy()
print("xml_info1",xml_info1['annotation']['object'])
xml_info1['annotation']['size']['width'] = size_1[1]
xml_info1['annotation']['size']['height'] = size_1[0]
bndbox = xml_info1['annotation']['object']
print("bndbox", len(bndbox))
info_object = []
try:
for j in range(len(bndbox)):
info = bndbox[j]
if ((float(info['bndbox']['xmin']) < int((i + 1)* half_h) and float(info['bndbox']['xmin']) > int(i * half_h)) \
or (float(info['bndbox']['xmax']) < int((i + 1)* half_h) and float(info['bndbox']['xmax']) > int(i * half_h)) \
or (float(info['bndbox']['xmin']) < int(i * half_h) and float(info['bndbox']['xmax']) > int((i + 1) * half_h))) and \
float(info['bndbox']['ymin']) < half_w:
info['bndbox']['xmax'] = int(float(info['bndbox']['xmax']) - int(i * half_h))
info['bndbox']['xmin'] = int(float(info['bndbox']['xmin']) - int(i * half_h))
if float(info['bndbox']['xmax']) >= half_h:
info['bndbox']['xmax'] = int(half_h)
if float(info['bndbox']['xmin']) <= 0:
info['bndbox']['xmin'] = int(0)
if float(info['bndbox']['ymax']) > half_w:
info['bndbox']['ymax'] = int(half_w)
info_object.append(info)
else:
print("del")
xml_info1['annotation']['object'] = info_object
except:
info = bndbox
if ((float(info['bndbox']['xmin']) < int((i + 1) * half_h) and float(info['bndbox']['xmin']) > int(
i * half_h)) \
or (float(info['bndbox']['xmax']) < int((i + 1) * half_h) and float(info['bndbox']['xmax']) > int(
i * half_h)) \
or (float(info['bndbox']['xmin']) < int(i * half_h) and float(info['bndbox']['xmax']) > int(
(i + 1) * half_h))) and \
float(info['bndbox']['ymin']) < half_w:
info['bndbox']['xmax'] = int(float(info['bndbox']['xmax']) - int(i * half_h))
info['bndbox']['xmin'] = int(float(info['bndbox']['xmin']) - int(i * half_h))
if float(info['bndbox']['xmax']) >= half_h:
info['bndbox']['xmax'] = int(half_h)
if float(info['bndbox']['xmin']) <= 0:
info['bndbox']['xmin'] = int(0)
if float(info['bndbox']['ymax']) > half_w:
info['bndbox']['ymax'] = int(half_w)
info_object.append(info)
else:
print("del")
print("img1", xml_info1['annotation']['object'])
xml_info = xml2dict(xml_path)
xml_info2 = xml_info.copy()
crop_img2 = img[half_w:-1, int(i * half_h):int((i + 1) * half_h), :]
size_2 = crop_img2.shape
xml_info2['annotation']['size']['width'] = size_2[1]
xml_info2['annotation']['size']['hight'] = size_2[0]
info2_object = []
try:
for ele in xml_info2['annotation']['object']:
print("ele", ele)
if ((float(ele['bndbox']['xmin']) < int((i + 1)* half_h) and float(ele['bndbox']['xmin']) > int(i * half_h)) \
or (float(ele['bndbox']['xmax']) < int((i + 1)* half_h) and float(ele['bndbox']['xmax']) > int(i * half_h)) \
or (float(ele['bndbox']['xmin']) < int(i * half_h) and float(ele['bndbox']['xmax']) > int((i + 1) * half_h))) and \
float(ele['bndbox']['ymax']) > half_w:
print(float(ele['bndbox']['xmin']), float(ele['bndbox']['xmax']), float(ele['bndbox']['ymin']), float(ele['bndbox']['ymax']))
ele['bndbox']['xmax'] = int(float(ele['bndbox']['xmax']) - int(i * half_h))
ele['bndbox']['xmin'] = int(float(ele['bndbox']['xmin']) - int(i * half_h))
ele['bndbox']['ymin'] = int(float(ele['bndbox']['ymin']) - half_w)
ele['bndbox']['ymax'] = int(float(ele['bndbox']['ymax']) - half_w)
print("change", float(ele['bndbox']['xmin']), float(ele['bndbox']['xmax']), float(ele['bndbox']['ymin']), float(ele['bndbox']['ymax']))
if float(ele['bndbox']['xmax']) >= half_h:
ele['bndbox']['xmax'] = int(half_h)
if float(ele['bndbox']['xmin']) <= 0:
ele['bndbox']['xmin'] = int(0)
if float(ele['bndbox']['ymin']) <= 0:
ele['bndbox']['ymin'] = int(0)
if float(ele['bndbox']['ymax']) >= half_w:
ele['bndbox']['ymin'] = int(half_w)
info2_object.append(ele)
else:
print("de2")
except:
ele = xml_info2['annotation']['object']
if ((float(ele['bndbox']['xmin']) < int((i + 1) * half_h) and float(ele['bndbox']['xmin']) > int(
i * half_h)) or (float(ele['bndbox']['xmax']) < int((i + 1) * half_h) and float(ele['bndbox']['xmax']) > int(
i * half_h)) \
or (float(ele['bndbox']['xmin']) < int(i * half_h) and float(ele['bndbox']['xmax']) > int(
(i + 1) * half_h))) and \
float(ele['bndbox']['ymax']) > half_w:
print(float(ele['bndbox']['xmin']), float(ele['bndbox']['xmax']), float(ele['bndbox']['ymin']),
float(ele['bndbox']['ymax']))
ele['bndbox']['xmax'] = int(float(ele['bndbox']['xmax']) - int(i * half_h))
ele['bndbox']['xmin'] = int(float(ele['bndbox']['xmin']) - int(i * half_h))
ele['bndbox']['ymin'] = int(float(ele['bndbox']['ymin']) - half_w)
ele['bndbox']['ymax'] = int(float(ele['bndbox']['ymax']) - half_w)
print("change", float(ele['bndbox']['xmin']), float(ele['bndbox']['xmax']),
float(ele['bndbox']['ymin']), float(ele['bndbox']['ymax']))
if float(ele['bndbox']['xmax']) >= half_h:
ele['bndbox']['xmax'] = int(half_h)
if float(ele['bndbox']['xmin']) <= 0:
ele['bndbox']['xmin'] = int(0)
if float(ele['bndbox']['ymin']) <= 0:
ele['bndbox']['ymin'] = int(0)
if float(ele['bndbox']['ymax']) >= half_w:
ele['bndbox']['ymin'] = int(half_w)
info2_object.append(ele)
else:
print("de2")
xml_info2['annotation']['object'] = info2_object
print("img2", xml_info2['annotation']['object'])
if len(xml_info1['annotation']['object']) >= 1:
xml_info1["annotation"]["filename"] = img1_name + '.jpg'
xml_info1["annotation"]["path"] = save_img1
print('save')
cv2.imwrite(save_img1, crop_img1)
jsontoxml(xml_info1, save_xml1)
if len(xml_info2['annotation']['object']) >= 1:
xml_info2["annotation"]["filename"] = img2_name + '.jpg'
xml_info2["annotation"]["path"] = save_img2
cv2.imwrite(save_img2 , crop_img2)
jsontoxml(xml_info2, save_xml2)
def crop_h(img, h_crop, xml_path):
size = img.shape
half_w = int(size[0]/2)
half_h = int(size[1]/h_crop)
for i in range(h_crop):
img1_name = 'crop1' + str(i) + '_' + str(time.time())
save_img1 = 'D:/fms/sendi/project_excavator/all_img/' + img1_name + '.jpg'
save_xml1 = 'D:/fms/sendi/project_excavator/all_xml/' + img1_name + '.xml'
img2_name = 'crop2' + str(i) + '_' + str(time.time())
save_img2 = 'D:/fms/sendi/project_excavator/all_img/' + img2_name + '.jpg'
save_xml2 = 'D:/fms/sendi/project_excavator/all_xml/' + img2_name + '.xml'
crop_img1 = img[:, int(i * half_h):int((i + 1) * half_h), :]
print("crop_size", int(i * half_h), int((i + 1) * half_h))
size_1 = crop_img1.shape
xml_info = xml2dict(xml_path)
print("xml_info", xml_info['annotation']['object'])
xml_info1 = xml_info.copy()
print("xml_info1",xml_info1['annotation']['object'])
xml_info1['annotation']['size']['width'] = size_1[1]
xml_info1['annotation']['size']['height'] = size_1[0]
bndbox = xml_info1['annotation']['object']
print("bndbox", len(bndbox))
info_object = []
try :
for j in range(len(bndbox)):
info = bndbox[j]
if ((float(info['bndbox']['xmin']) < int((i + 1)* half_h) and float(info['bndbox']['xmin']) > int(i * half_h)) \
or (float(info['bndbox']['xmax']) < int((i + 1)* half_h) and float(info['bndbox']['xmax']) > int(i * half_h)) \
or (float(info['bndbox']['xmin']) < int(i * half_h) and float(info['bndbox']['xmax']) > int((i + 1) * half_h))):
info['bndbox']['xmax'] = int(float(info['bndbox']['xmax']) - int(i * half_h))
info['bndbox']['xmin'] = int(float(info['bndbox']['xmin']) - int(i * half_h))
if float(info['bndbox']['xmax']) >= half_h:
info['bndbox']['xmax'] = int(half_h)
if float(info['bndbox']['xmin']) <= 0:
info['bndbox']['xmin'] = int(0)
info_object.append(info)
else:
print("del")
except:
info = bndbox
if ((float(info['bndbox']['xmin']) < int((i + 1) * half_h) and float(info['bndbox']['xmin']) > int(
i * half_h)) \
or (float(info['bndbox']['xmax']) < int((i + 1) * half_h) and float(info['bndbox']['xmax']) > int(
i * half_h)) \
or (float(info['bndbox']['xmin']) < int(i * half_h) and float(info['bndbox']['xmax']) > int(
(i + 1) * half_h))):
info['bndbox']['xmax'] = int(float(info['bndbox']['xmax']) - int(i * half_h))
info['bndbox']['xmin'] = int(float(info['bndbox']['xmin']) - int(i * half_h))
if float(info['bndbox']['xmax']) >= half_h:
info['bndbox']['xmax'] = int(half_h)
if float(info['bndbox']['xmin']) <= 0:
info['bndbox']['xmin'] = int(0)
info_object.append(info)
else:
print("del")
xml_info1['annotation']['object'] = info_object
print("img1", xml_info1['annotation']['object'])
if len(xml_info1['annotation']['object']) >= 1:
xml_info1["annotation"]["filename"] = img1_name + '.jpg'
xml_info1["annotation"]["path"] = save_img1
print('save')
cv2.imwrite(save_img1, crop_img1)
jsontoxml(xml_info1, save_xml1)
利用训练好的权重计算目标检测中每个类别的MAP
input文件夹下位需要准备的材料有
-
detection-results为代码跑出来的结果存放路径
该文件夹下每个图片对应一个txt文件,每个文件中的每行对应识别的一个类别以及置信度和box。可以在模型的测试中对每张图片进行保存。
-
ground-truth 为标注图片的路径
该目录下的文件对应检测的每张图片,每张图片生成一个txt文件,文件中每行包括类别和box
具体实现可以 可以通过keras_yolov3中生成的2007_test.txt得到。import os file_path = 'D:/fms/sendi/project_excavator/2007_test.txt' lines = open(file_path) for line in lines: img_name = line.split('.jpg ')[0].split('/') dirs = "d:/data/" + img_name[-1] + '.txt' file = open(dirs, 'w') r = line.split(' ') for i in range(1, len(r)): box = r[i].split(',') print(box) lable = float(box[4]) if i == 1: s = '' else: s = '\n' if lable == 0: file.write(s + 'wajueji' + ' ' + box[0] + ' ' + box[1] + ' ' + box[2] + ' ' + box[3] ) elif lable == 1: file.write(s +'benti' + ' ' + box[0] + ' ' + box[1] + ' ' + box[2] + ' ' + box[3]) elif lable == 2: file.write(s +'washou' + ' ' + box[0] + ' ' + box[1] + ' ' + box[2] + ' ' + box[3]) else: file.write('') # 没有检测出来对象,创建一个空白的对象
-
images-optional 为测试原图存放路径
只需要将测试的图片放置在该文件夹下,并且与上述两个文件夹中的名字一致即可。
配置好input文件夹下的三个文件夹内的内容就可以运行main.py函数。最后会输出分类中每个类别对应的MAP以及绘制MAP图。