惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
博客园 - 【当耐特】
量子位
博客园 - 叶小钗
有赞技术团队
有赞技术团队
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
博客园 - 司徒正美
爱范儿
爱范儿
美团技术团队
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
罗磊的独立博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
I
InfoQ
D
DataBreaches.Net
宝玉的分享
宝玉的分享

jdhao's digital space

Conversion between base64 and OpenCV or PIL Image 腾讯云对象存储博客图床开启 CDN 加速(不需要购买额外域名) Search and Replace in Multiple Files in Vim/Neovim Change Table Column Width in LaTeX Image or Table Side by Side in LaTeX LaTeX 并排显示图像或表格 Firenvim: Neovim inside Your Browser Content inside HTML tags missing in Latest Hugo? Creating Markdown Front Matter with Ultisnips Labelme JSON 标注格式转 voc XML 格式 Nifty Nvim Techniques That Make My Life Easier -- Series 6 macOS 下如何为视频制作字幕 Running Command Asynchronously inside Neovim Resolving Merge Conflict after Git Stash Pop Pylint: command not found? A Hands-on Experience with Neovim's Built-in LSP Support How to Convert PDF to Images with Imagemagick 互联网上常用缩略语集锦 File Backup in Neovim Converting PDF Pages to Images with Poppler Nifty Nvim Techniques That Make My Life Easier -- Series 5 Neovim Configuration for System-wide Use How to sort a list of tuple or list in Python -- lambda or itemgetter? Building A Vim Statusline from Scratch 人类第一颗原子弹爆炸始末 Distributed Training in PyTorch with Horovod Learning Expect Programming Essential Knowledge about SSH Nifty LaTeX Techniques -- Series 1 更改 Adsense 邮寄地址,重新寄送 PIN
How to Do Image Alpha-compositing in Pillow
2022-04-01 · via jdhao's digital space

What is alpha-compositing

If you have two images A and B, you alpha-composite A over B, it will create an effect that you can see both image A and B in the resulting image, as if A has transparency and you can see through A and also see B.

How does alpha-compositing work?

Suppose a is the src image, b is the destination image, we composite a over b. The equation for alpha value and pixel value of the generated image are:

\[\alpha = \alpha_a + \alpha_b (1-\alpha_a)\]

\[\begin{aligned} v &= \frac{ \alpha_a v_a + \alpha_b (1-\alpha_a) v_b}{\alpha_a + \alpha_b (1-\alpha_a)}\\ &= \frac{\alpha_a}{\alpha_a + \alpha_b (1-\alpha_a)} v_a + \frac{\alpha_b (1-\alpha_a)}{\alpha_a + \alpha_b (1-\alpha_a)} v_b \end{aligned}\]

So it is easy to see that the resulting pixel value is a linear combination of pixel value from a and b.

For RGB images, this is done for per-channel per-pixel.

Note that in Pillow, the alpha value is rescaled from \([0, 1.0]\) to \([0, 255]\). So in the following text, I will use the range \([0, 255]\) and it should not cause any confusion.

import numpy as np
from PIL import Image


def main():
    # alpha for im1 is 0.5 (rescaled in [0, 1.0] range)
    im1 = Image.new('RGBA', (100, 100), (200, 0, 0, 128))
    # alpha for im2 is also 0.5
    im2 = Image.new('RGBA', (100, 100), (0, 100, 0, 128))

    # im1.show()
    # im2.show()

    im1.alpha_composite(im2)
    im1.show()

    r, g, b, a = im1.split()
    print(np.array(g))


if __name__ == "__main__":
    main()

To test the correctness of the above equations, use the above script. After alpha compositing, the value for channel R and G of im1 should be all 67, for channel B should be all 0, and for alpha channel should be all 192 (without rescale, it should be 0.75).

Composite two JPEG images

So if we have a large image A and we want to overlay a small image B onto it. How do we do it correctly? Suppose A and B are just JPEG images with no alpha channels.

We should first add an alpha channel with value 255 for image A. This is the crucial step for correct alpha-compositing. This will not affect the region in A that do not have overlap with image B. Otherwise, you will get a opaque look for those regions.

Then we should set proper alpha for image B, depending on how you want to composite. In this case, the pixel value for the resulting image becomes simply:

\[v = \alpha v_a + (1-\alpha) v_b\]

You can change \(\alpha\) to control the contribution of A and B to the result image. For example, if you want B to completely dominate the overlapping region, you can set alpha to 255. If you want to show partial A and B, you can set alpha to a value between 0 and 255. The larger alpha gets, the less you will see the underlying part of image A.

import numpy as np

from PIL import Image, ImageFilter


def main():
    im = Image.open('./cat.jpg')
    im.putalpha(255)

    em_im = Image.open('./fish.jpg')
    em_im.putalpha(150)

    dst_coord = (360, 310)
    src_coord = (0, 0)

    im.alpha_composite(em_im, dst_coord, src_coord)
    im.save('cat-and-fish.png')


if __name__ == "__main__":
    main()

In the title image, I show the generated image using three different alpha values for image B. The transparency decreases as we increase the value of alpha.

References