c语言 - 如何像素化二进制 (P6) PPM 文件



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

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

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

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, "%sn%dn%dn%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, "%sn%dn%dn%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; 
}

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

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

实质上,您有一个一维数组,它由扫描线和扫描线组成,每个扫描线都包含像素。与 BMP 格式相反,您的格式似乎不使用填充字节来对齐单词边界上的扫描线。这使它更容易一些。

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

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

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

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

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

笔记:

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

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

免責聲明

我无法测试它,所以算法是一种心理结构。

最新更新