HOG(Histogram of Oriented Gradients)是一种在计算机视觉领域广泛使用的特征描述子,特别适用于人体检测和目标识别。我第一次接触HOG是在开发一个安防监控项目时,需要从复杂的背景中准确识别行人。当时试过多种算法,最终发现HOG+SVM的组合在准确率和性能之间取得了最佳平衡。
这个特征描述方法的核心思想很简单:物体的外观和形状能够通过局部区域的梯度方向分布很好地描述。2005年Dalal和Triggs首次将HOG应用于行人检测,从此改变了计算机视觉领域的目标检测格局。如今,虽然深度学习大行其道,但HOG因其计算效率高、无需训练的特点,仍在许多实时性要求高的场景中占据重要地位。
HOG的第一步是计算图像的梯度。在OpenCV中,我们通常使用Sobel算子来获取水平和垂直方向的梯度:
python复制import cv2
import numpy as np
# 读取图像并转为灰度
img = cv2.imread('pedestrian.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 计算x和y方向的梯度
gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=1)
# 计算梯度幅值和方向
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)
这里有几个关键点需要注意:
将图像划分为小的空间区域(称为"细胞"),通常是8x8像素。对每个细胞计算梯度方向的直方图:
python复制# 假设我们有一个8x8的细胞
cell_mag = mag[0:8, 0:8]
cell_angle = angle[0:8, 0:8]
# 创建9bin的直方图(0-180度,无符号梯度)
hist = np.zeros(9)
# 计算每个像素的贡献
for i in range(8):
for j in range(8):
# 确定所属的bin
bin_idx = int(cell_angle[i,j] / 20) % 9
hist[bin_idx] += cell_mag[i,j]
这个过程中有几个技巧:
为了对光照和阴影变化具有鲁棒性,我们需要对直方图进行归一化。将多个细胞(通常是2x2)组合成一个"块",对块内的所有直方图进行归一化:
python复制# 假设我们有4个细胞的直方图
hist1 = np.random.rand(9) # 模拟数据
hist2 = np.random.rand(9)
hist3 = np.random.rand(9)
hist4 = np.random.rand(9)
# 拼接成块特征
block_feature = np.concatenate([hist1, hist2, hist3, hist4])
# L2-Hys归一化
epsilon = 1e-7
norm = np.sqrt(np.sum(block_feature**2) + epsilon**2)
normalized = block_feature / norm
# 阈值截断(Hys表示先归一化再截断)
normalized = np.minimum(normalized, 0.2)
norm = np.sqrt(np.sum(normalized**2) + epsilon**2)
normalized = normalized / norm
归一化方法有多种选择,L2-Hys(L2范数+截断+重新归一化)通常效果最好。
OpenCV提供了完整的HOG实现,通过HOGDescriptor类可以方便地使用:
python复制# 初始化HOG描述符
winSize = (64,128) # 检测窗口大小
blockSize = (16,16) # 块大小
blockStride = (8,8) # 块步长
cellSize = (8,8) # 细胞大小
nbins = 9 # 直方图bin数量
hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins)
# 计算HOG特征
features = hog.compute(img)
参数选择经验:
理解HOG特征的一个好方法是可视化:
python复制import matplotlib.pyplot as plt
# 计算HOG并获取可视化图像
hog_image = hog.compute(img, winStride=(8,8), padding=(0,0))
hog_image = hog.describe(img)
# 显示原图和HOG特征
plt.figure(figsize=(10,5))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.subplot(122), plt.imshow(hog_image, cmap='gray')
plt.title('HOG Features')
plt.show()
可视化时可以看到,HOG特征清晰地勾勒出了物体的边缘和轮廓方向。
OpenCV自带了一个基于HOG+SVM的行人检测器:
python复制# 初始化检测器
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
# 检测行人
boxes, weights = hog.detectMultiScale(img, winStride=(4,4), padding=(8,8), scale=1.05)
# 绘制检测框
for (x,y,w,h) in boxes:
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
参数调优建议:
行人可能出现在图像的不同位置和大小,因此需要多尺度检测:
python复制# 设置检测参数
found, _ = hog.detectMultiScale(img,
winStride=(8,8),
padding=(32,32),
scale=1.05,
finalThreshold=2,
hitThreshold=0,
useMeanshiftGrouping=False)
# 非极大值抑制
def non_max_suppression(boxes, overlapThresh):
# 实现略...
filtered_boxes = non_max_suppression(boxes, 0.3)
在实际项目中,我发现scale=1.05和finalThreshold=2的组合能在精度和速度间取得不错平衡。
HOG的计算可能成为性能瓶颈,以下是几种优化方法:
python复制# 使用UMat加速
img_umat = cv2.UMat(img)
features = hog.compute(img_umat)
# GPU加速(需要OpenCV编译时启用CUDA)
hog_gpu = cv2.cuda.HOGDescriptor_create()
features_gpu = hog_gpu.compute(cv2.cuda_GpuMat(img))
问题1:漏检率高
问题2:误检多
问题3:检测框不稳定
问题4:小尺寸行人检测效果差
虽然OpenCV提供了预训练模型,但针对特定场景训练自己的模型效果更好:
python复制# 准备正负样本
pos_dir = 'dataset/positive/'
neg_dir = 'dataset/negative/'
# 提取HOG特征
def extract_features(image):
# 实现略...
return features
# 训练SVM
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.train(trainData, cv2.ml.ROW_SAMPLE, labels)
训练时要注意:
虽然CNN在很多任务上超越了HOG,但两者结合可以发挥各自优势:
python复制# 结合HOG和CNN特征的示例
hog_features = hog.compute(img)
cnn_features = model.predict(img)
combined_features = np.concatenate([hog_features.flatten(), cnn_features.flatten()])
在实际项目中,这种混合方法往往能在保持实时性的同时提高准确率。
经过多个项目的实践,我总结出以下参数选择经验:
细胞大小:
块大小:
块步长:
直方图bin数:
检测参数:
重要提示:参数优化应该基于验证集进行系统评估,而不是盲目尝试。建议使用网格搜索或贝叶斯优化方法寻找最优参数组合。
在一个商场人流统计项目中,我们使用HOG进行行人检测:
挑战:
解决方案:
结果:
在一个ADAS项目中,HOG用于前置的行人检测:
特殊要求:
技术方案:
优化成果:
对于想深入掌握HOG的开发者,可以考虑以下方向:
改进的HOG变体:
硬件加速:
多模态融合:
领域自适应:
python复制# CoHOG实现示例
def compute_cohog(gx, gy, cell_size=8):
# 计算联合梯度直方图
pass
在实际开发中,我发现结合运动信息的HOG对视频分析特别有效,能显著减少静态背景导致的误检。