hog特征总结

一、hog特征详解(点击[]可以进入我所参考的博客)

1.hog特征原理

1.HOG的核心思想是所检测的局部物体外形能够被光强梯度或边缘方向的分布所描述。[1]

  1. 通过将整幅图像分割成小的连接区域(称为cells),每个cell生成一个方向梯度直方图或者cell中pixel的边缘方向,这些直方图的组合可表示出(所检测目标的目标)描述子。为改善准确率,局部直方图可以通过计算图像中一个较大区域(称为block)的光强作为measure被对比标准化,然后用这个值(measure)归一化这个block中的所有cells.这个归一化过程完成了更好的照射/阴影不变性。
  2. hog特征优点:
    (1).几何和光学转化不变性,尤其适合人的检测。
    (2).Hog没有旋转和尺度不变性。(所以计算量小)
  3. 那么行人检测中怎么应用HOG呢?
    对于解决Scale-invariant的问题:将图片进行不同尺度的缩放,就相当于对模板进行不同尺度scale的缩放。
    对于解决Rotation-invariant 的问题:建立不同方向的模版(一般取15*7的)进行匹配。

2.hog实现原理

  1. 对于一个image,在一个大小为winSIZE的检测窗口中,用一个大小为blockSIZE的block来扫描整个检测窗口,每个block又分为四个cell,每次block扫描都输出四个cell的梯度信息然后保存到特征向量中。
  2. 这三者的示意图如下:[2]
    1. 窗口大小 winSize
      这里写图片描述
    2. 块大小 blockSize
      这里写图片描述
    3. 胞元大小 cellSize
      这里写图片描述
  3. 通俗的讲:
    HOG特征提取方法就是将一个image:
    1. 灰度化(将图像看做一个x,y,z(灰度)的三维图像)
    2. 划分成小cells(2*2)
    3. 计算每个cell中每个pixel的gradient(即orientation)
    4. 统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor
  4. 对opencv中的hog源码中一些固定参数来个简单说明:
       检测窗口大小为128*64;
       Block大小为16*16;
       Cell大小为8*8;
       Block在检测窗口中上下移动尺寸为8*8;
       1个cell的梯度直方图化成9个bin;
       滑动窗口在检测图片中滑动的尺寸为8*8;
    代码中的一个hog描述子是针对一个检测窗口而言的,所以一个检测窗口共有105=((128-16)/8+1)*((64-16)/8+1)个block;一个block中有4个cell,而一个cell的hog描述子向量的长度为9;所以检测窗口的hog向量长度=3780=105*4*9维。每次block扫描都输出四个cell的梯度信息然后保存到特征向量中,知道扫描完最后特征向量输出3780维的向量。

3.hog可视化理解[3]

这里写图片描述

//hog可视化代码
#include <opencv2/opencv.hpp>  
#include <cstdio>  
#include <cstdlib>  
#include <Windows.h>  

using namespace std;
using namespace cv;

