よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Pillow】RGBをRGBAに変換する方法と画像合成

画像モードのRGBをRGBAに変換し、アルファチャンネルを追加する方法と、画像合成。



はじめに


画像ファイルはR=赤・G=緑・B=青の3つの色チャンネルと、透過性を示すA=アルファチャンネルで表現されます。
マスゲームに例えると、各マスが3つか4つのチャンネルのデータをそれぞれ持ち、それらのマスが縦横に連なることで画像を表現する。

画像処理ライブラリのPillowなどで画像ファイルの「モード」を確認するとRGBRGBAと表示されます。 三原色の組み合わせで多様な色を作るように、コンピュータでは色チャンネルRGBで色を作り、アルファチャンネルで背景との透け具合を変えます。

RGBの3チャンネルしかない画像ファイルにアルファチャンネルを追加しRGBAの4チャンネルに変換する方法を見ていきます。



目次




作業環境


  • Androidスマホ
  • termux
  • Python3.8
  • JupyterNotebook
  • 外部画像処理ライブラリPillow
    • pip install pillowなどでインストールしておく

【参考】
Python, Pillowで透過png画像を作成するputalpha | note.nkmk.me



JupyterNotebookに画像表示用関数


作業中に画像を確認したいので関数を作っておきます。
matplotlibを使うと画像が並べて表示できますがIPython.displayでやります。

JupyterNotebookで画像を表示する方法 - よちよちpython

# 画像表示
def out_image(filename):
    from IPython.display import Image
    return Image(filename)

# 画像ファイル名と表示
filename = "noumen.JPG"
out_image(filename)

f:id:chayarokurokuro:20200814112213j:plain

今回はこの画像を使います。

アルファチャンネルの追加


画像モードの確認

# Pillowのインポート
from PIL import Image

# 画像オブジェクトに変換
im_obj = Image.open(filename)

# 画像のサイズとモードの確認
print("サイズ", im_obj.size)
print("モード", im_obj.mode)
サイズ (320, 320)
モード RGB

アルファチャンネルの抜けた3チャンネルになっています。



アルファチャンネルの追加


pillowのImage.putalpha()メソッドを使うとアルファチャンネルが追加できます。
引数には透過性をalpha=数値0~225の整数で与えます。

  • 0 : スケスケ
  • 255 : 透けない
  • 未指定 : 実行エラー

この透過性が画像全面に施される。

putalpha()は元の画像オブジェクトにアルファチャンネルを追加し上書きするので、元画像オブジェクト.copy()メソッドでコピーしてから行います。

  • 元画像がRGB(8bit x 3: フルカラー)またはL(8bit x 1: 白黒)の場合は新たにアルファチャンネルが追加され、
  • RGBAまたはLAの場合は元のアルファチャンネルが更新される。

参考

# ◇alpha=0 スケスケの場合
## 画像オブジェクトのコピー
im_obj_0 = im_obj.copy()
## アルファチャンネル追加
im_obj_0.putalpha(alpha=0)


# ◇alpha=128 半分ぐらい透ける場合
# コピー
im_obj_128 = im_obj.copy()
# アルファチャンネル追加
im_obj_128.putalpha(128)


# ◇alpha=255 透けないの場合
## コピー
im_obj_255 = im_obj.copy()
## アルファチャンネル追加
im_obj_255.putalpha(alpha=255)



モード確認

# モード確認
print("0", im_obj_0.mode)
print("128", im_obj_128.mode)
print("255", im_obj_255.mode)
0 RGBA
128 RGBA
255 RGBA

チャンネル追加されてRGBAに変わっています。



画像ファイルに保存

ファイル拡張子を「jpg」「JPG」「jpeg」にすると保存実行時に「RGBAはjpegにできない」というようなエラーが出ます。拡張子「png」で保存。

im_obj_0.save("noumen_alpha0.png")
im_obj_128.save("noumen_alpha128.png")
im_obj_255.save("noumen_alpha255.png")



画像表示

  • alpha=0のスケスケ画像
out_image("noumen_alpha0.png")

f:id:chayarokurokuro:20200814112326j:plain


何もない…

  • alpha=128の半スケ画像
out_image("noumen_alpha128.png")

f:id:chayarokurokuro:20200814112424j:plain

  • alpha=255の透けない画像
out_image("noumen_alpha255.png")

f:id:chayarokurokuro:20200814112503j:plain


画像サイズが、元のRGBの3チャンネルの能面画像は7KBほどでしたが、RGBAにすると60KBほどに重くなりました。



