前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Histogram of Oriented Gridients(HOG) 方向梯度直方图

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

作者头像
机器学习算法工程师
发布于 2018-03-06 04:23:56
发布于 2018-03-06 04:23:56
1.5K00
代码可运行
举报
运行总次数:0
代码可运行

作者 张旭 编辑 徐松

1. HOG简介

2. 数字图像梯度定义

3. HOG基本步骤

4. OpenCV实现HOG

5. 用KNN与HOG实现一个手写数字输入识别

1. HOG简介

方向梯度直方图(Histogram of Oriented Gradient, HOG)于2005年提出,是一种常用的特征提取方法,且HOG+SVM的方式在行人检测中有着优异的效果。经典的论文为《Histograms of oriented gradients for human detection》,这篇文章中,HOG就是用来做行人检测的。作者研究了行人检测的特征集问题,局部归一化的HOG描述子相比于现存的特征集有着更好的表现。HOG是大小统一的cell上进行梯度方向投影计算得到的,而且为了提高性能,还采用了重叠的局部对比度归一化。HOG描述子针对一个检测窗口,根据窗口中块的个数,块内cell的个数,以及cell内bins的个数串接得到最后的HOG描述子。

2. 数字图像梯度定义

在具体说明HOG算法之前,需要解释一下图像梯度的概念,在数字图像中,图像梯度信息包含梯度幅值(梯度大小)和梯度相位(梯度方向)大小,梯度幅值G(x,y)计算如公式(1)、(2)、(3)所示,其中Gx(x,y),Gy(x,y)分别为x,y方向上的分量。梯度相位θ(x,y)计算如公式(4)所示:

HOG流程图如下图所示:

(3)计算每个 cell 的特征描述符

在每个单元格(cell)里面,将其中包含的像素点的一维梯度投影到一定的方向上。把cell的角度范围均分成bin份,一般情况下bin=9。对每个像素点的梯度分别在这bin个方向进行加权投影之后,得到了各个方向的投影大小,然后对所有在同一个cell范围内的像素点的投影按照不同的方向进行累加计算,最后得出的就是该cell在这bin个方向上的特征值,即一个bin维的特征向量。cell的大小对接下来的分类是有关联的,所以需要选择合适大小的cell,根据现有的物体检测算法,为了取得较好的效果,一般都是选取4个或8个像素的大小。

(4)cell单元归一化

由于每一个cell计算出来的梯度直方图如果受到光照、阴影的干扰,相邻的cell可能也会呈现较大的差异,所以需要再次进行规范化处理,采用归一化的好处也是对干扰因素进一步的压缩。这里需要在样本图像上定义一个新的区块(block)来进行计算,这个块的范围定义为包含着几个连续的单元格。单元格归一化的过程是这样描述的,先求出每一个大的区块的特征值之和,即将块内包含的单元格的特征值相加,得到的结果也是该块的bin个特征值,然后是将同一个块内的每一个单元格的bin个不同方向的特征值除以上述值。一般来讲,因为检测窗口的滑动,是存在着重叠的块的。反过来说,每个单元格是被多个不同的块共享的。由于归一化是基于不同的块的,因此单独对每一个cell来说计算出来的值也不一样。也就是说对最后的结果而言,即便是同一个单元格的特征会多次出现在向量中,且每个值是不一样的。

(5)生成HOG描述子

将一个检测窗口中所有块的每一个cell单元内的bin个方向的投影大小串接结合成向量的形式就是该算法的输出的最终结果,即HOG 特征向量。特征维数的计算公式为:

其中为根据块滑动步长生成的块的个数,为一个块内cell的个数。

6. OpenCV实现HOG

OpenCV中,HOG被封装在了HOGDescriptor 类中,而且OpenCV提供了直接利用HOG+SVM进行多尺度行人检测的函数detectMultiScale(),在这里我们不介绍它,只说明如何利用HOG提取出特征,并把所有训练样本的特征组合成一个特征矩阵。

对于一个存在训练数据的路径下,比如D:\\data\\0文件夹下存放了连续命名的500张0数字图片,现在要把前300张拿出来作为label=0的traindata,特征为HOG,图片尺寸为20*20。那么我们可以用下面的代码组合特征:

6. OpenCV实现HOG