// HOGDescriptor visual_imagealizer
// adapted for arbitrary size of feature sets and training images
Mat get_hogdescriptor_visual_image(Mat& origImg,
    vector<float>& descriptorValues,//hog特征向量
    Size winSize,//图片窗口大小
    Size cellSize,             
    int scaleFactor,//缩放背景图像的比例
    double viz_factor)//缩放hog特征的线长比例
{   
    Mat visual_image;//最后可视化的图像大小
    resize(origImg, visual_image, Size(origImg.cols*scaleFactor, origImg.rows*scaleFactor));

    int gradientBinSize = 9;
    // dividing 180° into 9 bins, how large (in rad) is one bin?
    float radRangeForOneBin = 3.14/(float)gradientBinSize; //pi=3.14对应180°

    // prepare data structure: 9 orientation / gradient strenghts for each cell
    int cells_in_x_dir = winSize.width / cellSize.width;//x方向上的cell个数
    int cells_in_y_dir = winSize.height / cellSize.height;//y方向上的cell个数
    int totalnrofcells = cells_in_x_dir * cells_in_y_dir;//cell的总个数
    //注意此处三维数组的定义格式
    //int ***b;
    //int a[2][3][4];
    //int (*b)[3][4] = a;
    //gradientStrengths[cells_in_y_dir][cells_in_x_dir][9]
    //上面一个是来记录总的梯度信息,下面一个是记录这个cell被记录了多少次
    float*** gradientStrengths = new float**[cells_in_y_dir];
    int** cellUpdateCounter   = new int*[cells_in_y_dir];
    for (int y=0; y<cells_in_y_dir; y++)
    {
        gradientStrengths[y] = new float*[cells_in_x_dir];
        cellUpdateCounter[y] = new int[cells_in_x_dir];
        for (int x=0; x<cells_in_x_dir; x++)
        {
            //将每一点的梯度值分为9份
            gradientStrengths[y][x] = new float[gradientBinSize];
            cellUpdateCounter[y][x] = 0;

            for (int bin=0; bin<gradientBinSize; bin++)
                gradientStrengths[y][x][bin] = 0.0;//把每个cell的9个bin对应的梯度强度都初始化为0
        }
    }

    // nr of blocks = nr of cells - 1
    // since there is a new block on each cell (overlapping blocks!) but the last one
    //相当于blockstride = (8,8)
    int blocks_in_x_dir = cells_in_x_dir - 1;//这是代表是block也是在扫描的吗?最后一个不用组成一个block了,这里block横向纵向都是cell的俩倍
    int blocks_in_y_dir = cells_in_y_dir - 1;

    // compute gradient strengths per cell
    int descriptorDataIdx = 0;
    int cellx = 0;
    int celly = 0;

    for (int blockx=0; blockx<blocks_in_x_dir; blockx++)
    {
        for (int blocky=0; blocky<blocks_in_y_dir; blocky++)            
        {
            // 4 cells per block ...
            for (int cellNr=0; cellNr<4; cellNr++)
            {
                // compute corresponding cell nr
                //分为0,1,2,3四个格子,第0个即与block的坐标相同,其余相应加1
                int cellx = blockx;
                int celly = blocky;
                if (cellNr==1) celly++;
                if (cellNr==2) cellx++;
                if (cellNr==3)
                {
                    cellx++;
                    celly++;
                }
                //
                for (int bin=0; bin<gradientBinSize; bin++)
                {
                    float gradientStrength = descriptorValues[ descriptorDataIdx ];
                    descriptorDataIdx++;
                    //初始每个cell里面的都为0,blcok每扫描一次,都会有一个新的特征向量记录下来,这个是把每次重复计算cell时,得到的梯度特征信息都叠加到一起,然后再用于后面的显示
                    gradientStrengths[celly][cellx][bin] += gradientStrength;//因为C是按行存储

                } // for (all bins)


                // note: overlapping blocks lead to multiple updates of this sum!
                // we therefore keep track how often a cell was updated,
                // to compute average gradient strengths
                cellUpdateCounter[celly][cellx]++;//由于block之间有重叠,所以要记录哪些cell被多次计算了

            } // for (all cells)


        } // for (all block x pos)
    } // for (all block y pos)


    // compute average gradient strengths
    for (int celly=0; celly<cells_in_y_dir; celly++)
    {
        for (int cellx=0; cellx<cells_in_x_dir; cellx++)
        {

            float NrUpdatesForThisCell = (float)cellUpdateCounter[celly][cellx];

            // compute average gradient strenghts for each gradient bin direction
            for (int bin=0; bin<gradientBinSize; bin++)
            {
                //计算平均梯度信息
                gradientStrengths[celly][cellx][bin] /= NrUpdatesForThisCell;
            }
        }
    }


    cout << "winSize = " << winSize << endl;
    cout << "cellSize = " << cellSize << endl;
    cout << "blockSize = " << cellSize*2<< endl;
    cout << "blockNum = " << blocks_in_x_dir<<"×"<<blocks_in_y_dir << endl;
    cout << "descriptorDataIdx = " << descriptorDataIdx << endl;

    // draw cells
    for (int celly=0; celly<cells_in_y_dir; celly++)
    {
        for (int cellx=0; cellx<cells_in_x_dir; cellx++)
        {
            int drawX = cellx * cellSize.width;
            int drawY = celly * cellSize.height;

            int mx = drawX + cellSize.width/2;
            int my = drawY + cellSize.height/2;

            rectangle(visual_image,
                Point(drawX*scaleFactor,drawY*scaleFactor),
                Point((drawX+cellSize.width)*scaleFactor,
                (drawY+cellSize.height)*scaleFactor),         
                CV_RGB(0,0,0),//cell框线的颜色
                1);

            // draw in each cell all 9 gradient strengths
            for (int bin=0; bin<gradientBinSize; bin++)
            {
                float currentGradStrength = gradientStrengths[celly][cellx][bin];

                // no line to draw?
                if (currentGradStrength==0)
                    continue;

                float currRad = bin * radRangeForOneBin + radRangeForOneBin/2;//取每个bin里的中间值,如10°,30°,...,170°.

                float dirVecX = cos( currRad );
                float dirVecY = sin( currRad );
                float maxVecLen = cellSize.width/2;
                float scale = viz_factor; // just a visual_imagealization scale,
                // to see the lines better

                // compute line coordinates
                float x1 = mx - dirVecX * currentGradStrength * maxVecLen * scale;
                float y1 = my - dirVecY * currentGradStrength * maxVecLen * scale;
                float x2 = mx + dirVecX * currentGradStrength * maxVecLen * scale;
                float y2 = my + dirVecY * currentGradStrength * maxVecLen * scale;

                // draw gradient visual_imagealization
                line(visual_image,
                    Point(x1*scaleFactor,y1*scaleFactor),
                    Point(x2*scaleFactor,y2*scaleFactor),
                    CV_RGB(255,255,255),//HOG可视化的cell的颜色
                    1);

            } // for (all bins)

        } // for (cellx)
    } // for (celly)


    // don't forget to free memory allocated by helper data structures!
    for (int y=0; y<cells_in_y_dir; y++)
    {
        for (int x=0; x<cells_in_x_dir; x++)
        {
            delete[] gradientStrengths[y][x];            
        }
        delete[] gradientStrengths[y];
        delete[] cellUpdateCounter[y];
    }
    delete[] gradientStrengths;
    delete[] cellUpdateCounter;

    return visual_image;//返回最终的HOG可视化图像

}


