如何像素化二进制(P6)PPM文件 [英] How to pixelate a binary (P6) PPM file

查看:120
本文介绍了如何像素化二进制(P6)PPM文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am struggling to pixelate an image which is made up of RGB values stored in a binary (P6) PPM file. The steps to pixelate the image are as follows:

  1. 读取二进制数据并将其存储在一维数组中
  2. 在大小为"c"(行x列)的单元格中循环访问此数组中存储的数据.用户可以更改此变量"c",但是对于该程序,当前将其设置为 int c = 4; ,这意味着它将循环遍历 4 x 4 的像素数据.代码>
  3. 现在在每个大小为'c'的像元中查找平均颜色值
  4. 将每个平均颜色值输出到新的输出PPM文件
  1. Read in the binary data and store it in a 1-dimensional array
  2. Iterate through the data stored in this array, in cells of size 'c' (row x columns). This variable 'c' can be changed by the user, but for this program it's currently set to int c = 4; meaning it iterates through pixel data in block dimensions of 4 x 4
  3. Now find the average colour value within each of those cells of size 'c'
  4. Output each average colour value to a new output PPM file

每个二进制PPM文件都以以下格式的标头开头,包括魔术数字",然后是宽度,高度,最后是最大颜色值255.标头注释将被忽略.以下标头示例显示了格式为P6(因此为 binary 文件),宽度为16,高度为16和最大颜色值为255的PPM图像:

Each binary PPM file begins with a header in the following format, consisting of a 'magic number', followed by the width, height, and finally maximum colour value of 255. Header comments are ignored. The following header example shows a PPM image of format P6 (therefore a binary file), a width of 16, a height of 16, and a max colour value of 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; 
}

推荐答案

在注释中,我给了您一些有关代码错误的反馈.您可以自己修复这些问题.使用调试器来测试/检查所有这些准备步骤.例如,读取文件并立即将其写入(并显示图像),这样您就可以确定读取是正确的.

In the comments I gave you some feedback about errors in your code. You can fix those yourself. Take a debugger to test/check all those preliminary steps. For example read the file and write it immediately (and display the image) so you know the reading is OK.

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

Your main problem and question is about the loop.

从本质上讲,您有一个一维数组,该数组由scanline-after-scanline组成,每条扫描线均包含像素.与BMP格式相反,您的格式似乎未使用填充字节在字边界上对齐扫描线.这使它变得容易一些.

In essence you have a one-dimensional array that consists of scanline-after-scanline and each scanline contains pixels. Contrary to the BMP format, your format seems not to be using padding bytes to align scanlines on word boundaries. That makes it a bit easier.

一个像素由三个颜色值R,G和B组成,我假设每个颜色值都是一个字节(无符号字符).然后,内存分配和读取变为:

A pixel consists of three color values, R, G and B, and I assume each color value is one byte (unsigned char). The memory allocation and reading then becomes:

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

该循环现在以4的增量遍历所有行,并以4的增量遍历每个像素.因此,它一次处理一个正方形,计算平均值并将其写出:

The loop now goes through all the lines in increments of four and processes each pixel in increments of four. So it processes one square at a time, calculates the average and writes it out:

    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);
        }
    }

这可以使用指针算法进行优化,但这显示了所需的循环基础.

This could be optimized using pointer arithmetic, but this shows the basics of the loop you require.

注意:

  • ..&&y + dy< height 测试当最后一个正方形不适合高度时的边框情况.宽度相同.

  • ..&& y+dy<height tests the border case when the last square does not fit the height. Same for the width.

因此,平均值是通过除以(dx * dy)来计算的.

as a consequence the average is calculated by dividing by (dx*dy).

免责声明

我无法测试它,因此该算法是一种心理构造.

I couldn't test it so the algorithm is a mental construction.

这篇关于如何像素化二进制(P6)PPM文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