Transform portrait to squared image by filling


This is my problem. I have a photo (portrait.png).


From this photo, I need a squared image. However, taking the largest possible croping, this is what I can get.

Portrait cropped

Of course, I don’t want the face to be cropped like this. The following article is presented one possible solution to get something like this picture.

Portrait squared

In this article, we will only use ImageMagick.

Explain the procedure

To create a squared version of the portrait, I will extend the original picture by adding borders on left and right. Of course, I don’t want simple white borders so I will fill these borders; to do this, I will blur the current border of the picture (mainly green) with a blurred effect.

However, if I keep the existing picture as it is and add fuzzy colors in the borders, I will see the boundary between the center part of the image (containing the face) and the borders (only made of blurred colors). To avoid this virtual border, we will create borders with an increasing blurred effect as we go from the center of the image to the left and right borders. Of course, not exactly from the center since we need to keep the very center of the image (the one which contains the face) not blurred.

To process this blur effect, we will use a mask which will give the intensity of the blur for each pixel of the image.

Creating a mask

The mask will be a black and white image, white telling us that we want the maximum blur effect for this pixel and black telling us that we do not want any blur on this pixel. Basically, we want black on the important part of the visage then a black to white gradient as we go to the left and right borders.

Determine the parameters of the gradient

We will build it step by step. First, we need to define the size of our image to define the size of the gradients. identify will do the job.

identify portrait.png

Which results in the following informations.

portrait.png PNG 1111x1400 1111x1400+0+0 8-bit sRGB 1.854MB 0.000u 0:00.000

We will make a 1400x1400 squared image. This means that we will add 289 pixels on the left/right of the image (the half of it, 144.5 pixels, for each side).

To add borders , we can use the following method.

About the transparency

We will add transparent borders for the display but for the purpose of the mask, it is not required since all we be erased with black and white. All transparent parts in the image have been displayed as a checkerboard pattern in order to clearly identify it. The method is the following:

convert your_image.png \
	-size 1400x1400 \
	pattern:checkerboard \
	-compose dstover \
	-composite \
convert portrait.png \
	-bordercolor none \
	-border 144.5x0 \
Portrait with borders

Then we need to define the limit where we can begin the gradient. I choose the 250 pixels on each side as in the following image.

Portrait with the limits to create the mask

Building a one side gradient

I can now begin to create the gradient. I will create the right gradient first.

convert \
	-size 1400x1400 \
	xc: \
	-sparse-color Barycentric '1150.0,0 black %w,0 white' \
Mask gradient (right part)

Then for the left gradient, we can just rotate it.

convert \
	mask.gradient.right.png \
	-rotate 180 \
Mask gradient (left part)

Then to obtain the final mask, you combine the two parts.

convert \
	mask.gradient.right.png \
	mask.gradient.left.png \
	-compose lighten \
	-composite \

Fill the image

We now have the mask, we will use it on our image to produce the desired result. Of course, we will apply it on the squared version of our image, the previous portrait.borders.png file. To blur the image considering the mask, we will use the blur option from -compose. This option can take an argument, the same kind of argument that the -blur option takes (the intensity of the blurred effect). In our case, the intensity should be enough high to reach the borders of the image. Remember, the blur effect is an operation that take, for each pixel, an average color of all the pixels around; what I mean by the intensity of the blur effect is the diameter (in number of pixel) of the circle where we do this average.

Let’s try with a arbitrary value: 24.

convert \
	portrait.borders.png \
	mask.png \
	-compose blur \
	-set option:compose:args 24 \
	-composite \
Portrait with a low blur intensity

It seems that it’s not sufficient, and it already took a long time to generate the image…​ Wait! We forget something. It’s an average of colors but we have only transparent colors on the borders. So there is probably pixels in this image that have a blurred colored corresponding to the image but with a majority of transparent color: we do not see it. To only keep the RGB color (which is the meaningfull information in our picture), we will just remove the transparency in the image (deactivate the alpha channel).

convert \
	portrait.blur24.png \
	-alpha off \
Portrait with a low blur intensity and no transparency

There is clearly a problem. The size of our blurred effect was so small that the pixels on the extrems didn’t beneficied of any color. We need to increase the value of our argument. This time, we will apply the blurred effect and remove the transparency in one operation.

convert \
	portrait.borders.png \
	mask.png \
	-compose blur \
	-set option:compose:args 48 \
	-composite \
	-alpha on \
Final squared portrait

We finally have it, the squared portrait.