摘要: 通过调用 magnitude / phase / cartToPolar, 分别实现梯度幅值/角度的单独计算和同时计算, 并输出/显示. 需要说明的是, 示例函数可适用于三通道图像, 但计算三通道图像的梯度意义不大, 这里限定为单通道.


计算图像差分

参考来源: OpenCV Tutorials: Sobel Derivatives

Sobel 算子

  • Sobel算子是高斯平滑和微分操作的结合体, 能够更好的抗噪;
  • 使用扩展的Sobel算子可以计算一阶,二阶, 三阶或混合图像的差分;
  • x 方向指的是 Horizontal changes
  • y 方向指的是 Vertical changes
    x : [ <mstyle displaystyle="false" scriptlevel="0"> 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 2 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 2 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 1 </mstyle> ] , y : [ <mstyle displaystyle="false" scriptlevel="0"> 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 2 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 1 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 2 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 1 </mstyle> ] x: \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix} , \qquad y: \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix} x:121000+1+2+1,y:10+120+210+1

Scharr 算子

  • Sobel 算子计算的差分的近似值. 当ksize=3时, 可能产生较明显的误差;
  • Scharr 算子是Sobel算子的优化. OpenCV中只能使用ksize=3, 速度一致, 但结果更准确.

x : [ <mstyle displaystyle="false" scriptlevel="0"> 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 10 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 10 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 3 </mstyle> ] , y : [ <mstyle displaystyle="false" scriptlevel="0"> 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 10 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> 0 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 3 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 10 </mstyle> <mstyle displaystyle="false" scriptlevel="0"> + 3 </mstyle> ] x: \begin{bmatrix} -3 & 0 & +3 \\ -10 & 0 & +10 \\ -3 & 0 & +3 \end{bmatrix} , \qquad y: \begin{bmatrix} -3 & -10 & -3 \\ 0 & 0 & 0 \\ +3 & +10 & +3 \end{bmatrix} x:3103000+3+10+3,y:30+3100+1030+3

差分显示

void imshowDifferentiation(Mat img)
{
    assert(img.channels() == 1);
    Mat dx, dy, value;
    Sobel(img, dx, CV_32F, 1, 0, 3);
    Sobel(img, dy, CV_32F, 0, 1, 3);
    // 显示差分图像, 包含负值, 需要转换尺度
    convertScaleAbs(dx, dx);
    imshow("dx", dx);
    convertScaleAbs(dy, dy);
    imshow("dy", dy);
}

计算梯度幅值 magnitude

  • 一般认为, 梯度即为梯度的幅值大小, 但为了避免歧义, 这里统称梯度幅值, 梯度方向角
  • 计算公式如下:
    G = G x 2 + G y 2 G = \sqrt{ G_{x}^{2} + G_{y}^{2} } G=Gx2+Gy2
  • 代码示例:
void calculateGradientValue(Mat img)
{
    assert(img.channels() == 1);
    Mat dx, dy, value;
    Sobel(img, dx, CV_32F, 1, 0, 3);
    Sobel(img, dy, CV_32F, 0, 1, 3);
    magnitude(dx, dy, value);
    // 显示梯度幅值图像
    value.convertTo(value, CV_8U, 1);
    imshow("magnitude", value);
}

计算梯度方向角 phase

  • 计算公式如下:
    θ = arctan G y G x \theta = \arctan \frac{ G_y}{ G_x } θ=arctanGxGy
  • 代码示例:
void calculateGradienAngle(Mat img)
{
    assert(img.channels() == 1);
    Mat dx, dy, angle;
    Sobel(img, dx, CV_32F, 1, 0, 3);
    Sobel(img, dy, CV_32F, 0, 1, 3);
    // 计算梯度角度(弧度制)
    phase(dx, dy, angle);
    cout << "梯度方向角(弧度制): " << angle(Rect(0, 0, 10, 1)) << endl;
    // 计算梯度角度(角度制)
    phase(dx, dy, angle, true);
    cout << "梯度方向角(角度制): " << angle(Rect(0, 0, 10, 1)) << endl;
}

同时计算梯度幅值和方向角

void calculateGradient(Mat img)
{
    assert(img.channels() == 1);
    Mat dx, dy, value, angle;
    Sobel(img, dx, CV_32F, 1, 0, 3);
    Sobel(img, dy, CV_32F, 0, 1, 3);
    // 计算梯度幅值和角度(弧度制)
    cartToPolar(dx, dy, value, angle);
    cout << "梯度方向角(弧度制): " << angle(Rect(0, 0, 10, 1)) << endl;
    // 计算梯度幅值和角度(角度制)
    cartToPolar(dx, dy, value, angle, true);
    cout << "梯度方向角(角度制): " << angle(Rect(0, 0, 10, 1)) << endl;
    // 显示梯度幅值图像
    value.convertTo(value, CV_32F, 1. / 255);
    imshow("cartToPolar", value);
}

HOG特征

限于篇幅,请访问 https://blog.csdn.net/Augurlee/article/details/105034336