训练数据的时候,首先需要做下面几步:
1.准备训练样本;2.对样本进行Hog+Svm的训练;3.运用test图片进行预测
一、准备样本
样本分为正样本和负样本,样本的准备可以运用视频的分帧截取图片存储。这里pos文件夹下包含正样本,包含所检测的目标;neg不包含检测的目标为负样本。样本的准备的时候,图片的尺寸需要注意,这里的尺寸是64x128。
样本的获取的手段:1.来源于网络;2.自己收集。一个好的样本远胜过一个复杂的神经网络,容量大。通常我们是自己收集样本的,会通过视频截取帧来得到的,比如说一秒28帧,有100秒,则可以得到2800张图片。
正样本的特点:尽可能多样性,这里的多样性一般是指的环境的多样性,干扰性因素多,这样检测的效果较好。这里有截取本数据集的部分代码。如下,代码有部分解释,也较为简单,可以看懂:
# 视频分解成图片
# 1 load加载视频 2 读取info 3 解码 单帧视频parse 4 展示 imshow
import cv2
# 获取一个视频打开cap
cap = cv2.VideoCapture('1.mp4')
# 判断是否打开
isOpened = cap.isOpened
print(isOpened)
#帧率
fps = cap.get(cv2.CAP_PROP_FPS)
#宽度
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
#高度
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(fps,width,height)
i = 0
while(isOpened):
if i == 100:
break
else:
i = i+1
(flag,frame) = cap.read() # 读取每一张 flag读取是否成功 frame内容
fileName = 'imgs\\'+str(i) + '.jpg'
print(fileName)
if flag == True:
#写入图片
cv2.imwrite(fileName,frame,[cv2.IMWRITE_JPEG_QUALITY,100])
print('end!')
图片分解完后,我们这里其实准备的数据集的尺寸是64x128,所以这里我们需要裁剪和截取。下面是裁剪和截取的代码,这里的代码前面有所介绍,这里可以看到具体介绍:
# 540 * 960 ==>64*128
import cv2
for i in range(0,100):
fileName = 'imgs\\'+str(i+1)+'.jpg'
print(fileName)
img = cv2.imread(fileName)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]
mode = imgInfo[2]
dstHeight = 128
dstWidth = 64
dst = cv2.resize(img,(dstWidth,dstHeight))
cv2.imwrite(fileName,dst)
这里准备的正样本的数量是820个,负样本是1931个,一般来说,正负样本的比例是1:2或者1:3。
二、训练样本
在训练样本的时候,要注意一些问题,在代码注释中会有详细说明,如下,代码很多地方都有,可以看一下说明注释:
可以结合上一篇博客来看,就会较快的理解。
第七步(检测)的重点介绍:
没有直接运用svm.predict来进行预测。
resultArray是3780维的,rho是一个一行一列一维的,而最终的的数组myDetect是3781维度的,所以3780的维是来自resultArray的,最后一个维度是来自rho的。
检测需要创建myhog,需要用myhog进行检测,用detectMultiScale进行检测,没有用predict方法,得到宽高等信息返回,才能把目标绘制出来。代码如下:
# 1.参数的设置 2.hog的创建(实例对象) 3.获取SVM的参数
# 4.计算hog 5.label标签 6.完成train训练 7.完成predicr 8.绘图draw
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1 参数的设置:设置全局变量 在一个windows窗体中有105个block,每个block下有4个cell,每个cell下有9个bin,总共3780维
PosNum = 820 # 正样本的个数
NegNum = 1931 # 负样本的个数
winSize = (64, 128) # 窗体大小
blockSize = (16, 16) # 105个block
blockStride = (8, 8) # block的步长
cellSize = (8, 8)
nBin = 9
# 2 hog对象的创建,HOGDescriptor创建的方法,参数设置:1.窗体的大小,2.block的大小,3.步长,4.cell的大小,5.Bin的数量
hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nBin)
# 3 SVM分类器的创建,定义一个SVM的对象,来自机器学习的SVM创建的模块
svm = cv2.ml.SVM_create()
# 4 计算当前的hog,需要准备各种参数,并且我们需要把参数计算完后保存在某个地方,所以我们需要新设置一定的数组,或者是当前hog特征的维度
featureNum = int(((128-16)/8+1)*((64-16)/8+1)*4*9)
# 3780,特征的维度,是一个int类型;((128-16)/8+1)是block的数量,((64-16)/8+1)为windows的宽度,4就是指的一个block里面有4个cell,9是一个cell里面有9个Bin,这样计算可以的恶道3780维
# 还需要创建一个feature数组和一个label数组用于装载当前的特征,为接下来的第五步和第六步作准备,将标签和特征准备好
featureArray = np.zeros(((PosNum+NegNum), featureNum), np.float32)
# featureArray是一个二维的数组,第一个(PosNum+NegNum)是正负样本的个数,featureNum特征的维度
labelArray = np.zeros(((PosNum+NegNum), 1), np.int32) # 定义一个标签,也是一个二维的,参数和上面的一样
# SVM 是监督学习,所以需要样本和标签 SVM进行学习,而学习的是图片中的的hog特征,所以我们可以说hog特征可以说做是SVM真正的样本,标签是我们在进行SVM训练的时候,进行监督学习使用的
# 遍历所有的图片:正负样本都需要遍历
for i in range(0, PosNum):
fileName = 'pos\\'+str(i+1)+'.jpg'
img = cv2.imread(fileName) # 图片的读取
hist = hog.compute(img, (8, 8)) # 当前hog的计算 3780维的数据,用hist装载特征,计算HOG描述子,检测窗口移动步长(8,8)
# 当前的问题是:我们需要将hog特征装载到featureArray中,而featureArray是一个二维的,而计算出来的只是一个hist,那么这一步怎么完成呢?
# 其实我们这里的hist是一个3780维的,而featureArray是一个二维的,且第一个参数是正负样本的个数,第二个参数是featureNum,我们可以这么操作:
for j in range(0,featureNum):
# featureArray装载的是hog的特征,比如说i=1的时候,第一个hog表示为hog1,i=2的时候为hog2,第二个特征,每一个特征是3780维的,所以hist[j]要放到i行,第j列中
featureArray[i,j] = hist[j] # hog特征的装载
labelArray[i,0] = 1 # 正样本标签为1,labelArray也是一个二维的,n行1列的,所以纵坐标放置的是0,正样本处理完毕,接下来的负样本的设置是一样的
for i in range(0, NegNum):
fileName = 'neg\\'+str(i+1)+'.jpg'
img = cv2.imread(fileName)
hist = hog.compute(img,(8,8))
for j in range(0,featureNum):
featureArray[i+PosNum,j] = hist[j] # 同样要把特征放在featureArray中,所以i+PosNum是要记录负样本的时候必须装载在正样本的后面
labelArray[i+PosNum,0] = -1 # 负样本标签为-1
# 设置SVM的属性,setType设置属性,添加当前类型,再来设置setKernel为SVM的线性内核,再来设置setC
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setC(0.01)
# 6 train,调用train进行训练,参数:1.特征数组;2.机器学习ROW_SAMPLE;3.标签数组。返回值为ret
ret = svm.train(featureArray, cv2.ml.ROW_SAMPLE, labelArray)
# 预测可以运用svm.predict来进行预测,这里我们不用,使用其他方法来进行检测
# 7 预测(创建myHog--->myDect参数得到-->来源于resultArray(公式得到) rho(训练得到))
# rho是svm得到的一个hog的描述信息,这个在最后的阈值判决的时候起作用,在累加的时候起作用
# 深入到hog以及svm最后的判决中看
alpha = np.zeros((1), np.float32)
# getDecisionFunction方法最核心的还是svm的计算,svm来源于训练方法,样本准备好后训练可以得到svm,然后得到rho
rho = svm.getDecisionFunction(0, alpha)
print(rho)
print(alpha)
# resultArray,定义alphaArray,我们需要用alphaArray和支持向量机数组进行相乘
alphaArray = np.zeros((1, 1), np.float32)
# 设置支持向量机数组
supportVArray = np.zeros((1, featureNum), np.float32)
resultArray = np.zeros((1, featureNum), np.float32)
alphaArray[0, 0] = alpha # 一行一列的,所以第一个元素是[0, 0]
resultArray = -1*alphaArray*supportVArray # 计算公式;resultArray 计算,支持向量的个数,只需要当成参数就行
# mydect参数,构建detect
myDetect = np.zeros(3781, np.float32)
for i in range(0, 3780):
# 是一个一维的,myDetect
myDetect[i] = resultArray[0,i]
myDetect[3780] = rho[0]
# myHog的创建很重要
# 构建好Hog,用HOGDescriptor方法构建
myHog = cv2.HOGDescriptor()
# 返回的myHog,用setSVMDetector设置属性
myHog.setSVMDetector(myDetect)
# 待检测图片的加载
imageSrc = cv2.imread('test2.jpg', 1)
# 检测小狮子 (8,8)winds的滑动步长 1.05 缩放系数 (32,32)窗口大小
# 完成检测,返回的目标包含哪些信息,myHog有个方法detectMultiScale,和人脸识别比较类似,缩放。
# 它可以检测出图片中所有的目标,并将目标用vector保存各个目标的坐标、大小(用矩形表示),函数由分类器对象调用;1.05是缩放,(8, 8)windows的滑动步长,(32, 32)窗体大小
# myHog的创建很重要
objs = myHog.detectMultiScale(imageSrc, 0, (8, 8), (32, 32), 1.05, 2)
# 起始位置、宽和高 objs三维信息,获取目标的坐标,是三维的,所以我们需要定义一个三维的坐标,这些信息放在三维中的最后一维
x = int(objs[0][0][0]) # 坐标,w和h是宽高
y = int(objs[0][0][1])
w = int(objs[0][0][2])
h = int(objs[0][0][3])
# 目标的绘制 图片 起始位置 终止位置 颜色,绘制矩形框
cv2.rectangle(imageSrc, (x, y), (x+w, y+h), (255, 0, 0))
# 目标的展示
cv2.imshow('dst', imageSrc)
cv2.waitKey(0)