行人检测任务之HOG算子

HOG(Histogram of Oriented Gradients)特征是一种用于物体检测的特征描述子,尤其在行人检测中表现出色。该特征通过计算图像局部区域的梯度直方图,结合分类算法(如SVM)来实现目标检测。算法主要包括图像灰度化、 Gamma校正、梯度计算、细胞单元划分、直方图统计和对比度归一化等步骤。
摘要由CSDN通过智能技术生成

引言

在计算机视觉和图像处理中用来进行物体检测的特征描述子,它通过计算和统计图像局部区域的梯度方向直方图构成特征。Hog特征结合分类算法广泛应用于图像识别中,尤其是在行人检测中获得极大成功。HOG+SVM的行人检测方法2005年提出来之后,如今很多行人检测算法都是以此为思路的。

从特征描述子说起

  1. Haar
  2. SIFT
  3. HOG(Histogram of Oriented Gradient)
    在计算机视觉和图像处理中用来进行物体检测的特征描述子,它通过计算和统计图像局部区域
    的梯度方向直方图构成特征。Hog特征结合分类算法广泛应用于图像识别中,尤其是
    在行人检测中获得极大成功。HOG+SVM的行人检测方法2005年提出来之后,如今很多
    行人检测算法都是以此为思路的。

基本的一些特征检测方法

HOG特征描述子有什么特性

在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述。(本质:梯度的统计信息,而梯度主要存在于边缘的地方)

实现方法

  1. 首先将图像分成小的连通区域,我们把它叫细胞单元。然后采集细胞单元中各像素点的梯度的或边缘的方向直方图。最后把这些直方图组合起来就可以构成特征描述器。
  2. 为了提高性能,把这些局部直方图在图像的更大的范围内(我们把它叫区间或block)进行对比度归一化(contrast-normalized),所采用的方法是:先计算各直方图在这个block中的密度,然后根据这个密度对block中的各个细胞单元做归一化。通过这个归一化后,能对光照变化和阴影获得更好的效果。

算法步骤

HOG特征提取方法就是将一个image(你要检测的目标或者扫描窗口):

在这里插入图片描述

灰度化(将图像看做一个x,y,h(灰度)的三维图像);
采用Gamma校正法对输入图像进行颜色空间的标准化(归一化)

目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
为了减少光照因素的影响,首先需要将整个图像进行规范化(归一化)。在图像的纹理强度中,局部的表层曝光贡献的比重较大,所以,这种压缩处理能够有效地降低图像局部的阴影和光照变化。因为颜色信息作用不大,通常先转化为灰度图; I ( x , y ) = I ( x , y ) γ I(x, y) = I(x, y)^{\gamma} I(x,y)=I(x,y)γ通常 γ \gamma γ取0.5

计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。

图像中像素点的梯度:

G x ( x , y ) = H ( x + 1 , y ) − H ( x − 1 , y ) G_x(x, y) = H(x+1, y) - H(x-1, y) Gx(x,y)=H(x+1,y)H(x1,y)
G y ( x , y ) = H ( x , y + 1 ) − H ( x , y − 1 ) G_y(x, y) = H(x, y+1) - H(x, y-1) Gy(x,y)=H(x,y+1)H(x,y1)
G ( x , y ) = G x ( x , y ) 2 + G y ( x , y ) 2 G(x, y) = \sqrt{G_x(x, y)^2 + G_y(x, y)^2} G(x,y)=Gx(x,y)2+Gy(x,y)2
α ( x , y ) = t a n − 1 G y ( x , y ) G x ( x , y ) \alpha(x, y) = tan^{-1}{\frac{G_y(x, y)}{G_x(x, y)}} α(x,y)=tan1Gx(x,y)Gy(x,y)

将图像划分成小cells(例如16*16像素/cell);

