热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于python的HOG+SVM目标检测算法实现

目录一、场景需求解读二、HOG算法简介三、SVM算法简介四、基于HOG的目标检测算法训练流程五、目标检测代码实现六、非极大值抑制(NMS)简介及代码实现七、NMS效果展示与分析八、

目录

    • 一、场景需求解读
    • 二、HOG算法简介
    • 三、SVM算法简介
    • 四、基于HOG的目标检测算法训练流程
    • 五、目标检测代码实现
    • 六、非极大值抑制(NMS)简介及代码实现
    • 七、NMS效果展示与分析
    • 八、思维扩展
    • 参考资料
    • 注意事项

一、场景需求解读

  目标检测是一个很常见的计算机视觉任务,它在现实场景中具有很多的应用。随着深度学习技术的快速发展,当前主流的目标检测算法主要分为单阶段和双阶段,代表性的算法包括SSD和Faster-rcnn等;除此之外也可以分为基于Anchors和Anchors free的算法,尽管这些算法都能取得较高的精度,但是它们都需要依赖GPU和大量的训练样本,另外,这些算法的运行速度都比较慢,一般都是在GPU上面能获得近似实时的速度。对于现实生活中的一些场景而言,它们可能对算法的速度和成本有着较高的要求,但是检测任务又相对来讲比较简单,对于这种情况而言,传统的基于HOG+SVM的检测算法仍然具有较大的用武之地。下面展示了一个案例。
《基于python的HOG+SVM目标检测算法实现》

二、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特征描述子,这就是最终分类的特征向量。
    《基于python的HOG+SVM目标检测算法实现》

三、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。具体效果如下图所示:
    《基于python的HOG+SVM目标检测算法实现》

  • 步骤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,下图展示了一个实例。
《基于python的HOG+SVM目标检测算法实现》
NMS的原理-NMS的本质是搜索局部极大值,抑制非极大值元素。
NMS的作用-当算法对一个目标产生了多个候选框的时候,选择 score 最高的框,并抑制其他对于改目标的候选框。
NMS的应用场景-一幅图中有多个目标(如果只有一个目标,那么直接取 score 最高的候选框即可)。
NMS的输入-算法对一幅图产生的所有的候选框,以及每个框对应的 score (可以用一个 5 维数组 dets 表示,前 4 维表示四个角的坐标,第 5 维表示分数),阈值 thresh。
NMS的输出-正确的候选框组(dets 的一个子集)。

# coding=utf-8
# 导入python包
import numpy as np
import cv2
def non_max_suppression_slow(boxes, overlapThresh):
# 如果输入为空,直接返回空列表
if len(boxes) == 0:
return []
# 初始化列表索引
pick = []
# 获取边界框的坐标值
x1 = boxes[:,0]
y1 = boxes[:,1]
x2 = boxes[:,2]
y2 = boxes[:,3]
# 计算边界框的区域大小并按照右下角的y坐标进行排序
area = (x2 - x1 + 1) * (y2 - y1 + 1)
idxs = np.argsort(y2)
while len(idxs) > 0:
# 获取索引列表中的最后一个索引,将索引值添加到所选索引的列表中,然后使用最后一个索引初始化禁止显示列表。
last = len(idxs) - 1
i = idxs[last]
pick.append(i)
suppress = [last]
# 遍历索引列表中的所有索引
for pos in xrange(0, last):
# 获取当前的索引
j = idxs[pos]
# 查找边界框起点的最大(x,y)坐标和边界框终点的最小(x,y)坐标
xx1 = max(x1[i], x1[j])
yy1 = max(y1[i], y1[j])
xx2 = min(x2[i], x2[j])
yy2 = min(y2[i], y2[j])
# 计算边界框的宽和高
w = max(0, xx2 - xx1 + 1)
h = max(0, yy2 - yy1 + 1)
# 计算区域列表中计算的边界框和边界框之间的重叠率
overlap = float(w * h) / area[j]
# 如果它们具有较大的重叠率,则抑制掉它
if overlap > overlapThresh:
suppress.append(pos)
# 从禁止显示列表中的索引列表中删除所有索引
idxs = np.delete(idxs, suppress)
# 返回选择的边界框
return boxes[pick]
# 构建一个列表,其中包含将与其各自的边界框一起检查的图像
images = [
("audrey.jpg", np.array([
(12, 84, 140, 212),
(24, 84, 152, 212),
(36, 84, 164, 212),
(12, 96, 140, 224),
(24, 96, 152, 224),
(24, 108, 152, 236)])),
("bksomels.jpg", np.array([
(114, 60, 178, 124),
(120, 60, 184, 124),
(114, 66, 178, 130)])),
("gpripe.jpg", np.array([
(12, 30, 76, 94),
(12, 36, 76, 100),
(72, 36, 200, 164),
(84, 48, 212, 176)]))]
# 循环遍历所有的图像
for (imagePath, boundingBoxes) in images:
# 读取图片并进行复制
print ("[x] %d initial bounding boxes" % (len(boundingBoxes)))
image = cv2.imread(imagePath)
orig = image.copy()
# 遍历每一个矩形框并绘制它们
for (startX, startY, endX, endY) in boundingBoxes:
cv2.rectangle(orig, (startX, startY), (endX, endY), (0, 0, 255), 2)
# 应用非极大值抑制处理
pick = non_max_suppression_slow(boundingBoxes, 0.3)
print ("[x] after applying non-maximum, %d bounding boxes" % (len(pick)))
# 绘制处理之后的矩形框
for (startX, startY, endX, endY) in pick:
cv2.rectangle(image, (startX, startY), (endX, endY), (0, 255, 0), 2)
# 显示结果
cv2.imshow("Original", orig)
cv2.imshow("After NMS", image)
cv2.waitKey(0)

