我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python与C++中梯度方向直方图的实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python与C++中梯度方向直方图的实现

原文链接:Histogram of Oriented Gradients

(文中的图片均来自翻译原文)

什么是特征描述子

特征描述子一张图片或者一个图片块的一种表示,通过提取有用信息并扔掉多余的信息来简化图像。

通常,特征描述子将一张大小为width×height×3 (通道数)的图片化成一个长度为n的特征向量/数组。以HOG特征为例,输入图像的大小是64×128×3,输出是一个长度为3780的特征向量。

注意一点,HOG特征也可以是其它大小,但这里我使用原文献中使用的大小,这样你可以更容易地通过一个具体的例子来理解这个概念。

上面这些听起来不错,但是对于一张图片的信息,哪些是有用的哪些是冗余的呢?为了定义这个有用信息,我们需要知道它对什么有用。显然,特征向量对于我们看一张图像没什么用。但是,它对图像识别和目标检测这样的任务很有用。将由这些算法生成的特征向量作为支持向量机等分类算法的输入往往可以得到不错的结果。

但是,对于分类任务来说,哪类特征是有用的呢?让我们先用一个例子讨论一下。假设我们想设计一个目标检测器来检测衬衫或者外套上的纽扣。通常纽扣是圆的(可能在图片上会是椭圆)而且一般会有一些孔用于缝纫。你可以在一张纽扣的图片上执行边缘检测,仅仅通过观察边缘图像就可以判断它是不是一个纽扣。在这个例子中,边缘信息是有用的而颜色信息是无用的。此外,特征也需要有区分能力。例如,从某一张图片中提取的一个好的特征应具备区分纽扣和其他圆心物体(如硬币和车轮)的能力。

对于HOG特征描述子,选用梯度方向的分布作为特征。一张图像的梯度(x和y方向的导数)很有用因为在边缘和拐角(强度变化剧烈的区域)处的梯度幅值很大。而且我们知道边缘和拐角比其他平坦的区域包含更多关于物体形状的信息。

如何计算梯度方向直方图

在这一节,我们将详细介绍HOG描述子的计算。为了解释计算的每个步骤,我们使用一个图片块进行分析。

Step 1: 预处理

正像之前提到的那样,HOG特征通过在一张64×128的图片块上计算得到以用于行人检测。当然完整的图片可以是任意的尺寸。通常我们会在图片的不同位置分析多尺度图片块。唯一的要求就是图片块需要有固定的长宽比。在我们的例子中,图片块需要保持1:2 的纵横比。比如:100×200, 128×256或者1000×2000都可以,但101×205就不满足要求。

为了解释这一点,我在下面选用了一张720×475的图片。我们在图上选择一个图片块来计算HOG特征。这个小块是从原图像上裁剪下来的并且纵横比调整为64×128。这样我们就准备好计算这个图片块的HOG特征了。

Hog preprocess

原始文献中Dalal和Triggs也将 γ γ 矫正放在预处理步骤中,但是带来的增益很小因此这里我们忽略这一步。

Step 2: 计算梯度图

为了计算HOG特征,我们需要先计算图像水平和竖直方向的梯度。毕竟我们想要计算梯度直方图。这一步可以很容易通过的核对对原图像进行滤波实现。

gradient kernel

我们也可以使用OpenCV中的Sobel算子(kernel size设为1)得到相同的结果。


// C++ gradient calculation. 
// Read image
Mat img = imread("bolt.png");
img.convertTo(img, CV_32F, 1/255.0);

// Calculate gradients gx, gy
Mat gx, gy; 
Sobel(img, gx, CV_32F, 1, 0, 1);
Sobel(img, gy, CV_32F, 0, 1, 1);

# Python gradient calculation 

# Read image
im = cv2.imread('bolt.png')
im = np.float32(im) / 255.0

# Calculate gradient 
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)

下一步我们可以用下面的公式来计算梯度的幅值和方向。

formula

如果你使用OpenCV,可以通过下面的cartToPolar函数实现。


// C++ Calculate gradient magnitude and direction (in degrees)
Mat mag, angle; 
cartToPolar(gx, gy, mag, angle, 1); 

在Python中实现如下:


# Python Calculate gradient magnitude and direction ( in degrees ) 
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

下图显示了计算得到的梯度:

gradients

左:X方向梯度幅值图;中:Y方向梯度幅值图;右:梯度幅值图

X方向的梯度凸显竖直的线而Y方向的梯度凸显水平的线。梯度的幅值出现在强度变化剧烈的地方。在强度平坦的区域几乎没有梯度。我故意忽略了梯度方向图,因为在图像上显示梯度方向没有传递太多的信息。

