c++opencvimage-processinglaplacian

Laplacian Filter opencv c++


I was learning filters in OpenCV, but I'm a little confused about the Laplacian filter. My result is very different from the Laplacian filter in OpenCV lib.

For first, I use a Gaussian filter for the image:

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;
}

Then I use the Laplacian function:

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;
    }

And here is the final result from my code:


OpenCV result:


Solution

  • Let's rewrite the function a little, so it's easier to discuss:

    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;
    }
    

    The source of your problem is sum. Let's examine its range in scope of this algorithm, by taking the two extremes:

    When you perform output.at<uchar>(y, x) = sum; there's an implicit cast of the int back to unsigned char -- the high order bits simply get chopped off and the value overflows.

    The correct approach to handle this situation (which OpenCV takes), is to perform saturation before the actual cast. Essentially

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

    OpenCV provides function cv::saturate_cast<T> to do just this.


    There's an additional problem that you're not handling the edge rows/columns of the input image -- you just leave them at the original value. Since you're not asking about that, I'll leave solving that as an excercise to the reader.


    Code:

    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;
    }
    

    Sample input:

    Output of corrected filtroLaplace:

    Output of cv::Laplacian: