Histogram of orientend gradient 的理解1

为啥开始了解的原因就不说了。
从网上找了很多的资料,其中有一个项目简单修改后可以用vs2017编译通过。但是,仔细研究这份代码后发现它里面着重编写的是如何把hog的数据用图表达出来,具体来说呢就是用线的方向来表示梯度的方向,线的长短来表示梯度的大小。
可视化代码
运行后的效果如下图:
这里写图片描述

右边是梯度直方图。
在看代码的过程中,不出所料的有一段没有看懂,摘抄如下:

    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(255, 255, 255),//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)

主要是

                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;

这一段没有弄清楚用dirVecX怎么就计算出来了x1,y1,x2,y2来了。于是我仿照着原来的代码写了一段测试程序,部分摘抄如下:

#include<opencv2/opencv.hpp>
#include<iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>  

using namespace std;
using namespace cv;


int main()
{
    int width = 400;
    int height = 640;
    int mx = width / 2;
    int my = height / 2;
    int gradientBinSize = 9;
    float radRangeForOneBin = 3.14 / (float)gradientBinSize;

    Mat src = imread("003.jpg");
    Mat bg = Mat::zeros(Size(width, height), CV_8UC1);

    //line(bg,\
        Point(28,80),\
        Point(160,320),\
        CV_RGB(255,255,255),\
        1);

    for (int bin = 0; bin < gradientBinSize; bin++)
    {
        float currRad = bin * radRangeForOneBin + radRangeForOneBin / 2;
        float dirVecX = cos(currRad);
        float dirVecY = sin(currRad);
        float maxVecLen = width / 2;
        float x1 = mx - dirVecX * maxVecLen;
        float y1 = my - dirVecY * maxVecLen;
        float x2 = mx + dirVecX * maxVecLen;
        float y2 = my + dirVecY * maxVecLen;

        printf("bin=%d  ",bin);
        printf("x1=%f, y1=%f   ",x1,y1);
        printf("x2=%f, y2=%f \n",x2,y2);


        arrowedLine(bg,
            Point(x1, y1),
            Point(x2, y2),
            Scalar(255, 0, 0),
            1);
        //if (bin == 0)
            //break;
    }

    imshow("src", src);
    imshow("bg", bg);

    waitKey(0);

    return 0;   
}

1.首先width和height定义了一个窗口的大小,这个窗口主要用来绘制一个梯度直方图的示例。与实际使用时不同的是,这个梯度直方图在各个方向上的值是相等的。

2.mx和my作为这个窗口的中心点,为社么要使用中心点呢?看来后面的绘制直方图的方法就知道了。

3.通常情况下,把一个半圆分成9份来绘制直方图。这里有很多疑问,为什么是半圆?为什么是分成9份?这个后面再细说。总的来说,就是这些是经过验证的最佳值,在实际使用的时候直接用这个标准就行了。

4.opencv打开图片的语句就不说了。

5.for()循环依次绘制每个方向的梯度。具体流程是:
5.1 既然把半圆分成9份,即分成9个bin。注意,这里说的bin不是指二进制可执行程序的意思,而是bin的原义,每次看到bin就联想到可执行程序的你还记得bin的其他原义是什么吗?你自己去查一下吧。使用弧度值的话,就是把PI分成9份,每份是1/9,即近似值是3.14 / 9约等于0.34889,但是为什么还要再偏移一个1/2的bin大小呢?这主要是为了获取每个bin范围的中心线,即一个bin对应的弧度是0.34889,再加上一半的bin大小是0.17444。在for()循环中,当bin=0时,即第0个bin的中心角是0.17444,如下图中的θ:
这里写图片描述

三角形OAB中,cosθ的值为A点的横坐标x,sinθ的值为A点的纵坐标y。
因此以O为原点的话,A点的坐标就可以表示为(cosθ,sinθ),当然了,假设OA的长度为单位1,如果不为单位1的话,就再乘以一个倍数,主要是找到cosθ和sinθ的含义即可。
所以,dirVecX和dirVecY的含义也知道了,它就是A点的坐标。
想象一下,当for()中的bin等于其他值时,dirVecX和dirVecY的情况。
5.2 maxVecLen表示这个圆的最大半径,撑死了也就是width的一半。
5.3直接看图吧。
这里写图片描述
有了第1张图作为基础,这张图主要是说明(x1,y1)和(x2,y2)的计算方法。
x1和y1是在(mx,my)的基础上减少,x2和y2则是在(mx,my)的基础上增加。其中要注意的是,左上角为坐标原点,向右为x增加方向,向下为y增加方向。这是opencv中的坐标表示方法,既然使用opencv画图,就得按照它的规则。

6使用arroweLine()来画一条带有箭头的线,从(x1,y1)指向(x2,y2),运行效果如下图:
这里写图片描述
下面是实际的坐标,可以看一下x1,y1,x2,y2的大小关系。
这里写图片描述
下面依次递增bin的值。可以看到新添加的直线位置的变化:
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

以上是所有方向的梯度大小是相等的,如果每个方向的大小不一样的话,就会出现有长有短的效果。

猜你喜欢

转载自blog.csdn.net/k7arm/article/details/79809454
今日推荐