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!
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.