Matthias Preu
Matthias Preu

Reputation: 803

2D convolution - wrong results compared to opencv's output

I'm trying to implement a simple 2D convolution (mean filter in this case). But when I compare my results with an image generated by opencv's filter2D function I see a lot of differences. My current code is:

cv::Mat filter2D(cv::Mat& image, uint32_t kernelSize = 3)
{
    float divider = kernelSize*kernelSize;
    cv::Mat kernel = cv::Mat::ones(kernelSize,kernelSize,CV_32F) / divider;

    int kHalf = kernelSize/2.f;
    cv::Mat smoothedImage = cv::Mat::ones(image.rows,image.cols,image.type());

    for (int32_t y = 0; y<image.rows; ++y) {
    for (int32_t x = 0; x<image.cols; ++x) {
        uint8_t sum = 0;
        for (int m = -kHalf; m <= kHalf; ++m) {
        for (int n = -kHalf; n <= kHalf; ++n) {
            if (x+n >= 0 || x+n <= image.cols || y+m >= 0 || y <= image.rows) {
                sum += kernel.at<float>(m+kHalf, n+kHalf)*image.at<uint8_t>(y-m+1, x-n+1);
            } else {
                // Zero padding - nothing to do
            }
        }
        }
        smoothedImage.at<uint8_t>(y,x) = sum;
    }
    }
    return smoothedImage;
}

The results for a kernel size of five are (1. opencv, 2. my implementation):

1. image opencv, 2. my results

I would appreciate if someone can explain me what I'm doing wrong.

Upvotes: 0

Views: 726

Answers (1)

SleuthEye
SleuthEye

Reputation: 14577

For starter, your condition to account for edges should use && instead of || like so:

if (x+n >= 0 && x+n <= image.cols && y+m >= 0 && y <= image.rows)

This should help a little to remove artefacts around the edge.

Then, for the artefacts on the inner region, you should make sure the sum stays within the 0-255 range, and try to avoid loosing resolution every time you cast the partial result back to uint8_t as you assign to sum:

float sum = 0;
for (int m = -kHalf; m <= kHalf; ++m) {
  for (int n = -kHalf; n <= kHalf; ++n) {
    if (x+n >= 0 && x+n <= image.cols && y+m >= 0 && y <= image.rows) {
      sum += kernel.at<float>(m+kHalf, n+kHalf)*image.at<uint8_t>(y-m+1, x-n+1);
    } else {
      // Zero padding - nothing to do
    }
  }
}
smoothedImage.at<uint8_t>(y,x) = std::min(std::max(0.0f, sum), 255.0f);

Upvotes: 2

Related Questions