LoginSignup
5
3

More than 3 years have passed since last update.

alphaチャンネルを持った画像を他画像の任意の位置に描画する方法 [opencv]

Last updated at Posted at 2019-08-15

はじめに

opencvを使って、alphaチャンネルを持った画像をalphaチャンネルを持ってない画像(背景画像)の任意の位置に描画しようと思います。

alphaチャンネルとは

alphaチャンネルは、画像(np.ndarray)の透過率の情報を保持するチャンネルです。
opencvでは、BGR表現モデルの画像であれば各チャンネルが持っている情報は

  • img[:, :, 0]がB(Blue)
  • img[:, :, 1]がG(Green)
  • img[:, :, 2]がR(Red)

になります。これにalphaチャンネルを付け加えると

  • img[:, :, 3]が透過率

になります。
alphaチャンネルが持っている値は[0, 255]になりますが、透過率は0の方が高くて255の方が低くなる見たいです。
つまり、画像の一番左上のピクセルの値img[0, 0, :3]が(127, 127, 127)だったとすると、本来このピクセルの色はグレーになりますが、img[0, 0, 4]が0だったとすると、このピクセルの色情報は完全に無効になり、重ね合わせた背景のピクセルのみが表示されるようになります。

背景のピクセルの色情報をb, 透過付き画像の色情報をi, 透過率をa, 重ね合わせたあとのピクセルの色情報をpとすると、計算式は以下のようになります。

$p = b * (1-a/255) + i * (a / 255)$

方法

自前import

import cv2
import numpy

alphaチャンネル付き画像を読み込む

# -1がなかったらhogera.pngが透過付き画像でもBGR表現モデルになる
img = cv2.imread("hoge/geho/hogera.png", -1)  

背景画像作成

wall = np.full((500, 500, 3), 127)

任意の座標(x, y)に重ね合わせる処理

  • alphaチャンネル非対応
  • はみ出し非対応
img_h, img_w = img.shape[:2]
wall[y:y+img_h, x:x+img_w, :] = img[:, :, :3]

任意の座標(x, y)に重ね合わせる処理(はみ出し考慮バージョン)

  • alphaチャンネル非対応
wall_h, wall_w = wall.shape[:2]
img_h, img_w = img.shape[:2]

if x < 0:
    img_w += x        
    img = img[:, -x:, :]
    x = 0
elif x + img_w > wall_w:
    img_w = x + img_w - wall_w
    img = img[:, :img_w, :]

if y < 0:
    img_h += y
    img = img[-y:, :, :]        
    y = 0
elif y + img_h > wall_h:
    img_y = y + img_h - wall_h
    img = img[img_h:, :, :]

if img.size == 0:
    wall[y:y+img_h, x:x+img_w, :] = img[:, :, :3]

任意の座標(x, y)に重ね合わせる処理(alphaチャンネル対応バージョン)

  • はみ出し非対応
img_h, img_w = img.shape[:2]

alpha = img[:, :, 3] / 255  # [0, 1]に正規化
alpha = alpha.reshape(*alpha.shape, 1)  # broadcastのためにreshape
wall[y:y+img_h, x:x+img_w, :] = img[:, :, :3] * alpha + wall[y:y+img_h, x:x+img_w, :] * (1-alpha)

関数化

def plot_alpha_img(wall, img, loc):
    x, y = loc

    wall = wall.copy()
    wall_h, wall_w = wall.shape[:2]
    img_h, img_w = img.shape[:2]

    if x < 0:
        img_w += x        
        img = img[:, -x:, :]
        x = 0
    elif x + img_w > wall_w:
        img_w = x + img_w - wall_w
        img = img[:, :img_w, :]

    if y < 0:
        img_h += y
        img = img[-y:, :, :]        
        y = 0
    elif y + img_h > wall_h:
        img_h = y + img_h - wall_h
        img = img[img_h:, :, :]

    if img.size == 0:
        return wall

    alpha = img[:, :, 3] / 255
    alpha = alpha.reshape(*alpha.shape, 1)
    wall[y:y+img_h, x:x+img_w, :] = img[:, :, :3] * alpha + wall[y:y+img_h, x:x+img_w, :] * (1-alpha)

    return wall

使用例

plt.imshow(img)

image.png

plt.imshow(wall)

image.png

wall = plot_alpha_img(wall, img, (100, 100))
wall = plot_alpha_img(wall, img, (70, 70))
plt.imshow(wall)

image.png

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3