CS50滤镜作业:Sobel算子边缘检测函数故障排查求助
CS50滤镜作业:边缘检测功能故障排查
正在完成CS50的滤镜作业,其他功能仅存在小问题且易修复,但边缘检测功能完全失效。该功能需通过Sobel算子检测图像边缘,参考CS50官方文章中“image filtering”板块的“edges”子标题实现。
问题代码
void edges(int height, int width, RGBTRIPLE image[height][width]) { int gx[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1}; int gy[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1}; int blue_sum_x = 0; int red_sum_x = 0; int green_sum_x = 0; int blue_sum_y = 0; int red_sum_y = 0; int green_sum_y = 0; int count; RGBTRIPLE image_tmp[height][width]; for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { image_tmp[h][w] = image[h][w]; } } for (int h = -1; h <= height; h++) { image_tmp[h][-1].rgbtRed = 0; image_tmp[h][-1].rgbtGreen = 0; image_tmp[h][-1].rgbtBlue = 0; image_tmp[h][width].rgbtRed = 0; image_tmp[h][width].rgbtGreen = 0; image_tmp[h][width].rgbtBlue = 0; } for (int w = -1; w <= width; w++) { image_tmp[-1][w].rgbtRed = 0; image_tmp[-1][w].rgbtGreen = 0; image_tmp[-1][w].rgbtBlue = 0; image_tmp[height][w].rgbtRed = 0; image_tmp[height][w].rgbtGreen = 0; image_tmp[height][w].rgbtBlue = 0; } for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { count = 0; for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { { blue_sum_x += gx[count] * image_tmp[h + i][w + j].rgbtBlue; red_sum_x += gx[count] * image_tmp[h + i][w + j].rgbtRed; green_sum_x += gx[count] * image_tmp[h + i][w + j].rgbtGreen; blue_sum_y += gy[count] * image_tmp[h + i][w + j].rgbtBlue; red_sum_y += gy[count] * image_tmp[h + i][w + j].rgbtRed; green_sum_y += gy[count] * image_tmp[h + i][w + j].rgbtGreen; count++; } } } image[h][w].rgbtBlue = round(sqrt(pow(blue_sum_x, 2) + pow(blue_sum_y, 2))); if (round(sqrt(pow(blue_sum_x, 2) + pow(blue_sum_y, 2))) > 255) { image[h][w].rgbtBlue = 255; } image[h][w].rgbtRed = round(sqrt(pow(red_sum_x, 2) + pow(red_sum_y, 2))); if (round(sqrt(pow(red_sum_x, 2) + pow(red_sum_y, 2))) > 255) { image[h][w].rgbtRed = 255; } image[h][w].rgbtGreen = round(sqrt(pow(green_sum_x, 2) + pow(green_sum_y, 2))); if (round(sqrt(pow(green_sum_x, 2) + pow(green_sum_y, 2))) > 255) { image[h][w].rgbtGreen = 255; } } } }
代码说明
函数接收三个参数:
- 图像高度
- 图像宽度
- RGBTRIPLE类型数组,表示图像,每个元素包含
rgbtRed、rgbtGreen、rgbtBlue三个属性,对应像素的各颜色值。
实现逻辑:
- 初始化Sobel算子数组
gx、gy及求和变量 - 创建图像副本
image_tmp避免原图像修改影响计算 - 尝试为
image_tmp添加1像素黑边简化边缘/角落处理 - 遍历每个像素,对其3x3邻域计算颜色值与Sobel算子的乘积和
- 计算各颜色通道最终值(平方和开根号后取整),并限制在0-255范围内
问题现象
- 首次运行:输出图像类似电视雪花屏,全是随机颜色
- 添加颜色值上限255逻辑后:输出全白图像
- 当前版本(添加黑边处理):输出图像几乎与原图一致,但顶部出现1像素宽的亮紫蓝色条纹
核心问题分析
- 求和变量未重置:
blue_sum_x、red_sum_x等求和变量定义在函数开头,每次处理新像素时未重置为0,导致累加了之前所有像素的计算结果,这是输出异常的主要原因。 - 数组越界访问:
image_tmp的尺寸为height×width,但代码直接访问image_tmp[-1][w]、image_tmp[h][-1]等越界索引,触发未定义行为(读取内存垃圾值或破坏数据),顶部的异常条纹就是该问题的直接结果。 - 重复计算冗余:计算最终颜色值时重复调用
sqrt和pow,既降低效率,也可能引入精度问题。
修复方案及代码
修复要点
- 将求和变量移至每个像素的循环内部,确保每次计算前重置为0
- 替换越界数组访问逻辑,通过判断坐标是否在图像范围内来处理边缘像素
- 优化计算逻辑,先存储平方和的平方根结果,再判断是否超过255,减少重复计算
修复后代码
void edges(int height, int width, RGBTRIPLE image[height][width]) { int gx[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1}; int gy[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1}; RGBTRIPLE image_tmp[height][width]; // 复制原图像到临时数组 for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { image_tmp[h][w] = image[h][w]; } } for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { // 每次处理新像素时重置求和变量 int blue_sum_x = 0, red_sum_x = 0, green_sum_x = 0; int blue_sum_y = 0, red_sum_y = 0, green_sum_y = 0; int count = 0; for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { int current_h = h + i; int current_w = w + j; // 安全获取邻域像素值,越界则用0 unsigned char blue = 0, red = 0, green = 0; if (current_h >= 0 && current_h < height && current_w >= 0 && current_w < width) { blue = image_tmp[current_h][current_w].rgbtBlue; red = image_tmp[current_h][current_w].rgbtRed; green = image_tmp[current_h][current_w].rgbtGreen; } blue_sum_x += gx[count] * blue; red_sum_x += gx[count] * red; green_sum_x += gx[count] * green; blue_sum_y += gy[count] * blue; red_sum_y += gy[count] * red; green_sum_y += gy[count] * green; count++; } } // 计算并限制颜色值范围 int blue_final = round(sqrt(pow(blue_sum_x, 2) + pow(blue_sum_y, 2))); image[h][w].rgbtBlue = (blue_final > 255) ? 255 : blue_final; int red_final = round(sqrt(pow(red_sum_x, 2) + pow(red_sum_y, 2))); image[h][w].rgbtRed = (red_final > 255) ? 255 : red_final; int green_final = round(sqrt(pow(green_sum_x, 2) + pow(green_sum_y, 2))); image[h][w].rgbtGreen = (green_final > 255) ? 255 : green_final; } } }
内容的提问来源于stack exchange,提问作者Peter Van Voorhis
相关产品推荐
相关产品推荐

