嵌入式人工智能应用-第三章 opencv操作8 图像特征之HOG 特征
1 HOG特征
1.1 背景介绍
方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog 特征结合 SVM 分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。需要提醒的是,HOG+SVM 进行行人检测的方法是法国研究人员 Dalal 在 2005 的 CVPR 上提出的,而如今虽然有很多新的人检测算法不断提出,但基本都是以 HOG+SVM 的思路为主。
1.2 实现步骤
主要思想是在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述。(本质:梯度的统计信息,而梯度主要存在于边缘的地方)。
具体实现方法是首先将图像分成小的连通区域,我们把它叫细胞单元。
然后采集细胞单元中各像素点的梯度的或边缘的方向直方图。
最后把这些直方图组合起来就可以构成特征描述器。
提升性能是把这些局部直方图在图像的更大的范围内(我们把它叫区间或 block)进行对比度归一化(contrast-normalized),所采用的方法是:先计算各直方图在这个区间(block)中的密度,然后根据这个密度对区间中的各个细胞单元做归一化。通过这个归一化后,能对光照变化和阴影获得更好的效果。
采用 HOG 算法与其他的特征描述方法相比,HOG 有很多优点。
首先,由于 HOG 是在图像的局部方格单元上操作,所以它对图像几何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上。
其次,在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。因此 HOG 特征是特别适合于做图像中的人体检测的。
1.3 具体过程
HOG 实现的大体过程:
首先将输入的图像灰度化;
然后采用 gamma 校正法对输入图像进行颜色空间标准化处理,
再将图像切割成小的 cells,例如 16*16 的;
统计每个 cells 测梯度直方图,目的是得到每个直方图的 descriptor;将多个 cell 组成 block,将 block 中的所有 cell 的特征 descriptor 串联起来就得到了 block 的 HOG 特征 descriptor;
最后将图片中所有的 block 的 HOG 特征 descriptor 串联起来就可以得到该图片的 HOG 特征 descriptor,这个 descriptor 就是最终可供分类器使用的特征向量。
-
核心步骤
图像预处理
转换为灰度图像(减少计算量)。
可进行Gamma校正(增强对比度,但OpenCV默认不启用)。目的是调节图像的对比度,降低图像局部高光和阴影所造成的影响;
接下来计算图像的每个像素的梯度,目的是捕获轮廓信息,同时弱化光照的影响; -
计算梯度
使用Sobel算子计算每个像素的梯度幅值和方向
-
划分单元格(Cell)
将图像划分为小的单元格(如8×8像素),统计每个Cell内的梯度方向直方图(通常分9个bin) -
块(Block)归一化
将多个Cell组合成块(如2×2个Cell为一个Block),对块内直方图进行L2归一化,增强光照不变性。
- 生成HOG特征向量
将所有Block的特征串联成最终的特征向量
2 具体代码实现
2.1 特征点获取
#include <opencv2/opencv.hpp>
#include <vector>int main() {cv::Mat img = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);cv::resize(img, img, cv::Size(64, 128)); // 经典HOG输入尺寸(64x128)// 初始化HOG描述符cv::HOGDescriptor hog(cv::Size(64, 128), // 窗口大小cv::Size(16, 16), // 块大小cv::Size(8, 8), // 块步长(移动步长)cv::Size(8, 8), // 单元格大小9 // 方向bin数);// 计算HOG特征std::vector<float> descriptors;hog.compute(img, descriptors);std::cout << "HOG特征维度: " << descriptors.size() << std::endl;return 0;
}
输出特征维度:
2.2 HOG 特征可视化实现方法
-
计算每个单元格(Cell)的梯度直方图
-
提取每个Cell内像素的梯度方向和幅值。
-
按方向分bin统计幅值之和。
-
绘制方向直方图
-
在每个Cell中心绘制线段,线段方向代表bin方向,长度代表幅值。
#include <opencv2/opencv.hpp>
#include <vector>cv::Mat visualizeHOG(const cv::Mat& img, cv::Size cell_size = cv::Size(8, 8), int bin_num = 9) {cv::Mat gray;if (img.channels() == 3) {cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);} else {gray = img.clone();}// 计算梯度cv::Mat gx, gy;cv::Sobel(gray, gx, CV_32F, 1, 0);cv::Sobel(gray, gy, CV_32F, 0, 1);cv::Mat mag, ang;cv::cartToPolar(gx, gy, mag, ang, true);// 初始化可视化图像cv::Mat hog_image = cv::Mat::zeros(gray.size(), CV_8UC1);// 遍历每个Cell(确保不越界)for (int i = 0; i <= gray.rows - cell_size.height; i += cell_size.height) {for (int j = 0; j <= gray.cols - cell_size.width; j += cell_size.width) {cv::Rect cell_rect(j, i, cell_size.width, cell_size.height);cv::Mat cell_mag = mag(cell_rect);cv::Mat cell_ang = ang(cell_rect);// 计算直方图cv::Mat hist = cv::Mat::zeros(1, bin_num, CV_32F);for (int p = 0; p < cell_ang.rows; p++) {for (int q = 0; q < cell_ang.cols; q++) {float angle = cell_ang.at<float>(p, q);int bin = static_cast<int>(angle / (180 / bin_num)) % bin_num;hist.at<float>(0, bin) += cell_mag.at<float>(p, q);}}// 绘制方向线段cv::Point2f center(j + cell_size.width / 2.0f, i + cell_size.height / 2.0f);for (int k = 0; k < bin_num; k++) {float angle = (k * (180 / bin_num) + 90) * CV_PI / 180;float length = hist.at<float>(0, k) * 0.1f;cv::Point2f dir(length * cos(angle), length * sin(angle));cv::line(hog_image, center - dir / 2, center + dir / 2, cv::Scalar(255), 1);}}}return hog_image;
}int main() {cv::Mat img = cv::imread("pedestrian.jpg");cv::Mat hog_image = visualizeHOG(img);cv::imshow("HOG Visualization", hog_image);cv::waitKey(0);return 0;
}
也可以把特征点和原图叠加在一起
94 // 叠加原图和HOG结果95 cv::Mat overlay;96 float alpha = 0.7;97 cv::addWeighted(img, alpha, hog_color, 1 - alpha, 0, overlay);98 cv::imshow("HOG Overlay", overlay);
3 行人检测
3.1 基础函数介绍
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
)
parameters:
第一个参数:img,为需要检测的输入图像,img 可以是灰度图像,也可以是彩色图像;
第二个参数:foundLocations,存取检测到目标的位置信息;
第三个参数:hitThreshold,调用的时候可以不传入,是特征到 SVM 超平面的距离阈值;
第四个参数:winStride,也是可选参数,表示 HOG 检测窗口移动时的步长;
第五个参数:padding,可选参数,在原图的基础上添加边框像素,可以提高边缘目标的检测朱雀率;
第六个参数:scale,可选参数,默认是 1.05,可以修改 scale,数值越小处理的层数越多,检测的时间
就越长,配合上面的 winStride 合理调参,有助于精确检测又不太耗时。
第七个参数:finalThreshold,可选参数。
第八个参数:useMeanShiftGrouping,可选参数。
3.2 演示代码
cv::HOGDescriptor hog;
hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); // 加载预训练模型std::vector<cv::Rect> detections;
hog.detectMultiScale(img, detections); // 检测行人for (const auto& rect : detections) {cv::rectangle(img, rect, cv::Scalar(0, 255, 0), 2);
}
cv::imshow("people",img); // 显示图像
4 HOG的优缺点
优点:
- 对光照和微小形变鲁棒(归一化处理)。
- 能捕捉局部形状特征(梯度方向分布)。
缺点:
3. 计算量较大(尤其是高分辨率图像)。
4. 对遮挡敏感(依赖局部梯度统计)。
5 总结
核心用途:目标检测(行人、车辆等)、纹理分析。
OpenCV关键类:cv::HOGDescriptor。
典型流程:梯度计算 → Cell统计 → Block归一化 → 特征串联。
HOG虽逐渐被深度学习取代,但在资源受限的场景中仍是经典选择!