int main()
{

    HOGDescriptor hog;//使用的是默认的hog参数
    /*
    HOGDescriptor(Size win_size=Size(64, 128), Size block_size=Size(16, 16), Size block_stride=Size(8, 8), 
    Size cell_size=Size(8, 8), int nbins=9, double win_sigma=DEFAULT_WIN_SIGMA(DEFAULT_WIN_SIGMA=-1), 
    double threshold_L2hys=0.2, bool gamma_correction=true, int nlevels=DEFAULT_NLEVELS)

    Parameters: 
    win_size – Detection window size. Align to block size and block stride.
    block_size – Block size in pixels. Align to cell size. Only (16,16) is supported for now.
    block_stride – Block stride. It must be a multiple of cell size.
    cell_size – Cell size. Only (8, 8) is supported for now.
    nbins – Number of bins. Only 9 bins per cell are supported for now.
    win_sigma – Gaussian smoothing window parameter.
    threshold_L2hys – L2-Hys normalization method shrinkage.
    gamma_correction – Flag to specify whether the gamma correction preprocessing is required or not.
    nlevels – Maximum number of detection window increases.
    */
    //对于128*80的图片,blockstride = 8,15*9的block,2*2*9*15*9 = 4860

    int width = 80;
    int height = 128;
    hog.winSize=Size(width,height);
    vector<float> des;//HOG特征向量
    Mat src = imread("objimg.jpg");
    Mat dst ;
    resize(src,dst,Size(width,height));//规范图像尺寸
    //即一幅图一个wimSize全扫描完了
    imshow("src",src);
    hog.compute(dst,des);//计算hog特征
    Mat background = Mat::zeros(Size(width,height),CV_8UC1);//设置黑色背景图,因为要用白色绘制hog特征

    Mat d = get_hogdescriptor_visual_image(background,des,hog.winSize,hog.cellSize,3,2.5);
    imshow("dst",d);
    imwrite("hogvisualize.jpg",d);
    waitKey();

     return 0;
}

原图
原图
hog特征图
hog特征图

