本文翻译自 SATYA MALLICK 的 "Histogram of Oriented Gradients"

原文链接: https://www.learnopencv.com/histogram-of-oriented-gradients/

翻译:coneypo

在这篇文章中,我们将会学习 HOG (Histogram of Oriented Gradients,方向梯度直方图)特征描述子 的详细内容。

我们将学习 HOG 算法是如何实现的,以及在 OpenCv / MATLAB 或者其他工具里面如何计算特征子。

这篇文章是我正在写的,关于 Image Recognition / 图像识别Object Detection / 目标检测 系列文章中的一部分。

很多事情看起来困难又神秘,但是你一旦花时间去了解,揭开神秘面纱,你就会发现神奇之处。

如果你是一个初学者,觉得计算机视觉又难又神秘,请记住一句话:

问:如何吃掉一个大象?

答:一口一口吃

什么是 Feature Desciptor / 特征描述子

Feature Desciptor / 特征描述子 从图像中提取有用信息,剔除无关信息;

典型的,特征描述子从将一张 宽度 * 高度 * 3 ( 通道数 ) 大小的图像,提取出长度为 n 的 Feature Vector / 特征向量 或者 Feature Array / 特征矩阵

比如 HOG 特征描述子会从一张 64 * 128 * 3 的图像中提取出长度为 3780 的特征向量;

请记住, HOG 的特征描述子也可以计算其他尺寸,但是这篇文章中,我使用上述尺寸,以便你能够轻松的理解概念。

这些概念听起来都挺不错,但是哪些是“有用的信息”,有些又是“无用的信息"

定义“有用的信息”,我们需要知道有用的信息用来干什么的;

很明显,通过特征向量用来浏览图像是没用的,但是在图像识别或者目标检测中,特征向量会变得很有用;

在一些图像分类算法中比如 SVM,Support Vector Machine,支持向量机 中,用特征向量进行分类会达到很好的结果。

但是在分类任务中,哪些特征是有用的呢?

我们借助下面的例子来讨论,比如现在我们想通过一个目标检测器,可以检测衬衫和大衣的纽扣;

一个纽扣是一个圆形(图片中也有可能看起来像是椭圆),一般来说有几个孔,用于缝到衣服上面;

你可以在纽扣的图像上使用一个 Edge detector / 边缘检测器,可以轻松通过检测边缘来辨别它是不是一个纽扣;

这个例子中,边缘信息是“有用的”而颜色信息是 ”无用的“;

除此之外,特征也需要有足够特殊的地方。比如一个好的特征,应该能够让你辨别出纽扣和其他圆形的物体,比如硬币和汽车轮胎。

如何计算 Histogram of Oriented Gradients / 方向梯度直方图?

在这一节,我们会继续深入学习如何计算 HOG 特征描述子。

步骤1:预加工

之前提到用于行人检测的 HOG 特征描述子,是基于 64×128 大小的图像。当然,图像可能是任何尺寸的;

对于这些之后用于分析的图像,唯一需要进行的处理是调整纵横比图像大小;

在我们的例子中,需要调整纵横比为1:2,比如图像可以被调整为 100×200, 128×256, 或者 1000×2000,但是不能是 101×205;

原始图像大小是 720×475,我们截切出来 100×200 大小图像用来计算 HOG 特征描述子,然后重新调整大小到 64×128;

现在我们就做完了计算 HOG 特征描述子准备工作。

Dalal 和 Triggs 的论文也提到了 Gamma Correction / 伽马校正 作为预处理步骤,但性能提升很小,因此我们选择预处理中跳过这一步。

步骤 2 :计算梯度图像

为了计算 HOG 特征描述子,我们第一步需要计算水平和垂直方向的梯度。我们通过下面的 Kernel / 核 来处理图像,很容易计算出梯度的直方图。

我们可以使用核大小为 1 的 OpenCv 的 Sobel 算子:

    // C++ gradient calculation.
