目前的进度有些缓慢了。。。
要想用CUDA实现HOG特征的并行计算,那么首先就要熟悉HOG计算的相关内容、思想,以便实现并行计算方案。
关于HOG特征的介绍,在网上有非常非常多,其中英文原文:http://vision.stanford.edu/teaching/cs231b_spring1213/papers/CVPR05_DalalTriggs.pdf
故这里不作详细介绍,简单摘抄其他博客内容进行介绍:
HOG特征提取方法就是将一个image(你要检测的目标或者扫描窗口):
1)灰度化(将图像看做一个x,y,z(灰度)的三维图像);
2)采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
3)计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。
4)将图像划分成小cells(例如6*6像素/cell);
5)统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;
6)将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。
7)将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。
摘抄自:http://blog.csdn.net/liulina603/article/details/8291093
当然上述过程只是一个简单的介绍,其中还有很多重要的细节没有体现,比如说权重的分配等等。为了我们自己写的HOG特征和OpenCV中的HOG特征、SVM算法的接口匹配,这样的话,会使得我们的项目进展得更加顺利。显而易见,要想达到这种目的,最简单的方法就是研究OpenCV中的源码,接下来,我将尝试理解源码。
首先,我们要找到这个源码。我们用CMake对OpenCV249进行编译,得到一个新的文件夹,在这里面我们可以找到
OpenCV.sln的项目文件(我这里CMake选择的编译器是VS2012,各位视情况而定哦)。用VS2012打开该项目文件,找到
打开hog.cpp,其中就是hog的源码。
要得到HOG特征,并且结合我们要移植到CUDA一侧进行改写成并行HOG特征算法,于是,我们要对HOG特征源码中的核心函数进行分析,尤其是计算过程。http://www.cnblogs.com/tornadomeet/archive/2012/08/15/2640754.html博客中有比较详细的hog源码的介绍,拜读之后受益匪浅,既然前人已经有了成熟的经验,那在这里我也就借鉴一下了!
这里主要注意
void HOGDescriptor::compute
void HOGCache::init
void HOGDescriptor::computeGradient
三个函数,这些是和HOG特征向量的计算有着直接的关系。
这里我把一些值得注意的地方列举出来,希望对理解源码有所帮助!
1. gradPtr[x*2] = mag*(1.f - angle);
gradPtr[x*2+1] = mag*angle;
乍一看,gradPtr的长度是width,而for循环中x的最大值也是width,那岂不是越界了么?
我们看到gradPtr定义的地方:
float* gradPtr = (float*)grad.ptr(y);
看gard定义的地方:
grad.create(gradsize, CV_32FC2); // <magnitude*(1-alpha), magnitude*alpha>
这里是CV_32FC2,明显是两通道,所以实际上gradPtr的大小是2*width,偶数第一通道,奇数第二通道。
2.幅值统计的时候,并不是落在那个bin,就只有该bin+幅值,而是与其最近的一个bin也受到影响
3.一个block内的cell是相互影响的,具体可参见我上列的博客
CUDA移植方案:
我的目的是将hog.cpp中的特征计算函数移植到CUDA处进行并行计算加速,那么简单的计算当然是没问题,但是涉及到opencv的内容,CUDA是hold不住的,因为CUDA是类似C语言的编程语言,没有提供opencv的相关API。于是有关内容都要自己改写。
比如说:
struct HOGCache { struct BlockData { BlockData() : histOfs(0), imgOffset() {} int histOfs; Point imgOffset; }; struct PixData { size_t gradOfs, qangleOfs; int histOfs[4]; float histWeights[4]; float gradWeight; }; HOGCache(); HOGCache(const HOGDescriptor* descriptor, const Mat& img, Size paddingTL, Size paddingBR, bool useCache, Size cacheStride); virtual ~HOGCache() {}; virtual void init(const HOGDescriptor* descriptor, const Mat& img, Size paddingTL, Size paddingBR, bool useCache, Size cacheStride); Size windowsInImage(Size imageSize, Size winStride) const; Rect getWindow(Size imageSize, Size winStride, int idx) const; const float* getBlock(Point pt, float* buf); virtual void normalizeBlockHistogram(float* histogram) const; vector<PixData> pixData; vector<BlockData> blockData; bool useCache; vector<int> ymaxCached; Size winSize, cacheStride; Size nblocks, ncells; int blockHistogramSize; int count1, count2, count4; Point imgoffset; Mat_<float> blockCache; Mat_<uchar> blockCacheFlags; Mat grad, qangle; const HOGDescriptor* descriptor; };
HOGCache这个类型我们需要重写。具体重写办法下回我将介绍!
最后能得到HOG特征,再在opencv cpp处进行SVM训练。