2枚の画像の張り合わせ

背景画像に半透け画像を貼り付け

半分透ける能面を回転して別の画像に貼り付けてみる。

# 画像読み込み
im_half = Image.open("noumen_alpha128.png") #半分透ける
im_back = Image.open("noumen_alpha255.png") # 透けない

# 半透けを左90度回転させる
im_rotate90 = im_half.rotate(90)

# 透けない背景画像に半スケを貼る
im_back.paste(im_rotate90)

# 完全画像の保存
im_back.save("noumen_paste.png")

完全画像の表示

out_image("noumen_paste.png")

f:id:chayarokurokuro:20200814112824j:plain

あれ、思った図と違った。通常の画像と回転画像が透けて重なるかと想像していたが、単に背景画像に上書きされただけだった。



別のやり方で。

合成


これ重要(個人的に)。テストにでます。
【参考】
Python, Pillowで二枚の画像をマスク画像に従って合成 | note.nkmk.me

全面を一律の割合で合成 マスク画像としてベタ画像を使うと、画像全面が一律の割合で合成される。

Image.new()で階調が128のベタ画像を作成してマスク画像を作る。

# 画像読み込み
im_half = Image.open("noumen_alpha128.png") #半分透ける
im_back = Image.open("noumen_alpha255.png") # 

# 半透けを左90度回転させる
im_rotate90 = im_half.rotate(90)

# マスク画像 alpha=128で
mask = Image.new("L", im_half.size, 128)


# 透けない背景画像に半スケを貼る
im = Image.composite(im_back, im_rotate90, mask)

# 完全画像の保存
im.save("noumen_paste2.png")
out_image("noumen_paste2.png")

f:id:chayarokurokuro:20200814113025j:plain

うわッ、怖い!
背景画像と手前の画像の合成ができました。 マスクの作成の部分で透過性を変えると合成具合が変わる。
関数にしてマスク画像のalpha値を変えて見てみます。

def image_composite(num):
    # 画像読み込み
    im_half = Image.open("noumen_alpha128.png") #半分透ける
    im_back = Image.open("noumen_alpha255.png") # 

    # 半透けを左90度回転させる
    im_rotate90 = im_half.rotate(90)

    # マスク画像 alpha=128で
    mask = Image.new("L", im_half.size, num)

    # 透けない背景画像に半スケを合成
    im = Image.composite(im_back, im_rotate90, mask)

    # 完全画像の保存
    image_name = "noumen_compo{}.png".format(num)
    im.save(image_name)
    return image_name
    
image_list = []
for num in range(0,255,10):
    image_name = image_composite(num)
    image_list.append(image_name)
    
print(image_list)
['noumen_compo0.png', 'noumen_compo10.png', 'noumen_compo20.png', 'noumen_compo30.png', 'noumen_compo40.png', 'noumen_compo50.png', 'noumen_compo60.png', 'noumen_compo70.png', 'noumen_compo80.png', 'noumen_compo90.png', 'noumen_compo100.png', 'noumen_compo110.png', 'noumen_compo120.png', 'noumen_compo130.png', 'noumen_compo140.png', 'noumen_compo150.png', 'noumen_compo160.png', 'noumen_compo170.png', 'noumen_compo180.png', 'noumen_compo190.png', 'noumen_compo200.png', 'noumen_compo210.png', 'noumen_compo220.png', 'noumen_compo230.png', 'noumen_compo240.png', 'noumen_compo250.png']
i = 0
print(image_list[i])
out_image(image_list[i])
noumen_compo0.png

20200814113231

i = 5
print(image_list[i])
out_image(image_list[i])
noumen_compo50.png

20200814113317

i = 10
print(image_list[i])
out_image(image_list[i])
noumen_compo100.png

20200814113352

i = 15
print(image_list[i])
out_image(image_list[i])
noumen_compo150.png

20200814113428

i = 20
print(image_list[i])
out_image(image_list[i])
noumen_compo200.png

20200814113507

i = 25
print(image_list[i])
out_image(image_list[i])
noumen_compo250.png

20200814113544

gifアニメ動画を作って遊べそうです。

Pythonでpngファイルからgifアニメ画像作成 - よちよちpython



おわりに


OpenCVで顔認識した位置に自動的にお面を貼り付けられないか、と考えて今回と前回の投稿をやりました。

参考



次はもう少し手の込んだ加工にチャレンジ!



以上です。