梯度图像移除了很多不必要的信息(比如不变的背景),突出了轮廓信息。也就是说,你仅仅通过看梯度图像还是可以辨认出图像有有一个人。

对于每一个像素,梯度都会有幅值和方向。对于彩色图像,需要分别计算3个通道的梯度(如上图所示)。而该像素点的幅值是这3个通道梯度幅值的最大值,方向是最大梯度幅值对应的角度。

Step 3: 在8×8的cell中计算梯度直方图

在这一步,图像被分成8×8的很多cell,而梯度直方图是在这些cell中计算出来的。

hog-cells

我们一会儿将学习直方图,但在那之前,我们先理解一下为什么将图像切分成很多8×8的cell。使用特征描述子来描述图像中的一小块的一个重要原因是它用更为紧凑的表示方法刻画了原图像。一个8×8的图片块包含了8×8×3=192个像素值。而这个图像块的每个像素点梯度信息包含梯度幅值和方向两个值,一共是8×8×2=128个值,这128个值可以通过用包含9个bin的直方图表示成一个一维数组(包含9个值)。这样做不仅可以使图像表示更紧凑,而且在一个图片块中计算直方图可以让这种表示方法对噪声有更强的鲁棒性。单个像素的梯度信息可能包含噪声,而一个8×8的图片块中的直方图让这种表示方法对噪声更不敏感。

但是为什么要用8×8的图片块呢?为什么不是32×32?这是由我们需要寻找的特征比例决定的。HOG特征起初是被用来检测行人的。8×8的cell在一张64×128的行人图片块中的足以捕捉感兴趣的特征(如人脸、头顶等)。

上述梯度直方图本质上是一个包含9个数字的向量(或数组),这9个数字分别对应0°、20°、40°、… 160°。

我们来看一下图片块中的一个8×8 cell的梯度是什么样子。

这里写图片描述

中:一个RGB cell及其梯度(用箭头表示);右:cell中的梯度大小和方向(数字表示)

如果你是一个计算机视觉领域新手,中间的这幅图为你提供了很多信息。它展示了用箭头表示的梯度信息的梯度图——箭头的指向表示了梯度的方向而箭头的长度表示了梯度的大小。需要注意到的一点是箭头的方向指向了图像强度变化的方向,而梯度大小表示了强度的变化有多大。

在右边的图上,我们发现8×8的cell中表示梯度的数字有一点细微的差别——角度实在0°到180°之间的而不是0°到360°之间。这些叫做“无符号梯度”,因为因为一个正负方向的两个梯度由同一个数字表示。换句话说,某一个梯度箭头和它对应的另一个值(加上180°对应的那个值)被当作是同一个梯度。根据经验,无符号梯度被证明比有符号梯度效果更好。一些HOG特征的实现代码会允许你选择是否使用有符号梯度。

下一步就是在这些8×8的cells上创建梯度直方图。直方图包含9个bins分别对应着0°、20°、40°、… 160°。

下图解释了具体的过程。我们在和上面那个图一样的8×8的cells上查看梯度的大小和方向。每个bin是基于梯度方向选出来的,对应的票数(加在当前bin上的值)对应着梯度的大小。我们先来看看用蓝色圆圈出来的像素,它的梯度的角度是80°,大小为2。因此它在第5个bin上加2。下图中用红色圈出来的梯度的角度是10°,大小是4。由于10°是在0°和20°的中间位置, 因此改位置梯度对应票数被一分为二加到相邻的两个bin上。

这里写图片描述

还有一个细节需要注意。如果梯度方向大于160°。此时梯度的角度位于160°和180°之间。我们知道0°和180°是一样的(无符号梯度),因此在下面的例子中,梯度方向为165°的像素按比例将梯度大小分配到0°和160°的bin中。

这里写图片描述

8×8的cell中所有像素处的梯度按照方向将梯度大小累加到9个bin以创建最后的梯度直方图。上图中的cell对应的梯度直方图如下:

这里写图片描述

在我们的表示结果中,y轴对应0°。你可以发现上面的直方图中有大量的权重(梯度大小的投票结果)在0°和180°附近,这从另外一个角度说明了在这个cell中大部分梯度方向要么朝上要么朝下。

Step 4: 16×16 Block标准化

这里写图片描述

