Learn OpenCV----方向梯度直方图

本文详细介绍了如何使用OpenCV计算方向梯度直方图(HOG)特征描述符,包括预处理、图像梯度计算、8x8单元格梯度直方图构建、16x16图像块的正则化以及最终的HOG特征向量计算。HOG特征用于行人检测,通过提取图像的梯度信息和方向分布,形成紧凑的特征表示,对光照变化具有一定的鲁棒性。
摘要由CSDN通过智能技术生成

阅读原文

概述

在这篇博文中,我们将会学习方向梯度直方图(HOG)特征描述符的一些细节。我们将会了解这个特征是什么,并且使用Opencv来对其进行计算。

这里有个很有趣的比喻,如果你是一名初学者,你的目标是星辰大海,那可以看看。
Q:你打算怎么吃掉大象。
A:一口一口的吃掉。

什么是图像特征描述符

简单来说,就是某个东西,能够很好的表示图像,而这个东西具有有效的信息,并且没有太多的冗余。

如果有看过一些深度学习的小伙伴,第一感觉应该是,害,不就是我绞尽脑汁搭建出来的网络所输出的东西嘛。其实不全是。

对于HOG描述符而言,如果输入图像的大小为64x128x3,那么,输出的特征向量长度为3780。

怎么计算的?后边会有给出,不要急噢~

但是,对于一幅图像来说,什么信息是有用的?什么信息又是无用的?因此,特征描述符还是得在实际任务中,才能给出明确的有用与否的定义。

一个简单的例子,如果你有一个任务,是要检测出圆形的按钮。这时,按钮的边缘对你来说,是一个十分有用的信息,而按钮的颜色,可能并没有多大的用处。

此外,特征应该还要能做出唯一性的代表。例如,你所提取的特征,在某些场合下,应该能够区分出硬币和汽车轮胎的不同。
在我们所讨论的HOG特征描述符中,将梯度(有方向的梯度)的方向分布(直方图)作为特征。

如何计算方向梯度直方图

在本节中,我们将对如何计算方向梯度直方图进行详细的讨论。为了方便描述,使用了一个图像块。

第一步,预处理

对于行人检测这个任务来说,HOG特征描述符是在一幅64x128的图像块上计算的。因此,对于我们要检测的对象,只要控制所选择的图像的宽高比为1:2即可。例如下图所示。
在这里插入图片描述

Dalal和Triggs的论文提到了使用gamma矫正来作为预处理步骤,但是增益较小,故作者跳过了这一步。

第二步,计算图像梯度

为了计算HOG描述符,我们首先先计算图像的水平梯度和垂直梯度,再用这些梯度信息构建梯度直方图。计算图像的梯度很简单,使用下图所示的内核对图像进行过滤计算即可。
在这里插入图片描述
借助Opencv,可以使用Sobel操作符(内核大小为1)可以获得相同的效果。

im = cv2.imread('image.png')
# 归一化
img = np.float32(im) / 255.0
# 计算水平梯度
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
# 计算垂直梯度
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)

接下来,使用下面的公式来计算梯度的大小和方向。在这里插入图片描述
当然,有了Opencv的助力,可以使用cartToPolar来完成计算。

mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

下图给出了计算梯度后的图像表示。

上图中,第一幅图为水平方向的梯度图;第二幅图为垂直方向的梯度图;第三幅图为梯度的大小。

值得注意的是,水平方向的梯度检测的是垂直方向的线,垂直方向的梯度检测的是水平方向的线。从第一幅和第二幅图像左边的道路线可以看出一二。

梯度图像删除了很多不重要的信息(例如,不变的彩色背景),但突出轮廓。

这里也好理解,轮廓部分通常会带来梯度的陡变。

第三步,计算8x8单元格的梯度直方图

