Simple overlay of 2 images
This is an entertaining story about how one image overlays another. If you were engaged in raster graphics, wrote games or graphic editors, you are unlikely to find something for yourself in the article. Everyone else, I hope, will be interested to know that this task is not as trivial as it seems at first glance.
So, we have 2 pictures in RGBA format (i.e. 3 colors + alpha channel): Approx. 1: The criterion for selecting pictures was the presence of transparent, opaque and translucent areas. What is depicted on them is not so important. Note 2: Pictures will look like they are visible in Photoshop, links lead to real pictures. The task is to assemble a third of them, in the same RGBA format, with the correct transparency and colors. That's how Photoshop copes with this, take this image as a reference:
I will show examples on python using PIL .
So the simplest code is:
We mix both images using the alpha channel of the second one. In this case, each component of each pixel of the resulting image (including the alpha channel) will be calculated as X 2 × a + X 1 × (1-a), where a is the alpha value for this pixel from the transmitted alpha channel im2.
The code gives this result:
Inconsistencies are immediately visible - in the place where the red checkmark is overlapped by the second picture, it became even more transparent than on the original, although logic suggests that when one translucent object obscures the other, the result should be more opaque than both objects. This happened because, in addition to mixing colors, alpha channels were mixed. The intensity of the second alpha channel was approximately 0.25, the intensity of the first 0.5. We mixed them with the intensity of the second: 0.25 × 0.25 + 0.5 × (1-0.25) = 0.44.
If the background (first) picture were generally opaque, then after such mixing, translucent spots would appear on it, which contradicts all the laws of physics.
Let's try to eliminate this effect by clearing the alpha channel of the second picture before mixing:
The result will be this: The transparency at the overlay has become normal, but the shadow from the shape, which was barely visible on the original, has turned into clearly visible black stripes. This happened because, when calculating the color, the overlay algorithm no longer takes into account the fact that the second image at this place is more intense than the first (where should he get this data, because the second image no longer has an alpha channel), so it mixes the colors honestly, from the transmitted alpha channel of the second image. And in the alpha channel in this place, the intensity is about 0.5. Because the black color, which is actually quite a bit, participates in an equal proportion with the blue. But to guess, we’d better think about how real objects with such properties would look.
Imagine 2 translucent glass: one is blue, the other is red. Blue is closer than red. Then, the total opacity equals the sum of the two glasses low opacity Us glass and low transparency (i.e., 1 A- C ), multiplied by the opacity far: A C + (1 A- C ) × A K .
What about color? More precisely, it is not the color itself that is of interest, but the proportion of the color of the dipped glass. It is equal to the opacity of the dipped glass divided by the total opacity. Those. in general, the opacity value used to calculate colors is not equal to the opacity of the resulting pixel.
In this model, each glass is one pixel of the image. If you collect an image from them, you get a more general conclusion - the alpha channel used to calculate the color of the resulting pixels is not equal to the alpha channel of the resulting image.
If you implement the “correct” transparency using python, you get something like this:
And, in fact, the result: It almost does not differ from the result created in Photoshop. By the way, if someone knows if it is possible using PIL to get the same result without pixel-by-pixel work with the image, let me know.
So, we have 2 pictures in RGBA format (i.e. 3 colors + alpha channel): Approx. 1: The criterion for selecting pictures was the presence of transparent, opaque and translucent areas. What is depicted on them is not so important. Note 2: Pictures will look like they are visible in Photoshop, links lead to real pictures. The task is to assemble a third of them, in the same RGBA format, with the correct transparency and colors. That's how Photoshop copes with this, take this image as a reference:
I will show examples on python using PIL .
So the simplest code is:
im1 = Image.open('im1.png')
im2 = Image.open('im2.png')
im1.paste(im2, (0,0), im2) # Последний параметр — альфаканал, используемый для наложения
im1.save('r1.png')
We mix both images using the alpha channel of the second one. In this case, each component of each pixel of the resulting image (including the alpha channel) will be calculated as X 2 × a + X 1 × (1-a), where a is the alpha value for this pixel from the transmitted alpha channel im2.
The code gives this result:
Inconsistencies are immediately visible - in the place where the red checkmark is overlapped by the second picture, it became even more transparent than on the original, although logic suggests that when one translucent object obscures the other, the result should be more opaque than both objects. This happened because, in addition to mixing colors, alpha channels were mixed. The intensity of the second alpha channel was approximately 0.25, the intensity of the first 0.5. We mixed them with the intensity of the second: 0.25 × 0.25 + 0.5 × (1-0.25) = 0.44.
If the background (first) picture were generally opaque, then after such mixing, translucent spots would appear on it, which contradicts all the laws of physics.
Let's try to eliminate this effect by clearing the alpha channel of the second picture before mixing:
im1 = Image.open('im1.png')
im2 = Image.open('im2.png')
im1.paste(im2.convert('RGB'), (0,0), im2)
im1.save('r2.png')
The result will be this: The transparency at the overlay has become normal, but the shadow from the shape, which was barely visible on the original, has turned into clearly visible black stripes. This happened because, when calculating the color, the overlay algorithm no longer takes into account the fact that the second image at this place is more intense than the first (where should he get this data, because the second image no longer has an alpha channel), so it mixes the colors honestly, from the transmitted alpha channel of the second image. And in the alpha channel in this place, the intensity is about 0.5. Because the black color, which is actually quite a bit, participates in an equal proportion with the blue. But to guess, we’d better think about how real objects with such properties would look.
Imagine 2 translucent glass: one is blue, the other is red. Blue is closer than red. Then, the total opacity equals the sum of the two glasses low opacity Us glass and low transparency (i.e., 1 A- C ), multiplied by the opacity far: A C + (1 A- C ) × A K .
What about color? More precisely, it is not the color itself that is of interest, but the proportion of the color of the dipped glass. It is equal to the opacity of the dipped glass divided by the total opacity. Those. in general, the opacity value used to calculate colors is not equal to the opacity of the resulting pixel.
In this model, each glass is one pixel of the image. If you collect an image from them, you get a more general conclusion - the alpha channel used to calculate the color of the resulting pixels is not equal to the alpha channel of the resulting image.
If you implement the “correct” transparency using python, you get something like this:
def alpha_composite(background, image):
# get alphachanels
alpha = image.split()[-1]
background_alpha = background.split()[-1]
new_alpha = list(background_alpha.getdata())
new_blending = list(alpha.getdata())
for i in xrange(len(new_alpha)):
alpha_pixel = new_blending[i]
if alpha_pixel == 0:
new_alpha[i] = 0
else:
new_alpha[i] = alpha_pixel + (255 - alpha_pixel) * new_alpha[i] / 255
new_blending[i] = alpha_pixel * 255 / new_alpha[i]
alpha.putdata(new_alpha)
background_alpha.putdata(new_blending)
del new_alpha
del new_blending
result = background.copy()
result.paste(image, (0, 0), background_alpha)
result.putalpha(alpha)
return result
im1 = Image.open('im1.png')
im2 = Image.open('im2.png')
alpha_composite(im1, im2).save('r3.png')
And, in fact, the result: It almost does not differ from the result created in Photoshop. By the way, if someone knows if it is possible using PIL to get the same result without pixel-by-pixel work with the image, let me know.