为啥开始了解的原因就不说了。
从网上找了很多的资料,其中有一个项目简单修改后可以用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的值。可以看到新添加的直线位置的变化:
以上是所有方向的梯度大小是相等的,如果每个方向的大小不一样的话,就会出现有长有短的效果。