一、SVM支持向量机
什么是SVM支持向量机?
SVM支持向量机本质仍是一个分类器,其核心为寻求一个最优超平面最终实现分类,实现分类问题
在寻求超平面的时候有多种方式,可以使用若干条直线或曲线进行分类,这里使用的是直线,即SVM核为线性核
SVM支持许多核,这里使用的是线性核
数据准备,即准备训练样本,需要有正负样本两种情况,正样本和负样本的个数不一定相同;在准备样本的时候一定要准备label标签,这个label标签唯一描述当前训练的数据,正是因为有了label标签才使得其是一个监督学习的过程;监督学习:在学习一个数据的时候进行监督其对与错,即判断其是0还是1
训练:创建SVM并且设置其属性
通过train方法完成训练
训练完成后调用predict方法进行预测
实现案例:通过两组男生和女生的一些身高和体重的信息,进行训练,最后对指定身高和体重的人进行判别其性别。男生为1,女生为0。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#1 准备data 男生和女生的身高和体重
rand1 = np.array([[155,48],[159,50],[164,53],[168,56],[172,60]])#女生
rand2 = np.array([[152,53],[156,55],[160,56],[172,64],[176,65]])#男生# 2 label 表示当前数据的唯一属性,例如这里的0表示女生,1表示为男生
label = np.array([[0],[0],[0],[0],[0],[1],[1],[1],[1],[1]])#这里将男女生给合并处理了,女生在前男生在后,所以这里的array为五个0和五个1# 3 data
data = np.vstack((rand1,rand2))#垂直合并
data = np.array(data,dtype='float32')# svm 所有的数据都要有label
# [155,48] -- 0 女生 [152,53] ---1 男生
# 监督学习 0 负样本 1 正样本# 4 训练
svm = cv2.ml.SVM_create() # ml 机器学习模块、SVM_create() 创建
# 属性设置
svm.setType(cv2.ml.SVM_C_SVC) # svm type
svm.setKernel(cv2.ml.SVM_LINEAR) # line
svm.setC(0.01)
# 训练
result = svm.train(data,cv2.ml.ROW_SAMPLE,label)
# 预测
pt_data = np.vstack([[167,55],[162,57]]) #0 女生 1男生
pt_data = np.array(pt_data,dtype='float32')
print(pt_data)
(par1,par2) = svm.predict(pt_data)
print(par1)
print(par2)
效果图如下:
二、Hog特征
什么是Hog特征?
特征:某个特定区域的像素,进行某种四则运算之后得到的结果
Haar特征是直接经过模板计算的结果
而Hog特征则比较复杂些
Hog特征实现的步骤:
1、Hog特征的模块化划分
2、根据Hog特征模板计算梯度和方向,当然其也对应有相应的模板概念
3、根据梯度和方向进行bin投影
4、计算每个模块的Hog特征
模块划分: 和Haar特征一样,Hog特征也需要模块划分 image、windows、block、cell每个概念均有一个size()
image是整个图片、windows窗体时蓝色的长方形、block模块时红色的矩形、cell模块时绿色的矩形
image>windows>block>cell成包含关系
block在滑动的时候有个step步长
Windows在滑动的时候也有个step步长
cell在滑动的时候会出现一个bin
windows是特征计算的最顶层单元,整个Hog特征计算最顶层也就计算到windows窗体这个地方;
一个窗口必须包含一个目标obj的所有的描述信息,只有把这些描述信息全部包含进去后,根据这些描述信息得到的这些窗体,根据窗体再计算出的特征才可以唯一描述当前这个目标objwindows的大小size,窗体的大小是任意的,官方推荐的使用在人脸识别的windows大小为64 * 128
block的大小,block是位于蓝色windows窗体中,一般情况下windows的宽高必须是block的宽高的整数倍,一般block的大小为16 * 16
block的步长step,红色的block会沿着蓝色windows窗体从上到下,从左到右依次进行遍历,正是因为遍历的过程,所有才有了step这个概念
block每次向上向下或者向左向右滑动的长度即为step;
block的step描述的是block如何在windows下滑动;若block的大小为16 * 16,则一般情况下step的大小为8*8
在windows下block可以滑动多少次? {[(64-16)/8]+1} * {[(128-16)/8]+1} = 105次
若block的大小为1616,step为88;则一般情况下,cell大小推荐为88
一个cell大小为88,则一个block中包含4个cell,且cell是不可滑动的,是固定在block中;分别命名为:cell1、cell2、cell3、cell4bin、cell、梯度:简而言之就是一个运算
在计算每一个像素的梯度的时候,即每个像素都有其对应的梯度;梯度具有两个属性:大小(或幅值)、方向(角度)
圆有360°,将360°按40°进行划分,可以分为9块,这9块成为9个单元,每一个单元就成为一个bin,9块就是9个bin
一个bin就是40°,刚好9个bin是360°
在cell中必须完整包含一个360°的完整信息,即只需要将这个cell完整包含这9个bin即可,即一个cell对应9个bin
Hog特征得到的是一个向量,因为是向量故存在维度的概念,可以完全描述目标obj对象的所有信息info,既然是所有信息,故应该为整个windows窗体中的信息,因为窗体windows是特征计算的最顶层单元,可以包含描述目标对象obj的所有信息
Hog特征的维度 = 所有windows窗体中block数(即105)* 每个block中cell的个数(一个block对应4个cell)* 每个cell对应的多少个bin(一个cell中包含9个bin)= 10549=3780,故Hog特征为3780维。
梯度如何计算方向和大小?
在进行梯度运算的时候需要以像素为单位,每个像素都有一个梯度,所有的像素共同都构建在一起形成了Hog特征,总共有windows窗体下所有的像素共同构成了Hog特征
特征模板包括水平和竖直方向上的模板 水平方向上的模板为 [1 0 -1] 对于水平方向上的模板实际上:左中右三个像素分别与模板进行相乘 a = p1 * 1 + p2 * 0 + p3 * -1 = 相邻像素之差
竖直方向上的模板为 [[1],[0],[-1]] b = 上下像素之差
整个梯度幅值f = 根号下(a方 + b方) 当前的角度angle = arctan(a/b)bin的投影,其主要依赖于梯度 在bin中把0-360°划分为9个bin,每个bin的范围为0-40°
例如,bin1为0-20°,在这个0-20°这个范围上的,就表示其在bin1上;bin1也可能在180-200°,即bin1总共为40°即可对于某个像素i和j来说,其梯度计算出来的幅值为f,角度a=10°,这个10°刚好位于0-20°之间,故其投影到bin1上
当然若a=190°,位于180-200°之间,其也投影在bin1上;此时投影的幅度即为f(前提是在正中间位置)。
若a=15°,不在正中间的位置,此时会进行分解,f为正中间时的幅度,夹角函数f(夹角)的范围在0.0-1.0之间 f1 = f*(夹角函数)
f2 = f*(1-夹角函数)计算整体的Hog特征,以及cell的复用 整个Hog的维度为3780
3780来源于windows窗体,其中windows窗体中包含block、cell、bin,每一个维度就是其中的每一个bin
一个维度,即来源于一个windows下的一个block下的某一个cell下的每一个bin
把每一个block的第一个cell进行9等分,命名为cell0-cell3,同样bin有9个,命名为bin0-bin8
对于第一个cell来说,cell0总共有9个bin,即bin0-bin8 cell1、cell2、cell3均有9个bin
假设(i,j)像素投影在cell0像素的bin0下
根据梯度可以计算出梯度的方向和梯度的幅值,幅值为f,方向为某一个角度不知道,这个角度就会投影到cell0上的bin0上,这时候bin0的内容就变成了f0,bin0中描述了当前像素的梯度,投影完之后,bin0的内容就等于f0
像素(i+1,j)行加,列不变,所有的这些像素都位于cell0上,(i+1,j)也有可能投影到cell0上,角度也会投影到bin0上,这时候的bin0就变成了f1;
同样还会有(i+2,j),(i+3,j)等等等等
将所有的像素全部都遍历完,再使用sum累加的方式,把所有的bin0累加到一起;sum(bin0(f0+f1+…)) = bin0,这个bin0就是在cell0下计算出来的所有的bin0的值权重累加
对于像素(i,j)来说,它不仅可以影响bin0,还可能会影响bin1,所以在计算sum(bin0)的时候除了f0+f1还需要当前像素在其他下的投影,比如(i,j)像素,有可能仍然投影在bin0上,因为bin0是两部分,其中一个bin可能会与cell0上的bin相同
cell复用 在一个block中存在着4个cell,这4个cell正常情况下是并列排序的分别是cell0-cell3
对于cell0来说,它对应的是自己的bin0-bin9,其实在真正的计算中,会将block进行另外一个维度的划分,在这个维度上,将cell划分为cellx0、cellx2、cellx4 例如在cellx0上,所有元素(i,j),(i,j)计算出来的bin只对当前的cell起作用,虽然其会进行分解成当前的bin和相邻的bin(例如为bin+1)
无论是当前的bin还是bin+1也好,只对cell0起作用
对于cellx2来说,其所有的元素(i,j)不光对当前的cell2起作用,还对当前的另外一个cell(例如cell3),它同时投影到两个cell上,会进行分解为bin、bin+1,和cell3上分解的bin和bin+1
对于cellx4,它上面的所有的元素(i,j),会对4个cell起作用,则会分解成8个bin
再与4个cell进行合并,共同构成了9维,在于block中的4个cell组合到一起共同构成了36位,这36位和整个windows下的105个blokc组合在一起共同构成了3780维
3780维度的Hog特征如何进行判决,就需要SVM相关知识 以SVM线性分类器为例,这个线性分类器首先需要进行训练,训练完成之后同样会得到一个3780维度的向量,用Hog特征(3780维度向量) * SVM的3780维度向量特征 = 具体的值,这个值就是最终的判决文献,这个值与标准的判决文献进行比较,如果大于判决文献,则认为是目标;否则认为是非目标
三、具体实现
1,准备检测需要的各种样本;2,对这些样本进行Hog+SVM训练;3,训练好后,实验test图片进行预测
1、pos文件夹存放各种各样的正样本,正样本也就是每个图片样本都包含这个小狮子;正样本图片的大小为64 * 128;正样本尽可能多样,即环境多样、干扰多样;正样本个数为820
neg文件夹下为各种各样的负样本,负样本也是各种各样的图片,这些负样本中没有包含正样本小狮子图样,负样本中一定不能出现正样本的;负样本图片大小也为64*128;负样本也要尽可能的环境、干扰条件多样;负样本个数1931
一般正负样本比例在1:2 - 1:3之间;且名字的命名最好规范因为后续要进行遍历样本的获取:1,来源于网络;2、来源于公司内部;3、自己收集(写个爬虫自己爬或者录视频,100s的视频,1s就有30帧的图片,100s的视频就是3000张样本)
在深度学习中,一个好的样本,远胜过一个复杂的神经网络 对于机器学习中,样本需要几千几万个;而在深度学习中,需要的样本动则十几万,上百万个
2,对样本进行训练
1、完成为参数的设置,其中参数包括了windows窗体的大小、block的大小、block的步长、cell的大小、bin的个数
2、创建一个Hog
3、获取当前SVM的参数
4、计算Hog
5、准备label标签
6、完成训练
7、训练完后,进行预测
8、通过使用绘图操作,看下预测的完成效果
Hog的创建:
cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nBin)
参数一:当前windows窗口的大小
参数二:block的大小
参数三:block的步长
参数四:cell的大小
参数五:bin的个数
SVM的创建:
cv2.ml.SVM_create()
SVM的训练:
svm.train(featureArray,cv2.ml.ROW_SAMPLE,labelArray)
参数一:特征数组
参数二:机器学习
参数三:标签数组
detectMultiScale(imageSrc,0,(8,8),(32,32),1.05,2)
参数一:待检测的原图
参数二:windwos滑动步长
参数三:窗体大小
参数四:缩放
参数五:线条宽度
图片的绘制:
cv2.rectangle(imageSrc,(x,y),(x+w,y+h),(255,0,0),2)
参数一:图片
参数二:起始位置
参数三:终止位置
参数四:颜色
参数五:线条宽度
# 训练
# 1 参数 2hog 3 svm 4 computer hog 5 label 6 train 7 pred 8 draw
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 参数的定义
PosNum = 820#正样本个数
NegNum = 1931#负样本个数,与实际的图片个数得一致,不可随便填写
winSize = (64,128)#窗体大小
blockSize = (16,16)#每一个块的大小定义为16*16;一个windows下有105个block
blockStride = (8,8)#block的步长设置为8*8的步长;4 cell
cellSize = (8,8)#cell的大小seize也设置为8*8
nBin = 9#bin的个数设置为9个;每个cell下有9个bin;Hog+SVM的特征维度为3780# 2 hog创建
hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nBin)
# 3 svm分类器的创建
svm = cv2.ml.SVM_create()
# 4 计算hog
#定义当前Hog特征的维度
featureNum = int(((128-16)/8+1)*((64-16)/8+1)*4*9) #3780
featureArray = np.zeros(((PosNum+NegNum),featureNum),np.float32)#用于装载当前的特征;
labelArray = np.zeros(((PosNum+NegNum),1),np.int32)#用于装载当前的标签
'''
为什么要创建featureArray和labelArray数组?
首先SVM是监督学习,监督学习就需要样本和标签
实际上SVM在学习的过程中学习的是Hog的特征,Hog特征才是SVM学习的真正的样本
同样label标签是在SVM进行监督学习的过程中使用
'''# svm 监督学习 样本 标签 svm -》image hog
#处理正样本
for i in range(0,PosNum):fileName = 'E:\\Jupyter_workspace\\study\\DL\\pos\\'+str(i+1)+'.jpg'#正样本的路径img = cv2.imread(fileName)#获取当前的样本hist = hog.compute(img,(8,8))#计算当前的Hpg特征 3780维度for j in range(0,featureNum):featureArray[i,j] = hist[j]#featureArray装载的是Hog的特征;例如Hot1为[1,:]、Hot2为[2,:],# featureArray hog [1,:] hog1 [2,:]hog2 labelArray[i,0] = 1#正样本的label全部标记为1#处理正样本
for i in range(0,NegNum):fileName = 'E:\\Jupyter_workspace\\study\\DL\\neg\\'+str(i+1)+'.jpg'#负样本的路径img = cv2.imread(fileName)hist = hog.compute(img,(8,8))# 3780for j in range(0,featureNum):featureArray[i+PosNum,j] = hist[j]#正样本已经全部放在featureArray中,要装载负样本的时候必须从i+PosNum行开始labelArray[i+PosNum,0] = -1#负样本的label全部标记为-1#完成属性设置,从而开始进行训练
svm.setType(cv2.ml.SVM_C_SVC)#设置SVM的属性
svm.setKernel(cv2.ml.SVM_LINEAR)#设置SVM的内核为线性内核
svm.setC(0.01)# 6 train
ret = svm.train(featureArray,cv2.ml.ROW_SAMPLE,labelArray)# 7 myHog的核心参数为:myDetect
# myDetect是一个数组,数组的数据来源于resultArray和rho
# myHog调用当前的detectMultiScale方法,完成整个目标的预测# 7 检测 核心:create Hog -》 myDetect—》array-》
# resultArray来源于resultArray = -1*alphaArray*supportVArray
# rho来源于svm来源于svm.train
alpha = np.zeros((1),np.float32)
rho = svm.getDecisionFunction(0,alpha)
print(rho)
print(alpha)
alphaArray = np.zeros((1,1),np.float32)#定义alphaArray,为了与支持向量机数组进行相乘
supportVArray = np.zeros((1,featureNum),np.float32)#定义支持向量机数组supportVArray
resultArray = np.zeros((1,featureNum),np.float32)
alphaArray[0,0] = alpha
resultArray = -1*alphaArray*supportVArray
'''
supportVArray支持向量的个数
resultArray是3780维
rho一行一列的一维
myDetect这个数组是3781维
'''# detect MyHog的创建
myDetect = np.zeros((3781),np.float32)#myDetect其实就是一个array数组
for i in range(0,3780):myDetect[i] = resultArray[0,i]
myDetect[3780] = rho[0]
# rho svm (判决)
#构建Hog
myHog = cv2.HOGDescriptor()
myHog.setSVMDetector(myDetect)#设置Hog的属性# load 读取待检测的图片
imageSrc = cv2.imread('Test2.jpg',1)
# (8,8) win
objs = myHog.detectMultiScale(imageSrc,0,(8,8),(32,32),1.05,2)
# xy wh objs是个三维数组,而宽高信息放在最后一维
#获取特征的宽高特征
x = int(objs[0][0][0])
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),2)
cv2.imshow('dst',imageSrc)
cv2.waitKey(0)
效果图如下:
训练正样本:
训练负样本:
测试样本: