1

这似乎是一个非常简单的请求,但我发现问题有点微妙。我只想要 X 和 Y 导数的 8 位无符号版本。源图像也是 8 位无符号的。

这是我使用过的一种方法:

  cv::Mat dx, dy;
  cv::Sobel(img, dx, CV_16S, 1, 0);
  cv::Sobel(img, dy, CV_16S, 0, 1);
  dx = cv::abs(dx);
  dy = cv::abs(dy);
  cv::normalize(dx, dx8u, 0x00, 0xFF, cv::NORM_MINMAX, CV_8U);
  cv::normalize(dy, dy8u, 0x00, 0xFF, cv::NORM_MINMAX, CV_8U);

我真正不喜欢的是normalize函数的使用。好像很浪费。如果我尝试将 Sobel 运算符直接计算为 8 位图像(通过设置ddepth-1),它似乎会“切断”所有负值。如果我尝试计算绝对值然后除以 8(将 Sobel 算子的绝对值近似映射到 [0, 255]),它仍然是 16 位图像,我需要将其转换为 8 位,即再次显得浪费。有没有更好的方法来做到这一点?

我想这个问题的一个重要方面是如何对 16 位签名图像执行操作,并以某种方式将结果直接放入 8 位图像中。这样,您就不必在事后进行额外的转换。

更新:

这是最终的解决方案:

  cv::Mat dx, dy;
  cv::Sobel(m, dx, CV_16S, 1, 0);
  cv::Sobel(m, dy, CV_16S, 0, 1);
  cv::convertScaleAbs(dx / 8, dx);
  cv::convertScaleAbs(dy / 8, dy);
4

1 回答 1

4

如果您只需要梯度的绝对值,无需任何归一化,您可以使用cv::convertScaleAbs(). 这将找到绝对值并一次性转换为 8 位无符号类型。

cv::Mat dx, dy;
cv::Sobel(img, dx, CV_16S, 1, 0);
cv::Sobel(img, dy, CV_16S, 0, 1);
cv::convertScaleAbs(dx, dx);
cv::convertScaleAbs(dy, dy);

请注意,此方法仅计算导数的绝对值,并不会将结果归一化为[0, 255]

如果您需要标准化结果,那么您发布的代码似乎是最好的方法。至于您的方法看起来“浪费”,请确保在担心“浪费”之前进行多次数据传递是一个已证明的性能瓶颈。

不幸的是,OpenCV 对减少数据传递的数量没有太多支持。您可以修改源代码来执行此操作,或者如果确实需要,您可以自己实现 Sobel 操作。但同样,除非它是一个已证实的问题,否则不要担心它。

于 2013-07-23T16:52:21.460 回答