准备工作
由于是课程大作业,因此对于精度等等啥的没有太大的要求,考虑到疫情原因,自己的电脑又垃圾,因此只好选择一个轻量级的网络来训练了(大模型电脑可能就炸了),再加上老师要求大家算法场景都不一样。。。总之,最后选择了MobileNet-SSD这个轻量级模型来实现车辆检测了。这篇博客就不介绍mobilenet-ssd的原理,关于其原理及网络结构在网上有许多资料,大家可以自行搜索学习。写这篇博客的主要目的是给自己做个笔记和总结,当然也希望能给一些同学一点帮助,下文如有什么不足之处,也希望大家指正批评。下面进入正题。
源码下载
首先是源码下载,考虑到电脑环境:Win10+keras2.2+tensorflow-gpu1.11,因此选择了一个keras框架的mobilnet-ssd实现:
链接: github.
选择该代码的原因在于该代码功能相对简单,并未有太多目标检测的tricks,代码更容易看懂。当然还有个最重要的原因是他是用中文写的readme。
下载完成后,找一个路径解压即可(最好别有中文字符的路径,避免不必要的麻烦)。
数据集下载
然后是数据集下载,车辆的开源数据集很多(如KITTI、UA-DETRAC等),我选择的是UA-DETRAC数据集,它是车辆检测和跟踪的数据集,在北京和天津的24个不同地方拍摄了近10个小时的视频,视频以每秒25帧(fps)的速度录制,分辨率为960*540像素。手动标注了8250个车辆,总共有121万个标记的边界框。数据集中的场景包含白天、黑夜、正面、侧面、雨天等场景。下载的地址:
链接: UA-DETRAC数据集下载.
非常感谢以上两位大佬的分享。
数据集处理
如果不是使用的UA-DETRAC数据集,可跳过这一步。
因为后期需要测试视频,而数据集测试集中给出的是图像,因此可以先把每个文件转化成视频(如果是用自己的视频测试,也可以不用进行这一步),转化代码如下:
#coding:utf-8
"""
description:由于测试时是希望使用视频,因此希望将图像转化成视频
author: kuang
location: D:\Video_homework\image_to_videos.py
time: 2020.4.17 by home
"""
import cv2
import os
#图片路径
im_dir = 'Train/'
#输出视频路径
video_dir = 'train_video/'
#图片尺寸
img_size = (960,540)
#帧率
fps = 30
img_file = os.listdir(im_dir)
num_file = len(img_file)
print(img_file ,num_file)
#对图片文件进行遍历,从而对每一组文件创建每一个视频
for file in img_file:
#获取图片数
num_image = len(os.listdir(os.path.join(im_dir,file)))
print(num_image)
#fourcc = cv2.cv.CV_FOURCC('M','J','P','G')#opencv2.4
fourcc = cv2.VideoWriter_fourcc(*'XVID') #opencv3.0
videoWriter = cv2.VideoWriter(video_dir + file + ".avi", fourcc, fps, img_size)
for i in range(1,num_image):
im_name = os.path.join(im_dir + file, 'img' + str(i).zfill(5) +'.jpg')
print(im_name)
frame = cv2.imread(im_name)
cv2.imshow("data" , frame)
cv2.waitKey(1)
videoWriter.write(frame)
#print(im_name)
videoWriter.release()
print("%s finish" % file)
然后根据下载的数据集中的文件看出,其标注文件是两种:一种是视频类型的xml文件,一种是给出了gt的txt,txt中的参数形式是xmin,ymin,xmax,ymax。而源码需要将标注形式转化为voc格式,因此,可以先将gt的txt转化为对应的每一张图片的xml,然后再按照源码操作就可以了。(当然,也可以直接转化为训练使用的txt,此处是考虑到要和后面的github上的步骤一一对应因此选择转化成xml)。
转化代码如下,有两个文件,先运行第一个文件(将gt中的浮点型坐标转化为整形,这个代码就可以实现直接转化为可以训练的txt文件了):
#coding:utf-8
"""
description:将train_gt.txt转化为voc格式的txt文件
author: kuang
location: D:\Video_homework\txt_to_traintxt.py
time: 2020.4.18 by home
"""
import os
#读取txt的内容
with open("train_gt.txt" , "r") as f:
data = f.readlines()
with open("train.txt" , "w" ,encoding="utf-8") as fw:
#对每一行进行处理
for inf in data:
#分割每一行的数据
temp_data = inf.splitlines()[0].split(" ")
print(temp_data)
#提取图片名
img = temp_data[0]
#提取坐标信息,每4个1组(并且已经是xmin ymin xmax ymax),需要转化成整形同时变为xmin,ymin,xmax,ymax,class这种形式
axis = temp_data[1:]
temp = []
for i in range(0,len(axis),4):
location = axis[i:i+4]
#将列表中的每一项转化为整形
for a in range(4):
location[a] = str(int(float(location[a])))
#将4个列表转化为一个字符串并填充如, 同时每组加上对应的类别号(由于都是某一类别所以全部弄成0就可以了(也可以改为其他的))
string = ",".join(location) + "," + "0"
temp.append(string)
#将一整行的axis全部弄成一组
line_string = " ".join(temp)
#最后写入的某一整行数据
write_inf = img + " " + line_string
#写入txt
fw.write(write_inf)
fw.write("\n")
如果想再转化为xml文件,则再运行下面的代码:
#coding:utf-8
"""
description:将train_gt.txt转化为voc格式的txt文件
错误:train_gt(82085)与图片数(83800)不对应,从文件夹MVI_39761开始
author: 网上copy修改的
location: D:\Video_homework\txt_to_xml.py
time: 2020.4.18 by home
"""
import os, sys
import glob
from PIL import Image
#图像存储位置
src_img_dir = "Train/"
#图像的 ground truth 的 txt 文件存放位置
src_txt_dir = "train.txt"
src_xml_dir = "Annotations/"
#图像大小
img_size = (960,540)
# open the crospronding txt file
gt = open(src_txt_dir).read().splitlines()
a = 1
for inf in gt:
#提取dir
tempp = inf.split(" ")
label = tempp[1:]
dir_name,img_names = tempp[0].split("/")
img = img_names.split(".")[0]
if not os.path.exists(src_xml_dir + dir_name): #判断当前路径是否存在,没有则创建new文件夹
os.makedirs(src_xml_dir + dir_name)
#img_Lists = glob.glob(src_img_dir + file + '/*.jpg')
#获取img的width和height
width, height = img_size
# write in xml file
# if not os.path.exists(src_xml_dir + file + "/" + img + ".xml" ): #判断当前路径是否存在,没有则创建new文件夹
# open(src_xml_dir + file + "/" + img + ".xml" , "w" )
#os.mknod(src_xml_dir + '/' + img + '.xml')
xml_file = open((src_xml_dir + dir_name + '/' + img + '.xml'), 'w')
xml_file.write('<annotation>\n')
xml_file.write(' <folder>VOC2007</folder>\n')
xml_file.write(' <filename>' + dir_name + "/" + img + '.jpg' + '</filename>\n')
xml_file.write(' <size>\n')
xml_file.write(' <width>' + str(width) + '</width>\n')
xml_file.write(' <height>' + str(height) + '</height>\n')
xml_file.write(' <depth>3</depth>\n')
xml_file.write(' </size>\n')
# write the region of image on xml file
for img_each_label in label:
spt = img_each_label.split(',') #这里如果txt里面是以逗号‘,’隔开的,那么就改为spt = img_each_label.split(',')。
xml_file.write(' <object>\n')
xml_file.write(' <name>' + str(spt[4]) + '</name>\n')
xml_file.write(' <pose>Unspecified</pose>\n')
xml_file.write(' <truncated>0</truncated>\n')
xml_file.write(' <difficult>0</difficult>\n')
xml_file.write(' <bndbox>\n')
xml_file.write(' <xmin>' + str(spt[0]) + '</xmin>\n')
xml_file.write(' <ymin>' + str(spt[1]) + '</ymin>\n')
xml_file.write(' <xmax>' + str(spt[2]) + '</xmax>\n')
xml_file.write(' <ymax>' + str(spt[3]) + '</ymax>\n')
xml_file.write(' </bndbox>\n')
xml_file.write(' </object>\n')
xml_file.write('</annotation>')
a += 1
#循环添加,从而下一file读取gt时才会从对应位置读取
print("完成%s的转化" % tempp[0])
print(a)
至此,转化完成,数据集就准备完成了,接下来就可以进行网络的训练了。
训练模型
第一步:下载Mobilenet-SSD的预训练权重mobilenet_ssd_weights,这个在源码中给出的百度云中下载。
第二步:将训练集的所有图片放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中,标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的Annotation中。我的路径格式如图所示:
第三步:运行voc2ssd.py文件生成对应的txt(在ImageSets/main中会生成四个txt文件),但是运行前需根据自己的需求修改部分参数,在这个项目中可以把trainval_percent和train_percent都设置为1,因为这个项目在train.py中会随机划分训练和验证集,需要修改的地方如下图:
第四步:再运行根目录下的voc_annotation.py,运行前需要将classes改成你自己的classes。可参考修改成下图:
然后就会生成对应的2007_train.txt等三个文件,每一行对应其图片位置及其真实框的位置和类别。如下图(这里看出其实可以不用进行第二章的转化xml,而是直接将gt的txt转化成现在这个用于训练的txt):
第五步:在训练前需要修改model_data里面的voc_classes.txt文件,需要将classes改成你自己的classes。
第六步:然后将train.py文件中的部分参数修改,如batchsize、numclass以及预训练权重路径,如下图:
修改完成后,直接运行train.py即可开始训练。如图所示:
接下来就是漫长的等待了,当然如果电脑配置好还是非常快的。这个是我训练后的模型,效果还行,下载地址:
链接: 模型百度云下载.
提取码:avsl
测试
训练完成后即可开始测试模型了。
图片测试
修改根目录的ssd.py部分参数,主要是训练后的model和置信度,如图:
然后运行根目录的predict.py,即可进行图片的测试了,测试结果如图:
视频测试
操作同图片检测相同,只是还需要修改video.py中的测试视频地址,如图:
修改完成后,运行video.py,即可得到视频的测试结果了。
mAP评估
如果需要进行mAP的评估,则运行顺序如下:
1、首先,运行get_gt_txt.py,获取test集中的图片的ground_truth框,用于mAP计算
2、然后,运行get_dr_txt.py,用训练得到的模型对测试集进行测试,获取检测出的detection-results的txt
3、最后,运行get_map.py,即可获得mAP的计算结果了。
总结
最后,很感谢项目的作者以及数据集下载的博主的无私分享,也让我顺利的写完了整个大作业,下面是个人的一些总结:
1、正如最开始选择源码时所说的,这个项目的代码相对更简单,缺少很多目标检测的tricks,比方说多尺度训练等可以提升检测效果的方法,因此最后我测试的mAP也并没有太好。
2、这个项目是mobilenet-ssd,但是实际他的代码和mobilenet-ssd的网络结构还是有一定的出入的,比方说代码中每个尺度的priorbox数量是用的ssd的4、4、6、6、6、4,而不是mobilenet-ssd的3、6、6、6、6、6。
3、尽管代码还是有一定的错误,但是,个人觉得项目作者的代码还是很值得仔细的分析和学习的,有时间的同学可以考虑仔细研究一下代码。
以上是我个人的拙见,如果有不对的地方或是有什么问题,欢迎各位大佬留言指正批评,谢谢大家的阅读。
参考文献
感谢以下博文:
Mobilenet-SSD:轻量级目标检测模型在Keras当中的实现.
目标检测keras-ssd之训练自己数据.