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_x
和 grad_y
是 CV_16SC1 类型的 Mats,即其中的每个像素占用两个字节。
但是,您将 pixelX
和 pixelY
声明为指向 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:图像的梯度的主要内容,如果未能解决你的问题,请参考以下文章