最近要做交通管理和控制课程的实验,提到采集数据时老师说可以用多种方法来实现,因此想到自己编写一个车辆识别的进程。第一个想到的是循环神经网络,但是因为自己在机器学习方面造诣还是很浅的,因此选择了比较传统,也是相关论坛比较多的opencv进行实现,这里选用的级联分类器训练(Cascade Classifier),一路上很多网上的大神的博客给了很详细的指导,记录下来,希望自己以后可以用到。
参考文献:
使用资源:
opencv软件,cv2模块(在python中使用opencv),python,正负车辆样品集
1. 搭建需要在python中使用opencv的环境
本次项目不仅需要在python中设置opencv环境,还需要下载opencv的软件到计算机上。因为cv2模块仅仅提供了一些简单的接口,不能够用来创建分类文档xml文档。具体的内容会在后面提到,因此需要下面两个步骤:
step1: 安装opencv-python
1pip install opencv-python
step2:到官网下载opencv,并记住下载后存储的地址。
我的存储路径为D:Program Files (x86)随后打开路径D:Program Files (x86)opencvbuildx64vc14bin,可以看到后面我们需要用到的两个exe文档opencv_createsamples.exe,opencv_traincascade.exe分别用来创建正样品集和训练联机分类器,在bin文档中意味着它可以直接被cmd调用。
2. 创建正负训练样本集
2.1 创建正样本集
step1:准备数据集
正样本集可以从网上下载训练的开源数据,数据在前面的参考博客中已经给出,也可以在网上查找一些网友自己做的数据集,正样品应当尽量只包含目标物体,尽量减少周围环境的介入。如果非要自己做的话,可以使用objectmarker.exe一个网上可以下载到的进程,可以在我的github网页上下载,不过相对比较麻烦。注意无论是从网上找到的数据集还是自己创建都尽量创建比例较为近似的图片,以便后面做归一化处理。以下是我选择的数据集:
注:在未处理以前有很多jpg文档,转化方法可以在该文档夹中创建一个.txt文档,其中输入ren *.jpg *.bmp后将后缀改为.bat(cmd的代码),点击运行即可将文档夹中的每一个图片修改为.bmp格式。
step2:归一化处理
随后我们需要对数据集中的数据做归一化处理,并且转化为灰度图片,该过程的python代码如下。因为将图片缩小有利于训练的效率,因此将宽和高均设置为25(opencv本身的数据集为24)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from PIL import Image
import os.path
import glob
def (jpgfile,outdir,width=25,height=25):
print(jpgfile)
img=Image.open(jpgfile)
print(img)
try:
new_imgt = img.resize((width, height), Image.BILINEAR)
new_img = new_imgt.convert('L')
print(new_img)
new_img.save(os.path.join(outdir, os.path.basename(jpgfile)))
except Exception as e:
print(e)
for jpgfile in glob.glob("D:/anaconda/gpsdata/*.bmp"):
convertjpg(jpgfile,"D:/anaconda/gpsdata1")
归一化后的图片如下图所示:
step3:创建正样品描述vec文档
我的归一化正样品的储存目录为D:/anaconda/gpsdata1因此在该文档夹下,shift+右键->在此处打开命令窗口打开cmd后输入以操作:
1
2cd D:anacondagpsdata1
dir/s/b>pos.txt
即可在文档夹中得到一个pos.txt样本描述文档,文档记录了目录中的每一个图片的目录和文档名。具体样式如下图所示,虽然这还不是我们想要的格式,注意查看最后一行是不是图片还是txt文档本身:
这一步是比较特殊的,因为我的每一个图片大小相等且大小为25才可以这样做。对于一些图片大小非正方形或者大小大于30的慎用。随后我们将这个描述文档复制粘贴到word软件中,使用查找替换功能将每一行替换为如下图的格式,格式从左到右分别描述了表中的属性(此处宽和高均为30其实没有必要,因为图片大小只有25…):
图片相对路径
兴趣矩形个数
矩形左坐标
矩形上坐标
矩形宽度
矩形高度
gpsdata11039284.bmp
1
0
0
30
30
2.2 创建负样本集
创建负样本比创建正样本简单很多,但需要比正样本大很多的样本量,因为在视频/图片当中不是目标物体的实际上是比较多的。负样品的内容应该多是道路上非车辆的物体,例如行人、建筑物等,火星、外太空啥的就算了。以下是我从网上下载的负样品:
通过和正样本相同的处理方法,首先将图片转化为bmp文档,在缩小并转化为黑白的图片(此处应按比例转化,并且保证比处理后的大小比正样品大即可):
同样创建一个描述文档(方法和创建正样品是step 3第一步的方法一样),创建后如图:
2.3 生成vec文档
vec文档是opencv中用来描述图片的矩阵文档,就是将可视的图片转化为矩阵存储。下面分步骤完成。
step1 创建需要的环境
首先将正负样本及其描述文档,最开始文中提到的opencv_createsamples.exe文档放在一个文档夹下(很重要!),如下图所示(忽视那个bat文档,下文会讲到):
修改正负样本描述文档的相对路径,使其指向各自的文档夹,这一步可以在很久以前完成…但自己走了一波弯路…还是在word中使用查找替换。替换后的文档如图所示:
step2 编写创建vec的bat命令
这就是我们上文提到的bat文档了,在该文档夹下新建一个txt文档,写下如操作,不同的参数可以自己调整:
1
2opencv_createsamples.exe -info pos.txt -vec pos.vec -num 762 -h 24 -w 24
修改后缀为.bat并且运行就得到一个vec文档。
至此,准备工作结束,下面就是训练样品了。
3. 使用adaboost算法训练数据
首先我们在现在的文档夹中新建一个文档夹,命名为cascade_data,其中存储训练后的XML文档数据。然后将文中开头提到的opencv_traincascade.exe放入我们的现在的文档夹。再编写一个我们训练模型用的classify.bat文档,文档的内容如下:
1
2
3
4opencv_traincascade.exe -data data -vec pos.vec -bg neg.txt -numPos 762 -numNeg 7708 -numStages 18 -w 24 -h 24 -minHitRate 0.9999 -maxFalseAlarmRate 0.5 -mode ALL
pause
文档夹中的文档有以下几个:
点击classify.bat文档就可以开始训练了,我从下午6.30开始训练,训练到
4.对图片进行初步检测
5.使用cv2做视频处理step1 使用XML文档对cv2的联机分类器进行标定
这一部分的语法可以参考上面的关于cascade_detection的参考文献,其中有较为详细的语法。我们需要拿我们在上一步训练得到的XML文档首先对识别器进行标定。语法如下:
1car_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
step2 创建截取和写入视频的对象
首先我们知道视频是由一帧帧的图片组成的,衡量的单位为FPS(Frame Per Second),我们首先导入cv2并且创建视频截取的对象。它的参数可以是设备索引,也可以是要读取的视频文档的名称。
在大多数情况下,只有一台摄像机连接到系统。 所以,我们所做的只是传递’0’,OpenCV使用连接到计算机的唯一相机。 当多台摄像机连接到电脑时,我们可以通过’1’来选择第二台摄像机,通过’2’来选择第三台摄像机,等等。
1
2
3
4
5
6
7
8fps = 24 #视频帧率
fourcc = cv2.cv.CV_FOURCC('M','J','P','G')
cap = cv2.VideoCapture('chaplin.mp4')
videoWriter = cv2.VideoWriter('D:/testResults/match/flower2.avi', fourcc, fps, (1360,480))
step3 逐帧处理并写入视频
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35from moviepy.editor import *
import cv2
#from cv2 import cv
filename = 'E:/学海无涯/20180613_IMG_0627 (3).MOV'
fps = 24 #视频帧率
fourcc = cv2.VideoWriter_fourcc(*'XVID')
#clip = VideoFileClip(filename).subclip(242,291)
#clip.write_videofile("E:/学海无涯/rawvideo.mp4")
car_cascade = cv2.CascadeClassifier('C:/Users/Administrator/Desktop/lll/haarcascade_eye.xml')
car_cascade.load('C:/Users/Administrator/Desktop/lll/haarcascade_eye.xml')
VideoCap = cv2.VideoCapture("E:/学海无涯/rawvideo.mp4")
videoWriter = cv2.VideoWriter('E:/学海无涯/processedvideo.mp4', fourcc, fps, (1360,480))
while(VideoCap.isOpened()):
ret, frame = VideoCap.read() #逐帧获取视频,frame是图片
if ret == True: #处理并显示这一帧的图片
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #转化为灰度图
cars = car_cascade.detectMultiScale(gray_img, 1.3, 5) #车辆检测,返回(x,y,w,h)
for (x,y,w,h) in cars: #对每一个兴趣区画框
cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
cv2.imshow('Frame',frame) #显示图片
videoWriter.write(frame)
if cv2.waitKey(25) & 0xFF == ord('q'): #按Q停止
break
else: #没有帧后显示循环
break
#处理完后释放所有的对象
VideoCap.release()
videoWriter.release()
#关闭所有帧
cv2.destroyAllWindows()
在做完了这一波之后越来越觉得掌握算法本身比掌握如何使用算法更加的重要,相比而言现在的东西还是一个半成品,代码什么的后面还需要改进。