【问题标题】:How to pixelate a binary (P6) PPM file如何像素化二进制 (P6) PPM 文件
【发布时间】:2019-09-29 05:08:25
【问题描述】:

我正在努力像素化由存储在 二进制 (P6) PPM 文件中的 RGB 值组成的图像。 图像像素化步骤如下:

  1. 读入二进制数据并存储在一维数组中
  2. 遍历存储在此数组中的数据,单元格大小为“c”(行 x 列)。用户可以更改此变量“c”,但对于此程序,它当前设置为int c = 4;,这意味着它迭代块尺寸为4 x 4 的像素数据
  3. 现在找到每个大小为“c”的单元格中的平均颜色值
  4. 将每个平均颜色值输出到新的输出 PPM 文件

每个二进制 PPM 文件都以以下格式的标题开头,由“幻数”组成,后跟宽度、高度,最后是最大颜色值 255。标题 cmets 被忽略。 以下标题示例显示了 P6 格式的 PPM 图像(因此是 二进制 文件),宽度为 16,高度为 16,最大颜色值为 255:

P6
16
16
255 

我在哪里挣扎:

  1. 我不确定如何找到每个单元格的平均 RGB 值,然后将其输出到新的数据流。通过将该单元格中的总颜色除以循环迭代次数来线性读取数据(即不将其读入数组(或缓冲区))时,我想出了一种方法,但事实证明这是不正确的。
  2. 嵌套的 for 循环是否以改变输出图像方向的方式排序,例如它是旋转 180 度等吗?我正在努力通过读取原始二进制数据来确定这一点。

我的尝试:

#define _CRT_SECURE_NO_WARNINGS                 //preprocessor requirement 

#include <stdio.h>                              //library for I/O functions 
#include <stdlib.h>                             //library for general functions 

#define magic_size 10                           //macro for PPM character found within header

typedef struct {
    int t_r, t_g, t_b;                          //Struct to hold  RGB pixel data
} Pixel;

int main()
{
    char magic_number[magic_size];              //variable for PPM format
    int width = 0, height = 0, max_col = 0;     //imagine dimensions
    int c = 4;                                  //mosaic parameter

    /* INPUT FILE HANDLING */
    FILE *inputFile; 
    inputFile = fopen("Sheffield512x512.ppm", "r");
    //input file error handling
    if (inputFile == NULL)
    {
        printf(stderr, "ERROR: file cannot be opened");
        getchar();  //prevent cmd premature closure
        exit(1);    //exit program cleanly
    }

    /* OUTPUT FILE HANDLING */
    FILE *outputFile; 
    outputFile = fopen("mosaic.ppm", "w");
    //output file error handling
    if (outputFile == NULL)
    {
        printf(stderr, "ERROR: cannot write to file");
        getchar();  //prevent cmd premature closure
        exit(1);    //exit program cleanly
    }

    // Scan the header (these variables are used later on)
    fscanf(inputFile, "%s\n%d\n%d\n%d", &magic_number, &width, &height, &max_col);

    // Error handling. Program only supports binary files (i.e. of P6 format) 
    if (magic_number[1] != '6')
    {
        printf("Only Binary images supported!\n");
        getchar();  //prevent cmd premature closure
        return;
    }

    // Raw 1 dimensional store of pixel data
    Pixel *data = malloc(width*height * sizeof(Pixel));

    //2D index to access pixel data
    Pixel **pixels = malloc(height * sizeof(Pixel*));

    // Read the binary file data 
    size_t r = fread(data, width*height, sizeof(unsigned char), inputFile);

    // Build a 1-dimensional index for the binary data 
    for (unsigned int i = 0; i < height; ++i)
    {
        pixels[i] = data + (i * width); 
    }

    // Close the input file 
    fclose(inputFile);


    /* BEGIN PIXELATION PROCESS */

    // Print the OUTPUT file header 
    fprintf(outputFile, "%s\n%d\n%d\n%d", magic_number, width, height, max_col);

    //loop condition variables 
    int cw_x = ceil((double)(width / (float)c));
    int cw_y = ceil((double)(height / (float)c));

    //iterate through 2d array in cells of size c 
    for (int c_x = 0; c_x < cw_x; c_x += 1)
    {
        for (int c_y = 0; c_y < cw_y; c_y += 1)
        {

            //iterate within the cells
            for (int _x = 0; _x < c; _x++)
            {
                int x = c_x * c + _x;

                //bounds checking
                if (x < width)
                {
                    for (int _y = 0; _y < c; _y++)
                    {
                        int y = c_y * c + _y;

                        //bounds checking 
                        if (y < height)
                        {
                            //write data to the output FILE stream 
                            fwrite(data, width*height, sizeof(unsigned char), outputFile);
                        }
                    }
                }

            }
        }
    }

    //close the output file
    fclose(outputFile);

    return 0; 
}

【问题讨论】:

  • 请注意,fread(data, width*height, sizeof(unsigned char), ..) 的阅读量不够,因为您必须阅读 RGB 值,即 Pixels。
  • 请注意,sizeof(Pixel) 可能超过 RGB,因为编译器可以填充结构。在声明 Pixel 之前,使用 #pragma pack (push n)n 文件/格式中整数的大小,然后使用 #pragma pop
  • 您不要使用pixels。删除它。
  • fwrite(data, width*height, .. 当然总是会写入图像的第一个字节。

标签: c image-processing binary ppm pixelate


【解决方案1】:

在 cmets 中,我就代码中的错误向您提供了一些反馈。你可以自己修复这些。使用调试器来测试/检查所有这些初步步骤。例如读取文件并立即写入(并显示图像),以便您知道读取正常。

您的主要问题和疑问是关于循环的。

本质上,您有一个由扫描线后扫描线组成的一维数组,每条扫描线都包含像素。与 BMP 格式相反,您的格式似乎没有使用填充字节来对齐字边界上的扫描线。这样会更容易一些。

一个像素由三个颜色值 R、G 和 B 组成,我假设每个颜色值都是一个字节(无符号字符)。那么内存分配和读取就变成了:

unsigned char *data = malloc(width*height*3);
r = fread(data, width*height*3, 1, inputFile);

现在循环以四为增量遍历所有行并处理每个像素以四为增量。所以它一次处理一个正方形,计算平均值并将其写出来:

    c= 4;
    for (y=0; y<height; y += c)
    {
        for (x=0; x<width; x += c)
        {
            unsigned int avgR=0, avgG=0, avgB= 0;

            for (dy=0; dy<c && y+dy<height; dy++)
            {
                for (dx=0; dx<c && x+dx<width; dx++)
                {
                    avgR += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3         // R pixel in line of square
                                 ];
                    avgG += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3 + 1     // G pixel in line of square
                                 ];
                    avgB += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3 + 2     // B pixel in line of square
                                 ];
                }
            }
            unsigned char avgRb= avgR/(dx*dy);
            unsigned char avgGb= avgG/(dx*dy);
            unsigned char avgBb= avgB/(dx*dy);
            fwrite(&avgR,1,1,outputFile);
            fwrite(&avgG,1,1,outputFile);
            fwrite(&avgB,1,1,outputFile);
        }
    }

这可以使用指针算法进行优化,但这显示了您需要的循环的基础知识。

注意事项:

  • ..&amp;&amp; y+dy&lt;height 在最后一个正方形不适合高度时测试边框大小写。宽度相同。

  • 因此,平均值的计算方法是除以(dx*dy)

免责声明

我无法对其进行测试,因此该算法是一种心理结构。

【讨论】:

    猜你喜欢
    • 2016-02-09
    • 2021-12-16
    • 2018-07-08
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多