这两篇[1]【2】博客写的都非常详细。这里做个笔记记录一下。

HOG称为方向梯度直方图(Histogram of Oriented Gradient),主要是为了对图像进行特征提取。所以在传统目标检测算法中经常与SVM结合用于行人识别任务(当前都是基于深度学习来做了,毕竟效果不要太好了,并且省去了繁琐的特征检测过程)。

HOG主要是计算图像中每个像素的梯度值和梯度方向,从而来获得梯度特征,是一种特征描述子[1]

HOG特点

1.由于计算局部直方图和归一化,所以它对图像几何的和光学的形变都能保持很好的不变性;

2.细微的动作可以被忽略而不影响检测效果。

HOG计算步骤

1.对输入图像进行灰度化

2.利用gamma校正法对图像进行颜色空间归一化;(伽玛校正就是对图像的伽玛曲线进行编辑,以对图像进行非线性色调编辑的方法,检出图像信号中的深色部分和浅色部分,并使两者比例增大,从而提高图像对比度效果。主要是为了降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;)

3.计算图像中每个像素的梯度大小和方向;(主要是为了捕获轮廓信息,同时进一步弱化光照的干扰)

4.将图像划分cells,计算每个cell内的梯度直方图;

5.将每几个cell组成一个block,计算每个block内的梯度特征;

6.将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征描述子;

7.将图像image内的所有block的HOG特征描述子串联起来就可以得到该image(你要检测的目标)的HOG特征描述子了。这个就是最终的可供分类使用的特征向量了【2】

 算法实现

#coding:utf-8
#手敲的hog算法,跑通了,效果还行
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt class Hog_descriptor():
def __init__(self, img, cell_size=16, bin_size=8):
self.img = img
self.img = np.sqrt(img / np.max(img)) # 做完归一化取根号,取值范围[0,1]
self.img = img * 255
self.cell_size = cell_size
self.bin_size = bin_size
self.angle_unit = 360 / self.bin_size def extract(self):
height, width = self.img.shape
# 计算图像的梯度大小和方向
gradient_magnitude, gradient_angle = self.global_gradient()
gradient_magnitude = abs(gradient_magnitude)
cell_gradient_vector = np.zeros((int(height / self.cell_size), int(width / self.cell_size), self.bin_size))
for i in range(cell_gradient_vector.shape[0]):
for j in range(cell_gradient_vector.shape[1]):
# cell内的梯度大小
cell_magnitude = gradient_magnitude[i * self.cell_size:(i + 1) * self.cell_size,
j * self.cell_size:(j + 1) * self.cell_size]
# cell内的梯度方向
cell_angle = gradient_angle[i * self.cell_size:(i + 1) * self.cell_size,
j * self.cell_size:(j + 1) * self.cell_size]
# 转化为梯度直方图格式
cell_gradient_vector[i][j] = self.cell_gradient(cell_magnitude, cell_angle) # 绘制梯度直方图
hog_image = self.render_gradient(np.zeros([height, width]), cell_gradient_vector) # block组合、归一化
hog_vector = []
for i in range(cell_gradient_vector.shape[0] - 1):
for j in range(cell_gradient_vector.shape[1] - 1):
block_vector = []
block_vector.extend(cell_gradient_vector[i][j])
block_vector.extend(cell_gradient_vector[i][j + 1])
block_vector.extend(cell_gradient_vector[i + 1][j])
block_vector.extend(cell_gradient_vector[i + 1][j + 1])
mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))
magnitude = mag(block_vector)
if magnitude != 0:
normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]
block_vector = normalize(block_vector, magnitude)
hog_vector.append(block_vector)
return hog_vector, hog_image def global_gradient(self):
gradient_values_x = cv2.Sobel(self.img, cv2.CV_64F, 1, 0, ksize=5)
gradient_values_y = cv2.Sobel(self.img, cv2.CV_64F, 0, 1, ksize=5)
gradient_magnitude = cv2.addWeighted(gradient_values_x, 0.5, gradient_values_y, 0.5, 0)
gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True)
return gradient_magnitude, gradient_angle def cell_gradient(self, cell_magnitude, cell_angle):
orientation_centers = [0] * self.bin_size
for i in range(cell_magnitude.shape[0]):
for j in range(cell_magnitude.shape[1]):
gradient_strength = cell_magnitude[i][j]
gradient_angle = cell_angle[i][j]
min_angle, max_angle, mod = self.get_closest_bins(gradient_angle)
orientation_centers[min_angle] += (gradient_strength * (1 - (mod / self.angle_unit)))
orientation_centers[max_angle] += (gradient_strength * (mod / self.angle_unit))
return orientation_centers def get_closest_bins(self, gradient_angle):
idx = int(gradient_angle / self.angle_unit)
mod = gradient_angle % self.angle_unit
return idx, (idx + 1) % self.bin_size, mod def render_gradient(self, image, cell_gradient):
cell_width = self.cell_size / 2
max_mag = np.array(cell_gradient).max()
for x in range(cell_gradient.shape[0]):
for y in range(cell_gradient.shape[1]):
cell_grad = cell_gradient[x][y]
cell_grad /= max_mag
angle = 0
angle_gap = self.angle_unit
for magnitude in cell_grad:
angle_radian = math.radians(angle)
x1 = int(x * self.cell_size + magnitude * cell_width * math.cos(angle_radian))
y1 = int(y * self.cell_size + magnitude * cell_width * math.sin(angle_radian))
x2 = int(x * self.cell_size - magnitude * cell_width * math.cos(angle_radian))
y2 = int(y * self.cell_size - magnitude * cell_width * math.sin(angle_radian))
cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
angle += angle_gap
return image img = cv2.imread('qiao.jpg', cv2.IMREAD_GRAYSCALE)
# v2.IMREAD_COLOR:读取一副彩色图片,图片的透明度会被忽略,默认为该值,实际取值为1;
# cv2.IMREAD_GRAYSCALE:以灰度模式读取一张图片,实际取值为0
# cv2.IMREAD_UNCHANGED:加载一副彩色图像,透明度不会被忽略。
hog = Hog_descriptor(img, cell_size=8, bin_size=9)
vector, image = hog.extract() # 输出图像的特征向量shape
print(np.array(vector).shape)
plt.imshow(image, cmap=plt.cm.gray)
plt.show()

