Hog,即方向梯度直方图
直方图就是数据分布的一种图形表现,类似于柱形图,每个柱形代表一组处于一定范围的数据。这些柱形也成为组(bins),柱形越高意味着某组数据越多
以下图灰度图像为例
像素值的范围在0-255之间,将这些值分为32组,每组包含8个像素值,所以第一组的范围是0-7,以此类推到248-255.
要创建直方图,就是将该途中的各个像素值,放到对应的组里。而上图中的薄饼有很多亮值,背景则很黑,因此直方图大致如下:
什么是方向梯度?
方向很简单,指的就是图像梯度的方向或朝向。Hog就是一张有关图像梯度方向的直方图。首先HOG会接受一张图像,然后计算每个像素的梯度幅度和方向
Hog会把这些像素分成若干个较大的正方形单元,单元通常是8X8,如果图片小一些,单元就响应小一些,8X8即有64个梯度值,HOG会计算每个单元相同方向的梯度有多少,将这些梯度的幅值相加得到梯度强度。并将所有方向数据放到直方图中。以下直方图有9组,也就是9个值范围。
HOG会对图像的每个单元进行同样的处理,这个方向梯度直方图实际上就是个特征向量。对于物体相同但大小,方向不同的图像,可以利用HOG特征模式来检测物体,而不
管物体的位置和呈现方式。
正如在 ORB 算法中看到的,我们可以使用图像中的关键点进行匹配,以检测图像中的对象。当想要检测具有许多一致的内部特性且不受背景影响的对象时,这些类型的算法非常有用。例如,这些算法在人脸检测中可以取得良好的效果,因为人脸有许多不受图像背景影响的一致的内部特征,例如眼睛、鼻子和嘴巴。然而,当试图进行更一般的对象识别时,例如图像中的行人检测时,这些类型的算法并不能很好地工作。原因是人们的内在特征不像脸那样一致,因为每个人的体型和风格都不同(见下图)。这意味着每个人都会有一套不同的内部特征,因此我们需要一些能够更全面地描述一个人的东西。
一种选择是尝试通过行人的轮廓来检测他们。通过图像的轮廓(边界)来检测物体是非常具有挑战性的,因为我们必须处理背景和前景之间的对比带来的困难。例如,假设想检测一个图像中的行人,她正走在一栋白色建筑前,穿着白色外套和黑色裤子。我们可以在下图中看到,由于图像的背景大多是白色,黑色裤子的对比度将非常高,但由于外套也是白色的,所以对比度将非常低。
在这种情况下,检测裤子的边缘是很容易的,但是检测外套的边缘是非常困难的。而这就是为什么需要 HOG 。即 定向梯度柱状图(Histograms of Oriented Gradients),它是由 Navneet Dalal 和 Bill Triggs 于 2005 年首次引入的。
Hog 算法的工作原理是创建图像中梯度方向分布的柱状图,然后以一种非常特殊的方式对其进行归一化。这种特殊的归一化使得Hog 能够有效地检测物体的边缘,即使在对比度很低的情况下也是如此。这些标准化的柱状图被放在一个特征向量(称为 HOG 描述符)中,可以用来训练机器学习算法,例如支持向量机(SVM),以根据图像中的边界(边)检测对象。由于它的巨大成功和可靠性,HOG 已成为计算机视觉中应用最广泛的目标检测算法之一。
1.给定特定对象的图像,设置一个覆盖图像中整个对象的检测窗口(感兴趣区域)
2.计算检测窗口中每个像素的梯度大小和方向
3.将检测窗口分成像素的连接单元格,所有单元格的大小相同。单元的大小是一个自由参数,通常选择它来匹配要检测的特征的比例。例如,在一个64X128像素的检测窗口中,6X8像素宽的方型单元适用于检测人体肢体
4.为每个单元创建一个柱形图,首先将每个单元中所有像素的渐变方向分组为特定数量的方向(角度)箱,然后将每个角度箱中渐变的渐变幅度相加(见下图)。柱状图中的箱数是一个自由参数,通常设置为9个角箱。
5.将相邻单元分组成块(见下图)。每个块中的单元格数是一个自由参数,所有块的大小都必须相同。每个块之间的距离(称为跨距)是一个自由参数,但它通常设置为块大小的一半,在这种情况下,将得到重叠块(见动图)。经验表明,该算法能更好地处理重叠块。
6.使用每个块中包含的单元格来规范化该块中的单元格柱状图(见下图)。如果有重叠的块,这意味着大多数单元格将针对不同的块进行规格化(见动图)。因此,同一个单元可能有几个不同的归一化。
7.将所有块中的所有标准化柱状图收集到一个称为 HOG 描述符的特征向量中。
8.使用从包含同一对象的许多图像中得到的 HOG 描述符训练机器学习算法,例如使用 SVM,以检测图像中的这些对象。例如,可以使用来自许多行人图像的 HOG 描述符来训练 SVM 以检测图像中的行人。训练通过使用包含目标的正例和不包含目标的负例完成。
9.一旦对支持向量机进行了训练,就使用滑动窗口方法来尝试检测和定位图像中的对象。检测图像中的对象需要找到图像中与 SVM 学习到的 HOG 模式相似的部分。
正如我们上面所了解的,HOG 通过在图像的局部区域中添加特定方向的梯度大小来创建柱状图,称为“cells”。通过这样做可以保证更强的梯度对它们各自的角度柱状图的大小贡献更大,同时最小化由噪声引起的弱梯度和随机定向梯度的影响。通过这种方式,柱状图告诉我们每个单元格的主要梯度方向。
现在考虑一个问题,由于局部照明的变化以及背景和前景之间的对比度,梯度方向的大小可以有很大的变化。
为了考虑背景-前景对比度的差异,HOG 算法尝试在局部检测边缘。为了做到这一点,它定义了称为块的单元格组,并使用该局部单元格组规范化柱状图。通过局部归一化,HOG 算法可以非常可靠地检测每个块中的边缘,这称为块归一化。
除了使用块规范化之外,HOG 算法还使用重叠块来提高其性能。通过使用重叠块,每个单元为最终的 HOG 描述符提供几个独立的组成部分,其中每个组成部分对应于一个针对不同块进行规范化的单元。这似乎是多余的,但是经验表明,通过对每个单元对不同的局部块进行多次规格化,HOG 算法的性能显著提高。
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)
{}
下面是几个重要的参数:
winSize(64,128),blockSize(16,16),blockStride(8,8),cellSize(8,8),nbins=9
窗口大小winSize
块大小blockSize
块移动步长:blockStride(8,8)
这里是指的块移动的步长,括号中分别为X方向和Y方向块移动的步长。PS:这里指的是块移动的步长,窗口的移动步长需要在computer()函数中设置,窗口的步长若不设置好像默认跟块步长一样
nBins表示在一个胞元(cell)中统计梯度的方向数目,例如nBins=9时,在一个胞元内统9个方向的梯度直方图,每个方向为180/9=20°
OpenCV中一个Hog描述子是针对一个检测窗口而言的,一个检测窗口有((128-16)/8+1)((64-16)/8+1)=105个Block,一个Block有4个Cell,一个Cell的Hog描述子向量的长度是9,所以一个检测窗口的Hog描述子的向量长度是1054*9=3780维。
HOG特征提取是统计梯度直方图特征。具体来说就是将梯度方向(0->360°)划分为9个区间,将图像化为16x16的若干个block,每个block再化为4个cell(8x8)。对每一个cell,算出每一像素点的梯度方向和模,按梯度方向增加对应bin的值,最终综合N个cell的梯度直方图形成一个高维描述子向量。实际实现的时候会有各种插值。
我们可以通过将与每个细胞相关联的直方图绘制为矢量集合来可视化 HOG 描述符。 为此,我们将直方图中的每个 bin 绘制为单个向量,其大小由 bin 的高度给出,其方向由与其关联的角度 bin 给出。 由于任何给定的单元格可能有多个与之关联的直方图,因为存在重叠的块,我们将选择平均每个单元格的所有直方图,以便为每个单元格生成单个直方图。
要如何理解这个直方图呢?
首先先看一下三角形内部而不是边缘附近的单元格。如下图所示,在这种情况下,由于三角形几乎都是相同的颜色,因此在所选单元格中不应存在任何主要梯度
—分隔符----
其次,再看一下靠近水平边缘的单元格。因为边缘时图像中强度突然变化的区域。在这种情况下,某个特定方向上具有高强度的梯度。再90°方向,是强度剧烈变化的方向,因此在直方图中对应区域也最强
—分隔符—
再看一下靠近垂直边缘的单元格。在这种情况下,主导梯度是水平的,因此0和180°左右的梯度应该是最强的、
最后再看一下对角线边缘的单元格:
为了理解我们所看到的,让我们首先记住梯度由 x 部分(分量)和 y 部分(分量)组成,就像向量一样。因此,梯度的最终方向将由其分量的向量和给出。因此,在垂直边缘上,渐变是水平的,因为它们只有 x 分量,如上图所示。在水平边缘上,渐变是垂直的,因为它们只有 y 分量,正如上上图所示。因此,在对角线边缘,梯度也将是对角线,因为 * x * 和 * y * 分量都是非零的。由于图像中的对角线边缘接近 45 度,我们应该期望在 50 度箱(bin)中看到显著的梯度方向。而这实际上就是我们在直方图中看到的,但是,如上图所示,我们看到有两个主导梯度而不是一个。其原因在于,当创建直方图时,靠近区间边界的角度与相邻区间成比例地起作用。例如,角度为 40 度的梯度位于 30 度和 50 度箱的中间。因此,梯度的大小均匀地分成30度和50度的箱。这导致在对角线边缘附近的单元中存在两个主要梯度而不是仅一个。
3.1、HOG与SIFT的区别
HOG和SIFT都是描述子,以及由于在具体操作上有很多相似的步骤,所以致使很多人误认为HOG是SIFT的一种,其实两者在使用目的和具体处理细节上是有很大的区别的。HOG与SIFT的主要区别如下:
(1)SIFT是基于关键点特征向量的描述。
(2)HOG是将图像均匀的分成相邻的小块,然后在所有的小块内统计梯度直方图。
(3)SIFT需要对图像尺度空间下对像素求极值点,而HOG中不需要。
(4)SIFT一般有两大步骤,第一个步骤对图像提取特征点,而HOG不会对图像提取特征点。
3.2、HOG的优缺点
优点:
(1)HOG表示的是边缘(梯度)的结构特征,因此可以描述局部的形状信息;
(2)位置和方向空间的量化一定程度上可以抑制平移和旋转带来的影响;
(3)采取在局部区域归一化直方图,可以部分抵消光照变化带来的影响;
(4)由于一定程度忽略了光照颜色对图像造成的影响,使得图像所需要的表征数据的维度降低了;
(5)而且由于这种分块分单元的处理方法,也使得图像局部像素点之间的关系可以很好得到表征。
缺点:
(1)描述子生成过程冗长,导致速度慢,实时性差;
(2)很难处理遮挡问题;
(3)由于梯度的性质,该描述子对噪点相当敏感。
关键函数:
detectMultiScale函数
void detectMultiScale(
const Mat& image,
CV_OUT vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3,
int flags = 0,
Size minSize = Size(),
Size maxSize = Size()
);
1.image 表示的是要检测的输入图像
2.foundLocations 表示检测到的目标序列
3.hitThreshold 控制HOG特征与SVM最优超平面间的最大距离,当距离小于阈值时判定为目标
4.winStride HOG检测窗口移动时的步长
5.padding 在原图外添加像素,适当添加padding可以提高检测的准确率
6. scale 每层图像缩小值,并用gaussian平滑,可以控制图像金字塔层数,参数越小,层数越多,检测也越长
7. finalThreshold 优化bounding box
8. useMeanShiftGroupimg 是否应用meanshift来消除重叠-bool类型
代码:
#include
#include
#include
#include
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
Mat src,dst, gray_src;
const char* output_title = "output_win";
int corners = 20;
int max_corners = 50;
void Tomasi_Demo(int, void*);
void SubPixel_Demo(int, void*);
int main(int argc, char** argv) {
src = imread("C:/Users/18929/Desktop/博客项目/项目图片/18.jpg");
if (src.empty()) {
printf("could not load image");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
imshow("input_img", src);
//计算检测算子
//将目标图像转化为窗口大小
//resize(src, dst, Size(64, 128));
//cvtColot(dst, dst_gray, COLOR_BGR2GRAY);
//定义HOG检测器
//HOGDescriptor detector(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
//定义容器装特征描述子以及对应描述子的位置
//vector descriptors;
//vector locations;
//根据窗口大小自动计算窗口移动步长,并返回算子数据
//detector.compute(dst_gray,descriptors,Size(0,0),Size(0,0),locations);
//printf("number of HOG descriptors:%d", descriptors.size());//3780
//行人检测案例
//新建一个HOGDescriptor对象
HOGDescriptor hog = HOGDescriptor();
//使用open CV定义好的模型
//新建一个SVM分类检测器
//getDefaultPeopleDetector()--人像检测库
hog.setSVMDetector(hog.getDefaultPeopleDetector());
vector<Rect> foundLocation;//定义容器装检测结果的矩形
//detectMultiScale对图像进行多尺度检测
hog.detectMultiScale(src, foundLocations, 0, Size(8, 8), Size(32, 32), 1.05, 2);
//将结果在另一张图片中画出
Mat result = src.clone();
for (size_t i = 0; i < foundLocations.size(); i++)
{
rectangle(result, foundLocation[i], Scalar(0, 0, 255), 2, 8, 0);
}
imshow("HOG SVM Detector Demo", result);
waitKey(0);
return 0;
}