如何像素化图像
How to pixelate an image
我正在尝试通过以下步骤对 P6 PPM 格式图像进行像素化处理:
- 读取 4x4 网格中的 PPM 图像
- 找出每个 4x4 网格的平均 RGB 颜色值
- 写入新文件,方法是将新图像中的每个 4x4 像素网格设置为具有平均 RGB 颜色值。
PPM 文件以下列格式开头:
P6
# ignores comments in header
width
height
max colour value
我的问题:
输出的 PPM 图像文件(我使用 GIMP 图像编辑器打开,也可以在记事本等任何文本编辑器中打开以查看原始数据)由一个平面色块组成,而它应该类似于一种马赛克。
注意: 4x4 网格可以变化,即网格尺寸的值越高,输出图像的像素化程度越高。我的代码主要受到另一个 Stack Overflow 问题的影响,其中用户尝试在 C# 中进行类似的实现。这个问题的link:
https://codereview.stackexchange.com/questions/140162/pixelate-image-with-average-cell-color
更新:输出现在似乎像素化了图像的前 1/5,但输出图像的其余部分仍然是一个色块。我认为我的问题在于我将单元格视为线性顺序的像素。
我的尝试:
#include <stdio.h>
#include <assert.h>
//Struct to store RGB values
typedef struct {
unsigned char r, g, b;
} pixel;
int main()
{
int y, x; //Loop iteration variables
int yy = 0; //Loop iteration variables
int xx = 0; //Loop iteration variables
char magic_number[10]; //Variable which reads P6 in the header
int w, h, m; //Image dimension variables
pixel currentPix; //Current pixel variable
int avR; //Red declaration
int avG; //Green declaration
int avB; //Blue declarataion
int total; //Loop iteration counter declaration
//Input file
FILE* f;
f = fopen("Dog2048x2048.ppm", "r"); //Read PPM file
if (f == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open input file");
getchar();
exit(1);
}
//Scan the header of the PPM file to get the magic number (P6), width
//height and max colour value
fscanf(f, "%s %d %d %d", &magic_number, &w, &h, &m);
//initialize file for writing (open and header)
FILE* f_output;
f_output = fopen("some_file.ppm", "w");
//fprintf(f_output, "%s %d %d %d", magic_number, w, h, m);
fprintf(f_output, "P6\n%d %d\n255\n", w, h);
if (f_output == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open output file");
getchar();
exit(1);
}
// Loop through the image in 4x4 cells.
for (int yy = 0; yy < h && yy < h; yy += 4)
{
for (int xx = 0; xx < w && xx < w; xx += 4)
{
avR = 0;
avG = 0;
avB = 0;
total = 0;
// Store each color from the 4x4 cell into cellColors.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Reads input file stream
fread(¤tPix, 3, 1, f);
//Current pixels
avR += currentPix.r;
avG += currentPix.g;
avB += currentPix.b;
//Counts loop iterations for later use in colour averaging
total++;
}
}
//Average RGB values
avR /= total;
avG /= total;
avB /= total;
// Go BACK over the 4x4 cell and set each pixel to the average color.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Print out to new file
fprintf(f_output, "%i %i %i\t", avR, avG, avB);
}
}
}
fprintf(f_output, "\n");
}
return 0;
}
您的主要错误是您假设在实际线性访问像素数据时读取和写入 4×4 像素块;你的猜测是正确的。
考虑以下示例。假设有一个 12×4 的 1 通道图像:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45 46 47 48
每个像素的颜色与其在 PPM 文件中的位置相同。
现在这些是您在像素化第一个 4×4 块时期望读取的像素:
01 02 03 04
13 14 15 16
25 26 27 28
37 38 39 40
这些是通过顺序执行fread()
:
实际读取的像素
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16
所以最终您将输入图像视为看起来像这样:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 01 02 03 04 17 18 19 20 33 34 35 36
17 18 19 20 21 22 23 24 --> 05 06 07 08 21 22 23 24 37 38 39 40
25 26 27 28 29 30 31 32 09 10 11 12 25 26 27 28 41 42 43 44
33 34 35 36 13 14 15 16 29 30 31 32 45 46 47 48
37 38 39 40 41 42 43 44 45 46 47 48
而不是:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45 46 47 48
解决该问题的一种更简单的方法是分配一个数组,将数据读取到该数组中。一旦你用你的数据填充了这个数组,你将能够以任何顺序访问它的元素,而不是 fread()
暗示的严格线性顺序。
我正在尝试通过以下步骤对 P6 PPM 格式图像进行像素化处理:
- 读取 4x4 网格中的 PPM 图像
- 找出每个 4x4 网格的平均 RGB 颜色值
- 写入新文件,方法是将新图像中的每个 4x4 像素网格设置为具有平均 RGB 颜色值。
PPM 文件以下列格式开头:
P6
# ignores comments in header
width
height
max colour value
我的问题: 输出的 PPM 图像文件(我使用 GIMP 图像编辑器打开,也可以在记事本等任何文本编辑器中打开以查看原始数据)由一个平面色块组成,而它应该类似于一种马赛克。
注意: 4x4 网格可以变化,即网格尺寸的值越高,输出图像的像素化程度越高。我的代码主要受到另一个 Stack Overflow 问题的影响,其中用户尝试在 C# 中进行类似的实现。这个问题的link: https://codereview.stackexchange.com/questions/140162/pixelate-image-with-average-cell-color
更新:输出现在似乎像素化了图像的前 1/5,但输出图像的其余部分仍然是一个色块。我认为我的问题在于我将单元格视为线性顺序的像素。
我的尝试:
#include <stdio.h>
#include <assert.h>
//Struct to store RGB values
typedef struct {
unsigned char r, g, b;
} pixel;
int main()
{
int y, x; //Loop iteration variables
int yy = 0; //Loop iteration variables
int xx = 0; //Loop iteration variables
char magic_number[10]; //Variable which reads P6 in the header
int w, h, m; //Image dimension variables
pixel currentPix; //Current pixel variable
int avR; //Red declaration
int avG; //Green declaration
int avB; //Blue declarataion
int total; //Loop iteration counter declaration
//Input file
FILE* f;
f = fopen("Dog2048x2048.ppm", "r"); //Read PPM file
if (f == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open input file");
getchar();
exit(1);
}
//Scan the header of the PPM file to get the magic number (P6), width
//height and max colour value
fscanf(f, "%s %d %d %d", &magic_number, &w, &h, &m);
//initialize file for writing (open and header)
FILE* f_output;
f_output = fopen("some_file.ppm", "w");
//fprintf(f_output, "%s %d %d %d", magic_number, w, h, m);
fprintf(f_output, "P6\n%d %d\n255\n", w, h);
if (f_output == NULL) //Error notifiaction if file cannot be found
{
fprintf(stderr, "ERROR: cannot open output file");
getchar();
exit(1);
}
// Loop through the image in 4x4 cells.
for (int yy = 0; yy < h && yy < h; yy += 4)
{
for (int xx = 0; xx < w && xx < w; xx += 4)
{
avR = 0;
avG = 0;
avB = 0;
total = 0;
// Store each color from the 4x4 cell into cellColors.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Reads input file stream
fread(¤tPix, 3, 1, f);
//Current pixels
avR += currentPix.r;
avG += currentPix.g;
avB += currentPix.b;
//Counts loop iterations for later use in colour averaging
total++;
}
}
//Average RGB values
avR /= total;
avG /= total;
avB /= total;
// Go BACK over the 4x4 cell and set each pixel to the average color.
for (int y = yy; y < yy + 4 && y < h; y++)
{
for (int x = xx; x < xx + 4 && x < w; x++)
{
//Print out to new file
fprintf(f_output, "%i %i %i\t", avR, avG, avB);
}
}
}
fprintf(f_output, "\n");
}
return 0;
}
您的主要错误是您假设在实际线性访问像素数据时读取和写入 4×4 像素块;你的猜测是正确的。
考虑以下示例。假设有一个 12×4 的 1 通道图像:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45 46 47 48
每个像素的颜色与其在 PPM 文件中的位置相同。
现在这些是您在像素化第一个 4×4 块时期望读取的像素:
01 02 03 04
13 14 15 16
25 26 27 28
37 38 39 40
这些是通过顺序执行fread()
:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16
所以最终您将输入图像视为看起来像这样:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 01 02 03 04 17 18 19 20 33 34 35 36
17 18 19 20 21 22 23 24 --> 05 06 07 08 21 22 23 24 37 38 39 40
25 26 27 28 29 30 31 32 09 10 11 12 25 26 27 28 41 42 43 44
33 34 35 36 13 14 15 16 29 30 31 32 45 46 47 48
37 38 39 40 41 42 43 44 45 46 47 48
而不是:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45 46 47 48
解决该问题的一种更简单的方法是分配一个数组,将数据读取到该数组中。一旦你用你的数据填充了这个数组,你将能够以任何顺序访问它的元素,而不是 fread()
暗示的严格线性顺序。