OpenCV中,HOG被封装在了HOGDescriptor 类中,而且OpenCV提供了直接利用HOG+SVM进行多尺度行人检测的函数detectMultiScale(),在这里我们不介绍它,只说明如何利用HOG提取出特征,并把所有训练样本的特征组合成一个特征矩阵。

对于一个存在训练数据的路径下,比如D:\\data\\0文件夹下存放了连续命名的500张0数字图片,现在要把前300张拿出来作为label=0的traindata,特征为HOG,图片尺寸为20*20。那么我们可以用下面的代码组合特征:

7. 用KNN与HOG实现一个手写数字输入识别

在上面的部分,我们用数字0举例生成了一张图像的HOG特征,特征维数为8100,在OpenCV3的安装文件路径/opencv/sources/samples/data/digits.png下,有这样一张图:

图片大小为1000*2000,有0-9的10个数字,每5行为一个数字,总共50行,共有5000个手写数字,每个数字块大小为20*20。 为了后续方便处理,我们先写一段小程序把这5000个图截取出来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define Posnum 300
int main()
{
char adpos[100];
HOGDescriptor hog(Size(20,20),Size(10,10),Size(2,2),Size(2,2),9);
int DescriptorDim;
Mat samFeatureMat;
Mat samLabelMat;
for (int i = 0;i < Posnum ;i++)
{
sprintf_s(adpos, "D:\\data\\0\\%d.jpg", i);
Mat src = imread(adpos);
vector<float> descriptors;
hog.compute(src,descriptors);
if ( i == 0)
{
DescriptorDim = descriptors.size();
samFeatureMat = Mat::zeros(Posnum , DescriptorDim, CV_32FC1);
samLabelMat = Mat::zeros(Posnum , 1, CV_32FC1);
}
for(int j=0; j<DescriptorDim; j++)
{
samFeatureMat.at<float>(i,j) = descriptors[j];
samLabelMat.at<float>(i,0) = 0;
}
}
return 0;
}

其中HOGDescriptor hog(Size(20,20),Size(10,10),Size(2,2),Size(2,2),9);即创建hog对象并利用构造函数对其初始化,构造函数为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8),
cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),
histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true),
nlevels(HOGDescriptor::DEFAULT_NLEVELS)
{}
CV_WRAP HOGDescriptor(Size _winSize, Size _blockSize, Size _blockStride,
Size _cellSize, int _nbins, int _derivAperture=1, double _winSigma=-1,
int _histogramNormType=HOGDescriptor::L2Hys,
double _L2HysThreshold=0.2, bool _gammaCorrection=false,
int _nlevels=HOGDescriptor::DEFAULT_NLEVELS)

从构造函数可以看出,上述代码中检测窗口尺寸为20*20(和图像一样大),块尺寸为10*10,块步长为(2,2),cell尺寸为2*2,bin个数为9,那么计算一下描述子维数就是: 一个检测窗口中可以滑出36个块,一个块中可以划分25个cell,一个cell中产生9个方向,那么36*25*9=8100。

为考量HOG特征效果,我们设置一个简单的对比试验,分别用上述HOG特征(维度8100)与像素值特征(维度400)进行手写数字识别,分类算法选用KNN。

