这似乎是一个非常简单的请求,但是我发现问题有点微妙。我只想要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);

最佳答案

如果只需要渐变的绝对值而不进行任何归一化,则可以使用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操作。但是,除非这是一个已证明的问题,否则请不要担心。

09-06 10:59