CS50 Pset4 Filter blur函数实现不符合check50校验预期
CS50 Pset4 Filter (less comfortable) blur函数错误排查
问题描述
- 实现blur函数时,无论怎么调整方案都无法输出正确结果
- 已尝试多层for循环嵌套、创建二维图像副本避免已模糊像素干扰的方案,代码编译、运行全程无报错,但check50中所有blur相关用例均未通过
- 不希望直接照搬现成答案,仅需要逻辑错误点的排查指引
现有实现代码
void blur(int height, int width, RGBTRIPLE image[height][width]) { RGBTRIPLE copy[height][width]; RGBTRIPLE temp[height][width]; //temp equals image 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++) { // copy equals image on every iteration so the blurred parts become non-blurred for (int k = 0; k < height; k++) { for (int l = 0; l < width; l++) { copy[k][l] = image[k][l]; } } // just some variables for easy use int n = i + 1; int m = i - 1; int o = j + 1; int p = j - 1; if (n == height) {n -= 1;} else if (m < 0) {m = 0;} if (o == width) {o -= 1;} else if (p < 0) {p = 0;} // using the array copy and not image RGBTRIPLE center = copy[i][j]; RGBTRIPLE top = copy[n][j]; RGBTRIPLE bottom = copy[m][j]; RGBTRIPLE left = copy[i][p]; RGBTRIPLE right = copy[i][o]; RGBTRIPLE top_left = copy[n][p]; RGBTRIPLE bottom_left = copy[m][p]; RGBTRIPLE top_right = copy[n][o]; RGBTRIPLE bottom_right = copy[m][o]; int avgred = (center.rgbtRed + top.rgbtRed + bottom.rgbtRed + left.rgbtRed + right.rgbtRed + top_left.rgbtRed + bottom_left.rgbtRed + top_right.rgbtRed + bottom_right.rgbtRed) / 9.0; int avggreen = (center.rgbtGreen + top.rgbtGreen + bottom.rgbtGreen + left.rgbtGreen + right.rgbtGreen + top_left.rgbtGreen + bottom_left.rgbtGreen + top_right.rgbtGreen + bottom_right.rgbtGreen) / 9.0; int avgblue = (center.rgbtBlue + top.rgbtBlue + bottom.rgbtBlue + left.rgbtBlue + right.rgbtBlue + top_left.rgbtBlue + bottom_left.rgbtBlue + top_right.rgbtBlue + bottom_right.rgbtBlue) / 9.0; // saving the blurred in temp on every iteration temp[i][j].rgbtRed = round(avgred); temp[i][j].rgbtGreen = round(avggreen); temp[i][j].rgbtBlue = round(avgblue); } } // image equals the blurred temp for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { image[i][j] = temp[i][j]; } } return; }
依赖的RGBTRIPLE结构体定义
typedef struct { BYTE rgbtBlue; BYTE rgbtGreen; BYTE rgbtRed; } __attribute__((__packed__)) RGBTRIPLE;
check50校验失败返回
:( blur correctly filters middle pixel expected "127 140 149\n", not "126 140 149\n" :( blur correctly filters pixel on edge expected "80 95 105\n", not "66 80 90\n" :( blur correctly filters pixel in corner expected "70 85 95\n", not "51 64 74\n" :( blur correctly filters 3x3 image expected "70 85 95\n80 9...", not "51 64 74\n66 8..." :( blur correctly filters 4x4 image expected "70 85 95\n80 9...", not "51 64 74\n66 8..."
核心逻辑错误点
代码有3个关键问题,完全对应check50返回的错误现象:
- 边界处理逻辑完全错误
用if...else if结构处理越界坐标会导致两个问题:一是角落像素同时存在行、列两个方向的越界,else if只会处理其中一个方向;二是把越界坐标直接改成当前边界值,相当于角落、边缘像素计算时重复累加了自身/同边界像素的值,同时没有统计实际有效的邻域像素数量,永远固定除以9计算平均值——角落像素实际只有3个有效邻域,边缘非角落像素只有6个有效邻域,固定除以9会让计算出的平均值比正确值小1/3到1/4,和返回的角落、边缘像素值远低于预期的现象完全吻合。 - 四舍五入逻辑无效
把RGB总和除以9.0得到的浮点数直接赋值给int类型的avgred/avggreen/avgblue,赋值时浮点数会被直接截断取整,后续调用round()没有任何作用,这就是中间像素计算结果差1的原因:本该四舍五入到127的值被截断成126。 - 无意义的性能损耗
每次内层像素循环时都全量拷贝整个图像到copy数组完全没必要,只需要在进入像素遍历循环前拷贝一次原始图像到副本,所有计算都从这个原始副本读值,就不会出现已模糊像素污染后续计算的问题,不需要每次循环重拷全图。
修正方向提示
- 不要硬改越界坐标为边界值:遍历当前像素周围3*3范围的所有坐标时,先判断坐标是否落在
0<=行号<height、0<=列号<width的合法范围内,仅累加合法像素的RGB值,同时统计合法像素的总个数,最后用RGB总和除以合法像素总个数得到平均值,不要固定除以9 - 计算平均值时,先对浮点型的总和做
round()四舍五入,再把结果赋值给像素的BYTE类型通道值,不要提前转int导致截断 - 原始图像副本只需要在遍历所有像素前拷贝一次即可
内容的提问来源于stack exchange,提问作者DinoNyte
相关产品推荐
相关产品推荐