七、NMS效果展示与分析

《基于python的HOG+SVM目标检测算法实现》
《基于python的HOG+SVM目标检测算法实现》
《基于python的HOG+SVM目标检测算法实现》
  上图展示了NMS算法的处理效果。上面分别展示了3个不同测试图片上的测试效果,红色的边界框表示原始的进行NMS处理之前的效果,绿色的边界框表示进行NMS处理之后的效果,通过上面的结果我们可以发现NMS算法可以很好的抑制掉那些重复的边界框,最终找到一个最准确的边界框。

八、思维扩展

  上面仅仅展示了一种NMS算法,如果你进行实际的测试之后你可能会发现这个算法的处理速度比较慢,并不能满足你的性能要求。那你聪明的你肯定想要了一种可以用来进行算法加速的思路,那就是是使用numpy和cython进行算法加速,下面展示了一个加速版本的NMS算法实现。

# --------------------------------------------------------
# Fast R-CNN
# Copyright (c) 2015 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Written by Ross Girshick
# --------------------------------------------------------
import numpy as np
cimport numpy as np
cdef inline np.float32_t max(np.float32_t a, np.float32_t b):
return a if a >= b else b
cdef inline np.float32_t min(np.float32_t a, np.float32_t b):
return a if a <= b else b
def cpu_nms(np.ndarray[np.float32_t, ndim=2] dets, np.float thresh):
cdef np.ndarray[np.float32_t, ndim=1] x1 = dets[:, 0]
cdef np.ndarray[np.float32_t, ndim=1] y1 = dets[:, 1]
cdef np.ndarray[np.float32_t, ndim=1] x2 = dets[:, 2]
cdef np.ndarray[np.float32_t, ndim=1] y2 = dets[:, 3]
cdef np.ndarray[np.float32_t, ndim=1] scores = dets[:, 4]
cdef np.ndarray[np.float32_t, ndim=1] areas = (x2 - x1 + 1) * (y2 - y1 + 1)
cdef np.ndarray[np.int_t, ndim=1] order = scores.argsort()[::-1]
cdef int ndets = dets.shape[0]
cdef np.ndarray[np.int_t, ndim=1] suppressed = \
np.zeros((ndets), dtype=np.int)
# nominal indices
cdef int _i, _j
# sorted indices
cdef int i, j
# temp variables for box i's (the box currently under consideration)
cdef np.float32_t ix1, iy1, ix2, iy2, iarea
# variables for computing overlap with box j (lower scoring box)
cdef np.float32_t xx1, yy1, xx2, yy2
cdef np.float32_t w, h
cdef np.float32_t inter, ovr
keep = []
for _i in range(ndets):
i = order[_i]
if suppressed[i] == 1:
continue
keep.append(i)
ix1 = x1[i]
iy1 = y1[i]
ix2 = x2[i]
iy2 = y2[i]
iarea = areas[i]
for _j in range(_i + 1, ndets):
j = order[_j]
if suppressed[j] == 1:
continue
xx1 = max(ix1, x1[j])
yy1 = max(iy1, y1[j])
xx2 = min(ix2, x2[j])
yy2 = min(iy2, y2[j])
w = max(0.0, xx2 - xx1 + 1)
h = max(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (iarea + areas[j] - inter)
if ovr >= thresh:
suppressed[j] = 1
return keep

参考资料

[1] 参考链接1
[2] 参考链接2

注意事项

[1] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:1575262785@qq.com),我会在第一时间回复大家,谢谢大家的关注.
[2] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[3] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[4] 本文测试的图片可以通过该链接进行下载。网盘链接&#8211; 提取码:e31o。
[5] 本人业余时间承接各种本科毕设设计和各种小项目,包括图像处理(数据挖掘、机器学习、深度学习等)、matlab仿真、python算法及仿真等,有需要的请加QQ:1575262785详聊!!!


推荐阅读
  • 语义分割系列3SegNet(pytorch实现)
    SegNet手稿最早是在2015年12月投出,和FCN属于同时期作品。稍晚于FCN,既然属于后来者,又是与FCN同属于语义分割网络 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 如何优化Webpack打包后的代码分割
    本文介绍了如何通过优化Webpack的代码分割来减小打包后的文件大小。主要包括拆分业务逻辑代码和引入第三方包的代码、配置Webpack插件、异步代码的处理、代码分割重命名、配置vendors和cacheGroups等方面的内容。通过合理配置和优化,可以有效减小打包后的文件大小,提高应用的加载速度。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
  • 基于词向量计算文本相似度1.测试数据:链接:https:pan.baidu.coms1fXJjcujAmAwTfsuTg2CbWA提取码:f4vx2.实验代码:imp ... [详细]
  • 【Python 爬虫】破解按照顺序点击验证码(非自动化浏览器)
    #请求到验证码base64编码json_img_datajson_raw.get(Vimage)#获取到验证码编码 #保存验证码图片到本地defbase64_to_img(bstr ... [详细]
author-avatar
millottgerould
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有