【问题标题】:Matlab gradient equivalent in opencvopencv中的Matlab梯度等效
【发布时间】:2013-08-01 10:11:21
【问题描述】:

我正在尝试将一些代码从 Matlab 迁移到 Opencv,并且需要梯度函数的精确副本。我尝试了 cv::Sobel 函数,但由于某种原因,生成的 cv::Mat 中的值与 Matlab 版本中的值不同。我需要单独矩阵中的 X 和 Y 梯度以进行进一步计算。

任何可以实现这一点的解决方法都会很棒

【问题讨论】:

  • cv::Sobel() 正是您想要的。如果您发布您使用的代码(以及您期望它输出的代码),我们可以查看并了解您的问题。

标签: matlab opencv gradient derivative


【解决方案1】:

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(这是我们想要的)。

至于边界值,我没有一个非常简洁的答案。只需尝试手动计算一阶导数...

【讨论】:

  • 哦,其实你可以使用copyMakeBorder()来扩展一个具有特定常量值的Mat的边框。
  • 这是怎么得到这么多赞的??? “Sobel 只能计算二阶导数” No no no! Sobel 估计的是 一阶 导数,而不是二阶导数。
【解决方案2】:

你必须用参数调用 Sobel 2 次:

xorder = 1, yorder = 0

xorder = 0, yorder = 1

您必须选择合适的内核大小。

documentation

可能仍然是 MatLab 实现不同,理想情况下您应该检索那里使用的内核...

编辑:

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

【讨论】:

  • 我仍然不确定使用什么边界类型会产生类似的结果。对这些内容进行更详细的解释会很有帮助
  • 我正在寻找matlab梯度和cv::Sobel之后的矩阵精确结果。
  • @ArpanShah 是否清楚边框类型仅影响图像边框上的像素(因此,如果内核大小为 3,则为第一行、第一列、最后一行、最后一列,否则为前 2 行, 前 2 列等,如果您的内核大小为 5,依此类推)。仅当您计算 Sobel 的区域不是整个图像,而是其中的某些内容,超出区域限制的信息仍然有意义时,才使用与默认值不同的内容
  • 我对脉冲矩阵做了一个简单的测试,如下所示:[0 0 0; 0 1 0; 0 1 0],当我在 Matlab 中运行渐变和在 opencv 中运行 Sobel 时,我得到了不同的结果
  • @ArpanShah 那是因为 Sobel 有一个不同的内核,例如 [-1 -2 -1; 0 0 0; 1 2 1]。如果你想使用特定的内核,你的函数是filter2D,我已经更新了我的答案
【解决方案3】:

Matlab 以不同方式计算内部行和边界行的梯度(当然,列也是如此)。在边界,这是一个简单的前向差异gradY(1) = row(2) - row(1)。内部行的梯度由中心差 gradY(2) = (row(3) - row(1)) / 2 计算。

我认为仅在 OpenCV 中的整个矩阵上运行单个卷积滤波器无法达到相同的结果。将cv::Sobel()ksize = 1 一起使用,然后处理边框(手动或通过应用[ 1 -1 ] 过滤器)。

【讨论】:

    【解决方案4】:

    裴的回答部分正确。 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;
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 这里是解决方案。使用OpenCV的convertTo函数,将CV_8U类型转换为CV_32FC1,当我们完成处理后,再次将CV_32FC1类型转换为CV_8U!
      猜你喜欢
      • 2015-04-11
      • 2013-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      相关资源
      最近更新 更多