Zaevras
Zaevras

Reputation: 5

How to implement Laplace filter using the kernel's absolute value?

I'm trying to implement Laplace filter by using convolution and the kernel's absolute value. The problem is, the image I get after running the function is not what it should be. Can you please point out what do I miss or what should I do differently?

I'm using OpenCV and C++ and trying to run on a greyscale image.

#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace std;
using namespace cv;

void convulutionCalc(Mat imgOut, Mat imgIn, Mat kernel,int radius) {

    for (int i = 0; i < imgIn.rows - (2 * radius); i++) {
        for (int j = 0; j < imgIn.cols - (2 * radius); j++) {
            for (int k = 0; k < 2 * radius + 1; k++) {
                for (int l = 0; l < 2 * radius + 1; l++) {
                    imgOut.at<unsigned char>(i + radius, j + radius) += (imgIn.at<unsigned char>(i + k, j + l) * kernel.at<double>(k, l));
                }
            }
        }
    }
};


int main(int argc, char** argv) {
    CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");
    Mat src = imread(samples::findFile(parser.get<String>("@input")), IMREAD_COLOR);
    Mat gsrc;
    cvtColor(src, gsrc, cv::COLOR_RGB2GRAY);

    if (src.empty())
    {
        return EXIT_FAILURE;
    }

    Mat kernel = (Mat_<double>(3, 3) << 0.0, 1.0 / 4.0, 0.0, 1.0 / 4.0, 4.0 / 4.0, 1.0 / 4.0, 0.0, 1.0 / 4.0, 0.0);

    Mat laplaceImg = gsrc.clone();
    convulutionCalc(laplaceImg, gsrc, kernel, 1); //image out, image in, kernel, kernel radius

    imshow("Lalpace", laplaceImg);
    waitKey();

    return EXIT_SUCCESS;
}

The image I get looks like this:

enter image description here

Even if I'm trying with a different kernel the result is not what it should be, so I think thats's not the problem.

Upvotes: 0

Views: 200

Answers (1)

BigCore
BigCore

Reputation: 517

You are almost there. The main issue is that you are not scaling the output of the convolution. Remember that the output of the convolution is just the sum of the element-wise product of the input image and the kernel. So, if your kernel is all ones, then the output will just be the sum of all the pixels in the input image.

To fix this, you need to scale the output by the sum of the kernel values. In your case, the sum of the kernel values is 1/4 + 1/4 + 4/4 + 1/4 + 1/4 = 2. So, you just need to divide the output of the convolution by 2.

Here is my version:

#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace std;
using namespace cv;

void convolutionCalc(Mat& imgOut, Mat& imgIn, Mat& kernel, int radius)
{
    for (int i = 0; i < imgIn.rows - (2 * radius); i++)
    {
        for (int j = 0; j < imgIn.cols - (2 * radius); j++)
        {
            for (int k = 0; k < 2 * radius + 1; k++)
            {
                for (int l = 0; l < 2 * radius + 1; l++)
                {
                    imgOut.at<unsigned char>(i + radius, j + radius) += (imgIn.at<unsigned char>(i + k, j + l) * kernel.at<double>(k, l));
                }
            }
            // scale the output by the sum of the kernel values
            imgOut.at<unsigned char>(i + radius, j + radius) /= (4.0 + 1.0 + 4.0 + 1.0);
        }
    }
}

int main(int argc, char** argv)
{
    CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");
    Mat src = imread(samples::findFile(parser.get<String>("@input")), IMREAD_COLOR);
    Mat gsrc;
    cvtColor(src, gsrc, cv::COLOR_RGB2GRAY);

    if (src.empty())
    {
        return EXIT_FAILURE;
    }

    Mat kernel = (Mat_<double>(3, 3) << 0.0, 1.0 / 4.0, 0.0, 1.0 / 4.0, 4.0 / 4.0, 1.0 / 4.0, 0.0, 1.0 / 4.0, 0.0);

    Mat laplaceImg = gsrc.clone();
    convolutionCalc(laplaceImg, gsrc, kernel, 1); // image out, image in, kernel, kernel radius

    imshow("Laplacian", laplaceImg);
    waitKey();

    return EXIT_SUCCESS;
}

Upvotes: 1

Related Questions