// Read image
Mat img = imread("bolt.png");
img.convertTo(img, CV_32F, /255.0);
 
// Calculate gradients gx, gy
Mat gx, gy;
Sobel(img, gx, CV_32F, , , );
Sobel(img, gy, CV_32F, , , );
1    # Python gradient calculation
2  
3 # Read image
4 im = cv2.imread('bolt.png')
5 im = np.float32(im) / 255.0
6  
7 # Calculate gradient
8 gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
9 gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)

接下来,我们通过下面的公式来计算梯度的幅值和方向:

在 OpenCv 中,我们可以使用 cartToPolar 函数来计算上述数值:

// C++ Calculate gradient magnitude and direction (in degrees)
Mat mag, angle;
cartToPolar(gx, gy, mag, angle, );
The same code in python looks like this.
# Python Calculate gradient magnitude and direction ( in degrees )
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

下图展示了梯度计算结果:

左边:x 方向梯度的绝对值
中间:y 方向梯度的绝对值
右边:梯度的幅值

注意 x 方向的梯度代表垂直方向的变化趋势,而 y 方向代表的是水平方向的变化;

如果图像像素变换迅速的话,可以在梯度图中明显看出,而当区域内变化缓慢时,不会出现梯度幅值;

梯度图像去除了很多不必要的信息,保留了关键信息。换句话说,你可以看着梯度图,然后轻松的辨别出来照片里的人;

每一个像素点,都有一个 Magnitude / 幅值 Direction / 方向

对于彩色的图像,三种通道的梯度都会被评估计算,取的是最大的梯度。

步骤 3:在 8*8 cells / 网格 中计算梯度直方图

这一步,图像会被分割成 8*8 大小的单独 cells / 小格子,然后对于每个 8*8 的小格子,分别计算梯度直方图;

我们先来了解下为什么要把图像分割为 8*8 的小格子;

有一个重要的原因是使用特征描述子来描述一幅 image / 图像 的一个 patch / 子图像 的话,网格分割会提供了一个 compact / 紧凑 的表示方式;

一个 8*8 的子图像包含 8*8*3 = 192 个像素值。每个像素梯度有两个值( Magnitude / 幅值Direction / 方向 ),所以每个子图像会有 8*8*2=128 个数值;

在这节结束之前,我们会看到这 128 个数值如何使用 9 位的数组来 存储在 9 位的直方图中。经过压缩处理之后的数据具有更好的 抗噪性

但是为什么取得是 8*8 的子图像而不是 32*32? 这是根据我们所要检测的目标来决定的;

对于 HOG 行人检测, 从 64*128 的行人图像中提取出的 8*8 子图像,已经足够用来提取出有用的信息(比如脸部,头的顶部等等)

直方图有必要是 9 位的向量,与 0,20,40,60… 160 度对应;

让我们来看看在一个 8*8 的子图像中,梯度是什么样的:

中间:用箭头来代表颜色和梯度的变化;

右边:用数字来代表子图像中的梯度;

如果你是一个计算机视觉的初学者,中间的图像会很有帮助很形象;

通过箭头来表示图像中梯度的变化,箭头的方向表示着像素强度变化的方向,幅值表示变化的缓慢;

通过右边的图,我们可以看到 8*8 子图像中提取出来的代表梯度的数值,这些角度从 0~180 度而不是 0~360 度,这些被称之为 unsigned gradients / 无符号梯度,因为一个梯度和它取负之后得到的是同样的数值;

换句话说,一个梯度箭头旋转180度之后被认为是一样的;

但是为什么我们不使用 0-360 度呢?经验告诉我们使用无符号的梯度,比使用有符号的梯度在行人检测中性能更好。不过一些 HOG 的实现中也可以允许你使用有符号的梯度。

接下来就是为这些 8*8 的子图像,建立一个梯度直方图。直方图有 9 位,来与 0, 20, 40…160 度相对应;

下面的图像向我们展示了操作过程,我们关注从 8*8 子图像中提取出来的幅值和方向;

