2

我正在尝试通过以下步骤对 P6 PPM 格式图像进行像素化:

  1. 读取 4x4 网格中的 PPM 图像
  2. 查找每个 4x4 网格的平均 RGB 颜色值
  3. 通过将新图像中的每个 4x4 像素网格设置为具有平均 RGB 颜色值来写入新文件

PPM 文件以以下格式开始:

P6 
# ignores comments in header 
width 
height 
max colour value

我的问题: 输出 PPM 图像文件(我使用 GIMP 图像编辑器打开,也可以在记事本等任何文本编辑器中打开以查看原始数据)由一个平面颜色块组成,而它应该类似于一种马赛克。

注意: 4x4 网格可以变化,即网格尺寸的值越高,输出图像的像素化程度越高。我的代码主要受到另一个 Stack Overflow 问题的影响,用户在 C# 中尝试了类似的实现。这个问题的链接: 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(&currentPix, 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

1 回答 1

2

Your main mistake is that you assume to be reading and writing 4×4 blocks of pixels while actually accessing pixel data linearly; your suspicion is correct.

Consider the following example. Let there be a 12×4 1-channel image:

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

Each of the pixels has the color that equals its position in a PPM file.

Now these are the pixels which you are expecting to read when pixelating the first 4×4 block:

01 02 03 04
13 14 15 16
25 26 27 28
37 38 39 40

And these are the pixels which are actually being read by sequentially executing fread():

01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16

So eventually you are treating the input image as if it looked like that:

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

instead of:

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

One of the simpler methods of resolving that issue is to allocate an array into which the data is to be read. Once you have that array filled with your data, you will be able to access its elements in any order, instead of the strictly linear order fread() implies.

于 2019-04-06T20:07:07.030 回答