什么是梯度方向和梯度大小?

47

我正在学习计算机视觉模块中的边缘检测。我试图理解梯度方向和梯度大小的含义。

2个回答

120

根据Dima在他的answer中所解释的,为了更好地理解图像处理领域中的梯度,您应该熟悉数学概念gradient

我的答案基于mevatron对这个questionanswer

这里有一个简单的初始图像,一个白色圆盘在黑色背景上:

enter image description here

你可以计算这张图片的梯度近似值。就像Dima在他的回答中所解释的那样,梯度有两个分量,一个是水平分量,一个是垂直分量。
下面的图片展示了水平分量:

enter image description here

它展示了图像中灰度级在水平方向上的变化情况(即从左到右扫描图像的正x方向),这种变化被“编码”在图像水平分量的灰度级中:均值灰度表示无变化,亮度级别表示从暗值到明值的变化,暗度级表示从明值到暗值的变化。因此,在上面的图像中,你可以看到圆的左侧有更亮的值,因为在你的初始图像的左侧有黑色到白色的过渡,这给出了圆盘的左边缘;同样,在上面的图像中,你可以看到圆的右侧有更暗的值,因为在初始图像的右侧有白色到黑色的转换,这给出了圆盘的右边缘。在上述图像中,圆盘的内部部分和背景的灰度均值是一致的,因为圆盘内部和背景没有变化。
我们可以对于垂直分量做类似的观察,它展示了图像在竖直方向上的变化情况,即从上到下扫描图像。

enter image description here

现在,您可以将这两个组件结合起来,以获得梯度的大小和方向。
以下图像是梯度的大小:

enter image description here

在上面的图像中,初始图像的变化是通过灰度级进行编码的:白色表示初始图像中发生了很大的变化,而黑色则表示根本没有变化。因此,当您查看梯度幅度图像时,可以说“如果图像很亮,则表示初始图像中有很大的变化;如果很暗,则表示没有变化或变化非常小”。
下面的图像是梯度的方向:

enter image description here

在上面的图像中,方向再次被编码为灰度级别:您可以将方向视为箭头的角度,该箭头从图像的暗部指向图像的亮部;角度是指xy框架中的角度,其中x从左到右运行,而y从上到下运行。在上面的图像中,您可以看到从黑色(零度)到白色(360度)的所有灰度级别。我们可以使用颜色对信息进行编码:

enter image description here

在上面的图像中,信息是这样编码的:
红色:角度在0到90度之间
青色:角度在90到180度之间
绿色:角度在180到270度之间
黄色:角度在270到360度之间
以下是生成上述图像的C++ OpenCV代码。
请注意,为了计算方向,我使用cv::phase函数,如doc所解释的那样,当梯度的垂直分量和水平分量都为零时,该函数会给出一个0度的角度;这可能很方便,但从数学角度来看是错误的,因为当两个分量都为零时,方向未定义,而在浮点C++类型中保留方向的唯一有意义的值是NaN
这是明显错误的,因为0度方向已经与水平边缘相关联,不能用于表示没有边缘的区域,因此方向在该区域中是没有意义的。
// original code by https://stackoverflow.com/users/951860/mevatron
// see https://dev59.com/8WXWa4cB1Zd3GeqPLEKS#11157426
// https://stackoverflow.com/users/15485/uvts-cvs added the code for saving x and y gradient component 

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

Mat mat2gray(const cv::Mat& src)
{
    Mat dst;
    normalize(src, dst, 0.0, 255.0, cv::NORM_MINMAX, CV_8U);

    return dst;
}

Mat orientationMap(const cv::Mat& mag, const cv::Mat& ori, double thresh = 1.0)
{
    Mat oriMap = Mat::zeros(ori.size(), CV_8UC3);
    Vec3b red(0, 0, 255);
    Vec3b cyan(255, 255, 0);
    Vec3b green(0, 255, 0);
    Vec3b yellow(0, 255, 255);
    for(int i = 0; i < mag.rows*mag.cols; i++)
    {
        float* magPixel = reinterpret_cast<float*>(mag.data + i*sizeof(float));
        if(*magPixel > thresh)
        {
            float* oriPixel = reinterpret_cast<float*>(ori.data + i*sizeof(float));
            Vec3b* mapPixel = reinterpret_cast<Vec3b*>(oriMap.data + i*3*sizeof(char));
            if(*oriPixel < 90.0)
                *mapPixel = red;
            else if(*oriPixel >= 90.0 && *oriPixel < 180.0)
                *mapPixel = cyan;
            else if(*oriPixel >= 180.0 && *oriPixel < 270.0)
                *mapPixel = green;
            else if(*oriPixel >= 270.0 && *oriPixel < 360.0)
                *mapPixel = yellow;
        }
    }

    return oriMap;
}

int main(int argc, char* argv[])
{
    Mat image = Mat::zeros(Size(320, 240), CV_8UC1);
    circle(image, Point(160, 120), 80, Scalar(255, 255, 255), -1, CV_AA);

    imshow("original", image);

    Mat Sx;
    Sobel(image, Sx, CV_32F, 1, 0, 3);

    Mat Sy;
    Sobel(image, Sy, CV_32F, 0, 1, 3);

    Mat mag, ori;
    magnitude(Sx, Sy, mag);
    phase(Sx, Sy, ori, true);

    Mat oriMap = orientationMap(mag, ori, 1.0);

    imshow("x", mat2gray(Sx));
    imshow("y", mat2gray(Sy));

    imwrite("hor.png",mat2gray(Sx));
    imwrite("ver.png",mat2gray(Sy));

    imshow("magnitude", mat2gray(mag));
    imshow("orientation", mat2gray(ori));
    imshow("orientation map", oriMap);
    waitKey();

    return 0;
}

9
好的,谢谢您的信任。华丽的回答,继续保持。 - Vinoj John Hosan
但是,从梯度的大小获取梯度x和梯度y是否可能? - Abc
@Abc 假设x是某个特定像素的水平梯度分量,而y是垂直分量。那么该像素的梯度幅值MM=sqrt(pow(x,2)+pow(y,2)),如果只知道M,似乎很难推出xy - Alessandro Jacopson
如果我不为这个答案投票,我会变成疯子。对于初学者来说非常棒。 - Whoami
如果您能添加一张带有小箭头的渐变方向图片就太好了。 - Kentzo

25
函数的梯度是关于两个变量x和y的偏导数的向量。因此,如果你的函数是f(x,y),梯度就是向量(f_x, f_y)。图像是关于(x,y)的离散函数,所以你也可以谈论图像的梯度。
图像的梯度有两个分量:x方向的导数和y方向的导数。因此,你可以将其看作在每个像素点上定义的向量(f_x, f_y)。这些向量有一个方向atan(f_y / fx)和一个大小sqrt(f_x^2 + f_y^2)。因此,你可以将图像的梯度表示为x导数图像和y导数图像,或者作为方向图像和大小图像。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接