C - rgb 值 - 计算模糊滤镜的 rgb 值的平均值
C - rgb values - Calculating the average of rgb values for a blur filter
前两个没那么难,但第三个让我抓狂。
模糊过滤器必须计算某些像素组的 rgb 值的平均值,以替换中心像素的值。
想象一个 3x3 的网格,其中必须使用八个周围像素的平均值和中心像素本身的 rgb 值来处理中心像素。
到目前为止我所做的如下:
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
int n;
int m;
int averageRed;
int averageBlue;
int averageGreen;
//For each row..
for (int i = 0; i < height; i++)
{
//..and then for each pixel in that row...
for (int j = 0; j < width; j++)
{
//...if i and j equal 0...
if (i == 0 && j == 0)
{
for (m = i; m <= 1; m++)
{
for (n = j; n <= 1; n++)
{
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
printf("%i\n", averageRed);
printf("%i\n", averageBlue);
printf("%i\n", averageGreen);
}
}
image[i][j].rgbtRed = round((float)averageRed / 4);
image[i][j].rgbtBlue = round((float)averageBlue / 4);
image[i][j].rgbtGreen = round((float)averageGreen / 4);
printf("%i\n", image[i][j].rgbtRed);
printf("%i\n", image[i][j].rgbtBlue);
printf("%i\n", image[i][j].rgbtGreen);
}
//If i equals 0 and j is greater than 0...
else if (i == 0 && j > 0)
{
//..take the line that equals i..
for (m = i; m <= 1; m++)
{
//..and take from each pixel ot that line...
for (n = j - 1; n <= 1; n++)
{
//..the color values and add them to the average-variables
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
//Set the current pixel values to the averages
image[i][j].rgbtRed = round((float)averageRed / 6);
image[i][j].rgbtBlue = round((float)averageBlue / 6);
image[i][j].rgbtGreen = round((float)averageGreen / 6);
printf("%i\n", image[i][j].rgbtRed);
printf("%i\n", image[i][j].rgbtBlue);
printf("%i\n", image[i][j].rgbtGreen);
}
else if (i > 0 && j == 0)
{
for (m = i - 1; m <= 1; m++)
{
for (n = j; n <= 1; n++)
{
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
image[i][j].rgbtRed = round((float)averageRed / 6);
image[i][j].rgbtBlue = round((float)averageBlue / 6);
image[i][j].rgbtGreen = round((float)averageGreen / 6);
}
else if (i > 0 && j > 0 )
{
// ..take every line from i - 1 to i + 1...
for (m = i - 1; m <= 1; m++)
{
//...and in each line take every pixel from j - 1 to j + 1...
for (n = j - 1; n <= 1; n++)
{
//...and add the RGB value to average-variables
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
//Set current value to the rounded average
image[i][j].rgbtRed = ((float)averageRed / 9);
image[i][j].rgbtBlue = ((float)averageBlue / 9);
image[i][j].rgbtGreen = ((float)averageGreen / 9);
}
}
}
return;
}
编译工作没有任何抱怨,但结果有点奇怪(尤其是前四个块)- Test.bmp 只是一个 55px x 55px black/white bmp 文件:
> ~/pset4/filter/ $ ./filter -b images/test.bmp blur.bmp0 38118032 0 0
> 38118032 0 0 38118032 0 0 38118032 0 helpers.c:93:40: runtime error:
> 9.52951e+06 is outside the range of representable values of type 'unsigned char' 0 164 0 helpers.c:120:40: runtime error: 6.35303e+06
> is outside the range of representable values of type 'unsigned char' 0
> 137 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0
> 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160
> 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0
> 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0
> 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160
> 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0
> 160 0 0 160 0 helpers.c:142:40: runtime error: 6.35311e+06 is outside
> the range of representable values of type 'unsigned char'
> helpers.c:167:40: runtime error: 4.23546e+06 is outside the range of
> representable values of type 'unsigned char' ~/pset4/filter/ $
非常感谢您的建议!
问候
请注意 average*
变量是 未初始化的 ,所以当你对它们求和时,你有 UB。它们需要预设为 0,当然是在一开始,但可能在每个主要循环之前。
此外,除了其他人注意到的其他问题之外,您可能需要进行 饱和度 数学运算。
那是因为 rgbt*
(例如 rgbtRed
)是一个 byte,所以该值可能会被错误地剪裁。
你正在做:
image[i][j].rgbtRed = round((float)averageRed / 6);
这可以重写为:
averageRed = round((float)averageRed / 6);
image[i][j].rgbtRed = averageRed;
但是,如果(例如)averageRed
是 256,那么 rgbtRed
最终会变成 1 [因为对 image
的赋值是 [有效]:
image[i][j].rgbtRed = averageRed & 0xFF;
因此,您存储的不是鲜红色,而是近乎黑色。最终需要为 255,即 "saturated" 最大颜色值。
所以,要解决这个问题[或者只是为了防止它],请执行:
averageRed = round((float)averageRed / 6);
if (averageRed > 255)
averageRed = 255;
image[i][j].rgbtRed = averageRed;
编辑:进一步思考,如果右侧可以超过255,你才需要这样做,但我[现在]不确定是否可以。要对此进行检查,您可以添加(例如):
if (averageRed > 255) {
fprintf(stderr,"value overflow\n");
exit(1);
}
您可以将它包装在 #ifdef
中,进行测试,如果它没有触发,您可以稍后将其删除。
更新:
As stupid as the question might sound, but how can the value ever reach 256? Even if every pixel is white, none of the values can reach 256 or where is my mistake? (1 white Px: 255 255 255 -> 10 white Px: 2550 2550 2550 / 10 --> .....
是的,根据我上面的 "Edit:",可能不会。我最近回答了一个类似的问题,其中值 could exceed 255.
但是,您的运行时错误显示值 确实 超出了一个字节的容量(即 unsigned char
)。
这可能是由于未初始化的总和变量。
但是,它 也是 因为 sum/average 变量 而不是 在循环开始时被重置。您 永远不会 重置它们,因此它们会继续增长和增长。
完成每个 3x3 卷积核后(即存储每个输出像素后)需要重置它们。
而且,我认为您的 for (n = j; n <= 1; n++)
循环不正确。您混淆了绝对坐标值(来自 j
)和坐标偏移量。
你可能想要这样的东西:
for (m = -1; m <= 1; m++) {
for (n = -1; n <= 1; n++) {
averageRed += image[i + m][j + n].rgbtRed;
}
}
更新#2:
使用一些额外的限制变量,使用一组循环可能更容易。
此外,在每个像素的基础上,使用浮点数(即 round
)可以是 慢。虽然,我没有这样做,但它可以很容易地用整数数学代替。
此外,使用更具描述性的名称而不是 i, j, m, n
有助于使代码更易于理解和维护。
无论如何,这是您的函数的重构版本,它更简单一些:
#include <math.h>
#if 1
typedef struct {
unsigned char rgbtRed;
unsigned char rgbtGreen;
unsigned char rgbtBlue;
} __attribute__((__packed__)) RGBTRIPLE;
#endif
// Blur image
void
blur(int height, int width,
RGBTRIPLE image[height][width],
RGBTRIPLE imgout[height][width])
{
int wid = width - 1;
int hgt = height - 1;
RGBTRIPLE *pixel;
// For each row..
for (int ycur = 0; ycur <= hgt; ++ycur) {
int ylo = (ycur == 0) ? 0 : -1;
int yhi = (ycur == hgt) ? 0 : 1;
// ..and then for each pixel in that row...
for (int xcur = 0; xcur <= wid; ++xcur) {
int xlo = (xcur == 0) ? 0 : -1;
int xhi = (xcur == wid) ? 0 : 1;
int avgRed = 0;
int avgGreen = 0;
int avgBlue = 0;
for (int yoff = ylo; yoff <= yhi; ++yoff) {
for (int xoff = xlo; xoff <= xhi; ++xoff) {
pixel = &image[ycur + yoff][xcur + xoff];
avgRed += pixel->rgbtRed;
avgGreen += pixel->rgbtGreen;
avgBlue += pixel->rgbtBlue;
}
}
int tot = ((yhi - ylo) + 1) * ((xhi - xlo) + 1);
pixel = &imgout[ycur][xcur];
pixel->rgbtRed = roundf((float) avgRed / tot);
pixel->rgbtGreen = roundf((float) avgGreen / tot);
pixel->rgbtBlue = roundf((float) avgBlue / tot);
}
}
}
为了正确性,您需要保留原始值。
为了速度,您只需要保留原始值,直到不再需要它们为止;水平和可以循环使用以减少增加。
更具体地说,忽略 top/bottom/left/right 边缘(这需要格外小心)并假装它是单色的(对于 RGB,你只需做 3 次),对于每一行像素:
对于行中的每个像素,执行 buffer[next_buffer_row][x] = image[y+2][x-1] + image[y+2][x] + image[y+2][x+1]
以将水平总和存储在缓冲区中。
为行中的每个像素计算模糊值,如 image[y][x] = (buffer[previous_buffer_row][x] + buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 9
前进到图像中的下一行 (y++
);并旋转缓冲区 (previous_buffer_row++; if(previous_buffer_row>= 3) previous_buffer_row = 0;
and current_buffer_row++; if(current_buffer_row>= 3) current_buffer_row = 0;
and next_buffer_row++; if(next_buffer_row>= 3) next_buffer_row = 0;
)
要处理 left/right 边,您需要 "peel off" "for each pixel in row" 循环的第一次迭代和 "for each pixel in row" 循环的最后一次迭代;然后修改它们以适应。例如。对于你想要做的第一个像素 buffer[next_buffer_row][x] = image[y+2][x] + image[y+2][x+1]
(因为 image[y+2][x-1]
处的像素不存在)和 image[y][x] = (buffer[previous_buffer_row][x] + buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 6
(因为只有 6 个像素被平均,因为 3 个像素超过了左边缘图片的)。
注意:当我说 "peel off" 时,我的意思是您不是执行(例如)for(i = 0; i < something; i++) {
,而是复制并越过循环的中间,以便在循环前后重复循环并执行 for(i = 1; i < something-1; i++) {
.
要处理 top/bottom 边,您需要 "peel off" "for each row" 循环的第一次迭代和 "for each row" 循环的最后一次迭代;然后修改它们以适应。例如。对于第一行像素,您想要生成 2 行(不是一行)的水平总和,然后执行 image[y][x] = (buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 6
因为一行(3 像素)不存在(因为它超过了顶部边缘)。请注意,这实际上最终会给您 9 个案例 ("left/middle/right for horizontals * top/middle/bottom for verticals")。
对于求平均值,使用整数除法,结果会比应有的稍暗(由于 rounding/truncation)。为避免这种情况(如果您关心),请使用 result = (max * (sums + max/2)) / (9 * max)
(例如,如果最大值为 255,则 result = 255 * (sums + 127) / 2295
。但是,这会增加开销和复杂性,并且大多数人不会注意到图像是稍微暗一点,所以这是好是坏取决于你的用例。
为了更好的模糊质量,您可以使用权重,使离中心像素越远的像素对像素的最终值的影响越小。这里的问题是去毛刺应该用圆圈来做,但你用的是正方形;这将使对角线边缘看起来 "more blurred" 而不是 horizontal/vertical 边缘。通常选择的权重被描述为一个矩阵。例如:
| 1 2 1 |
| 2 4 2 |
| 1 2 1 |
... 表示中心像素的权重为 4(因此您将中间像素的值乘以 4),其上方像素的权重为 2,等等。在这种情况下,您将除以权重的总和,恰好是 16(这意味着除法可以更快地完成 "shift right")。
我所描述的方法(仅对 3 行具有 "horizontal sums" 的缓冲区)可以轻松应用于某些权重(例如我上面显示的权重),因为权重的中间行是top/bottom 权重的倍数(2 4 2
是 1 2 1
的 2 倍)。如果不是这种情况,那么我所描述的方法需要为中间行(可以是 2 个像素而不是整行像素)提供一个额外的单独缓冲区;并且您将无法为中间行重新使用 "horizontal sum (of weighted values)"。
最后;要获得极其准确的结果,您需要意识到 RGB 值通常是伽马编码的(参见 https://en.wikipedia.org/wiki/Gamma_correction )。这意味着做 "gamma decode",然后模糊,然后 "gamma re-encode"。但是,gamma encoding/decoding 很昂贵(即使您使用查找表来避免 pow()
);如果你关心这种完美程度,那么最好为原始值(没有伽玛编码)设计整个管道(包括存储 and/or 生成的 image/s 将被模糊)然后做伽玛最后编码一次。
前两个没那么难,但第三个让我抓狂。 模糊过滤器必须计算某些像素组的 rgb 值的平均值,以替换中心像素的值。 想象一个 3x3 的网格,其中必须使用八个周围像素的平均值和中心像素本身的 rgb 值来处理中心像素。
到目前为止我所做的如下:
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
int n;
int m;
int averageRed;
int averageBlue;
int averageGreen;
//For each row..
for (int i = 0; i < height; i++)
{
//..and then for each pixel in that row...
for (int j = 0; j < width; j++)
{
//...if i and j equal 0...
if (i == 0 && j == 0)
{
for (m = i; m <= 1; m++)
{
for (n = j; n <= 1; n++)
{
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
printf("%i\n", averageRed);
printf("%i\n", averageBlue);
printf("%i\n", averageGreen);
}
}
image[i][j].rgbtRed = round((float)averageRed / 4);
image[i][j].rgbtBlue = round((float)averageBlue / 4);
image[i][j].rgbtGreen = round((float)averageGreen / 4);
printf("%i\n", image[i][j].rgbtRed);
printf("%i\n", image[i][j].rgbtBlue);
printf("%i\n", image[i][j].rgbtGreen);
}
//If i equals 0 and j is greater than 0...
else if (i == 0 && j > 0)
{
//..take the line that equals i..
for (m = i; m <= 1; m++)
{
//..and take from each pixel ot that line...
for (n = j - 1; n <= 1; n++)
{
//..the color values and add them to the average-variables
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
//Set the current pixel values to the averages
image[i][j].rgbtRed = round((float)averageRed / 6);
image[i][j].rgbtBlue = round((float)averageBlue / 6);
image[i][j].rgbtGreen = round((float)averageGreen / 6);
printf("%i\n", image[i][j].rgbtRed);
printf("%i\n", image[i][j].rgbtBlue);
printf("%i\n", image[i][j].rgbtGreen);
}
else if (i > 0 && j == 0)
{
for (m = i - 1; m <= 1; m++)
{
for (n = j; n <= 1; n++)
{
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
image[i][j].rgbtRed = round((float)averageRed / 6);
image[i][j].rgbtBlue = round((float)averageBlue / 6);
image[i][j].rgbtGreen = round((float)averageGreen / 6);
}
else if (i > 0 && j > 0 )
{
// ..take every line from i - 1 to i + 1...
for (m = i - 1; m <= 1; m++)
{
//...and in each line take every pixel from j - 1 to j + 1...
for (n = j - 1; n <= 1; n++)
{
//...and add the RGB value to average-variables
averageRed = averageRed + image[m][n].rgbtRed;
averageBlue = averageBlue + image[m][n].rgbtBlue;
averageGreen = averageGreen + image[m][n].rgbtGreen;
}
}
//Set current value to the rounded average
image[i][j].rgbtRed = ((float)averageRed / 9);
image[i][j].rgbtBlue = ((float)averageBlue / 9);
image[i][j].rgbtGreen = ((float)averageGreen / 9);
}
}
}
return;
}
编译工作没有任何抱怨,但结果有点奇怪(尤其是前四个块)- Test.bmp 只是一个 55px x 55px black/white bmp 文件:
> ~/pset4/filter/ $ ./filter -b images/test.bmp blur.bmp0 38118032 0 0
> 38118032 0 0 38118032 0 0 38118032 0 helpers.c:93:40: runtime error:
> 9.52951e+06 is outside the range of representable values of type 'unsigned char' 0 164 0 helpers.c:120:40: runtime error: 6.35303e+06
> is outside the range of representable values of type 'unsigned char' 0
> 137 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0
> 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160
> 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0
> 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0
> 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160
> 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0 160 0 0
> 160 0 0 160 0 helpers.c:142:40: runtime error: 6.35311e+06 is outside
> the range of representable values of type 'unsigned char'
> helpers.c:167:40: runtime error: 4.23546e+06 is outside the range of
> representable values of type 'unsigned char' ~/pset4/filter/ $
非常感谢您的建议!
问候
请注意 average*
变量是 未初始化的 ,所以当你对它们求和时,你有 UB。它们需要预设为 0,当然是在一开始,但可能在每个主要循环之前。
此外,除了其他人注意到的其他问题之外,您可能需要进行 饱和度 数学运算。
那是因为 rgbt*
(例如 rgbtRed
)是一个 byte,所以该值可能会被错误地剪裁。
你正在做:
image[i][j].rgbtRed = round((float)averageRed / 6);
这可以重写为:
averageRed = round((float)averageRed / 6);
image[i][j].rgbtRed = averageRed;
但是,如果(例如)averageRed
是 256,那么 rgbtRed
最终会变成 1 [因为对 image
的赋值是 [有效]:
image[i][j].rgbtRed = averageRed & 0xFF;
因此,您存储的不是鲜红色,而是近乎黑色。最终需要为 255,即 "saturated" 最大颜色值。
所以,要解决这个问题[或者只是为了防止它],请执行:
averageRed = round((float)averageRed / 6);
if (averageRed > 255)
averageRed = 255;
image[i][j].rgbtRed = averageRed;
编辑:进一步思考,如果右侧可以超过255,你才需要这样做,但我[现在]不确定是否可以。要对此进行检查,您可以添加(例如):
if (averageRed > 255) {
fprintf(stderr,"value overflow\n");
exit(1);
}
您可以将它包装在 #ifdef
中,进行测试,如果它没有触发,您可以稍后将其删除。
更新:
As stupid as the question might sound, but how can the value ever reach 256? Even if every pixel is white, none of the values can reach 256 or where is my mistake? (1 white Px: 255 255 255 -> 10 white Px: 2550 2550 2550 / 10 --> .....
是的,根据我上面的 "Edit:",可能不会。我最近回答了一个类似的问题,其中值 could exceed 255.
但是,您的运行时错误显示值 确实 超出了一个字节的容量(即 unsigned char
)。
这可能是由于未初始化的总和变量。
但是,它 也是 因为 sum/average 变量 而不是 在循环开始时被重置。您 永远不会 重置它们,因此它们会继续增长和增长。
完成每个 3x3 卷积核后(即存储每个输出像素后)需要重置它们。
而且,我认为您的 for (n = j; n <= 1; n++)
循环不正确。您混淆了绝对坐标值(来自 j
)和坐标偏移量。
你可能想要这样的东西:
for (m = -1; m <= 1; m++) {
for (n = -1; n <= 1; n++) {
averageRed += image[i + m][j + n].rgbtRed;
}
}
更新#2:
使用一些额外的限制变量,使用一组循环可能更容易。
此外,在每个像素的基础上,使用浮点数(即 round
)可以是 慢。虽然,我没有这样做,但它可以很容易地用整数数学代替。
此外,使用更具描述性的名称而不是 i, j, m, n
有助于使代码更易于理解和维护。
无论如何,这是您的函数的重构版本,它更简单一些:
#include <math.h>
#if 1
typedef struct {
unsigned char rgbtRed;
unsigned char rgbtGreen;
unsigned char rgbtBlue;
} __attribute__((__packed__)) RGBTRIPLE;
#endif
// Blur image
void
blur(int height, int width,
RGBTRIPLE image[height][width],
RGBTRIPLE imgout[height][width])
{
int wid = width - 1;
int hgt = height - 1;
RGBTRIPLE *pixel;
// For each row..
for (int ycur = 0; ycur <= hgt; ++ycur) {
int ylo = (ycur == 0) ? 0 : -1;
int yhi = (ycur == hgt) ? 0 : 1;
// ..and then for each pixel in that row...
for (int xcur = 0; xcur <= wid; ++xcur) {
int xlo = (xcur == 0) ? 0 : -1;
int xhi = (xcur == wid) ? 0 : 1;
int avgRed = 0;
int avgGreen = 0;
int avgBlue = 0;
for (int yoff = ylo; yoff <= yhi; ++yoff) {
for (int xoff = xlo; xoff <= xhi; ++xoff) {
pixel = &image[ycur + yoff][xcur + xoff];
avgRed += pixel->rgbtRed;
avgGreen += pixel->rgbtGreen;
avgBlue += pixel->rgbtBlue;
}
}
int tot = ((yhi - ylo) + 1) * ((xhi - xlo) + 1);
pixel = &imgout[ycur][xcur];
pixel->rgbtRed = roundf((float) avgRed / tot);
pixel->rgbtGreen = roundf((float) avgGreen / tot);
pixel->rgbtBlue = roundf((float) avgBlue / tot);
}
}
}
为了正确性,您需要保留原始值。
为了速度,您只需要保留原始值,直到不再需要它们为止;水平和可以循环使用以减少增加。
更具体地说,忽略 top/bottom/left/right 边缘(这需要格外小心)并假装它是单色的(对于 RGB,你只需做 3 次),对于每一行像素:
对于行中的每个像素,执行
buffer[next_buffer_row][x] = image[y+2][x-1] + image[y+2][x] + image[y+2][x+1]
以将水平总和存储在缓冲区中。为行中的每个像素计算模糊值,如
image[y][x] = (buffer[previous_buffer_row][x] + buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 9
前进到图像中的下一行 (
y++
);并旋转缓冲区 (previous_buffer_row++; if(previous_buffer_row>= 3) previous_buffer_row = 0;
andcurrent_buffer_row++; if(current_buffer_row>= 3) current_buffer_row = 0;
andnext_buffer_row++; if(next_buffer_row>= 3) next_buffer_row = 0;
)
要处理 left/right 边,您需要 "peel off" "for each pixel in row" 循环的第一次迭代和 "for each pixel in row" 循环的最后一次迭代;然后修改它们以适应。例如。对于你想要做的第一个像素 buffer[next_buffer_row][x] = image[y+2][x] + image[y+2][x+1]
(因为 image[y+2][x-1]
处的像素不存在)和 image[y][x] = (buffer[previous_buffer_row][x] + buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 6
(因为只有 6 个像素被平均,因为 3 个像素超过了左边缘图片的)。
注意:当我说 "peel off" 时,我的意思是您不是执行(例如)for(i = 0; i < something; i++) {
,而是复制并越过循环的中间,以便在循环前后重复循环并执行 for(i = 1; i < something-1; i++) {
.
要处理 top/bottom 边,您需要 "peel off" "for each row" 循环的第一次迭代和 "for each row" 循环的最后一次迭代;然后修改它们以适应。例如。对于第一行像素,您想要生成 2 行(不是一行)的水平总和,然后执行 image[y][x] = (buffer[current_buffer_row][x] + buffer[next_buffer_row][x]) / 6
因为一行(3 像素)不存在(因为它超过了顶部边缘)。请注意,这实际上最终会给您 9 个案例 ("left/middle/right for horizontals * top/middle/bottom for verticals")。
对于求平均值,使用整数除法,结果会比应有的稍暗(由于 rounding/truncation)。为避免这种情况(如果您关心),请使用 result = (max * (sums + max/2)) / (9 * max)
(例如,如果最大值为 255,则 result = 255 * (sums + 127) / 2295
。但是,这会增加开销和复杂性,并且大多数人不会注意到图像是稍微暗一点,所以这是好是坏取决于你的用例。
为了更好的模糊质量,您可以使用权重,使离中心像素越远的像素对像素的最终值的影响越小。这里的问题是去毛刺应该用圆圈来做,但你用的是正方形;这将使对角线边缘看起来 "more blurred" 而不是 horizontal/vertical 边缘。通常选择的权重被描述为一个矩阵。例如:
| 1 2 1 |
| 2 4 2 |
| 1 2 1 |
... 表示中心像素的权重为 4(因此您将中间像素的值乘以 4),其上方像素的权重为 2,等等。在这种情况下,您将除以权重的总和,恰好是 16(这意味着除法可以更快地完成 "shift right")。
我所描述的方法(仅对 3 行具有 "horizontal sums" 的缓冲区)可以轻松应用于某些权重(例如我上面显示的权重),因为权重的中间行是top/bottom 权重的倍数(2 4 2
是 1 2 1
的 2 倍)。如果不是这种情况,那么我所描述的方法需要为中间行(可以是 2 个像素而不是整行像素)提供一个额外的单独缓冲区;并且您将无法为中间行重新使用 "horizontal sum (of weighted values)"。
最后;要获得极其准确的结果,您需要意识到 RGB 值通常是伽马编码的(参见 https://en.wikipedia.org/wiki/Gamma_correction )。这意味着做 "gamma decode",然后模糊,然后 "gamma re-encode"。但是,gamma encoding/decoding 很昂贵(即使您使用查找表来避免 pow()
);如果你关心这种完美程度,那么最好为原始值(没有伽玛编码)设计整个管道(包括存储 and/or 生成的 image/s 将被模糊)然后做伽玛最后编码一次。