【问题标题】:Laplacian Filter opencv c++拉普拉斯滤波器opencv c ++
【发布时间】:2019-02-02 03:54:34
【问题描述】:

我在 OpenCV 中学习过滤器,但我对拉普拉斯过滤器有点困惑。我的结果与 OpenCV 库中的拉普拉斯滤波器非常不同。

首先,我对图像使用高斯滤波器:

Mat filtroGauss(Mat src){
    Mat gauss = src.clone();
    Mat temp(src.rows+2,src.cols+2,DataType<uchar>::type);

    int y,x;
    for (y=0; y<src.rows; y++){
    for (x=0; x<src.cols; x++) temp.at<uchar>(y+1,x+1) = src.at<uchar>(y,x);
        }

    int mask[lenMask*lenMask];
    mask[0] = mask[2] = mask[6] = mask[8] = 1;
    mask[1] = mask[3] = mask[5] = mask[7] = 2;
    mask[4] = 4;

    int denominatore = 0;
    for (int i=0; i<lenMask*lenMask; i++) denominatore += mask[i];

    int value[lenMask*lenMask];
    for(y=0; y<src.rows; y++){
        for (x=0; x<src.cols; x++){
            value[0] = temp.at<uchar>(y-1,x-1)*mask[0];
            value[1] = temp.at<uchar>(y-1,x)*mask[1];
            value[2] = temp.at<uchar>(y-1,x+1)*mask[2];
            value[3] = temp.at<uchar>(y,x-1)*mask[3];
            value[4] = temp.at<uchar>(y,x)*mask[4];
            value[5] = temp.at<uchar>(y,x+1)*mask[5];
            value[6] = temp.at<uchar>(y+1,x-1)*mask[6];
            value[7] = temp.at<uchar>(y+1,x)*mask[7];
            value[8] = temp.at<uchar>(y+1,x+1)*mask[8];

            int avg = 0;
            for(int i=0; i<lenMask*lenMask; i++)avg+=value[i];
            avg = avg/denominatore;

            gauss.at<uchar>(y,x) = avg;
        }
    }
    return gauss;
}

然后我使用拉普拉斯函数:

L(y,x) = f(y-1,x) + f(y+1,x) + f(y,x-1) + f(y,x+1) + 4*f(y,x)

Mat filtroLaplace(Mat src){
    Mat output = src.clone();
    Mat temp = src.clone();

    int y,x;
    for (y =1; y<src.rows-1; y++){
        for(x =1; x<src.cols-1; x++){

            output.at<uchar>(y,x) = temp.at<uchar>(y-1,x) + temp.at<uchar>(y+1,x) + temp.at<uchar>(y,x-1) + temp.at<uchar>(y,x+1) -4*( temp.at<uchar>(y,x));
        }
    }
    return output;
    }

这是我的代码的最终结果:


OpenCV 结果:

【问题讨论】:

  • 你能分享你的高斯和openCV高斯的结果吗
  • 看起来你的结果是按比例缩放的。
  • filtroGauss 将尝试越界访问temp -- 第二组循环以x=0, y=0 开头,您要做的第一件事是temp.at&lt;uchar&gt;(y-1,x-1)...
  • filtroLaplace 中的问题是将加权分量的总和分配给output 时的隐式转换。具体来说,在某些情况下,总和可能是负数,或者大于 > 255 - OpenCV 执行饱和(即,它将值剪辑到范围 [0..255],你会溢出。简单修复 - 使用 cv::saturate_cast&lt;uchar&gt;

标签: c++ opencv image-processing laplacian


【解决方案1】:

让我们稍微重写一下函数,这样更容易讨论:

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&lt;uchar&gt;(y, x) = sum; 时,会隐式地将 int 转换回 unsigned char —— 高位被截断,值溢出。

处理这种情况的正确方法(OpenCV 采用)是在实际投射之前执行饱和。本质上

if (sum < 0) {
    sum = 0;
} else if (sum > 255) {
    sum = 255;
}

OpenCV 提供了函数cv::saturate_cast&lt;T&gt; 来做这件事。


还有一个问题是您没有处理输入图像的边缘行/列——您只是将它们保留为原始值。既然你没有问这个问题,我将把解决这个问题作为练习留给读者。


代码:

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 的输出:

【讨论】:

  • 但由于负值在这里很重要且相关,更好的解决方案是 (a) 将结果从 [-1020,1020] 范围缩放到 [0,255]范围,或 (b) 使用带符号的 16 位整数作为输出类型。
猜你喜欢
  • 2023-03-24
  • 2011-04-28
  • 1970-01-01
  • 2011-02-02
  • 2021-02-26
  • 2013-09-21
  • 2019-05-01
  • 1970-01-01
相关资源
最近更新 更多