CS50x作业:C语言BMP图像Sobel算子边缘检测代码排障求助
Hey there! Let's work through your Sobel edge detection implementation—there are a couple of key issues that are probably causing incorrect results, plus some optimizations we can make to clean things up.
1. Critical Formula Error (The Big One!)
Your biggest mistake is in how you calculate the final Sobel edge magnitude. The correct formula for Sobel edge strength is:
√(Gx² + Gy²)
But right now, you're calculating it as:
resultR = sqrt(sumxR * sumxR) + sqrt(sumyR * sumyR);
This is equivalent to |Gx| + |Gy|, not the square root of the sum of squares. That's going to over-amplify edges and give you washed-out or distorted edge detection results.
Fix this by updating the result calculations to match the correct formula:
resultR = sqrt((sumxR * sumxR) + (sumyR * sumyR)); resultG = sqrt((sumxG * sumxG) + (sumyG * sumyG)); resultB = sqrt((sumxB * sumxB) + (sumyB * sumyB));
2. Avoid Unnecessary 3x3 Arrays (Optimization)
You're using GR, GG, GB to store the 3x3 neighborhood before calculating sums, but this adds extra steps and potential for bugs. You can compute the sums directly as you iterate over the neighborhood, which makes the code cleaner and more efficient.
3. Minor: Cleaner Bounds Handling & Clamping
Your current bounds check works, but we can simplify it by skipping out-of-bounds pixels entirely (since they contribute 0 to the sum). We can also use fmin() for a more concise way to clamp values to 255 instead of multiple if statements.
Revised Code
Here's the fixed, optimized version of your code:
// Detect edges void edges(int height, int width, RGBTRIPLE image[height][width]) { // Create a temporary copy of the original image RGBTRIPLE temp[height][width]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { temp[i][j] = image[i][j]; // Simplified struct copy } } // Sobel kernels int Gx[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}; int Gy[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}}; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int sumxR = 0, sumyR = 0; int sumxG = 0, sumyG = 0; int sumxB = 0, sumyB = 0; // Iterate over 3x3 neighborhood using offset values for (int dx = 0; dx < 3; dx++) { for (int dy = 0; dy < 3; dy++) { int x = i + dx - 1; // Convert 0-2 dx to -1,0,1 offset int y = j + dy - 1; // Skip pixels outside image bounds if (x < 0 || x >= height || y < 0 || y >= width) { continue; } // Accumulate sums by multiplying pixel values with kernel weights sumxR += temp[x][y].rgbtRed * Gx[dx][dy]; sumyR += temp[x][y].rgbtRed * Gy[dx][dy]; sumxG += temp[x][y].rgbtGreen * Gx[dx][dy]; sumyG += temp[x][y].rgbtGreen * Gy[dx][dy]; sumxB += temp[x][y].rgbtBlue * Gx[dx][dy]; sumyB += temp[x][y].rgbtBlue * Gy[dx][dy]; } } // Calculate final edge magnitude with correct formula float resultR = sqrt((float)(sumxR * sumxR) + (float)(sumyR * sumyR)); float resultG = sqrt((float)(sumxG * sumxG) + (float)(sumyG * sumyG)); float resultB = sqrt((float)(sumxB * sumxB) + (float)(sumyB * sumyB)); // Clamp values to 0-255 range resultR = fmin(resultR, 255.0); resultG = fmin(resultG, 255.0); resultB = fmin(resultB, 255.0); // Assign rounded values back to the original image image[i][j].rgbtRed = round(resultR); image[i][j].rgbtGreen = round(resultG); image[i][j].rgbtBlue = round(resultB); } } }
Key improvements here:
- Fixed the core Sobel magnitude formula to match the correct mathematical definition
- Removed unnecessary 3x3 arrays by calculating sums directly during neighborhood iteration
- Simplified image copying using struct assignment
- Used
fmin()for cleaner, more concise value clamping - Made bounds checking more readable with offset-based indexing
内容的提问来源于stack exchange,提问作者guiartbp

