参考记录
(1)opencv之HOG源代码注释
链接http://blog.youkuaiyun.com/antter0510/article/details/20564627
主要参考该博文进行解析,该博文注释比较详细,
(2)opencv之HOG源代码分析
http://blog.youkuaiyun.com/antter0510/article/details/20565045
该博文是(1)的姊妹篇,主要对hog算子用到的各个重要函数做了解析。
(3)opencv源码解析之(6):hog源码分析
http://www.cnblogs.com/tornadomeet/archive/2012/08/15/2640754.html
该篇博文的1到8节值得进行学习。
(4)HOG:从理论到OpenCV实践
http://blog.youkuaiyun.com/zhazhiqiang/article/details/21047207
从该篇博文可以学习hog算子的理论知识。
一个简单例子
对于如下例子:来自参考(4)
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/gpu/gpu.hpp>
#include <stdio.h>
using namespace cv;
int main(int argc, char** argv)
{
Mat img;
vector<Rect> found;
img = imread(argv[1]);
if(argc != 2 || !img.data)
{
printf("没有图片\n");
return -1;
}
HOGDescriptor defaultHog;
defaultHog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
//进行检测
defaultHog.detectMultiScale(img, found);
//画长方形,框出行人
for(int i = 0; i < found.size(); i++)
{
Rect r = found[i];
rectangle(img, r.tl(), r.br(), Scalar(0, 0, 255), 3);
}
namedWindow("检测行人", CV_WINDOW_AUTOSIZE);
imshow("检测行人", img);
waitKey(0);
return 0;
}
算法流程
算法流程中1是opencv的主要算法流程,2是detect算法的流程。
1、detectMultiScale算法执行流程。
1.1在该函数中,首先求得可以变化的层数,根据窗口大小增长到刚超过img大小为准。
1.2使用HOGInvoker初始化函数对各个尺度下的图片进行检测。
1.2.1对图片尺度数量进行循环,求出各个尺度下图片的检测结果
1.2.1.1求出当前尺度下图片的信息,将原图片进行压缩。
1.2.1.2对每个尺度下的图片,使用detect进行单幅图片的行人检测
1.2.1.3将使用detect检测到的行人的窗口位置信息重新变换到正常尺寸图片上。并且将检测到的行人矩形框位置、比例信息、weitht信息添加到1.2的函数中。
1.3将tempScales中的内容复制到foundScales中;
1.4 清除所有位置信息 foundLocations.clear();并将找到的行人位置信息容器复制到foundLocations中 。
1.5 清空foundWeights将将候选目标可信度保存在foundWeights中。
1.6 使用meanshift对矩形框进行聚类或者简单的对矩形框进行聚类。
2、detect函数算法执行流程
2.1一些初始化工作,如将locations清空等。
2.2使用HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); 完成img的梯度图和方向图的计算及blockData,pixData的初始化工作。
2.2.1使用computeGradient计算出当前图像img的梯度、方向矩阵。
2.2.1.1为grad和qangle开辟内存
2.2.1.1.1按照paddingTL和paddingBR建立一个扩展grandsize。
2.2.1.1.2按照grandsize给grad和qangle开辟内存,并且每个mat都初始化为双通道
2.2.1.2如果需要,进行gamma校正。
2.2.1.3 建立扩展后位置与原图像位置的对应关系
2.2.1.3.1开辟内存mapbuf,长度为gradsize.width + gradsize.height +4;
2.2.1.3.2使用指针xmap建立扩展后x方向位置与原图像x位置的对应关系,同理使用ymap,需要注意的是,xmap和ymap指向的内存空间是mapbuf。
2.2.1.3.3建立对应关系,建立对应关系后, 例子xmap[10]=5,意思是在扩展后图像x方向10的位置对应与原图像x方向5的位置。
2.2.1.4 开始求梯度幅值和角度
2.2.1.4.1 准备工作
2.2.1.4.1.1开辟内存_dbuf,其大小为width*4,其中width为扩展后的宽度。
2.2.1.4.1.2创建4个Mat,Dx,Dy, Mag, Angle,这些Mat使用_dbuf内存,这些mat变量都是临时变量,用来存入每行计算出来的数据。
其中,Dx中存放每个像素计算出来x方向的梯度,Dy中存放y方向的梯度,Mag中存放梯度幅值,Angle中存放角度值,
2.2.1.4.2 对img每行进行遍历
2.2.1.4.2.1如果是单通道,计算出每个像素点的x和y方向的梯度值,然后放到Dx和Dy中
2.2.1.4.2.2 如果是多通道(也就是三通道),计算出每个像素点每个通道的x和y方向的梯度值和梯度幅值。
2.2.1.4.2.3 使用cartToPolar( Dx, Dy, Mag, Angle, false)计算出来Mag和Angle。
2.2.1.4.2.4 对每行计算出来的Mag数据和Angle数据进行遍历,然后将得到的数据存入
grad和qangle(其中grad的计算方式为gradPtr[x*2] = mag*(1.f - angle);
gradPtr[x*2+1] = mag*angle; qangle的计算方式为将0-8个整数值赋值给qangle[x*2]和gradPtr[x*2+1] )
2.2.2 接下来函数的主要工作是给 vector<PixData> pixData;和vector<BlockData> blockData;
各个像素点对不同cell的作用数据,如histOfs[4]用来存放对哪个cell起作用,其取值是0,1,2,3;)
2.2.2.1 初始化工作,计算各个变量。
2.2.2.1.1对 blockSize.width和blockSize.height进行循环,然后计算一个block中各个
像素点的数据,即给pixData容器赋值。blocksize大小,当前计算的所有数据都存在第三段中。
2.2.2.1.3对blockData进行赋值,其内部的所有赋值都是相对于一个移动窗口计算的
2.3 进行行人检测工作
2.3.1 一些初始化工作
2.3.2 循环每个扫描窗口,然后对每个扫描窗口内的block数量进行遍历
2.3.2.1 求出每个Block的直方图 const float* vec = cache.getBlock(pt, &blockHist[0]);
2.3.2.2 将直方图的每个值与svmVec相乘,然后将所有值进行相加,每个窗口计算出一个s值与阈值进行比较
if( s >= hitThreshold )//超过既定阈值,说明包含行人
{
hits.push_back(pt0);
weights.push_back(s);
}
(这里的疑问是,使用的svmVec向量好像是一个Blocksize大小的向量,有些太小了,回头再仔细看)
2.3全部工作结束。