@按照角度分bin的示意图
每一个点的梯度角度可能是0~180度之间的任意值,而程序中将其离散化为9个bin,即每个bin占20度。所以滑动窗口中每个像素点的梯度角度如果要离散化到这9个bin中,则一般它都会有2个相邻的bin(如果恰好位于某个bin的中心,则可认为对该bin的权重为1即可)。从源码中可以看到梯度的幅值是用来计算梯度直方图时权重投票的,所以每个像素点的梯度幅值就分解到了其角度相邻的2个bin了,越近的那个bin得到的权重越大。因此幅度图像用了2个通道,每个通道都是原像素点幅度的一个分量。同理,不难理解,像素点的梯度角度也用了2个通道,每个通道中存储的是它相邻2个bin的bin序号。序号小的放在第一通道。
在这里插入图片描述
其中,假设那3条半径为离散化后bin的中心,红色虚线为像素点O(像素点在圆心处)的梯度方向,梯度幅值为A,该梯度方向与最近的相邻bin为bin0,这两者之间的夹角为a.这该像素点O处存储的梯度幅值第1通道为A*(1-a),第2通道为A*a;该像素点O处存储的角度第1通道为0(bin的序号为0),第2通道为1(bin的序号为1)。
另外在计算图像的梯度图和相位图时,如果该图像时3通道的,则3通道分别取梯度值,并且取梯度最大的那个通道的值为该点的梯度幅值。

统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;
将每几个cell组成一个block(例如2*2个cell/block)

@cell示意图
一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。这个在OpenCV中有HogCache中getBlock进行实现的。
如图所示,黑色框代表1个block,红实线隔开的为4个cell,每个cell用绿色虚线隔开的我们称之为4个区域,所以该block中共有16个区域,分别为A、B、C、…、O、P。
将这16个区域分为4组:
第1组:A、D、M、P;该组内的像素点计算梯度方向直方图时只对其所在的cell有贡献。
第2组:B、C、N、O;该组内的像素点计算梯度直方图时对其所在的左右cell有贡献。
第3组:E、I、H、L;该组内的像素点计算梯度直方图时对其所在的上下cell有贡献。
第4组:F、G、J、K;该组内的像素点对其上下左右的cell计算梯度直方图时都有贡献。

那到底是怎么对cell贡献的呢?举个例子来说,E区域内的像素点对cell0和cell2有贡献。本来1个block对滑动窗口贡献的向量维数为36维,即每个cell贡献9维,其顺序分别为cell0,cell1,cell2,cell3.而E区域内的像素由于同时对cell0和cell2有贡献,所以在计算E区域内的像素梯度投票时,不仅要投向它本来的cell0,还要投向下面的cell2,即投向cell0和cell2有一个权重,该权重与该像素点所在位置与cell0,cell2中心位置的距离有关。具体的关系可以去查看源码。

将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。

在这里插入图片描述

实际实现的时候,首先用[-1,0,1]梯度算子对原图像做卷积运算,得到x方向(水平方向,以向右为正方向)的梯度分量gradscalx,然后用[1,0,-1]T梯度算子对原图像做卷积运算,得到y方向(竖直方向,以向上为正方向)的梯度分量gradscaly。然后再用以上公式计算该像素点的梯度大小和方向。

HOG源码分析

在读源码时,由于里面用到了intel的ipp库,优化了算法的速度。为了学习方便,我对OpenCV中关于加速的
部分进行了删减,只剩下算法的精要部分。
在这里插入图片描述
头文件中有关于一些参数的默认设置:
检测窗口大小为12864;
Block大小为16
16;
Cell大小为88;
Block在检测窗口中上下移动尺寸为8
8;
1个cell的梯度直方图化成9个bin;
滑动窗口在检测图片中滑动的尺寸为8*8;

头文件

//HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector //

//! struct for detection region of interest (ROI)
struct DetectionROI
{
   
   //! scale(size) of the bounding box
   double scale;
   //! set of requrested locations to be evaluated
   std::vector<cv::Point> locations;
   //! vector that will contain confidence values for each location
   std::vector<double> confidences;
};

struct HOGDescriptor
{
   
public:
    enum {
    L2Hys = 0};
    enum {
    DEFAULT_NLEVELS = 64};

