传统目标分类器主要包括Viola Jones Detector、HOG Detector、DPM Detector,本文主要介绍HOG Detector与SVM分类器的组合实现行人检测。
HOG(Histograms of Oriented Gradients:定向梯度直方图)是一种基于图像梯度的特征提取方法,被广泛应用于计算机视觉和机器学习领域。由Navneet Dalal和Bill Triggs在2005年提出。
HOG特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子,是与SIFT、SURF、ORB属于同一类型的描述符。HOG不是基于颜色值而是基于梯度来计算直方图的,它通过计算和统计图像局部区域的梯度方向直方图来构建特征。HOG特征结合SVM分类器已经被广泛应用到图像识别中,尤其在行人检测中获得了极大的成功。
此方法的基本观点是:局部目标的外表和形状可以被局部梯度或边缘方向的分布很好的描述,即使我们不知道对应的梯度和边缘的位置。(本质:梯度的统计信息,梯度主要存在于边缘的地方)
首先,将图像分成很多小的连通区域,我们把它叫做cell,然后采集cell中各像素点的梯度大小和方向,然后在每个cell中通过某种方式绘制一个一维的梯度方向直方图。
其次,为了对光照和阴影有更好的不变性,需要对直方图进行对比度归一化,这可以通过把这些直方图在图像的更大的范围内(我们把它叫做区间或者block)进行对比度归一化。我们把归一化的块描述符叫作HOG描述子。
将检测窗口中的所有块的HOG描述子组合起来就形成了最终的特征向量,然后使用SVM分类器进行行人检测。下图描述了特征提取和目标检测流程。检测窗口划分为重叠的块,对这些块计算HOG描述子,形成的特征向量放到线性SVM中进行目标/非目标的二分类。检测窗口在整个图像的所有位置和尺度上进行扫描,并对输出的金字塔进行非极大值抑制来检测目标。(检测窗口的大小一般为128×64128×64)
Gamma校正公式为:
f(I)=Iγ
其中I为图像像素值,γ为Gamma校正系数。γ系数设定影响着图像的调整效果,结合下图,我们来看一下Gamma校正的作用:
γ<1在低灰度值区域内,动态范围变大,图像对比度增加强;在高灰度值区域,动态范围变小,图像对比度降低,同时,图像的整体灰度值变大;
γ>1在低灰度值区域内,动态范围变小,图像对比度降低;在高灰度值区域,动态范围变大,图像对比度提高,同时,图像的整体灰度值变小;
左边的图像为原图,中间图像的γ=12.2,右图γ=2.2。
作者在他的博士论文里有提到,对于涉及大量的类内颜色变化,如猫,狗和马等动物,没标准化的RGB图效果更好,而牛,羊的图做gamma颜色校正后效果更好。是否用gamma校正需要分析具体的训练集情况。
像素点的幅值和幅度
首先,利用中心差分近似计算每个像素点对x、y的偏导:
Gx(x,y)=I(x+1,y)−I(x−1,y)
Gy(x,y)=I(x,y+1)−I(x,y−1)
上式中Gx(x,y)、Gy(x,y)分别表示输入图像在像素点(x,y)处的水平方向梯度和垂直方向梯度,该点处的幅值和幅度为:
G(x, y) = √Gx(x, y)2+Gy(x, y)2
α = arctan(Gy(x,y)/Gx(x,y))
cell梯度直方图
为什么要将图像按cell尺寸分割呢?将图像分成cell单元是为了可以用多个cell组合成block,通过block获得较好的特征和抗光照影响。
我们先把整个图像划分为若干个8x8的小单元,称为cell,并计算每个cell的梯度直方图。这个cell的尺寸也可以是其他值,根据具体的特征而定。一个8x8的cell包含了8_8_2=128个值,因为每个像素包括梯度的大小和方向。
将cell单元中的幅度作为横轴,幅度对应像素点的幅度的累加值作为相应纵轴值,组成一个梯度直方图。论文作者,将幅度分成9份取得了较为理想的行人检测效果。我们将横轴的角度分成9份,也就是9个bins,每20°为一份(有符号角度是40°为一份),横轴坐标为0, 20, 40, …, 160。那么横轴值对应的纵轴值该如何计算呢,我们用一个例子来学习:
比如上面方向图中蓝圈包围的像素,角度为80度,这个像素对应的幅值为2,所以在直方图80度对应的bin加上2。红圈包围的像素,角度为10度,介于0度和20度之间,其幅值为4,那么这个梯度值就被按比例分给0度和20度对应的bin,也就是各加上2。
还有一个细节需要注意,如果某个像素的梯度角度大于160度,也就是在160度到180度之间,那么把这个像素对应的梯度值按比例分给0度和160度对应的bin。将这 8x8 的cell中所有像素的梯度值加到各自角度对应的bin中,就形成了长度为9的直方图:
可以看到直方图中,0度和160附近有很大的权重,说明了大多数像素的梯度向上或者向下,也就是这个cell是个横向边缘。现在我们就可以用这9个数的梯度直方图来代替原来很大的三维矩阵,即代替了8x8x2个值。
于局部光照的变化,以及前景背景对比度的变化,使得梯度强度的变化范围非常大,这就需要对梯度做局部对比度归一化。归一化能够进一步对光照、阴影、边缘进行压缩,使得特征向量对光照、阴影和边缘变化具有鲁棒性。
具体的做法:将细胞单元组成更大的空间块(block),然后针对每个块进行对比度归一化。最终的描述子是检测窗口内所有块内的细胞单元的直方图构成的向量。事实上,块之间是有重叠的,也就是说,每个细胞单元的直方图都会被多次用于最终的描述子的计算。块之间的重叠看起来有冗余,但可以显著的提升性能 。如下动图所示。
如上图所示,每个block由2×2个cell组成,每一个cell包含8×8个像素点,每个cell提取9个直方图通道,因此一个块的特征向量长度为2×2×9=36。
假设v是未经归一化的特征向量。||v||k是v的k范数,k=1,2,对块的特征向量进行归一化,一般有以下四种方法:
在人体检测系统中进行HOG计算时一般使用L2−norm,Dalal的文章也验证了对于人体检测系统使用L2−norm的时候效果最好。
最后一步就是对一个样本中所有的块进行HOG特征的提取,并将它们结合成最终的特征向量送入分类器。
那么一个样本可以提取多少个特征呢?之前我们已经说过HOG特征的提取过程:
例如:对于128×64128×64的输入图片(后面我所有提到的图像大小指的是h×w),每个块由2×2个cell组成,每个cell由8×8个像素点组成,每个cell提取9个bin大小的直方图,以1个cell大小为步长,那么水平方向有15个扫描窗口,垂直方向有7个扫描窗口,也就是说,一共有15∗7∗2∗2∗9=3780个特征。
这里我们介绍一下Dalal等人的训练方法:
这种二次训练的处理过程显著提高了每个检测器的表现,一般可以使得每个窗口的误报率(FPPW False Positives Per Window)下降5%。
前面虽然介绍了HOG特征的提取,但是想把HOG特征应用到目标检测上,我们还需考虑两个问题:
图像金字塔、滑动窗口可以解决上述两个问题。
图像金字塔有助于解决不同尺度下的目标检测问题,图像金字塔使图像的多尺度表示,如上图所示。构建图像金字塔一般包含以下步骤:
上一节---Viola Jones Detector中我们使用的detectMultiScale()
函数就涉及到了图像金字塔,该函数利用scaleFactor参数缩放图像(或检测窗口)实现图像金字塔,scaleFactor越小,金字塔的层数就越多,计算就越慢,计算量也会更大,但是计算结果相对更精确。
滑动窗口是用在计算机视觉的一种技术,它包括图像中要移动部分(滑动窗口)的检查以及使用图像金字塔对各部分进行检测,这是为了在多尺度下检测对象。滑动窗口通过扫描较大图像的较小区域来解决定位问题,进而在同一图像的不同尺度下重复扫描。
使用这种方法进行目标检测会出现一个问题:区域重叠,针对区域重叠问题,我们可以利用非极大值抑制,来消除重叠的窗口。
opencv附带一个预训练的 HOG + 线性 SVM 模型,可用于在图像和视频流中执行行人检测
首先,使用cv2.HOGDescriptor()
实例化HOG特征描述符类;然后再用cv2.HOGDescriptor_getDefaultPeopleDetector()
静态函数获取行人检测训练的分类器的系数x;再之后将系数x传入cv2.HOGDescriptor.setSVMDetector()
函数,用于激活默认的SVM分类器;最后使用cv2.HOGDescriptor.detectMultiScale()
函数实现行人检测,它返回检测到的对象的矩形框和权重值。
语法:cv2.HOGDescriptor(),无需传参
将opencv内置的HOG描述符实例化。
语法:cv2.HOGDescriptor_getDefaultPeopleDetector(),无需传参
获取opencv内置的行人检测训练的分类器的系数x
语法:cv2.HOGDescriptor.setSVMDetector(svmdetector)
参数:svmdetector---svm分类器的系数
用于激活默认的SVM分类器。
语法:cv2.HOGDescriptor.detectMultiScale(img[, foundLocations[, foundWeights[, hitThreshold[, winStride[, padding[, scale[, groupThreshold[, useMeanshiftGrouping]]]]]]]])--->rects, weights
参数:
img---输入的检测图像。
wimStride---表示 HOG 检测窗口移动步幅,它必须是块步幅的倍数。
scale---表示构造金字塔结构图像时使用的缩放因子,默认值为 1.05。
useMeanshiftGrouping---表示是否消除重叠的检测结果。
返回检测到的对象的矩形框和权重值。
import cv2
import tkinter as tk
from tkinter import filedialog
def img_test():
# 获取选择文件路径,人机交互式的选择要测试的图片
# 实例化
root = tk.Tk()
root.withdraw()
# 获取文件的绝对路径路径
return filedialog.askopenfilename()
def is_inside(o, i):
# 判断矩形o是否在矩形i中
ox, oy, ow, oh = o
ix, iy, iw, ih = i
return ox > ix and oy > iy and ox + ow < ix + iw and oy + oh < iy + ih
def detect_test():
img = cv2.imread(img_test())
img_gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
# 使用默认的HOG特征描述符
hog = cv2.HOGDescriptor()
# cv2.HOGDescriptor_getDefaultPeopleDetector函数返回为行人检测训练的分类器的系数
detector = cv2.HOGDescriptor_getDefaultPeopleDetector()
# 使用默认的行人分类器(检测窗口64x128)
hog.setSVMDetector(detector)
# 使用detecMultiScale函数检测图像中的行人,返回值为行人对应的矩形框和权重值
found, weight = hog.detectMultiScale(img_gray, scale=1.02)
found_filtered = []
for ri, r in enumerate(found):
for qi, q in enumerate(found):
# r在q内?
if ri != qi and is_inside(r, q):
break
else:
found_filtered.append(r)
for person in found_filtered:
x, y, w, h = person
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 2)
return img
if __name__ == '__main__':
image = cv2.imread('image\\icon.jpg')
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', image)
while 1:
k = cv2.waitKey()
if k == ord('q'):
break
elif k == ord('n'):
image = detect_test()
cv2.imshow('image', image)
cv2.destroyAllWindows()
运行结果:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章