OpenCVの公式ドキュメントに、透過画像を張り付けるチュートリアルが存在するが、縁取りとか回転とかの方法までは書かれていなかったので記事を作成した。
結果
チュートリアルそのまま
縁取り
どうしても透過画像をそのまま張り付けると画像の縁がジャギジャギになってしまうので、以下の画像では透過情報を膨張・縮小して縁を調整している。
ランダム回転とランダム移動
以下の画像のように、ランダムに回転・移動ができると画像のバリエーションが簡単に増やせる。Deep Learningなどでデータを水増ししたい場合に利用することが多い。
解説
以下がメイン文。二枚の画像(前景hoge.png
と背景huga.png
)を読み込み、縁取りや回転・移動のフラグを設定すると合成された画像が表示される。cv2.imread()
の第二引数にはアルファチャンネルまで読み込むように-1
を設定する。縁取りしたい場合は一つ目のFalse
をTrue
に、ランダム回転・移動をしたい場合は二つ目のFalse
をTrue
にする。
main.py
import cv2
from paste import paste
if __name__ == '__main__':
img = paste(
cv2.imread('hoge.png', -1),# 前景
cv2.imread('huga.png', -1),# 背景
False, False # 縁フラグ、回転・移動フラグ
)
cv2.imshow('test', img) # 画像の表示
cv2.waitKey(0)
前景と背景の合成
基本的にはチュートリアルと同じ。
paste.py
import cv2
import numpy as np
from rotate import rotateR
def paste(fg, bg, mask_flg=True, random_flg=True):
"""
背景に前景を重ね合せる
[in] fg: 重ね合せる背景
[in] bg: 重ね合せる前景
[in] mask_flg: マスク処理を大きめにするフラグ
[in] random_flg: 前景をランダムに配置するフラグ
[out] 重ね合せた画像
"""
# Load two images
img1 = bg.copy()
if random_flg:# ランダム回転
img2, _ = rotateR(fg, [-90, 90], 1.0)
else:
img2 = fg.copy()
# I want to put logo on top-left corner, So I create a ROI
w1, h1 = img1.shape[:2]
w2, h2 = img2.shape[:2]
if random_flg:# ランダム移動
x = np.random.randint(0, w1 - w2 + 1)
y = np.random.randint(0, w1 - w2 + 1)
else:
x = 0
y = 0
roi = img1[x:x + w2, y:y + h2]
# Now create a mask of logo and create its inverse mask also
mask = img2[:, :, 3]
ret, mask_inv = cv2.threshold(
cv2.bitwise_not(mask),
200, 255, cv2.THRESH_BINARY
)
if mask_flg:# 縁を膨張・収縮で作成(膨張大きめ)
kernel1 = np.ones((5, 5), np.uint8)
kernel2 = np.ones((3, 3), np.uint8)
mask_inv = cv2.dilate(mask_inv, kernel1, iterations=1)
mask_inv = cv2.erode(mask_inv, kernel2, iterations=1)
# Now black-out the area of logo in ROI
img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)
# Take only region of logo from logo image.
img2_fg = cv2.bitwise_and(img2, img2, mask=mask)
# Put logo in ROI and modify the main image
dst = cv2.add(img1_bg, img2_fg)
img1[x:x + w2, y:y + h2] = dst
return img1
ランダム回転
ランダム回転に使用している自作関数は、cv2.warpAffine()
を利用している。
rotate.py
import cv2
import numpy as np
def rotate(img, angle, scale):
"""
画像を回転(反転)させる
[in] img: 回転させる画像
[in] angle: 回転させる角度
[in] scale: 拡大率
[out] 回転させた画像
"""
size = img.shape[:2]
mat = cv2.getRotationMatrix2D((size[0] // 2, size[1] // 2), angle, scale)
return cv2.warpAffine(img, mat, size, flags=cv2.INTER_CUBIC)
def rotateR(img, level=[-10, 10], scale=1.2):
"""
ランダムに画像を回転させる
[in] img: 回転させる画像
[in] level: 回転させる角度の範囲
[out] 回転させた画像
[out] 回転させた角度
"""
angle = np.random.randint(level[0], level[1])
return rotate(img, angle, scale), angle
以上。