Blurred Images

Carlie Anglemire
Level Up Coding
Published in
5 min readJun 26, 2020

--

Recently, while completing a function in C, my task was to average the red, green, and blue values of each pixel and its surrounding pixels in a bitmap in order to make an image appear blurry. A two-dimensional array called ‘image’ was passed into the function with its height and width specified, and it had to be looped over to transform each pixel. The average I had to find was of the color values for each pixel within one row and one column of the pixel currently being transformed in the loop.

That meant that if the pixel was in one of the corners of the two-dimensional array (think of a square four columns long and four columns wide, for example) then the average would be of the values of four pixels. If the pixel was on an edge, then it would be of six pixels. If it was neither a corner nor an edge, then it would be of nine pixels (including the pixel with the value to be transformed, the one to the left of it, the one to the right, the one directly below it, the one above it, and the ones diagonal to the left and right in the rows above and below it, forming a square of 3 x 3 inside the original image).

Making a Copy

One of the first things I had to do was to create an empty nested array that would store values to be used to transform the image later on. The reason I had to do this was because if I transformed the color values in the array directly, then the averages taken as the loop proceeded would be of these transformed values and not the original ones. The third array below has a length of three, because that is where the new color values will be stored, with the average amount of red in the 0 position, blue in the 1 position, and green at the 2 index position. At the end of the function, once finalArray is full of the new values taken from the averages, ‘image’ is looped over again, and the color values stored in finalArray take the place of the original values.

int finalArray [height][width][3];

Loop the Loop

The function starts with an initial nested loop; the outside ‘for loop’ is set up like this:

for (int i = 0; i < height; i++)

and the inner loop starts with:

for (int p = 0; p < width; p++)

The inner loop allows each item in the row to be looped over before the outer loop moves on to the next column.

Specifying the Conditions

After trial and error, I broke the problem down into three main if/else statements with more if/else if statements nested inside of them.

The first condition was

if (p == 0)

and then inside of that condition there were three more:

if (i == 0)else if (i == height - 1)else if (0 < i < height - 1)

If ‘p’ and ‘i’ are both zero, that means the loop is on the top left corner. If ‘p’ is zero and ‘i’ is height - 1, then the loop is on the bottom left corner. If ‘p’ is zero and ‘i’ is greater than one but less than the height minus one, then the loop is on the top edge.

This pattern continues with the two other conditions (p == width - 1 for the next one, and 0 < p < width - 1 for the one after that) and their own nested if/else if statements to cover the whole bitmap.

This is the first ‘if’ statement, with all of the variables having been initialized earlier on. The variables ‘middleRed’, ‘middleBlue’, and ‘middleGreen’ are the same for all of the conditions here and later in the function, because they are for the current pixel in the loop. That is why they come before the first ‘if’ statement — so they have a wider scope.

middleRed = image[i][p].rgbtRed;
middleBlue = image[i][p].rgbtBlue;
middleGreen = image[i][p].rgbtGreen;
if (p == 0)
{
rightRed = image[i][p + 1].rgbtRed;
rightBlue = image[i][p + 1].rgbtBlue;
rightGreen = image[i][p + 1].rgbtGreen;
bottomRightRed = image[i + 1][p + 1].rgbtRed;
bottomRightBlue = image[i + 1][p + 1].rgbtBlue;
bottomRightGreen = image[i + 1][p + 1].rgbtGreen;
topRightRed = image[i - 1][p + 1].rgbtRed;
topRightBlue = image[i - 1][p + 1].rgbtBlue;
topRightGreen = image[i - 1][p + 1].rgbtGreen;
belowRed = image[i + 1][p].rgbtRed;
belowBlue = image[i + 1][p].rgbtBlue;
belowGreen = image[i + 1][p].rgbtGreen;
aboveRed = image[i - 1][p].rgbtRed;
aboveBlue = image[i - 1][p].rgbtBlue;
aboveGreen = image[i - 1][p].rgbtGreen;
//top left corner
if (i == 0)
{
averageRed = (middleRed + rightRed + bottomRightRed + belowRed) / 4.0;
averageBlue = (middleBlue + rightBlue + bottomRightBlue + belowBlue) / 4.0;
averageGreen = (middleGreen + rightGreen + bottomRightGreen + belowGreen) / 4.0;
}
//bottom left corner
else if (i == height - 1)
{
averageRed = (middleRed + rightRed + topRightRed + aboveRed) / 4.0;
averageBlue = (middleBlue + rightBlue + topRightBlue + aboveBlue) / 4.0;
averageGreen = (middleGreen + rightGreen + topRightGreen + aboveGreen) / 4.0;
}
//left side edge
else if (0 < i < height - 1)
{
averageRed = (middleRed + rightRed + bottomRightRed + topRightRed + aboveRed + belowRed) / 6.0;
averageBlue = (middleBlue + rightBlue + bottomRightBlue + topRightBlue + aboveBlue + belowBlue) / 6.0;
averageGreen = (middleGreen + rightGreen + bottomRightGreen + topRightGreen + aboveGreen + belowGreen) / 6.0;
}
}

Then, later on in the function, the averageRed, averageBlue, and averageGreen values for the pixel are rounded and added to finalArray.

finalArray[i][p][0] = round(averageRed);
finalArray[i][p][1] = round(averageBlue);
finalArray[i][p][2] = round(averageGreen);

Final Loop

After all of ‘image’ has been looped over and ‘finalArray’ is full, then another nested loop that has the same structure as the one before runs to set the values of red, blue, and green in ‘image’ equal to the averaged value stored in the corresponding position in finalArray.

for (int v = 0; v < height; v++){for (int l = 0; l < width; l++){image[v][l].rgbtRed = finalArray[v][l][0];
image[v][l].rgbtBlue = finalArray[v][l][1];
image[v][l].rgbtGreen = finalArray[v][l][2];
}}

And voila! You now have a blurry picture. But this time it was intentional.

--

--