什么意思呢,简单来说,就是用一个8x8的小方块,复制N份,已达到全部覆盖图像的地步。如下图所示。
在这里插入图片描述
这里有个疑问,就是我们为什么要这么做呢?
首先,我们使用特征描述符来描述一个图像块的重要原因是,特征描述符提供了一个紧凑的表示。
其次,对于每个8x8的图像块,它的每个像素包含两个值(梯度的大小和方向),因此,共有8x8x2=128个值。在文章的后边,会使用9个bin的直方图来表示这128个值。而这样的表示更为紧凑。
为什么是8x8呢?
可以这么理解,这是对行人进行检测,8x8能够更好的处理人脸和头顶的部分。
得到图像块的梯度方向和大小后,该如何构建直方图呢?
在这里插入图片描述
对于上幅图像,首先看梯度直方图,即最底端的那个条状方块。可以看到,这个条状方块分为9个块,每个块占20,总共表示了0-180。其次,看到上图的蓝色标注的数据。梯度方向为80,对应的梯度大小为2,因此,对应到梯度直方图中,就直接把2这个数据放入标号为80的块中即可。再看到上图中红色标注的数据。梯度方向为10,梯度大小为4,但是梯度直方图中没有合适的标号数据。因为10正好在0-20的中间,因此,将4平均分为2份,并分别放入标号为0和标号为20的块中。

从上述例子可以看出,所谓的平分,仅仅只是10在0-20中所占有的比例为0.5。为了更好的展示,给出下列的例子。

在这里插入图片描述
可以看到,5/20 = 0.25。因此,标号为160的块占地图重量的0.75,即85*0.75 = 63.75。
最后,得到的梯度直方图如下所示。在这里插入图片描述
上图中,标号0和标号7和标号8占有了较大的比重,因此可以认为,在这个图像块中,上下为梯度的主方向。

第四步,将16x16的图像块正则化

在上述的步骤中,图像的梯度对光照十分的敏感,但这是一个令人讨厌的缺点。怎么解决呢?一种很简单的方法是将直方图归一化,让它们不受光照变化的影响。
在这里插入图片描述
上图中的蓝色方框就是所谓的16x16,它包含了4个直方图,即可以得到36x1的向量。对着36x1的向量做归一化,可以得到一个对光照不敏感的直方图。

第五步,计算HOG特征向量

在第四步中,我们知道,每个归一化块(16x16)包含了36x1的向量。为了计算整幅图像块的最终特征向量,我们需要将每个归一化块的向量进行拼接。
可以简单计算得到,每行有7个块,每列有15个块,即,共有7x15=105个归一化块。
所以,最终的特征向量的大小为36x105=3780!这个数字就是该篇博文最先出现的那个数字啦。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用OpenCV进行手写体识别,可以遵循以下步骤: 1. 收集手写数字的数据集:可以使用MNIST数据集或自己收集手写数字的图片数据集。 2. 对数据集进行预处理:将图像进行二值化处理、规范化大小并去除噪声。 3. 提取特征:使用特征提取算法,如HOG(方向梯度直方图)或LBP(局部二值模式)等,将每个数字图像转换成一个特征向量。 4. 训练分类器:使用机器学习算法,如SVM(支持向量机)或神经网络等,训练一个分类器来识别手写数字。 5. 测试分类器:使用测试集来评估分类器的准确性。 以下是一个简单的示例代码,使用OpenCV和scikit-learn库进行手写数字的识别: ```python import cv2 import numpy as np from sklearn.externals import joblib # 加载分类器 clf = joblib.load('classifier.pkl') # 读取测试图像 img = cv2.imread('test.png', 0) # 预处理图像 img = cv2.resize(img, (28, 28)) img = cv2.bitwise_not(img) img = img.reshape(1, -1) # 预测数字 prediction = clf.predict(img) # 显示结果 print('Prediction:', prediction[0]) cv2.imshow('image', cv2.imread('test.png')) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在上述代码中,`classifier.pkl`是训练好的分类器模型文件,`test.png`是待识别的手写数字图像。首先,加载分类器模型,然后读取测试图像并进行预处理。最后,使用分类器对预处理后的图像进行预测,并将结果显示在屏幕上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值