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:
- Read in the binary data and store it in a 1-dimensional array
- 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 of4 x 4
- Now find the average colour value within each of those cells of size 'c'
- Output each average colour value to a new output PPM file
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
Where I am struggling:
- I am unsure of how to find the average RGB value for each cell, and then output it to the new data stream. I figured out a way to do this when reading the data linearly (i.e. not reading it into an array (or buffer)) by dividing the total colours in that cell by the number of loop iterations, but this proved to be incorrect.
- Are the nested for loops ordered in a way which changes the orientation of the output image e.g. is it rotating it 180 degrees etc? I am struggling to determine this with reading raw binary data.
My attempt:
#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), ..)
you don't read enough because you must read RGB values, which are yourPixel
s. – Paul Ogilviesizeof(Pixel)
may be more than RGB because the compiler could pad the struct. Before declaringPixel
, use a#pragma pack (push n)
withn
the size of the ints in the file/format, and afterwards#pragma pop
. – Paul Ogilviepixels
. Remove it. – Paul Ogilviefwrite(data, width*height, ..
will of course always write the first bytes of the image. – Paul Ogilvie