Opencv中hog特征提取写在类HOGDescriptor中
默认的构造函数如下:
CV_WRAPHOGDescriptor() :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),
free_coef(-1.f),nlevels(HOGDescriptor::DEFAULT_NLEVELS)
{}
通过默认构造函数可以看出其一些参数的默认配置
检测窗口的大小,默认的大小是64x128,与图像的大小不是同一回事,但是要求图像的大小必须大于等于窗口大小。同时窗口大小必须满足如下约束
CV_Assert((winSize.width -blockSize.width) %blockStride.width == 0 &&
(winSize.height -blockSize.height) %blockStride.height == 0 );
即窗口大于减去块大小必须是块步长的正数倍
假如输入的图像大于大于窗口大小,那么可能该图像可能会有多个检测窗口,同时输出的hog特征为这多个检测窗口的hog特征串联起来。
块大小,默认是16x16,它除了满足与窗口大小的约束外,还与cellSize有如下约束:
CV_Assert(blockSize.width %cellSize.width == 0 &&
blockSize.height %cellSize.height == 0);
即blockSize必须是cellSize的正数倍
块滑动的步长,默认大小为8x8,与winSize满足上面说到的约束
胞元大小,默认为8x8
默认参数大小下,一个滑动窗口的hog特征长度为:
size_tHOGDescriptor::getDescriptorSize()const
{
return (size_t)nbins*
(blockSize.width/cellSize.width)*
(blockSize.height/cellSize.height)*
((winSize.width -blockSize.width)/blockStride.width + 1)*
((winSize.height -blockSize.height)/blockStride.height + 1);
}
9*2*2*7*15= 3780
同时这里看出,在求解block的个数的时候,计算式为:
nblocks = Size((winSize.width -blockSize.width)/blockStride.width + 1,
(winSize.height -blockSize.height)/blockStride.height + 1);
这里除以的是blockStride的大小而不是blockSize的大小,且默认参数里blockStride的大小为8而blockSize的大小为16,也就是说block间会有重叠,重叠度接近50%
原生的HOG计算过程比较简单,即先计算每个cell的HOG特征,然后将多个block的HOG串联起来即可。
但是opencv提取HOG的过程有不同之处,它提供了缓存机制来加速处理。
其中hog特征的提取过程为:先计算图像中每个像素的梯度特征,然后使用窗口去滑动统计直方图。
计算梯度的过程没有原生hog计算方式那么简单,对于单通道的图像,计算梯度的强度的时候直接使用灰度值,而对于三通道的,则取三个通道中梯度强度最小的那个通道的梯度数据作为当前像素的梯度数据(初始的梯度数据为dx和dy)。
计算完初始梯度后,要开始计算方向和强度,方向即为梯度的角度,强度为其L2norm,角度在这里要被归一化到对应的bin,opencv里取反正切函数值的范围为0-180,同时初始的bin个数为9个,那么一个bin段对应的角度为20度。
需要说明的是,opencv里面的hog数据并不是原生的hog数据,它的强度和梯度数据是这样存放的:
gradPtr[x*2] =mag*(1.f - angle);
gradPtr[x*2+1] =mag*angle;
qanglePtr[x*2] = (uchar)hidx;
hidx++;
hidx &= hidx < _nbins ? -1 : 0;
qanglePtr[x*2+1] = (uchar)hidx;
可以看出这里并不是原生的hog数据。
然后开始滑窗,滑窗的过程也与原生的HOG算法不一样,这里使用了缓存操作进行加速处理。
检测函数写在detect和detectMultiScale两个函数中
detect函数实现的功能是针对各个block进行探测,假如某个block的响应值大于某个阈值,则将该block的位置保存下来,在这个的检测过程中,进行检测的窗口是固定大小的,就是特征提取时所用的block大小。
detectMultiScale是通过一组对图像指定一系列的尺度(scale)来进行检测,具体为根据尺度来缩小需要检测的图片,然后使用缩小后的图片使用detect函数来进行检测。这样可以检测到一系列的不同尺度下的响应点(矩形),最终的目标需要通过对这一系列想响应点进行聚类,取聚类中心的矩形作为目标所在的位置。