求助:C语言实现Sobel边缘检测时遭遇Segmentation Fault错误
我尝试用C语言编写edge函数实现Sobel边缘检测,但程序每次运行都会触发Segmentation Fault错误。以下是我的源代码,实在找不到问题所在:
edges函数代码
void edges(int height, int width, RGBTRIPLE image[height][width]) { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { // Detect pixels along the edges and corners if ((i == 0 || i == height - 1) && (j == 0 || j == width - 1)) { // For pixels at the top-left corner if (i == 0 && j == 0) { loop_parameter_2(i, j, 2, 2, height, width, image, i, j); } // For pixels at the top-right corner else if (i == 0 && j == width - 1) { loop_parameter_2(i, j - 1, 2, 2, height, width, image, i, j); } // For pixels at the buttom-left corner else if (i == height - 1 && j == 0) { loop_parameter_2(i - 1, j, 2, 2, height, width, image, i, j); } // For pixels at the buttom-right corner else if (i == height - 1 && j == width - 1) { loop_parameter_2(i - 1, j - 1, 2, 2, height, width, image, i, j); } // For pixels along the top edges else if (i == 0 && j > 0 && j < width - 1) { loop_parameter_2(i, j - 1, 2, 3, height, width, image, i, j); } // For pixels along the buttom edges else if (i == height - 1 && j > 0 && j < width - 1) { loop_parameter_2(i - 1, j - 1, 2, 3, height, width, image, i, j); } // For pixels along the left edges else if (j == 0 && i > 0 && i < height - 1) { loop_parameter_2(i - 1, j, 3, 2, height, width, image, i, j); } // For pixels along the right edges else if (j == width - 1 && i > 0 && i < height - 1) { loop_parameter_2(i - 1, j - 1, 3, 2, height, width, image, i, j); } } else { loop_parameter_2(i - 1, j - 1, 3, 3, height, width, image, i, j); } } } return; }
loop_parameter_2函数代码
void loop_parameter_2(int height, int width, int times_to_loop1, int times_to_loop2, int h, int w, RGBTRIPLE image[h][w], int i, int j) { // Kernel for the x direction int Gx[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; // Kernel for the y direction int Gy[3][3] = { {-1, -2, -1}, {0, 0, 0}, {1, 2, 1} }; int t1; int t2; // Detect pixels along the edges and corners if ((i == 0 || i == height - 1) && (j == 0 || j == width - 1)) { // For pixels at the top-left corner if (i == 0 && j == 0) { t1 = 1; t2 = 1; } // For pixels at the top-right corner and top edges else if ((i == 0 && j == width - 1) || (i == 0 && j > 0 && j < width - 1)) { t1 = 1; t2 = 0; } // For pixels at the buttom-left corner and left edges else if ((i == height - 1 && j == 0) || (j == 0 && i > 0 && i < height - 1)) { t1 = 0; t2 = 1; } } else { t1 = 0; t2 = 0; } // Variables to store weighted sums int sumRGx = 0; int sumRGy = 0; int sumGGx = 0; int sumGGy = 0; int sumBGx = 0; int sumBGy = 0; for (int a = height; a < height + times_to_loop1; a++) { for (int b = width; b < width + times_to_loop2; b++) { // Weighted sums for the red channel sumRGx += (image[a][b].rgbtRed * Gx[t1][t2]); sumRGy += (image[a][b].rgbtRed * Gy[t1][t2]); // Weighted sums for the green channel sumGGx += (image[a][b].rgbtGreen * Gx[t1][t2]); sumGGy += (image[a][b].rgbtGreen * Gy[t1][t2]); // Weighted sums for the blue channel sumBGx+= (image[a][b].rgbtBlue * Gx[t1][t2]); sumBGy += (image[a][b].rgbtBlue * Gy[t1][t2]); t2++; } t2 = 0; t1++; } image[i][j].rgbtRed = sqrt(sumRGx * sumRGx + sumRGy * sumRGy); image[i][j].rgbtGreen = sqrt(sumGGx * sumGGx + sumGGy * sumGGy); image[i][j].rgbtBlue = sqrt(sumBGx * sumBGx + sumBGy * sumBGy); }
我曾尝试给Gx和Gy内核添加额外元素防止数组越界,但问题依旧。Valgrind能绕过段错误输出图像,但生成的图像非常模糊,完全无法辨识。
问题分析与修复方案
1. 参数命名冲突导致逻辑混乱
loop_parameter_2的前两个参数命名为height和width,但实际传递的是像素区域的起始坐标(比如edges传入的i、j-1等值),函数内部却用这两个参数判断i == height -1,导致逻辑完全错误。比如处理顶部边缘时,传入的height是0,此时判断i == -1,直接搞混了边缘逻辑,进而让t1、t2取值错误,最终引发数组越界。
修复:重命名这两个参数为start_row和start_col,避免和图像尺寸参数混淆。
2. 内核索引递增逻辑错误
loop_parameter_2中循环时直接递增t1、t2,但没有让图像遍历区域和内核区域一一对应。比如处理顶部角落时,应该只使用内核的右下角2x2区域,但当前代码会让t1从1递增到2,后续计算时内核索引和图像区域不匹配,导致加权求和完全错误。
修复:为每个遍历的图像像素对应正确的内核位置,比如遍历图像的start_row+a时,对应内核的kernel_start_row+a。
3. 直接修改原图像导致计算污染
计算Sobel边缘时直接修改原图像,后续像素会使用已修改的错误数据计算,最终导致图像模糊。
修复:创建临时图像副本保存原始数据,所有计算基于副本,最后将结果写回原图像。
4. 平方根结果未做截断处理
sqrt返回浮点数,而rgbtRed等是0-255的8位无符号整数,直接赋值会导致数值溢出或截断错误,需要将结果限制在合法范围内。
修复后的代码示例
修改后的edges函数
void edges(int height, int width, RGBTRIPLE image[height][width]) { // 创建临时图像副本保存原始数据 RGBTRIPLE temp[height][width]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { temp[i][j] = image[i][j]; } } for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int start_row, start_col; int loop_rows, loop_cols; int kernel_start_row, kernel_start_col; // 确定每个像素对应的遍历区域和内核起始位置 if (i == 0 && j == 0) { // 左上角:遍历(0,0)-(1,1),对应内核(1,1)-(2,2) start_row = 0; start_col = 0; loop_rows = 2; loop_cols = 2; kernel_start_row = 1; kernel_start_col = 1; } else if (i == 0 && j == width - 1) { // 右上角:遍历(0,w-2)-(1,w-1),对应内核(1,0)-(2,1) start_row = 0; start_col = width - 2; loop_rows = 2; loop_cols = 2; kernel_start_row = 1; kernel_start_col = 0; } else if (i == height - 1 && j == 0) { // 左下角:遍历(h-2,0)-(h-1,1),对应内核(0,1)-(1,2) start_row = height - 2; start_col = 0; loop_rows = 2; loop_cols = 2; kernel_start_row = 0; kernel_start_col = 1; } else if (i == height - 1 && j == width - 1) { // 右下角:遍历(h-2,w-2)-(h-1,w-1),对应内核(0,0)-(1,1) start_row = height - 2; start_col = width - 2; loop_rows = 2; loop_cols = 2; kernel_start_row = 0; kernel_start_col = 0; } else if (i == 0) { // 顶部边缘:遍历(0,j-1)-(1,j+1),对应内核(1,0)-(2,2) start_row = 0; start_col = j - 1; loop_rows = 2; loop_cols = 3; kernel_start_row = 1; kernel_start_col = 0; } else if (i == height - 1) { // 底部边缘:遍历(h-2,j-1)-(h-1,j+1),对应内核(0,0)-(1,2) start_row = height - 2; start_col = j - 1; loop_rows = 2; loop_cols = 3; kernel_start_row = 0; kernel_start_col = 0; } else if (j == 0) { // 左侧边缘:遍历(i-1,0)-(i+1,1),对应内核(0,1)-(2,2) start_row = i - 1; start_col = 0; loop_rows = 3; loop_cols = 2; kernel_start_row = 0; kernel_start_col = 1; } else if (j == width - 1) { // 右侧边缘:遍历(i-1,w-2)-(i+1,w-1),对应内核(0,0)-(2,1) start_row = i - 1; start_col = width - 2; loop_rows = 3; loop_cols = 2; kernel_start_row = 0; kernel_start_col = 0; } else { // 内部像素:遍历3x3区域,对应完整内核 start_row = i - 1; start_col = j - 1; loop_rows = 3; loop_cols = 3; kernel_start_row = 0; kernel_start_col = 0; } // 调用计算函数,传入临时图像 compute_sobel(i, j, start_row, start_col, loop_rows, loop_cols, kernel_start_row, kernel_start_col, height, width, temp, image); } } return; }
新的compute_sobel函数(替代原loop_parameter_2)
void compute_sobel(int target_row, int target_col, int start_row, int start_col, int loop_rows, int loop_cols, int kernel_start_row, int kernel_start_col, int height, int width, RGBTRIPLE temp[height][width], RGBTRIPLE image[height][width]) { 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} }; int sumRGx = 0, sumRGy = 0; int sumGGx = 0, sumGGy = 0; int sumBGx = 0, sumBGy = 0; for (int a = 0; a < loop_rows; a++) { for (int b = 0; b < loop_cols; b++) { int img_row = start_row + a; int img_col = start_col + b; int kernel_row = kernel_start_row + a; int kernel_col = kernel_start_col + b; sumRGx += temp[img_row][img_col].rgbtRed * Gx[kernel_row][kernel_col]; sumRGy += temp[img_row][img_col].rgbtRed * Gy[kernel_row][kernel_col]; sumGGx += temp[img_row][img_col].rgbtGreen * Gx[kernel_row][kernel_col]; sumGGy += temp[img_row][img_col].rgbtGreen * Gy[kernel_row][kernel_col]; sumBGx += temp[img_row][img_col].rgbtBlue * Gx[kernel_row][kernel_col]; sumBGy += temp[img_row][img_col].rgbtBlue * Gy[kernel_row][kernel_col]; } } // 计算梯度并截断到0-255 int red = sqrt(sumRGx * sumRGx + sumRGy * sumRGy); int green = sqrt(sumGGx * sumGGx + sumGGy * sumGGy); int blue = sqrt(sumBGx * sumBGx + sumBGy * sumBGy); image[target_row][target_col].rgbtRed = (red > 255) ? 255 : red; image[target_row][target_col].rgbtGreen = (green > 255) ? 255 : green; image[target_row][target_col].rgbtBlue = (blue > 255) ? 255 : blue; }
内容的提问来源于stack exchange,提问作者Sylvester Adade

