这似乎是一个非常简单的请求,但是我发现问题有点微妙。我只想要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操作。但是,除非这是一个已证明的问题,否则请不要担心。