a.k.a.曲老师的期末大作业
观察了一下老师的给的训练集,一共有五类:交通灯d,指示标志s,指路标志l,禁令标志z,警告标志j。训练集里的交通灯太小了,最小的只有3个像素,之前读的一些论文都提到YOLO的缺点就是检测小目标物体,于是我决定选择Faster RCNN
整体流程没啥好说,完全参照大佬的教程:
使用faster-rcnn.pytorch训练自己数据集(完整版)
主要是训练的过程中,出现了loss:nan的情况,这个是因为代码中有数据扩充的部分,在扩充的过程中,边界框的坐标xmin,xmax,ymin,ymax出现了错误,因此需要在lib/dataset/pascal_voc.py里修改_load_pascal_annotation函数
# Load object bounding boxes into a data frame.
wh = tree.find('size')
w, h = int(wh.find('width').text), int(wh.find('height').text)
for ix, obj in enumerate(objs):
bbox = obj.find('bndbox')
# Make pixel indexes 0-based
x1 = float(bbox.find('xmin').text)
y1 = float(bbox.find('ymin').text)
x2 = float(bbox.find('xmax').text)
y2 = float(bbox.find('ymax').text)
x1 = max(x1, 0)
y1 = max(y1, 0)
x2 = min(x2, w)
y2 = min(y2, h)
终端输入README里的提示
python test_net.py --dataset pascal_voc --net res01 --checksession 1 --checkepoch 20 --checkpoint 5047 --cuda
最终测试结果
j AP: 0.8983
z AP: 0.8654
s AP: 0.8990
l AP: 0.8882
d AP: 0.4870
mAP: 0.8077
嗯?交通灯才0.48,这么低,要改进。
统计了一下所有xml中所有gt box的大小:
# coding=utf-8
import os
import os.path
import xml.dom.minidom
from shutil import copyfile
path = "data/VOCdevkit2007/VOC2007/Annotations/"
sourcepath = 'data/VOCdevkit2007/VOC2007/JPEGImages/'
tarpath = '/home/huangyan/桌面/CV大作业/未标注文件/'
files = os.listdir(path) # 得到文件夹下所有文件名称
bbox = []
for xmlFile in files: # 遍历文件夹
if not os.path.isdir(xmlFile): # 判断是否是文件夹,不是文件夹才打开
# print(xmlFile)
# 将获取的xml文件名送入到dom解析
dom = xml.dom.minidom.parse(os.path.join(path, xmlFile)) # 输入xml文件具体路径
root = dom.documentElement
# 获取标签以及的值
name = root.getElementsByTagName('name')
folder = root.getElementsByTagName('folder')
objects = root.getElementsByTagName('object')
# if len(objects) == 0:
# print(xmlFile)
# # copyfile(sourcepath+xmlFile.split('.xml')[0]+'.jpg',tarpath+xmlFile.split('.xml')[0]+'.jpg')
# os.remove(sourcepath+xmlFile.split('.xml')[0]+'.jpg')
# os.remove(path+xmlFile)
# 对每个xml文件的多个同样的属性值进行修改。此处将每一个属性修改为plane,每一个属性修改为VOC2007
for object in objects:
delete = False
xmin = object.getElementsByTagName('xmin')[0]
xmin = int(xmin.childNodes[0].data)
ymin = object.getElementsByTagName('ymin')[0]
ymin = int(ymin.childNodes[0].data)
xmax = object.getElementsByTagName('xmax')[0]
xmax = int(xmax.childNodes[0].data)
ymax = object.getElementsByTagName('ymax')[0]
ymax = int(ymax.childNodes[0].data)
bbox.append([xmin,ymin,xmax,ymax])
# if xmax - xmin == 3:
# print(xmlFile)
# print(bbox)
import numpy as np
np.save('boxes.npy',bbox)
# import numpy as np
bboxes = np.load('boxes.npy')
print(bboxes.shape)
scale = []
width = []
for bbox in bboxes:
x1,y1,x2,y2 = bbox[0],bbox[1],bbox[2],bbox[3]
w = x2 - x1
h = y2 - y1
s = w / h
scale.append(s)
width.append(w)
max_width = np.argmax(width)
print(max(width))
print(min(width))
最后得到gt box的width范围在0~300之间。原本代码中的anchor的scale为[8,16,32],如果改成[4,8,16]似乎就有点小了,于是我把anchor的尺度变成了[4,8,16,32],这样每个像素点生成的anchor box就从原来的9个变成了12个。
在统计的时候发现,xml里面有些gt box是0×0大小,1×1大小,这根本不科学,果断删除。
import os
import os.path
import xml.dom.minidom
path = "data/VOCdevkit2007/VOC2007/Annotations/"
files = os.listdir(path) # 得到文件夹下所有文件名称
for xmlFile in files: # 遍历文件夹
if not os.path.isdir(xmlFile): # 判断是否是文件夹,不是文件夹才打开
print(xmlFile)
# 将获取的xml文件名送入到dom解析
dom = xml.dom.minidom.parse(os.path.join(path, xmlFile)) # 输入xml文件具体路径
root = dom.documentElement
# 获取标签以及的值
name = root.getElementsByTagName('name')
folder = root.getElementsByTagName('folder')
objects = root.getElementsByTagName('object')
for object in objects:
delete = False
xmin = object.getElementsByTagName('xmin')[0]
xmin = int(xmin.childNodes[0].data)
ymin = object.getElementsByTagName('ymin')[0]
ymin = int(ymin.childNodes[0].data)
xmax = object.getElementsByTagName('xmax')[0]
xmax = int(xmax.childNodes[0].data)
ymax = object.getElementsByTagName('ymax')[0]
ymax = int(ymax.childNodes[0].data)
if xmax == xmin and ymax == ymin:
object.parentNode.removeChild(object)
delete = True
print('delete Obj: {},{},{},{}'.format(xmin, xmax, ymin, ymax))
with open(os.path.join(path, xmlFile), 'w') as fh:
dom.writexml(fh)
print('已写入')
在统计的时候还发现训练集里面的有些xml根本就没有obj(我猜这是老师挖的坑),于是挑出没有obj的xml,把对应图片存到另一个文件夹,用labelImg重新标注
最后的最后,修改代码里config.py
里_C.TRAIN.RPN_MIN_SIZE=2
,_C.TEST.RPN_MIN_SIZE=2
,rpn会过滤掉小于这个数的box。
重新训练以后的结果:
j AP: 0.9066
z AP: 0.8592
s AP: 0.8963
l AP: 0.8856
d AP: 0.5536
mAP: 0.8203