这似乎是一个非常简单的要求,但我发现这个问题有点微妙。我只想要8位无符号版本的X和Y衍生物。源图像也是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);
答案 0 :(得分: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操作。但同样,不要担心它,除非它是一个经证实的问题。