任务描述
在前面的任务中我们介绍了使用OpenCV进行样本数据处理,以及HOG特征的提取,在本次任务中,我们将会基于OpenCV,使用提取的HOG特征来训练SVM分类器。
相关知识
为了完成本关任务,你需要了解:
Hard example的简单了解;
基于OpenCV,创建并训练SVM分类器。
Hard example的简单了解
Hard example是指利用第一次训练的分类器在负样本图片上(一定没有人体)进行行人检测时,所得到的检测矩形框,这些矩形框区域很明显都是误报,把这些误报的矩形框保存为图片,加入到初始的负样本集合中,重新进行SVM的训练,就可在一定程度上提升检测效果,这种方法又称为自举法(Bootstrap)。这种方法首先会使用原始负样本数据集合去训练得到一个模型,然后收集被这个模型错误分类的负样本,从而形成一个负样本难例集,再将其进行二次训练,用于新的模型,该过程可重复进行。换句话来说,hard example的意义就在于告诉训练的模型,“这个题目你之前做错过了,你再多做几遍,之后再碰到才能不出错”。
上图将树干误认为是人体,这些就是Hard example,将这些矩形框保存为64 * 128的图片文件,加入到负样本集合中。
基于OpenCV,使用HOG特征来训练SVM分类器
在第六个任务中,我们对SVM分类器有了一个简单了解,在本次任务中我们不再讨论这一部分内容,我们使用OpenCV中已经封装好的SVM相关内容。这里我们可以使用OpenCV中已经训练好的行人检测分类器,需要注意的是OpenCV自带的分类器是利用HOG论文作者Navneet Dalal和Bill Triggs提供的样本进行训练的,得到的模型不一定能够适用于你的应用场合,因此针对你的特定应用场景,很有必要进行重新训练得到适合的分类器,也就是创建并使用自己的数据训练一个SVM分类器。
检测有两种方式,如果仅仅使用线性核的话,只需要Hog类自带的setSVMDetector和detect(detectMultiScale),如果使用RBF,就需要使用SVM类的predict。但predict是判断“是与不是”的,如果要得区域的话,使用Hog类的setSVMDetector和detect(detectMultiScale),这里我们使用线性核。
线性SVM进行训练之后得到的文本文件里面,有一个数组,叫做support vector,还有一个数组,叫做alpha,有一个浮点数,叫做rho;将alpha矩阵同support vector相乘,注意,alpha * supportVector,将得到一个列向量。之后,再该列向量的最后添加一个元素rho。如此,变得到了一个分类器,利用该分类器,直接替换OpenCV中行人检测默认的那个分类器(HOGDescriptor::setSVMDetector()),就可以利用你的训练样本训练出来的分类器进行行人检测了。
import cv2
import numpy as np
#法一:使用OpenCV自带的已经训练好的SVM分类器
hog = cv2.HOGDecriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
#法二:创建一个SVM分类器
svm = cv2.ml.SVM_create()
###下面的为SVM参数的设置,之前没有进行设置,得到的支持向量sv维度较高,导致合并部分维度不匹配,报错
svm.setCoef0(0.0)
svm.setDegree(3)
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 1000, 1e-3)
svm.setTermCriteria(criteria)
svm.setGamma(0)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setNu(0.5)
svm.setP(0.1)
svm.setC(0.01)
svm.setType(cv2.ml.SVM_EPS_SVR)
###对SVM进行训练
svm.train(np.array(hosList), cv2.ml.ROW_SAMPLE, np.array(labels))
#获取所有的支持向量
sv = svm.getSupportVectors()
rho, _, _ = svm.getDecisionFunction(0)
sv = np.transpose(sv)
hog1 = cv2.HOGDescriptor()
###设置线性分类器参数
hog1.setSVMDetector(np.append(sv, [[-rho]], 0))
编程要求
本次任务主要实现Hard example数据提取,以及基于OpenCV训练SVM分类器。
根据提示,在右侧编辑器补充代码,针对给定的注释提示进行对应操作。具体要求如下:
获取Hard example,即使用数据集训练SVM分类器,并将其用于负样本的检测得到的矩形框保存为64 * 128大小的图像加入到训练数据中,用于二次训练;
使用OpenCV的ml.SVM_create函数创建SVM分类器,并使用数据对其进行训练;
使用自己训练的分类器替换OpenCV的HOGDescriptor()类中的默认行人检测分类器。
预期输出 ———
posImageList: 8
posList 8
hosList 8
negImageList: 5
negList 50
hosList 58
labels 58
hardNegList: 0
hosList 58
labels 58
import cv2
import numpy as np
#获取检测子
def getHOGDetector(svm):
######## Begin1 ########
# 获取所有的支持向量
sv = svm.getSupportVectors()
######## End1 ########
rho, _, _ = svm.getDecisionFunction(0)
sv = np.transpose(sv)
return np.append(sv, [[-rho]], 0)
#获取Hard example
def getHardExamples(negImageList, svm):
hardNegList = []
######## Begin2 ########
# 声明HOG类,并使用参数svm代替默认检测器
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
for i in range(len(negImageList)):
rects, wei = hog.detectMultiScale(negImageList[i], winStride=(4, 4),padding=(8, 8), scale=1.05)
for (x,y,w,h) in rects:
hardExample = negImageList[i][y:y+h, x:x+w]
# 将获取的矩形框使用resize函数将其保存为64 * 128大小的图片,并将其就如hardNegList中
hardExample = cv2.resize(negImageList[i][y:y+h, x:x+w], (64, 128))
hardNegList.append(hardExample)
######## End2 ########
return hardNegList
#获取自己训练的SVM分类器
def getSVMClassifier(hosList, labels):
######## Begin3 ########
# 声明一个空的SVM分类器
svm = cv2.ml.SVM_create()
svm.setCoef0(0.0)
svm.setDegree(3)
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 1000, 1e-3)
svm.setTermCriteria(criteria)
svm.setGamma(0)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setNu(0.5)
svm.setP(0.1)
svm.setC(0.01)
svm.setType(cv2.ml.SVM_EPS_SVR)
# 训练SVM
svm.train(np.array(hosList), cv2.ml.ROW_SAMPLE, np.array(labels))
######## End3 ########
return svm