方向梯度直方图HOG(理解与c++代码实现)
菜鸟一个,学习视觉跟踪要用到HOG特征,想要深入了解一下HOG特征,但是找了半天很多人的介绍都没有代码,还有的有部分代码,想要全部的代码,还要花钱下载,很不爽,我这里练习写了一个,不怎么好,但是可以加深对于特征的理解,有兴趣的可以参考一下。
首先,对于HOG的理解可以参考这篇博客
https://blog.youkuaiyun.com/u013066730/article/details/83015490
也是一篇转载的,但是没找到原链接。
后面的我写的代码就是参考这里面的步骤。
开发环境:
Ubuntu16.04 + QT5 + CMake3.11.3 + OpenCV3.4.7
先把CMakeLists.txt文件列在这里:
cmake_minimum_required(VERSION 2.8)
add_compile_options(-std=c++11)
project(getHOG)
aux_source_directory(. SRC_LIST)
find_package(OpenCV REQUIRED)
include_directories(${
OpenCV_INCLUDE_DIRS} ${
SOURCE_DIR} )
add_executable(${
PROJECT_NAME} "main.cpp")
target_link_libraries(${
PROJECT_NAME} ${
OpenCV_LIBS})
用于测试的图像
1.提取cell中的梯度方向直方图并且显示出来
main.cpp
#include <iostream>
#include <math.h>
#include <string.h>
#include <opencv2/opencv.hpp>
#define PI (3.1415927)
#define CELL_SIZE (8) //8*8大小cell
#define BLOCK_SIZE (2) //2*2个cell
#define BIN_NUMBER (9)
#define BIN_WIDTH (180/BIN_NUMBER)
using namespace std;
//函数声明
float* calCellFeature(cv::Point) ;
void drawHist(float* );
float cell_bin[BIN_NUMBER]; //每个cell中分配一个数组,用于存放每个cell的梯度方向直方图
cv::Mat img = cv::imread("/home/mk90/Pictures/nvpai.jpeg", cv::IMREAD_GRAYSCALE);
int main()
{
img.convertTo(img, CV_32F, 1/255.0); // 归一化
cv::Point p(200, 50); //随便选择的一个点,用来测试
calCellFeature(p);
drawHist(cell_bin);
cv::imshow("image", img);
cv::waitKey(0);
return 0;
}
// 根据cell左上角点来计算该cell的梯度方向直方图
// 这里bin的划分, 0-20 20-40 40-60 60-80 80-100 100-120 120-140 140-160 160-0 每个bin不含右边界角度
float* calCellFeature(cv::Point leftTop){
cv::Mat cell_img(img, cv::Rect(leftTop.x, leftTop.y, CELL_SIZE, CELL_SIZE));//每个cell图像
cv::Mat gx, gy;//用来存放x和y方向梯度
// CV_32F 后面的1,0表示x方向梯度,0,1表示y方向梯度,最后的1代表ksize,表示采用模板为3*1或者1*3
cv::Sobel(cell_img, gx, CV_32F, 1, 0, 1);
cv::Sobel(cell_img, gy, CV_32F, 0, 1, 1);
// C++ Calculate gradient magnitude and direction (in degrees)
cv::Mat mag, angle;//存放计算得到的幅值和角度
cv::cartToPolar(gx, gy, mag, angle, 1); //参数1表示得到的是角度而不是弧度
int iWhichBin; // 索引号,用于判断每个角度应该在的bin
memset(cell_bin, 0, BIN_NUMBER*sizeof (float)); //每次使用cell_bin先赋值为0
for (int i =0; i< cell_img.cols; i++){
for (int j = 0; j < cell_img.rows; j++){
// 把角度转化为0—180之间
if (angle.at<float>(i, j) < 0)
angle.at<float>(i, j) += 180.0f;
if (angle.at<float>(i, j) > 180.0f)
angle.at<float>(i, j) -= 180.0f;
iWhichBin = angle.at<float>(i, j) / BIN_WIDTH ; // 该角度所处的bin位置, bin序号从0开始
cell_bin[iWhichBin] += mag.at<float>(i, j); // 相应的bin的值加上相应的幅值
}
}
//查看数据
// for (int n=0; n< BIN_NUMBER; n++) {
// printf("cell_bin[%d]: %f\n", n, cell_bin[n]);
// }
return cell_bin;
}
//取数组的最大值, drawHist函数中调用
float getMax(float* array){
float max;
for (int i =0; i< sizeof (array); i++) {
if (array[i] > max)
max =array[i];
}
cout<< "max: " << max << endl;
return max;
}
// 绘制直方图
void drawHist(float* bin_data){
int histRows = 200; //直方图的高
int histCols = 500; //直方图的宽
float maxValue = getMax(bin_data);
cv::Scalar backGroundColor = cv::Scalar(150, 150, 150); //背景颜色
cv::Scalar graphColor = cv::Scalar(0, 100, 200); //图像颜色
cv::LineTypes lineType = cv::LINE_AA; //线条类型
cv::Mat theGraph(histRows, histCols, CV_8UC3, backGroundColor); //用于绘图的Mat对象
cv::Point p1(0, 0), p2(0, theGraph.rows-1); //用于绘图的两点
for (int i=0; i<BIN_NUMBER; i++) {
float value = bin_data[i] * 0.95f; //在直方图上方留有一些空白
value = maxValue - value; //翻转,注意绘图的位置
value = value / maxValue *theGraph.rows; //在图上的高度
p1.y = value; //该bin方框左上角的点
p2.x = float(i+1) *