* 根据梯度的方向来选择使用填充到哪一位,然后根据梯度的幅值来填充数值

我们先来看看 蓝圈的数值,角度为 80,幅值为2,所以在直方图第五位加 2;

再来看看 红圈的数值,角度为 10,幅值为 4,角度 10 的话在 0 和 20 之间,所以将它的幅值 4 被一分为 2 ,分别在直方图的 "0 位" 和 "20 位" 里面放 2 。

还需要注意的一点是,如果 角度比 160 大,在 160 和 180 之间。我们知道在这里 0 度和 180 度一样,所以下面这个例子,角度 165 度被分到了 0 度和 160 度 两个位里面。

8*8 子图像提取出来的数值,经过处理,可以得到一个 9 位的直方图,对于上面的子图像,我们可以得到如下的直方图:

在我们的表示中,y 轴默认为 0 度。你可以从直方图中看到,在 0~180 度之间有很多分布,这也表明子图像中的梯度方向要么朝上要么朝下。

步骤 4:16*16 块归一化

在之前的步骤中,我们根据图像的梯度制作了直方图。但是对于亮度不同的图像,梯度很敏感。

如果你让所有像素点的数值除 2 来让图像变暗,梯度幅值也会相应的减半,因此直方图也会对应着减半。

理想情况下,我们希望我们的描述器是不随着亮度变化而变化的,换句话说,我们想要归一化直方图,所以让它不受亮度影响;

在我说明直方图如何被归一化之前,让我们来看看,一个长度为 3 的向量是如何被归一化的;

比如我们有个 RGB 颜色向量为 [ 128, 64, 32 ],计算出长度为:

这也被称为这个向量的 L2 范数;

对向量的每个元素除以 146.64,得到归一化之后的向量 [ 0.87, 0.43, 0.22 ]。

现在考虑另一个向量,它的数值是之前向量的两倍,2 x [ 128, 64, 32 ] = [ 256, 128, 64 ];

通过同样的计算方式,你可以得到同样的归一化向量 [ 0.87, 0.43, 0.22 ],这就可以解决之前提到的亮度的影响问题。

现在我们知道了如何去归一化向量,也许你会认为,归一化 9*1 的直方图和上面介绍的 3*1 的向量归一化一样。这想法并没有错,但是更好的方式是用一个更大尺寸 16*16 的块去归一化;

也就是 36*1 的直方图可以看成 4 个 9*1 的直方图构成,然后窗口以 8 像素移动(见上图),计算出归一化的 36*1 大小的向量然后重复这个过程遍历图像。

可视化 HOG

通过在 8*8 子图像里面进行 9*1 归一化的直方图,我们可以可视化子图像的 HOG 的描述子。

在下图中你会发现,直方图的 Dominant direction / 主要方向 捕获了这个人的外形,尤其在躯干和腿。

不幸的是,在 OpenCv 中进行 HOG 的特征描述子的可视化比较困难。

# 英文版权 @ SATYA MALLICK 

# 翻译中文版权 @ coneypo

# 转载请注明出处

