OpenCV中的Matlab梯度等效函数

3

我正在尝试将一些Matlab代码迁移到OpenCV,并需要一个完全复制的梯度函数。我已经尝试过cv :: Sobel函数,但由于某种原因,结果cv :: Mat中的值与Matlab版本中的值不同。我需要将X和Y梯度分别存储在矩阵中以进行进一步的计算。

任何可以实现此目的的解决方法都很好。


cv::Sobel() 正是你想要的。如果你发布你所使用的代码(以及你期望它输出什么),我们可以查看并了解你的问题所在。 - Aurelius
5个回答

6

Sobel只能计算图像像素的二阶导数,这不是我们想要的。

(f(i+1,j) + f(i-1,j) - 2f(i,j)) / 2

我们需要的是

(f(i+i,j)-f(i-1,j)) / 2

因此我们需要应用

Mat kernelx = (Mat_<float>(1,3)<<-0.5, 0, 0.5);
Mat kernely = (Mat_<float>(3,1)<<-0.5, 0, 0.5);
filter2D(src, fx, -1, kernelx)
filter2D(src, fy, -1, kernely);

Matlab将边界像素与内部像素区分对待。因此,上面的代码在边界值处是错误的。可以使用BORDER_CONSTANT将边界值延伸出一个常数值,不幸的是OpenCV默认的常数值为-1,而且不能更改为0(这是我们想要的)。

因此,在处理边界值时,我没有一个非常简洁的答案。只需尝试手动计算一阶导数...


1
哦,实际上,您可以使用copyMakeBorder()函数来使用特定常量值扩展Mat的边框。 - Pei Guo
1
这怎么会有这么多赞??? “Sobel只能计算二阶导数” 不不不! Sobel估计的是一阶导数,而不是二阶。 - Cris Luengo

0

你需要调用Sobel两次,参数如下:

xorder = 1, yorder = 0

并且

xorder = 0, yorder = 1

你需要选择适当的内核大小。

请参阅文档

可能MatLab实现不同,最好检索使用了哪个内核...

编辑:

如果您需要指定自己的内核,可以使用更通用的filter2D。您的目标深度将是CV_16S(16位有符号)。


我仍然不确定使用哪种borderType会产生类似的结果。更详细的解释会真正有所帮助。 - Arpan Shah
我正在寻找使用Matlab梯度和cv::Sobel后得到的矩阵精确结果。 - Arpan Shah
@ArpanShah 是否清楚边框类型仅影响图像边框上的像素(因此,如果您有内核大小为3,则仅影响第一行、第一列、最后一行、最后一列,否则影响前2行、前2列等,如果您有内核大小为5等)。仅当您计算Sobel的区域不是整个图像,而是其中的某些内容时,才使用与默认值不同的内容,在该区域限制之外的信息仍然具有意义。 - Antonio
我对一个脉冲矩阵进行了简单的测试,例如以下内容:[0 0 0; 0 1 0; 0 1 0],当我在Matlab中运行梯度和在OpenCV中运行Sobel时,得到了不同的结果。 - Arpan Shah
@ArpanShah 这是因为Sobel有不同的卷积核,例如[-1 -2 -1; 0 0 0; 1 2 1]。如果您想使用特定的卷积核,可以使用filter2D函数,我已经更新了我的答案。 - Antonio
嘿,我发现大小为1的内核镜像Matlab。然而,边界值仍然不匹配。您有任何想法如何使其匹配吗? - Arpan Shah

0
Jorrit的回答部分正确。在某些情况下,方向导数的值可能为负数,MATLAB将保留这些负数,但OpenCV Mat会将负数设置为0。

这里是解决方案。使用OpenCV的convertTo函数,将CV_8U类型转换为CV_32FC1类型,在处理完成后,再将CV_32FC1类型转换回CV_8U类型! - Harry Lee

0

Matlab在计算梯度时,对于内部行和边界行进行了不同的处理(当然,列也是如此)。在边界处,它是一个简单的前向差分gradY(1) = row(2) - row(1)。内部行的梯度是通过中心差分计算的gradY(2) = (row(3) - row(1)) / 2

我认为你不能仅通过在OpenCV上运行单个卷积滤波器来实现相同的结果。使用cv::Sobel()并设置ksize = 1,然后处理边界(可以手动或应用[1 -1]过滤器)。


0

Pei的回答部分正确。Matlab使用以下计算来处理边界:

G(:,1) = A(:,2) - A(:,1); G(:,N) = A(:,N) - A(:,N-1);

因此,使用以下OpenCV代码完成梯度:

static cv::Mat kernelx = (cv::Mat_<double>(1, 3) << -0.5, 0, 0.5);
static cv::Mat kernely = (cv::Mat_<double>(3, 1) << -0.5, 0, 0.5);
cv::Mat fx, fy;

cv::filter2D(Image, fx, -1, kernelx, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE);
cv::filter2D(Image, fy, -1, kernely, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE);

fx.col(fx.cols - 1) *= 2;
fx.col(0) *= 2;
fy.row(fy.rows - 1) *= 2;
fy.row(0) *= 2;

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