【问题标题】:How to pixelate an image如何像素化图像
【发布时间】:2019-08-25 05:10:13
【问题描述】:

我正在尝试通过以下步骤对 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;
}

【问题讨论】:

  • 您正在读取 P6 ppm,但输出的是 P3 ppm,顺便说一句。需要适当调整表头。添加错误检查和处理会很好。另外,如果您的数据有可能,您的代码将中断最大颜色字段 > 255 的图像...
  • 感谢@Shawn 的提醒,我添加了错误检查和处理,并将输出 fopen 函数更改为:fprintf(f_output, "P6\n%d %d\n255\n", w, h);。我只使用最大色域小于或等于 255 的图像。你知道为什么我的输出图像是一种纯色吗?

标签: c image ppm pixelate


【解决方案1】:

您的主要错误是您假设在实际线性访问像素数据时读取和写入 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() 所暗示的严格线性顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-23
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 2013-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多