3.Histograms of oriented gradients for human detection原文翻译

  • 5
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HOG(Histogram of Oriented Gradients)是一种计算机视觉中的特征提取算法,常用于目标检测和行人识别等任务中。在Python中,可以使用OpenCV或scikit-image等库来实现HOG特征提取。 以scikit-image为例,可以通过以下代码实现HOG特征提取: ``` from skimage.feature import hog from skimage import data, exposure # 读取图像 image = data.astronaut() # 计算HOG特征 fd, hog_image = hog(image, orientations=8, pixels_per_cell=(16, 16), cells_per_block=(1, 1), visualize=True, multichannel=True) # 对HOG特征进行可视化 hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10)) # 显示原始图像和HOG特征图像 import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True) ax1.axis('off') ax1.imshow(image, cmap=plt.cm.gray) ax1.set_title('Input image') hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10)) ax2.axis('off') ax2.imshow(hog_image_rescaled, cmap=plt.cm.gray) ax2.set_title('Histogram of Oriented Gradients') plt.show() ``` 其中,`image`代表输入的图像,`orientations`指定方向的个数,`pixels_per_cell`指定每个细胞的像素数,`cells_per_block`指定每个块包含的细胞数。`fd`表示提取得到的HOG特征向量,`hog_image`表示HOG特征图像。最后,使用`matplotlib`库进行可视化,显示原始图像和HOG特征图像。 ### 回答2: HOG(Histogram of Oriented Gradients)特征提取是一种用于计算图像特征的方法,最初是由Navneet Dalal和Bill Triggs在2005年提出的。它在计算机视觉领域被广泛应用于物体检测和图像分类任务。 HOG特征提取的过程可以分为以下几个步骤: 1. 归一化图像大小:为了保持计算效率,首先需要将图像缩放为固定的大小。通常,使用缩放后的图像尺寸在64x128到128x256之间。 2. 计算梯度:对于每个像素,通过计算其在水平和垂直方向上的梯度,确定其梯度的大小和方向。这些梯度用于描述图像的边缘和纹理信息。 3. 划分图像为小单元:将缩放后的图像划分为一系列重叠的小单元。每个小单元通常为8x8像素。 4. 创建梯度方向直方图:对于每个小单元,根据其中像素的梯度方向和大小,创建梯度方向直方图。一个直方图通常包含9个方向的梯度值。 5. 归一化块:将相邻的若干小单元组合成块,并对每个块内的直方图进行归一化处理。这有助于提高特征的鲁棒性和可区分性。 6. 拼接特征向量:将所有块的特征向量拼接在一起,形成最终的HOG特征向量。 HOG特征提取通过描述图像中梯度的方向信息来提取特征,而不是关注像素的具体值。这使得HOG特征对于光照变化和几何变换相对不敏感,具有较好的鲁棒性。在图像处理和计算机视觉任务中,HOG特征已被广泛应用于人体检测、行人检测、物体识别等领域。 ### 回答3: HOG(方向梯度直方图)是一种计算机视觉领域常用的特征提取算法,它用于对图像进行描述和识别。Python中有各种库和模块可以用来实现HOG特征提取。 HOG特征提取的步骤如下: 1. 图像预处理:将图像转化为灰度图,如果图像尺寸较大,还可以进行降采样。 2. 计算图像的梯度:使用Sobel等算子计算图像在水平和竖直方向上的梯度。计算梯度的目的是为了检测图像中的边缘和纹理。 3. 划分图像为小的块(cells):将图像分割为大小固定的小块,每个小块包含多个像素。 4. 计算每个小块的梯度直方图:对于每个小块,统计其内像素的梯度方向和强度,并将其组织成直方图。 5. 归一化梯度直方图:对于每个小块的梯度直方图,可以对其进行归一化,使得特征对光照等变化更加不敏感。 6. 将小块的特征组合成一个全局的特征向量:将所有小块的特征向量进行串联,形成一个用于描述整个图像的全局特征向量。 通过以上步骤,我们可以得到一个用于描述图像的HOG特征向量。这个特征向量可以用于识别和分类任务,比如行人检测、物体识别等。 在Python中,我们可以使用第三方库如OpenCV或scikit-image来实现HOG特征提取。这些库提供了方便的函数和方法,可以直接使用。 例如,使用OpenCV库,我们可以使用以下代码来实现HOG特征提取: ```python import cv2 def hog_feature_extraction(image): # 图像预处理 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 创建HOG对象 hog = cv2.HOGDescriptor() # 计算HOG特征向量 features = hog.compute(gray) return features ``` 上述代码中,我们首先将彩色图像转换为灰度图像,然后创建一个HOG对象,并使用`compute`函数计算图像的HOG特征向量。 总结来说,Python中可以使用第三方库实现HOG特征提取,该特征提取方法可以用于图像描述和识别任务,具有良好的性能和鲁棒性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值