二、HOG算法简介
HOG是一种在计算机视觉和图像处理中用来进行物体检测的描述子。通过计算和统计局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。
主要思想:在一幅图像中,局部目标的表象和形状能够利用梯度或边缘的方向密度分布来进行描述。其本质是梯度的统计信息,而梯度主要存在于边缘所在的地方。
算法优点:与其他的特征描述方法相比,HOG具有较多优点。由于HOG是在图像的局部方格单元上进行操作的,所以它对图像的几何和光学形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上。其次,在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。因此HOG特征特别适合于做图像中的人体检测。
实现流程:
步骤1-读取待检测的图片;
步骤2-将输入图像灰度化(将输入的彩色图像的r,g,b值通过特定公式转换为灰度值);
步骤3-采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);
步骤4-计算图像中每个像素的梯度值(包括大小和方向),捕获轮廓信息;
步骤5-统计每个cell内的梯度直方图(不同梯度的个数),形成每个cell的特征描述子;
步骤6-将每几个cell组成一个block(以3*3为例),一个block内所有cell的特征串联起来得到该block的HOG特征描述子;
步骤7-将图像image内所有block块的HOG特征描述子串联起来得到该image(检测目标)的HOG特征描述子,这就是最终分类的特征向量。
三、SVM算法简介
支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;SVM还包括核技巧,这使它成为实质上的非线性分类器。SVM的的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。SVM的的学习算法就是求解凸二次规划的最优化算法。具体的算法实现原理请参考该博客。
四、基于HOG的目标检测算法训练流程
步骤1-从训练数据集中获取P个正样本块,并计算这P个正样本块的HOG特征描述子;
步骤2-从训练数据集中获取N个负样本块,并计算这N个负样本块的HOG特征描述子,其中N>>P;
步骤3-在这些正样本和负样本块上面训练一个SVM分类器模型;
步骤4-应用hard-negative-mining。对于负面训练集中的每个图像和每个可能的图像比例,在图像上面应用滑动窗口。在每个窗口中计算相应的HOG特征描述符并应用分类器。如果您的分类器(错误地)将给定窗口分类为一个对象(它将绝对存在误报),记录与误报补丁相关的特征向量以及分类的概率。这种方法被称为hard-negative-mining。具体效果如下图所示:
步骤5-首先获取使用hard-negative-mining技术获取到的错误的正样本块,然后按照概率值对它们进行排序;接着使用这些样本块重新训练分类器模型;
步骤6-将训练好的模型应用到测试图片中;
步骤7-对预测的结果使用NMS去除冗余的BB。
五、目标检测代码实现
训练代码如下所示,具体的训练数据集从该链接下载,最终将会获得一个训练好的分类模型。
import cv2
import numpy as np
import random
def load_images(dirname, amout = 9999):
img_list = []
file = open(dirname)
img_name = file.readline()
while img_name != '': # 文件尾
img_name = dirname.rsplit(r'/', 1)[0] + r'/' + img_name.split('/', 1)[1].strip('\n')
img_list.append(cv2.imread(img_name))
img_name = file.readline()
amout -= 1
if amout <= 0: # 控制读取图片的数量
break
return img_list
# 从每一张没有人的原始图片中随机裁出10张64*128的图片作为负样本
def sample_neg(full_neg_lst, neg_list, size):
random.seed(1)
width, height = size[1], size[0]
for i in range(len(full_neg_lst)):
for j in range(10):
y = int(random.random() * (len(full_neg_lst[i]) - height))
x = int(random.random() * (len(full_neg_lst[i][0]) - width))
neg_list.append(full_neg_lst[i][y:y + height, x:x + width])
return neg_list
# wsize: 处理图片大小,通常64*128; 输入图片尺寸>= wsize
def computeHOGs(img_lst, gradient_lst, wsize=(128, 64)):
hog = cv2.HOGDescriptor()
# hog.winSize = wsize
for i in range(len(img_lst)):
if img_lst[i].shape[1] >= wsize[1] and img_lst[i].shape[0] >= wsize[0]:
roi = img_lst[i][(img_lst[i].shape[0] - wsize[0]) // 2: (img_lst[i].shape[0] - wsize[0]) // 2 + wsize[0], \
(img_lst[i].shape[1] - wsize[1]) // 2: (img_lst[i].shape[1] - wsize[1]) // 2 + wsize[1]]
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
gradient_lst.append(hog.compute(gray))
# return gradient_lst
def get_svm_detector(svm):
sv = svm.getSupportVectors()
rho, _, _ = svm.getDecisionFunction(0)
sv = np.transpose(sv)
return np.append(sv, [[-rho]], 0)
# 主程序
# 第一步:计算HOG特征
neg_list = []
pos_list = []
gradient_lst = []
labels = []
hard_neg_list = []
svm = cv2.ml.SVM_create()
pos_list = load_images(r'G:/python_project/INRIAPerson/96X160H96/Train/pos.lst')
full_neg_lst = load_images(r'G:/python_project/INRIAPerson/train_64x128_H96/neg.lst')
sample_neg(full_neg_lst, neg_list, [128, 64])
print(len(neg_list))
computeHOGs(pos_list, gradient_lst)
[labels.append(+1) for _ in range(len(pos_list))]
computeHOGs(neg_list, gradient_lst)
[labels.append(-1) for _ in range(len(neg_list))]
# 第二步:训练SVM
svm.setCoef0(0)
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) # for EPSILON_SVR, epsilon in loss function?
svm.setC(0.01) # From paper, soft classifier
svm.setType(cv2.ml.SVM_EPS_SVR) # C_SVC # EPSILON_SVR # may be also NU_SVR # do regression task
svm.train(np.array(gradient_lst), cv2.ml.ROW_SAMPLE, np.array(labels))
# 第三步:加入识别错误的样本,进行第二轮训练
# 参考 http://masikkk.com/article/SVM-HOG-HardExample/
hog = cv2.HOGDescriptor()
hard_neg_list.clear()
hog.setSVMDetector(get_svm_detector(svm))
for i in range(len(full_neg_lst)):
rects, wei = hog.detectMultiScale(full_neg_lst[i], winStride=(4, 4),padding=(8, 8), scale=1.05)
for (x,y,w,h) in rects:
hardExample = full_neg_lst[i][y:y+h, x:x+w]
hard_neg_list.append(cv2.resize(hardExample,(64,128)))
computeHOGs(hard_neg_list, gradient_lst)
[labels.append(-1) for _ in range(len(hard_neg_list))]
svm.train(np.array(gradient_lst), cv2.ml.ROW_SAMPLE, np.array(labels))
# 第四步:保存训练结果
hog.setSVMDetector(get_svm_detector(svm))
hog.save('myHogDector.bin')
测试代码如下所示。
import cv2
import numpy as np
hog = cv2.HOGDescriptor()
hog.load('myHogDector.bin')
cap = cv2.VideoCapture(0)
while True:
ok, img = cap.read()
rects, wei = hog.detectMultiScale(img, winStride=(4, 4),padding=(8, 8), scale=1.05)
for (x, y, w, h) in rects:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow('a', img)
if cv2.waitKey(1)&0xff == 27: # esc键
break
cv2.destroyAllWindows()
六、非极大值抑制(NMS)简介及代码实现
对于目标检测算法而言,通常检测出的结果中会存在一些重复或者冗余的情况,即输出了多个可能是人脸的BB,那么我们通常都需要使用NMS技术来获得一个最准确的BB,下图展示了一个实例。