结果:

原图 特征

HOG算法的笔记与python实现的更多相关文章

  1. 笔记之Python网络数据采集

    笔记之Python网络数据采集 非原创即采集 一念清净, 烈焰成池, 一念觉醒, 方登彼岸 网络数据采集, 无非就是写一个自动化程序向网络服务器请求数据, 再对数据进行解析, 提取需要的信息 通常, ...

  2. 机器学习经典算法具体解释及Python实现--线性回归(Linear Regression)算法

    (一)认识回归 回归是统计学中最有力的工具之中的一个. 机器学习监督学习算法分为分类算法和回归算法两种,事实上就是依据类别标签分布类型为离散型.连续性而定义的. 顾名思义.分类算法用于离散型分布预測, ...

  3. 风变编程笔记(二)-Python爬虫精进

    第0关  认识爬虫 1. 浏览器的工作原理首先,我们在浏览器输入网址(也可以叫URL),然后浏览器向服务器传达了我们想访问某个网页的需求,这个过程就叫做[请求]紧接着,服务器把你想要的网站数据发送给浏 ...

  4. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  5. 机器学习经典算法详解及Python实现--基于SMO的SVM分类器

    原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector  ...

  6. Noah的学习笔记之Python篇:命令行解析

    Noah的学习笔记之Python篇: 1.装饰器 2.函数“可变长参数” 3.命令行解析 注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/) ...

  7. Noah的学习笔记之Python篇:函数“可变长参数”

    Noah的学习笔记之Python篇: 1.装饰器 2.函数“可变长参数” 3.命令行解析 注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/) ...

  8. Noah的学习笔记之Python篇:装饰器

    Noah的学习笔记之Python篇: 1.装饰器 2.函数“可变长参数” 3.命令行解析 注:本文全原创,作者:Noah Zhang  (http://www.cnblogs.com/noahzn/) ...

  9. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  10. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

随机推荐

  1. AI/机器学习(计算机视觉/NLP)方向面试复习1

    1. 判断满二叉树 所有节点的度要么为0,要么为2,且所有的叶子节点都在最后一层. #include <iostream> using namespace std; class TreeN ...

  2. vue3源码学习api-vue-sfc文件编译

    vue 最有代表性质的就是.VUE 的文件,每一个vue文件都是一个组件,那么vue 组件的编译过程是什么样的呢 Vue 单文件组件 (SFC)和指令 ast 语法树 一个 Vue 单文件组件 (SF ...

  3. python中不同方法的按索引读取数组的性能比较——哪种按索引读取数组的性能更好

    写python代码这么多年,从来也没有想过不同方式的读取python数组会有什么太大的性能差距,不过这段时间写代码突然发现这个差别还挺大,于是就多研究了一下. 本文研究的是使用不同方式来对python ...

  4. 微信支付退款和退款结果查询接口简单实现(.Net 7.0)

    〇.前言 相较于支付宝,微信支付对 .Net 的支持就没那么充分,官方没有提供 SDK. 但值得庆幸的是,在社区有大佬封装了 v3 版 .Net SDK. 原文链接:https://developer ...

  5. Apache DolphinScheduler 4月简报:社区发展与技术革新速递

    各位热爱 DolphinScheduler 的小伙伴们,4 月份的 DolphinScheduler 社区月报更新啦!这里将记录 DolphinScheduler 社区每月的重要更新,欢迎关注! 月度 ...

  6. java关于二维数组的操作

    代码: ''' package tests; public class Yanghui { public static void main(String[] args) { //声明二维数组的三种方式 ...

  7. SpringBoot复习

    SpringBoot 开启事务:Application 加@EnableTransactionManagement ,Service方法加@Transaction 注解 注意:@Transaction ...

  8. 12-es6类的方式封装折线图

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  9. Linux驱动 | 从0写一个设备树节点实例

    一.前言 设备树是每一个Linux驱动工程师都必须掌握的一个知识点,有很多之前做单片机的朋友刚接触Linux驱动时,会一脸懵! 其实设备树的使用并没有大家想像的那么复杂,对于大部分工程师来说,只要会修 ...

  10. P7706 文文的摄影布置 题解

    P7706 文文的摄影布置 题解 原题 读完题,发现是线段树.单点修改+区间查询. 不过查询的值有些奇怪,就是了,我们考虑用线段树维护这个 ψ 值(下称待求值). 对于一个区间的待求值,大概有四种情况 ...