Obtaining the Averages


The point of extracting data chunks from a grid is to obtain the average of their values. This approach is how I reduce the massive amount of data stored in an image for representation as ASCII text. (See the thumbnail above.)

This week’s code continues the process, though it removes a few statements from what was presented in last week’s Lesson. After all, the purpose of last week’s program was to demonstrate that the data was being chopped into chunks and that the chopping part worked. It does. So the next step is to examine (but not output) the chunks and obtain their average values.

The code update is more compact and the output is mercifully shorter:

2026_04_25-Lesson.c

#include <stdio.h>

#define ROWS 16
#define COLUMNS 16
#define AREA ROWS*COLUMNS
#define NORMAL "\e[m"

int main()
{
    unsigned char grid[AREA];
    int x,y,v,h,row,col;
    int chunk_horz,chunk_vert,grid_width,grid_height;
    int total;

    for( y=0; y<ROWS; y++ )
    {
        for( x=0; x<COLUMNS; x++ )
        {
            grid[y*COLUMNS+x] = (y*COLUMNS)+x;
            if( y<ROWS/2 )
                printf("\e[3%cm%02X%s ",
                        (x/4+1)+'0',
                        grid[y*COLUMNS+x],
                        NORMAL
                      );
            else
                printf("\e[4%c;30m%02X%s ",
                        (x/4+1)+'0',
                        grid[y*COLUMNS+x],
                        NORMAL
                      );
        }
        putchar('\n');
    }

    /* split into chunks */
    chunk_horz = 4;
    chunk_vert = 8;
    grid_height = ROWS/chunk_vert;
    grid_width = COLUMNS/chunk_horz;

    /* part out the chunks */
    for( y=0; y<grid_height; y++ )
    {
        for( x=0; x<grid_width; x++ )
        {
            total = 0;
            printf("Chunk %d:%d",y,x);
            for( v=0; v<chunk_vert; v++ )
            {
                for( h=0; h<chunk_horz; h++ )
                {
                    row = y*chunk_vert+v;
                    col = x*chunk_horz+h;
                    total += grid[row*COLUMNS+col];
                }
            }
            printf(" average = %2X\n",total/(chunk_vert*chunk_horz));
        }
    }

    return 0;
}

The update for this code occurs in the double nested loop. The inner nest calculates offsets within each data chunk, but instead of outputting the bytes (and colors) the values are accumulated in variable total: total += grid[row*COLUMNS+col];

After a chunk is examined, the average value is output:

printf(" average = %2X\n",total/(chunk_vert*chunk_horz));

The program still generates color for the initial display, but the secondary output is reduced to showing only the average values for each chunk. Figure 1 shows a sample run.

Colorful text output in grids

Figure 1. Program output showing the original grid and each chunk’s average value.

The average values eyeball okay. Even so, I hand calculated a chunk’s value to confirm that the average was calculated. Everything looks good.

The goal of this exercise was to reduce pixel data in an image so that it could be represented on the text screen as a character value. The character value is chosen based on the pixel’s intensity or luminosity. (I’m not a scientist, so I don’t know which is which.) Other elements come into play as well, such as calculating the ratio of the original image’s pixel size to a terminal window’s dimensions. But the part that fascinated me the most was the double-nested loop to examine a chunk within a grid. That part was the most fun to code.

I may pick up on this process in a future Lesson. You see the results as the thumbnail atop this post, which uses the Girl with the Pearl Earring as the sample JPEG file. Obviously, a lot more coding is required to generate an ASCII image from a JPEG. It was a fun exercise.

Leave a Reply