    HOGDescriptor() : 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), signedGradient(false)
    {
   }

    //! with found weights output
    virtual void detect(const Mat& img, std::vector<Point>& foundLocations,
                        std::vector<double>& weights,
                        double hitThreshold = 0, Size winStride = Size(),
                        Size padding = Size(),
                        const std::vector<Point>& searchLocations = std::vector<Point>()) const;
    //! without found weights output
    virtual void detect(const Mat& img, std::vector<Point>& foundLocations,
                        double hitThreshold = 0, Size winStride = Size(),
                        Size padding = Size(),
                        const std::vector<Point>& searchLocations=std::vector<Point>()) const;

    //! with result weights output
    virtual void detectMultiScale(InputArray img, std::vector<Rect>& foundLocations,
                                  std::vector<double>& foundWeights, double hitThreshold = 0,
                                  Size winStride = Size(), Size padding = Size(), double scale = 1.05,
                                  double finalThreshold = 2.0,bool useMeanshiftGrouping = false) const;
    //! without found weights output
    virtual void detectMultiScale(InputArray img, std::vector<Rect>& foundLocations,
                                  double hitThreshold = 0, Size winStride = Size(),
                                  Size padding = Size(), double scale = 1.05,
                                  double finalThreshold = 2.0, bool useMeanshiftGrouping = false) const;

    virtual void computeGradient(const Mat& img, Mat& grad, Mat& angleOfs,
                                 Size paddingTL = Size(), Size paddingBR = Size()) const;

    static std::vector<float> getDefaultPeopleDetector();
    static std::vector<float> getDaimlerPeopleDetector();

    Size winSize;		// 窗口大小 64x128
    Size blockSize;		// block size 16x16
    Size blockStride;	// block 之间的stride
    Size cellSize;		// cell的size
    int nbins;			// 
    int derivAperture;	//
    double winSigma;
    int histogramNormType;
    double L2HysThreshold;
    bool gammaCorrection;
    std::vector<float> svmDetector;
    UMat oclSvmDetector;
    float free_coef;
    int nlevels;
    bool signedGradient;


    //! evaluate specified ROI and return confidence value for each location
    virtual void detectROI(const cv::Mat& img, const std::vector<cv::Point> &locations,
                                   std::vector<cv::Point>& foundLocations, std::vector<double>& confidences,
                                   double hitThreshold = 0, cv::Size winStride = Size(),
                                   cv::Size padding = Size()) const;

    //! evaluate specified ROI and return confidence value for each location in multiple scales
    virtual void detectMultiScaleROI(const cv::Mat& img,
                                                       std::vector<cv::Rect>& foundLocations,
                                                       std::vector<DetectionROI>& locations,
                                                       double hitThreshold = 0,
                                                       int groupThreshold = 0) const;

};

//! @} objdetect

源文件


#include "cascadedetect.hpp"
#include "opencv2/core/core_c.h"
#include "opencl_kernels_objdetect.hpp"

#include <cstdio>
#include <iterator>
#include <limits>

/****************************************************************************************\
      The code below is implementation of HOG (Histogram-of-Oriented Gradients)
      descriptor and object detection, introduced by Navneet Dalal and Bill Triggs.

      The computed feature vectors are compatible with the
      INRIA Object Detection and Localization Toolkit
      (http://pascal.inrialpes.fr/soft/olt/)
\****************************************************************************************/

namespace cv
{
   

#define NTHREADS 256

enum {
   DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW};

static int numPartsWithin(int size, int part_size, int stride)
{
   
    return (size - part_size + stride) / stride;
}

static Size numPartsWithin(cv::Size size, cv::Size part_size,
                                                cv::Size stride)
{
   
    return Size(numPartsWithin(size.width, part_size.width, stride.width),
        numPartsWithin(size.height, part_size.height, stride.height));
}

static size_t getBlockHistogramSize(Size block_size, Size cell_size, int nbins)
{
   
    Size cells_per_block = Size(block_size.width / cell_size.width,
        block_size.height / cell_size.height);
    return (size_t)(nbins * cells_per_block.area());
}

size_t HOGDescriptor::getDescriptorSize() const
{
   
    CV_Assert(blockSize.width % cellSize.width == 0 &&
        blockSize.height % cellSize.height == 0);
    CV_Assert((winSize.width - blockSize.width) % blockStride.width == 0 &&
        (winSize.height - blockSize.height) % blockStride.height == 0 );

    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);
}

double HOGDescriptor::getWinSigma() const
{
   
    return winSigma >= 0 ? winSigma : (blockSize
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值