具体代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
KNN_of_PixelValue:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
char ad[128]={0};
int main()
{
Mat traindata ,trainlabel;
int k=5,testnum=0,truenum=0;
//读取训练数据 4000张
for (int i = 0; i < 10; i++)
{
for (int j =0;j<400;j++)
{
sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
Mat srcimage = imread(ad);
srcimage = srcimage.reshape(1,1);
traindata.push_back(srcimage);
trainlabel.push_back(i);
}
}
traindata.convertTo(traindata,CV_32F);
CvKNearest knn( traindata, trainlabel, cv::Mat(), false, k );
cv::Mat nearests( 1, k, CV_32F);
//读取测试数据 1000张
for (int i = 0; i < 10; i++)
{
for (int j =400;j<500;j++)
{
testnum++;
sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
Mat testdata = imread(ad);
testdata = testdata.reshape(1,1);
testdata.convertTo(testdata,CV_32F);
int response = knn.find_nearest(testdata,k,0,0,&nearests,0);
if (response==i)
{
truenum++;
}
}
}
cout<<"测试总数"<<testnum<<endl;
cout<<"正确分类数"<<truenum<<endl;
cout<<"准确率:"<<(float)truenum/testnum*100<<"%"<<endl;
return 0;
}
KNN_of_HOG
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
char ad[128]={0};
int main()
{
Mat samFeatureMat ,samLabelMat;
int k=5,testnum=0,truenum=0;
HOGDescriptor hog(Size(20,20),Size(10,10),Size(2,2),Size(2,2),9);//利用构造函数,给对象赋值。
int DescriptorDim;//HOG描述子的维数
//读取训练数据 4000张
for (int i = 0; i < 10; i++)
{
for (int j =0;j<400;j++)
{
sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
Mat srcimage = imread(ad);
vector<float> descriptors;//HOG描述子向量
hog.compute(srcimage,descriptors);
if ( i == 0&&j==0)
{
DescriptorDim = descriptors.size();
samFeatureMat = Mat::zeros(4000 , DescriptorDim, CV_32FC1);
samLabelMat = Mat::zeros(4000 , 1, CV_32FC1);
}
for(int k=0; k<DescriptorDim; k++)
{
samFeatureMat.at<float>(i*400+j,k) = descriptors[k];
samLabelMat.at<float>(i*400+j,0) = i;
}
}
}
samFeatureMat.convertTo(samFeatureMat,CV_32F);
CvKNearest knn( samFeatureMat, samLabelMat, cv::Mat(), false, k );
cv::Mat nearests( 1, k, CV_32F);
//读取测试数据 1000张
for (int i = 0; i < 10; i++)
{
for (int j =400;j<500;j++)
{
testnum++;
sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
Mat testFeatureMat =Mat::zeros(1,DescriptorDim, CV_32FC1);
Mat testdata = imread(ad);
vector<float> descriptors;//HOG描述子向量
hog.compute(testdata,descriptors);
for(int k=0; k<DescriptorDim; k++)
{
testFeatureMat.at<float>(0,k) = descriptors[k];
}
testFeatureMat.convertTo(testFeatureMat,CV_32F);
int response = knn.find_nearest(testFeatureMat,k,0,0,&nearests,0);
if (response==i)
{
truenum++;
}
}

结果比较:


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器学习算法全栈工程师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Flutter之MaterialApp使用详解
22个参数 字段 类型 navigatorKey(导航键) GlobalKey<NavigatorState> home(主页) Widget routes(路由) Map<String, WidgetBuilder> initialRoute(初始路由) String onGenerateRoute(生成路由) RouteFactory onUnknownRoute(未知路由) RouteFactory navigatorObservers(导航观察器) List<NavigatorObserver
rhyme_lph
2018/09/13
5.1K0
Flutter之WidgetsApp使用详解&与MaterialApp的纠缠
如果对MaterialApp不熟悉,可先看我上一篇文章: Flutter之MaterialApp使用详解
rhyme_lph
2018/09/13
2.1K0
【Flutter 专题】110 页面间小跳转 (四)
和尚计划针对页面间跳转的路由相关知识做一个汇总,发现有两类特殊方法暂未研究,今天特补充 Navigator 相关方法应用;
阿策小和尚
2021/01/27
4740
【Flutter 专题】110 页面间小跳转 (四)
Flutter中日期组件DatePicker及组件汉化
Flutter提供了DatePicker组件进行时间选择。 日期组件及时间组件代码示例: import 'package:flutter/material.dart'; // 第三方插件,需要提前配置 import 'package:date_format/date_format.dart'; class DatePickerPage extends StatefulWidget { DatePickerPage({Key key}) : super(key: key); @ov
越陌度阡
2021/01/05
1.5K0
Flutter中日期组件DatePicker及组件汉化
Flutter配置路由组件抽离及页面传参
在lib目录下新建个routes文件夹,里面新建个Routes.dart文件来统一管理我们的路由跳转及传参, 在这里我们就以Home.dart文件跳转到Search.dart文件并传参为例
明知山
2020/09/03
1.2K0
Flutter “跳转页面”(一)
“跳转页面”为啥加双引号,其实所谓的跳转页面可能和以前认识的不太一样。因为在Flutter里,所有能看到的东西一般都是widget,但是,没有说那个app是由一个页面构成的,所以,这个概念确实还是有的。这个功能的实现需要用到两个东西Route和Navigator。
坑吭吭
2018/08/31
2.2K0
Flutter Lesson 4: Flutter组件之App布局组件
首先来看一下我怎么来学习Flutter,我要了解每一个组件,同时,这又是一个App,所以,我的目标是直接生产一个App,里面就是对Flutter组件的介绍,同时写上一些demo以及源代码,这一个点子源于React Native With Code这一款App,在我学习React Native的时候给予了我很大的帮助。如果要构建这样的一款App,我需要先构建一个App首页,包含了一个可以滚动的列表,如果可以,还可以添加一些其他的组件。以及基础的布局组件。
踏浪
2019/07/31
1.8K0
Flutter Lesson 4: Flutter组件之App布局组件
『Flutter』命名路由
在上一篇文章中,我们介绍了如何使用 Flutter 中的导航器进行路由跳转,但是在实际开发中,我们往往会使用命名路由的方式进行跳转,本文将介绍如何使用命名路由进行跳转。
杨不易呀
2024/01/22
3140
『Flutter』命名路由
Flutter 系列之路由学习
Navigator 是 Flutter 中用于管理路由的组件,它维护着一个路由堆栈。页面可以被推入堆栈(push),也可以被弹出堆栈(pop)。
心安事随
2024/11/28
2100
flutter系列之:Navigator的高级用法
上篇文章我们讲到了flutter中navigator的基本用法,我们可以使用它的push和pop方法来进行Router之间的跳转。
程序那些事
2023/02/27
9470
Flutter | 路由管理
MaterialPageRoute 是 Material 组件库提供的组件,他可以针对不同的平台,实现与平台页面切换动画风格一致切换动画
345
2022/02/15
1K0
Flutter | 路由管理
Flutter 使用Navigator进行局部跳转页面
老孟导读:Navigator组件使用的频率不是很高,但在一些场景下非常适用,比如局部表单多页填写、底部导航一直存在,每个tab各自导航场景。
老孟Flutter
2020/09/11
1.9K0
Flutter混合开发:Android中如何启动Flutter
flutter可以独立完成项目,但是在现有项目情况下最好的方式就是混合开发,逐步过渡。这样就会共存native和flutter代码,而其中最关键的就是native如何启动flutter页面,及flutter与native如何交互。
BennuCTech
2021/12/10
1.7K0
Flutter混合开发:Android中如何启动Flutter
【Flutter】StatefulWidget 组件 ( 创建 StatefulWidget 组件 | MaterialApp 组件 | Scaffold 组件 )
创建空的 dart 文件 StatelessWidgetPage.dart , 导入最基础的材料设计包 ,
韩曙亮
2023/03/28
2.1K0
【Flutter】StatefulWidget 组件 ( 创建 StatefulWidget 组件 | MaterialApp 组件 | Scaffold 组件 )
还记得第一个看到的Flutter组件吗?
在学习Flutter的过程中我们第一个看见的控件应该就是MaterialApp,毕竟创建一个新的Flutter项目的时候,项目第一个组件就是MaterialApp,这是一个Material风格的根控件,基本用法如下:
老孟Flutter
2020/09/11
5790
Flutter开发-路由
一个route是一个屏幕或页面的抽象,Navigator是管理route的Widget。
码客说
2020/05/14
8350
动手编写你的第一个 Flutter 应用
我将带领大家尝试编写一个 Flutter 应用,感受一下 Flutter 开发的语法特点和运行效率。
CSDN技术头条
2020/02/26
1K0
12.Flutter学习之路由即Android上的页面跳转
Flutter中的路由通俗来讲就是页面跳转,在Flutter中通过Navigator组件管理路由导航。并且提供了管理堆栈的方法,如:Navigator.push和Navigator.pop Flutter提供了两种配置路由跳转的方式:1、基本路由 2、命名路由
易帜
2022/02/09
1.3K0
12.Flutter学习之路由即Android上的页面跳转
【错误记录】Flutter 界面跳转报错 ( Navigator operation requested with a context that does not include a Naviga )
上面是 MaterialApp 的注释 , MaterialApp 中会自动创建一个 Navigator , 此处使用了 MaterialApp 仍然报上述错误 ;
韩曙亮
2023/03/29
7110
【错误记录】Flutter 界面跳转报错 ( Navigator operation requested with a context that does not include a Naviga )
Flutter | 路由管理
MaterialPageRoute 是 Material 组件库提供的组件,他可以针对不同的平台,实现与平台页面切换动画风格一致切换动画
345
2022/02/11
8910
Flutter | 路由管理
推荐阅读
相关推荐
Flutter之MaterialApp使用详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验