I am attempting to pixelate a P6 PPM format image via the following steps:
The PPM file begins in the following format:
P6
# ignores comments in header
width
height
max colour value
My problem: The output PPM image file (which I am opening using GIMP image editor, and can also be opened in any text editor like Notepad to view the raw data) consists of one flat colour block, when instead it should resemble a sort of mosaic.
Note: The 4x4 grid can be varied i.e. the higher the value of the grid dimensions, the more pixelated the output image becomes. My code has mostly been influenced from another Stack Overflow question where the user attempted a similar implementation in C#. The link to this question: https://codereview.stackexchange.com/questions/140162/pixelate-image-with-average-cell-color
UPDATE: The output now seems to pixelate the first 1/5 of the image, but the rest of the output image remains one colour block. I think my issue lies in that I am treating the cells as the pixels in linear order.
My attempt:
#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(¤tPix, 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;
}
Your main mistake is that you assume to be reading and writing 4×4 blocks of pixels while actually accessing pixel data linearly; your suspicion is correct.
Consider the following example. Let there be a 12×4 1-channel image:
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
Each of the pixels has the color that equals its position in a PPM file.
Now these are the pixels which you are expecting to read when pixelating the first 4×4 block:
01 02 03 04
13 14 15 16
25 26 27 28
37 38 39 40
And these are the pixels which are actually being read by sequentially executing fread()
:
01 02 03 04 05 06 07 08 09 10 11 12
13 14 15 16
So eventually you are treating the input image as if it looked like that:
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
instead of:
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
One of the simpler methods of resolving that issue is to allocate an array into which the data is to be read. Once you have that array filled with your data, you will be able to access its elements in any order, instead of the strictly linear order fread()
implies.