c++canny-operatorstb-image

how to apply gaussian filter correctly


I'm trying to implement Canny edge detection in C++ using only stb_image and no third-party libraries. Currently, I'm stuck at the Gaussian blur step. When I apply it to a grayscale image, the top portion of the image appears blurred, while the rest looks mostly unprocessed.

I'm using a test image in RGB format from Wikipedia's Canny edge detection page, and here's the relevant part of my code:

// Generate gaussian kernel
int size = 5, center = (size - 1) / 2;
float sigma = 1.4f;
std::vector<std::vector<double>> kernel(size, std::vector<double>(size));
double sum = 0.0;

for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
        int x = j - center;
        int y = i - center;
        kernel[i][j] = (1.0 / (2 * M_PI * sigma * sigma)) * exp(-(x * x + y * y) / (2 * sigma * sigma));
        sum += kernel[i][j];
    }
}

// Normalize kernel
for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
        kernel[i][j] /= sum;
    }
}

// Apply Gaussian filter to image
std::vector<unsigned char> temp(width * height * channels); // Fixed buffer size

for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        for (int c = 0; c < 3; c++) { // Apply to R, G, B channels
            double val = 0.0;
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    int neighbor_x = x + j - center;
                    int neighbor_y = y + i - center;
                    int clamped_x = std::min(std::max(neighbor_x, 0), width - 1);
                    int clamped_y = std::min(std::max(neighbor_y, 0), height - 1);
                    val += kernel[i][j] * data[(clamped_y * width + clamped_x) * channels + c];
                }
            }
            temp[(y * width + x) * channels + c] = static_cast<unsigned char>(val);
        }
    }
}

// Copy blurred data back
std::copy(temp.begin(), temp.end(), data);

// Write to file
stbi_write_png("gaussian.png", width, height, channels, data, width * channels);

What I've tried:

I don't know what causing the Gaussian blur to apply only to part of the image? Is there a mistake in how I'm handling the RGB image or memory?

Thanks for any help!


Solution

  • I have found my mistake, the code below:

    int x = j - (center+1), y = i - (center+1);
    

    should be this:

    int x = j - center, y = i - center;
    

    The kernel is 5×5, then center = 2. Because of trying to shift the kernel window such that it centers around (0,0) — so it should range from -2 to +2. My mistake had it from -3 to +1, which is off by one and leads to asymmetric blur.