利用Faster RCNN实现交通标志识别(检测小目标)

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

利用Faster RCNN实现交通标志识别(检测小目标)_第1张图片

你可能感兴趣的:(计算机视觉)