openCV:图像的梯度

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了openCV:图像的梯度相关的知识,希望对你有一定的参考价值。

参考技术A Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 它Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

我们假设被作用图像为 I.然后进行如下的操作:

由sobel算子的卷积核可知,它是固定右减左的,白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以实战时通常要做绝对值处理

与水平方向同理

将两个方向的结果整合到一起,有两种方法:

当内核大小为 3 时, 我们的Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值而已)。 为解决这一问题,OpenCV提供了Scharr 函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确,其内核是这样的:

因为Sobel算子结合了高斯平滑和分化(differentiation),因此结果会具有更多的抗噪性。大多数情况下,我们使用sobel函数

Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad()的散度div()。因此如果f是二阶可微的实函数,则f的拉普拉斯算子定义为:

根据图像处理的原理我们知道,二阶导数可以用来进行检测边缘 。 因为图像是 “二维”, 我们需要在两个方向进行求导。使用Laplacian算子将会使求导过程变得简单。

Laplacian 算子的定义:

scharr算子比sobel算子更关注细节,边界信息更细致。
laplacian算子单独使用效果并不是很好,要结合其它的算法一起使用。

使用 OpenCV 的 Sobel 运算计算图像梯度方向

【中文标题】使用 OpenCV 的 Sobel 运算计算图像梯度方向【英文标题】:Calculating image gradient direction using OpenCV's Sobel operation 【发布时间】:2013-11-20 15:54:53 【问题描述】:

我正在尝试使用 OpenCV 的 Sobel 方法的结果来确定图像梯度方向。

我知道这应该是一项非常简单的任务,我想我理解了理论,但实现它比我想象的更具挑战性。

我希望渐变方向在 0-360 度之间,但是我的代码显示所有渐变都在 180 到 270 度之间。

我提交了此代码的先前版本,其中包含整数除法问题。我已经解决了这个问题,但它并没有解决方向角度受限的问题。

我已经浏览了所有代码,但我就是看不出哪里出错了?谁能发现我的错误?

谢谢。

void getGradients(IplImage* original, cv::Mat* gradArray)

cv::Mat original_Mat(original, true);

// Convert it to gray
cv::cvtColor( original_Mat, original_Mat, CV_RGB2GRAY );
//cv::blur(original_Mat, original_Mat, cv::Size(7,7));

/// Generate grad_x and grad_y
cv::Mat grad_x = cv::Mat::zeros(original->height, original->width, CV_16S); 
cv::Mat grad_y = cv::Mat::zeros(original->height, original->width, CV_16S);

/// Gradient X
cv::Sobel(original_Mat, grad_x, CV_16S, 1, 0, 3);

/// Gradient Y
cv::Sobel(original_Mat, grad_y, CV_16S, 0, 1, 3);

uchar* pixelX = grad_x.data;
uchar* pixelY = grad_y.data;
uchar* grad1 = gradArray[0].data;
uchar* grad2 = gradArray[1].data;
uchar* grad3 = gradArray[2].data;
uchar* grad4 = gradArray[3].data;
uchar* grad5 = gradArray[4].data;
uchar* grad6 = gradArray[5].data;
uchar* grad7 = gradArray[6].data;
uchar* grad8 = gradArray[7].data;
int count = 0;
int min = 999999;
int max = -1;

for(int i = 0; i < grad_x.rows * grad_x.cols; i++) 

        double directionRAD = atan2(pixelY[i], pixelX[i]);
        int directionDEG = (int)(180 + directionRAD / M_PI * 180);

        if(directionDEG < min)min = directionDEG;
        if(directionDEG > max)max = directionDEG;

        if(directionDEG >= 0 && directionDEG <= 45)          grad1[i] = 255; count++;         
        if(directionDEG >= 45 && directionDEG <= 90)         grad2[i] = 255; count++;         
        if(directionDEG >= 90 && directionDEG <= 135)        grad3[i] = 255; count++;         
        if(directionDEG >= 135 && directionDEG <= 190)       grad4[i] = 255; count++;         
        if(directionDEG >= 190 && directionDEG <= 225)       grad5[i] = 255; count++;         
        if(directionDEG >= 225 && directionDEG <= 270)       grad6[i] = 255; count++;     
        if(directionDEG >= 270 && directionDEG <= 315)       grad7[i] = 255; count++;
        if(directionDEG >= 315 && directionDEG <= 360)       grad8[i] = 255; count++;

        if(directionDEG < 0 || directionDEG > 360)
        
            cout<<"Weird gradient direction given in method: getGradients.";
        


【问题讨论】:

【参考方案1】:

grad_xgrad_y 是 CV_16SC1 类型的 Mats,即其中的每个像素占用两个字节。

但是,您将 pixelXpixelY 声明为指向 8 位字节的指针。因此pixelX[1]是第一个梯度的第二个字节,而不是第二个梯度。

你需要

short* pixelX = grad_x.ptr<short>(0);
short* pixelY = grad_y.ptr<short>(0);

【讨论】:

非常感谢。由于您的解释,我终于明白了这个问题。我根本没有考虑过这样的问题,希望将来我能够更好地发现这样的问题。再次感谢。 另外,当您遇到非连续的 Mat 时,您直接在 .data 上进行迭代的习惯会影响您 - 请参阅 docs.opencv.org/doc/tutorials/core/how_to_scan_images/… 谢谢。是的,实际上我通常以与示例类似的方式执行此操作,但在这种情况下,我知道它是连续的,所以只是快速而肮脏地进行;)【参考方案2】:

问题来了

uchar* pixelX = grad_x.data;
uchar* pixelY = grad_y.data;

这里

 double directionRAD = atan2(pixelY[i], pixelX[i]);

你不用 abs(),而是使用无符号指针。这就是为什么你不能得到 x 或 y 负数的原因。

应该是:

short* pixelX = (short*) grad_x.data;
short* pixelY = (short*) grad_y.data;

 double directionRAD = atan2((double)pixelY[i], (double)pixelX[i]);

【讨论】:

非常感谢。似乎我的问题是缺乏对数据表示和正确使用指针的理解。看来我需要重温这些东西了!再次感谢。

以上是关于openCV:图像的梯度的主要内容,如果未能解决你的问题,请参考以下文章

图像梯度(opencv-c++)

图像梯度(opencv-c++)

OpenCV 图像梯度算子

OpenCV中的图像处理 —— 图像梯度+Canny边缘检测+图像金字塔

opencv python:图像梯度

openCV:图像的梯度