我正在尝试将一些Matlab代码迁移到OpenCV,并需要一个完全复制的梯度函数。我已经尝试过cv :: Sobel函数,但由于某种原因,结果cv :: Mat中的值与Matlab版本中的值不同。我需要将X和Y梯度分别存储在矩阵中以进行进一步的计算。
任何可以实现此目的的解决方法都很好。
我正在尝试将一些Matlab代码迁移到OpenCV,并需要一个完全复制的梯度函数。我已经尝试过cv :: Sobel函数,但由于某种原因,结果cv :: Mat中的值与Matlab版本中的值不同。我需要将X和Y梯度分别存储在矩阵中以进行进一步的计算。
任何可以实现此目的的解决方法都很好。
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(这是我们想要的)。
因此,在处理边界值时,我没有一个非常简洁的答案。只需尝试手动计算一阶导数...
Matlab在计算梯度时,对于内部行和边界行进行了不同的处理(当然,列也是如此)。在边界处,它是一个简单的前向差分gradY(1) = row(2) - row(1)
。内部行的梯度是通过中心差分计算的gradY(2) = (row(3) - row(1)) / 2
。
我认为你不能仅通过在OpenCV上运行单个卷积滤波器来实现相同的结果。使用cv::Sobel()
并设置ksize = 1
,然后处理边界(可以手动或应用[1 -1]过滤器)。
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;
cv::Sobel()
正是你想要的。如果你发布你所使用的代码(以及你期望它输出什么),我们可以查看并了解你的问题所在。 - Aurelius