本文主要参考博客:1.https://www.cnblogs.com/panchuangai/p/12567973.html
2.https://blog.csdn.net/weixin_38367817/article/details/83153954
3.https://blog.csdn.net/weixin_44947897/article/details/107165653
4.https://www.jianshu.com/p/395f0582c5f7
5.https://blog.csdn.net/qq_27396861/article/details/88374215
6.https://blog.csdn.net/akadiao/article/details/79685323
7.https://blog.csdn.net/u012762410/article/details/78846085
8.https://blog.csdn.net/sinat_26917383/article/details/69666371
9.https://blog.csdn.net/zhazhiqiang/article/details/21047207
特征描述子是图像的简化表示,仅包含图像的最重要信息。本博客将介绍几种常用的特征描述子,包括HOG、SIFT、SURF、ORB.
原来在另一篇博客简单介绍过该特征的一点关键知识,此处更加详细的总结一下,附上原来链接
https://blog.csdn.net/u013972657/article/details/110631959
目录
1. HOG(Histogram of Oriented Gradients,方向梯度直方图)
1.1主要思想
1.2适用领域
1.3计算方向梯度直方图的过程
1.4opencv hog特征描述子的调用及计算
1.5 行人检测
在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述。(本质:梯度的统计信息,而梯度主要存在于边缘的地方)。它与图像边缘特征的主要区别是图像边缘特征只识别像素是否是边缘(只计算梯度),而HOG特征还计算边缘(梯度)的方向。
hog是一种在计算机视觉和图像处理中用来对物体检测的特征描述子,Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。
输入图像-》图像灰度化-》Gamma校正-》计算图像梯度-》计算cell的描述子-》将cell组合为block,block内特征归一化-》将block串联起来,得到图像描述子
描述步骤之前,先总体介绍一些预备知识:
(1)在原论文中处理图像时,图像的尺寸缩放为64*128(64为宽,128为高),这个大小是为了方便之后划分为cell以及block。
(2)将图像划分为8*8的cell(对于8*8的网格已经足够大来表示有用的特征比如脸,头等等)
(3)每个cell都要计算一个直方图,该直方图是对幅值magnitude和方向direction的统计(每个像素都有一个幅值和方向),方向范围是0~180度,使用的是无符号梯度,此时一个梯度和它的负数是用同一个数字表示的,也就是说一个梯度的箭头以及它旋转180度之后的箭头方向被认为是一样的。那为什么不用0-360度的表示呢?在事件中发现unsigned gradients比signed gradients在行人检测任务中效果更好。
(4)block(16*16):由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。这就需要对梯度强度做归一化。在block内归一化是为了增强描述子对光照、阴影和边缘变化的鲁棒性。它能进一步对光照、阴影和边缘进行压缩。一个block内所有cell的特征向量串联起来便得到该block的HOG特征。这些区间是互有重叠的,这就意味着:每一个单元格的特征会以不同的结果多次出现在最后的特征向量中。
各步骤关键:
1.图像灰度化是可选操作,因为灰度图像和彩色图像都可以用于计算梯度图。对于彩色图像,先对三通道颜色值分别计算梯度,然后取梯度值最大的那个作为该像素的梯度。但在计算机视觉处理中一般都以灰度图作为处理对象(不包括深度学习)。
2.Gamma校正:调节图像对比度,减少光照对图像的影响,使过曝或者欠曝的图像恢复正常,更接近人眼看到的图像。
3.计算梯度:计算一个像素的x方向梯度及y方向的梯度。根据它们确定每个像素的大小和方向
梯度方向(角度的值):atan()
梯度幅值为:
4.计算cell描述子:
按照角度不同,直方图分为9个bin,代表角度0,20,40,60,80,100,120,140,160;根据方向确定选择哪个bin,根据幅值确定bin的大小,如果角度值为介于上边9个值的值,那么需要根据距离9个值左右值的远近在幅值上分别乘以比例来确定在这两个bin上的大小。如下图(改图截取自参考博客1,为便于理解放于此处,如有侵权,联系笔者删除):
5.组合为block并且归一化
一个block是由四个cell组成的,将每个cell的的9个Bin的直方图(即9*1的向量)组合成一个36*1的向量,之后对其归一化,接着,窗口再朝后面挪8个像素(看动图)。重复这个过程把整张图遍历一遍。如下图(该图源于参考链接4,为便于理解放于此处,如有侵权,联系笔者删除)
归一化方法有L1-norm、L2-norm、L1-sqrt(L1-norm后在平方)。通常使用L2-norm.
L2-norm的计算方法如下:
对于cell组合成的36维向量v=[a1,a2,a3,...,a36];
计算平方和的根:k=
并将向量v中的每个值除以此值k:
归一化后的向量=(a1/k,a2/k,a3/k,...,a36/k)
6.图像特征描述子
以5中图示华东,64*128将有7*15个16*16大小的block,这105个块中每一个有36个向量作为特征,所以该64*128大小的图像描述子大小是105*36=3780。
HogDescriptor: 构建一个Hog描述子及检测器
cv::HOGDescriptor::HOGDescriptor( Size winSize=Size(64,128), Size blockSize=Size(16,16),
Size blockStride=Size(8,8), Size cellSize=Size(8,8), int nbins=9,
double winSigma = DEFAULT_WIN_SIGMA, double threshold_L2Hys = 0.2,
bool gamma_Correction = true,int nlevels=DEFAULT_NLEVELS )
参数解释:
<1>win_size:检测窗口大小。
<2>block_size:块大小,目前只支持Size(16, 16)。
<3>block_stride:块的滑动步长,大小只支持是单元格cell_size大小的倍数。
<4>cell_size:单元格的大小,目前只支持Size(8, 8)。
<5>nbins:直方图bin的数量(投票箱的个数),目前每个单元格Cell只支持9个。
<6>win_sigma:高斯滤波窗口的参数。
<7>threshold_L2hys:块内直方图归一化类型L2-Hys的归一化收缩率
<8>gamma_correction:是否gamma校正
<9>nlevels:检测窗口的最大数量
compute函数:计算hog特征
void HOGDescriptor:: compute(const Mat& img,
vector& descriptors,
Size winStride,
Size padding,
const vector& locations
) const
(3)参数注释
<1> img:源图像。只支持CV_8UC1和CV_8UC4数据类型。
<2> descriptors:返回的HOG特征向量,descriptors.size是HOG特征的维数。
<3> winStride:窗口移动步长。
<4> padding:扩充像素数。
<5> locations:对于正样本可以直接取(0,0),负样本为随机产生合理坐标范围内的点坐标。
getDescriptorSize函数:获取一个检测窗口HOG特征向量的位数
对百度下载一幅图像截取出人形做测试:
计算hog描述子代码:
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("oneperson.jpg");
Mat grayImage, dstImage;
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
resize(grayImage, dstImage, Size(64, 128));
HOGDescriptor dectector(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
cout <<"dectector.getDescriptorSize():"<< dectector.getDescriptorSize() << endl;
// HOG的描述子
vector descriptors;
vector locations;
dectector.compute(dstImage, descriptors, Size(0, 0), Size(0, 0), locations);
cout << "descriptors.size():" << descriptors.size() << endl;
cout << "feature:" << endl;
for (int j = 0; j < 3780; j++)
{
cout << descriptors[j] << ' ';
}
cout << endl;
system("pause");
return 0;
}
结果显示:
OpenCV中自己带的训练模板里面有行人检测,可以直接调用,主要用到下面三个函数
setSVMDetector 函数:设置线性SVM分类器的系数
getDefaultPeopleDetector 函数:获取行人分类器(默认检测窗口大小)的系数(获得3780维检测算子)
detectMultiScale 函数(前边需有setSVMDetector):用多尺度的窗口进行物体检测
void HOGDescriptor::detectMultiScale(
const Mat& img, vector& foundLocations, vector& foundWeights,
double hitThreshold, Size winStride, Size padding,
double scale0, double finalThreshold, bool useMeanshiftGrouping)
参数注释
<1>img:源图像。
<2>foundlocations:检测出的物体的边缘。
<3>foundWeights: 检测窗口得分
<4>hit_threshold:阀值,特征向量和SVM划分超平面的距离,大于这个值的才作为目标返回。
<5>win_stride:窗口步长,必须是block步长的整数倍。
<6>padding:图片边缘补齐参数,gpu版本必须是(0,0)。
<7>scale0:检测窗口增长参数。
<8>finalThreshold:检测结果聚类参数
<9>useMeanshiftGrouping:聚类方式选择的参数
从百度图库下载一张行人的图像:
测试代码:
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat src, dst;
src = imread("people.jpg", 1);
if (src.empty())
{
printf("load image error...\n");
return -1;
}
dst = src.clone();
vector findrects, findrect;
HOGDescriptor HOG;
//SVM分类器
HOG.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
//多尺度检测
HOG.detectMultiScale(src, findrects, 0, Size(4, 4), Size(0, 0), 1.05, 2);
//框选出检测结果
for (int i = 0; i
测试结果:
文中若有错误或不妥之处,还望指出!