Давайте немного перепишем функцию, чтобы ее было проще обсудить:
cv::Mat filtroLaplace(cv::Mat src)
{
cv::Mat output = src.clone();
for (int y = 1; y < src.rows - 1; y++) {
for (int x = 1; x < src.cols - 1; x++) {
int sum = src.at<uchar>(y - 1, x)
+ src.at<uchar>(y + 1, x)
+ src.at<uchar>(y, x - 1)
+ src.at<uchar>(y, x + 1)
- 4 * src.at<uchar>(y, x);
output.at<uchar>(y, x) = sum;
}
}
return output;
}
Источник вашей проблемы sum
. Давайте рассмотрим его диапазон в рамках этого алгоритма, взяв две крайности:
- Черный пиксель, окруженный 4 белыми. Это значит
255 + 255 + 255 + 255 - 4 * 0 = 1020
.
- Белый пиксель, окруженный 4 черными. Это значит
0 + 0 + 0 + 0 - 4 * 255 = -1020
.
Когда вы выполняете output.at<uchar>(y, x) = sum;
, происходит неявное приведение int
обратно к unsigned char
- биты старших разрядов просто обрезаются, а значение переполняется.
Правильный подход к решению этой ситуации (которую использует OpenCV) заключается в выполнении насыщения перед фактическим приведением. По существу
if (sum < 0) {
sum = 0;
} else if (sum > 255) {
sum = 255;
}
OpenCV предоставляет функцию cv::saturate_cast<T>
, чтобы сделать это.
Существует дополнительная проблема, заключающаяся в том, что вы не обрабатываете края / столбцы входного изображения - вы просто оставляете их с исходным значением. Поскольку вы не спрашиваете об этом, я оставлю решение для читателя в качестве упражнения.
Код:
cv::Mat filtroLaplace(cv::Mat src)
{
cv::Mat output = src.clone();
for (int y = 1; y < src.rows - 1; y++) {
for (int x = 1; x < src.cols - 1; x++) {
int sum = src.at<uchar>(y - 1, x)
+ src.at<uchar>(y + 1, x)
+ src.at<uchar>(y, x - 1)
+ src.at<uchar>(y, x + 1)
- 4 * src.at<uchar>(y, x);
output.at<uchar>(y, x) = cv::saturate_cast<uchar>(sum);
}
}
return output;
}
Пример ввода:
Выход исправлен filtroLaplace
:
Выход cv::Laplacian
: