使用skimage.feature.hog计算梯度幅值结果与手动计算不符的问题
问题
我尝试计算如下数组中值为100的像素的总梯度幅值:
img=[[150,50,121],[12,100,25],[201,243,244]]
编写的代码如下:
from skimage.feature import hog from skimage import data, exposure fdt, hog_image = hog([[150,50,121],[12,100,25],[201,243,244]], orientations=2, pixels_per_cell=(1, 1), cells_per_block=(1, 1), visualize=True) print(hog_image)
运行后得到的结果为43,但通过如下手动计算得到的结果为193.437:
import math math.sqrt((25-12)**2+(243-50)**2)
两者结果不一致,使用的是Python 3版本,请问哪里出错了?
错误原因及解决办法
1. HOG的梯度计算逻辑和手动计算完全不同
你手动计算用的是相邻像素直接差值(右减左、下减上),但HOG默认使用Sobel算子计算梯度。Sobel通过3x3卷积核卷积图像得到x/y方向梯度:
- x方向(垂直)卷积核:
[-1, 0, 1] [-2, 0, 2] [-1, 0, 1] - y方向(水平)卷积核:
[-1,-2,-1] [ 0, 0, 0] [ 1, 2, 1]
对中心像素(1,1),用Sobel计算的x方向梯度为(121+50+244) - (150+24+201) = 40,y方向梯度为(201+486+244) - (150+100+121) = 560,梯度幅值为sqrt(40²+560²) ≈ 561.427,这才是HOG底层的原始梯度值。
2. HOG可视化图像经过了归一化处理
你打印的hog_image是用于可视化的结果,skimage会自动对其做曝光归一化(通过exposure.rescale_intensity),将数值缩放到适合显示的范围,因此这个值不是原始梯度幅值,不能直接代表像素的梯度大小。
3. HOG核心是方向直方图,而非单个像素梯度
HOG的输出fdt是方向直方图特征,即使设置pixels_per_cell=(1,1),它也会将每个像素的梯度分配到指定方向区间(你设置了orientations=2),再经过块归一化,最终特征向量和原始梯度幅值完全不是一回事。
正确计算单个像素梯度幅值的方法
如果要直接计算图像中单个像素的梯度幅值,应该用Sobel算子直接计算,而非通过HOG:
from skimage.filters import sobel_h, sobel_v import numpy as np img = np.array([[150,50,121],[12,100,25],[201,243,244]], dtype=np.float64) # 计算x(垂直)、y(水平)方向梯度 gx = sobel_v(img) gy = sobel_h(img) # 计算梯度幅值 gradient_magnitude = np.sqrt(gx**2 + gy**2) # 输出中心像素(1,1)的梯度幅值 print(gradient_magnitude[1, 1])
运行结果约为561.427,这才是HOG底层使用的原始梯度幅值。
内容的提问来源于stack exchange,提问作者sakeesh
相关产品推荐
相关产品推荐