【翻译】HOG, Histogram of Oriented Gradients / 方向梯度直方图 介绍的更多相关文章

  1. 【笔记】HOG (Histogram of Oriented Gradients, 方向梯度直方图)的开源实现

    wiki上的介绍 OpenCV的实现 cv::HOGDescriptor Struct Reference opencv cv::HOGDescriptor 的调用例子 HOGDescriptor h ...

  2. Histogram of Oriented Gridients(HOG) 方向梯度直方图

    Histogram of Oriented Gridients,缩写为HOG,是目前计算机视觉.模式识别领域很常用的一种描述图像局部纹理的特征.这个特征名字起的也很直白,就是说先计算图片某一区域中不同 ...

  3. 【计算机视觉】Histogram of Oriented Gridients(HOG) 方向梯度直方图

    Histogram of Oriented Gridients(HOG) 方向梯度直方图 Histogram of Oriented Gridients,缩写为HOG,是目前计算机视觉.模式识别领域很 ...

  4. SIFT(Scale-invariant feature transform) & HOG(histogram of oriented gradients)

    SIFT :scale invariant feature transform HOG:histogram of oriented gradients 这两种方法都是基于图像中梯度的方向直方图的特征提 ...

  5. HOG(方向梯度直方图)

    结合这周看的论文,我对这周研究的Histogram of oriented gradients(HOG)谈谈自己的理解: HOG descriptors 是应用在计算机视觉和图像处理领域,用于目标检測 ...

  6. (转)梯度方向直方图HOG(Histograms of Oriented Gradients )

    HOG(Histograms of Oriented Gradients )梯度方向直方图 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视 ...

  7. 机器视觉 Histogram of oriented gradients

    Histogram of oriented gradients 简称 HoG, 是计算机视觉和图像处理领域一种非常重要的特征,被广泛地应用于物体检测,人脸检测,人脸表情检测等. HoG 最早是在200 ...

  8. 特征描述子(feature descriptor) —— HOG(方向梯度直方图)

    HOG(Histogram of Oriented Gradients),描述的是图像的局部特征,其命名也暗示了其计算方法,先计算图像中某一区域不同方向上梯度的值,然后累积计算频次,得到直方图,该直方 ...

  9. (转)matlab练习程序(HOG方向梯度直方图)

    matlab练习程序(HOG方向梯度直方图)http://www.cnblogs.com/tiandsp/archive/2013/05/24/3097503.html HOG(Histogram o ...

随机推荐

  1. Java设计模式16:常用设计模式之观察者模式(行为型模式)

    1. Java之观察者模式(Observer Pattern) (1)概述: 生活中我们在使用新闻app,当我们对某一栏比较感兴趣,我们往往会订阅这栏新闻,比如我对军事栏感兴趣,我就会订阅军事栏的新闻 ...

  2. Spring Boot中使用EhCache实现缓存支持

     SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...

  3. virtualbox+vagrant学习-2(command cli)-6-vagrant init命令

    Init——创建Vagrantfile文件 格式: vagrant init [options] [name [url]] 通过创建初始的Vagrantfile文件(如果不存在的话),将当前目录初始化 ...

  4. mongodb的学习-6-命令简单使用

    1.创建数据库 use DATABASE_NAME 举例说明: > use another //创建了数据库another switched to db another > db anot ...

  5. 平台+插件软件设计思想及基于COM的原型实现

    引言:我们已经习惯于一个人独立进行软件开发,每个人都使用自己的风格进行程序设计,但随着工程项目变大或者是对时间要求比较紧时,就需要几个人,十几个人,甚至是上百个人协作进行软件开发与设计,这时一个比较棘 ...

  6. maven错误The JAVA_HOME environment variable is not defined correctly

    晚上,当我准备将好的spring boot通过mvn clean package 打包成jar文件上传到linux服务器时,却在打包过程中出现了错误: C:\>mvn -version The ...

  7. 非const引用参数传入不同类型编译不过的理解(拒绝将临时对象绑定为非const的引用的形参是有道理的)

    int f (int & I) { cout<<I<<std::endl; } void main() { long L; f(L); // 编译不过 f((int)L ...

  8. transform CSS3 2D知识点汇总

    transform转换属性的5个值: 1. translate(x值,y值)  移动效果. 2.rotate(45deg)  旋转效果. 3.scale(x轴倍数,y轴倍数)  缩放效果. 4.ske ...

  9. mac下安装phalcon

    PHP版本:7.1.16 1. 安装 brew tap tigerstrikemedia/homebrew-phalconphp brew install php71-phalcon 2.配置php. ...

  10. C 没得写的水文

    引言 - 没得写 最近工作上需要处理事情很多(接手, 维稳, 危机), 还有深入读书计划也提上了日程. 为了每月水经验, 这里带大家写个 C 的多值返回吧 : ) #include <stdio ...