概要
OpenCV で 2 枚の画像をアルファブレンド (alpha blend) して、合成する方法について紹介します。
アルファブレンド
アルファブレンド (alpha blend) とは、2 枚の画像を係数 $\alpha \in [0, 1]$ を用いて、以下の式で合成する処理を指します。
$$ \text{dst}(x, y) = \text{src1}(x, y) \times \alpha + \text{src2}(x, y) \times (1 – \alpha) $$この式は、2 枚の入力画像 $\text{src1}$ と $\text{src2}$ の画素値を $\alpha:1 – \alpha$ の割合で組み合わせ、出力画像の画素値を生成することを意味します。この加算処理を行うためには、2 枚の入力画像の大きさが同じである必要があります。
cv2.addWeighted
この関数は以下の計算を行います。
$$ \text{dst} = \text{src1} \times \alpha + \text{src2} \times \beta + \gamma $$この関数で、$\beta = 1 – \alpha, \gamma = 0$ とすれば、アルファブレンドの式になります。
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
名前 | 型 | デフォルト値 |
---|---|---|
src1 | ndarray | |
入力画像1 | ||
alpha | ndarray | |
$\alpha$ | ||
src2 | ndarray | |
入力画像2 | ||
beta | ndarray | |
$\beta$ | ||
gamma | ndarray | |
$\gamma$ |
名前 | 説明 | ||
---|---|---|---|
dst | 出力画像 |
すべての画素を同じアルファ値でブレンドする
import cv2
import numpy as np
from IPython.display import Image, display
def imshow(img):
"""ndarray 配列をインラインで Notebook 上に表示する。"""
ret, encoded = cv2.imencode(".jpg", img)
display(Image(encoded))
def alpha_blend(img1, img2, alpha):
dst = cv2.addWeighted(img1, alpha, img2, 1 - alpha, 0)
return dst
img1 = cv2.imread("sample1.jpg")
img2 = cv2.imread("sample2.jpg")
assert img1.shape == img2.shape, "2つの画像のサイズが異なります。"
dst = alpha_blend(img1, img2, 0.5)
imshow(dst)
画素によって異なるアルファ値でブレンドする
画素ごとに異なるアルファ値を用いることで、グラデーションを利用した合成が可能になります。
横方向にグラデーションする
形状が (1, Width, 1) で値が $[0, 1]$ に連続的に変化する配列を作っておくと、アルファブレンドした際に (Height, Width, Channels) にブロードキャストされて計算されます。
def alpha_blend_horizontal(img1, img2):
# アルファ値を作成する。
w = img1.shape[1]
alpha = np.linspace(0, 1, w).reshape(1, -1, 1) # (1, W, 1)
dst = img1 * alpha + img2 * (1 - alpha)
return dst
img1 = cv2.imread("sample3.jpg")
img2 = cv2.imread("sample4.jpg")
assert img1.shape == img2.shape, "画像の大きさは同じでなければならない"
dst = alpha_blend_horizontal(img1, img2)
imshow(dst)
縦方向にグラデーションする
形状が (height, 1, 1)
で、値が $[0, 1]$ の範囲で連続的に変化する配列を作成すると、アルファブレンドを行う際にこの配列が (height, width, channels)
にブロードキャストされ、計算が行われます。
def alpha_blend_vertical(img1, img2):
# アルファ値を作成する。
h = img1.shape[0]
alpha = np.linspace(0, 1, h).reshape(-1, 1, 1) # (H, 1, 1)
dst = img1 * alpha + img2 * (1 - alpha)
return dst
img1 = cv2.imread("sample3.jpg")
img2 = cv2.imread("sample4.jpg")
assert img1.shape == img2.shape, "画像の大きさは同じでなければならない"
dst = alpha_blend_vertical(img1, img2)
imshow(dst)
矩形外をグレーアウトする
アルファブレンドの応用例として、画像の注目領域以外をグレーアウトする方法を紹介します。
まず、入力画像の残す画素に対してはアルファ値を 1、グレーアウトする画素に対してはアルファ値を 0.5 とします。このアルファ値を用いて、入力画像と同じ大きさの黒い画像をアルファブレンドします。
def alpha_blend_rect(img, p1, p2):
# 入力画像の残す画素を (1, 1, 1)、グレーアウトする画素を (0, 0, 0) で表したマスクを作成する。
img2 = np.zeros_like(img)
alpha = np.full(img.shape, 0.5, dtype=float)
cv2.rectangle(alpha, p1, p2, color=(1, 1, 1), thickness=-1)
dst = img * alpha + img2 * (1 - alpha)
return dst
img = cv2.imread("sample2.jpg")
dst = alpha_blend_rect(img, (100, 100), (250, 250))
imshow(dst)
コメント