在上述步骤中,我们基于图像的梯度创建直方图。一张图片的梯度对整体的光线的光线比较敏感。如果你把图像所有的像素值除以2使整张图像变暗,梯度的大小也会变为原来的一半,从而梯度直方图的值也会将为原来的一半。理想情况下,我们想让特征描述子独立于光线变化。换句话说,我们想要“标准化”这个直方图使它不受光线变化的影响。

在我讲梯度直方图如何标准化之前,我们先来看看一个长度为3的向量如何标准化。

假设我们有一个RGB颜色向量[128, 64, 32]。这个向量的长度是

。这也叫做向量的L2范数。将这个向量的所有元素除以向量长度146.64就可以得到一个标准化的向量[0.87, 0.43, 0.22],现在考虑另外一个向量,这个向量的元素是第一个向量的两倍即2×[128, 64, 32]=[256, 128, 64]。你可以自己计算一下它对应的标准化结果,发现结果仍然是[0.87, 0.43, 0.22],这个值和第一个RGB向量的标准化向量相同。你可以发现标准化一个向量移除了这个向量的尺度信息。

现在我们知道如何标准化一个向量,你可能想到当计算HOG特征的时候你可以像之前标准化一个3×1向量一样去标准化9×1的直方图。这的确是个不错的想法,但是更好的做法是标准化一个更大的16×16的block。一个16×16的block包含4个直方图,这4个直方图可以连接成一个36×1的向量,而且这个向量仍然可以像那个3×1的向量一样进行标准化。每次标准化后,整个窗口移动8个像素并再次计算得到一个36×1的标准化向量,就这样一直重复这个过程。

Step 5: 计算HOG特征向量

为了计算整个图片块最终的特征向量所有的36×1的向量被连接成一个大向量。这个大向量的维度是多少大呢?

我们来计算:

- 1. 我们有多少个不同位置的16×16的block?一共有 (64-8)/8=7 个水平的位置和 (128-8)/8=15 个竖直的位置,所以总计7×15=105个。

- 2. 每一个16×16的block被表示成一个36×1的向量。因此,当我们把他们连接成一个大向量的时候会得到一个36×105=3780维度的向量。

梯度直方图可视化

一个图像块梯度特征的可视化通常通过在所有8×8的cell里画出对应的标准化的9×1向量(直方图)。如下图所示。你会注意到直方图的主要方向捕捉了人的形状,尤其是在躯干和腿附近。

不幸的是,目前在OpenCV中并没有一个简单的方法方法来可视化HOG特征。

到此这篇关于Python与C++中梯度方向直方图的实现的文章就介绍到这了,更多相关Python 梯度方向直方图内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python与C++中梯度方向直方图的实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++怎么实现直方图中最大的矩形

这篇文章主要介绍“C++怎么实现直方图中最大的矩形”,在日常操作中,相信很多人在C++怎么实现直方图中最大的矩形问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么实现直方图中最大的矩形”的疑惑有所帮助!
2023-06-19

基于Python+Matplotlib怎么实现直方图的绘制

今天小编给大家分享一下基于Python+Matplotlib怎么实现直方图的绘制的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧
2023-06-30

Node与Python双向通信的实现方法

这篇文章主要介绍“Node与Python双向通信的实现方法”,在日常操作中,相信很多人在Node与Python双向通信的实现方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node与Python双向通信的实
2023-06-20

Python中OpenCV彩色与灰度图像转换的方法

这篇文章主要介绍Python中OpenCV彩色与灰度图像转换的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!彩色图像转换为灰度图像第一种方式通过 imread 读取图像的时候直接设置参数为 0 ,自动转换彩色图像
2023-06-15

Python实现批量绘制遥感影像数据的直方图

这篇文章主要为大家详细介绍了如何基于Python中gdal模块,实现对大量栅格图像批量绘制直方图,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
2023-02-27

Python如何实现批量绘制遥感影像数据的直方图

这篇文章主要介绍“Python如何实现批量绘制遥感影像数据的直方图”,在日常操作中,相信很多人在Python如何实现批量绘制遥感影像数据的直方图问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python如何实
2023-07-05

C#中委托的基础介绍与实现方法

这篇文章主要讲解了“C#中委托的基础介绍与实现方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#中委托的基础介绍与实现方法”吧!目录前言关于委托委托的实现一、基本实现方式二、使用委托时的
2023-06-20

Python中Matplotlib图像添加标签的方法实现

本文主要介绍了Python中Matplotlib图像添加标签的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-14

JAVA11中图片与BASE64相互转换的实现方法

这篇文章主要介绍了JAVA11中图片与BASE64相互转换的实现方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。常用的java框架有哪些1.SpringMVC,Spring
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录