はじめに
背景画像と透過画像を用意して、背景画像の上で透過画像を舞わせたい。
やりたいことの大半は下の記事と全く同じ↓
しかしこの記事のやり方では透過がうまくいっていないようだ。
透過画像の合成について書かれた記事を見つけた↓
要はこの2記事の合わせ技である。ソースコードもほぼコピペなのだ…
やったこと
BG.pngの上にchar.pngを重ねて、移動、回転、拡大縮小ができるようにした
BG.png (アルファなし)↓
char.png (アルファ付き)↓
ソースコード
import cv2
import numpy as np
def main():
filename_back = "BG.png"
filename_front = "char.png"
img_back = cv2.imread(filename_back)
img_front = cv2.imread(filename_front, -1) #アルファチャンネルを読み込む
pos=[50,500] #char.pngを表示する座標(左上)
xc, yc = int(img_front.shape[0]/2),int(img_front.shape[1]/2) #前景画像の回転中心はchar.pngの中心
angle=0
size=1
while True:
back = img_back.copy() #直接上書きされないようにコピー
img = putSprite_mask2(back, img_front, pos, angle, (xc,yc), size) #(x,y)からposに変更、sizeを追加
cv2.putText(img, f"angle={angle}", (10,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
cv2.putText(img, f"pos={pos}", (10,60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
cv2.putText(img, f"size={size}", (10,90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
cv2.imshow("putSprite_mask2", img)
key = cv2.waitKey(1) & 0xFF
if key == 27: #escキー
break
elif key == ord("w"): #上へ
pos[1]-=1
elif key == ord("s"): #下へ
pos[1]+=1
elif key == ord("a"): #左へ
pos[0]-=1
elif key == ord("d"): #右へ
pos[0]+=1
elif key == ord("q"): #左回転
angle+=1
elif key == ord("e"): #右回転
angle-=1
elif key == ord("r"): #大きく
size+=0.1
elif key == ord("f"): #小さく
size-=0.1
def putSprite_mask2(back, front4, pos, angle=0, center=(0,0), size=1):
x, y = pos
xc, yc = center
fh, fw = front4.shape[:2]
bh, bw = back.shape[:2]
# 回転中心と四隅の距離の最大値を求める
pts = np.array([(0,0), (fw,0), (fw,fh), (0,fh)])
ctr = np.array([(xc,yc)])
r = int(np.sqrt(max(np.sum((pts-ctr)**2, axis=1)))) #回転中心から最も遠い頂点の直線距離
s=abs(size) #追記
r=int(r*s) if s>1 else r #追記
# 回転する
M = cv2.getRotationMatrix2D((xc,yc), angle, size) #1からsizeに変更
M[0][2] += r - xc
M[1][2] += r - yc
imgRot = cv2.warpAffine(front4, M, (2*r,2*r)) # 回転画像を含む外接四角形
# 外接四角形の全体が背景画像外なら何もしない
x0, y0 = x+xc-r, y+yc-r
if not ((-2*r < x0 < bw) and (-2*r < y0 < bh)) :
return back
# 外接四角形のうち、背景画像内のみを取得する
x1, y1 = max(x0, 0), max(y0, 0)
x2, y2 = min(x0+2*r, bw), min(y0+2*r, bh)
imgRot = imgRot[y1-y0:y2-y0, x1-x0:x2-x0]
# マスク手法で外接四角形と背景を合成する
#front_roi = imgRot[:, :, :3]
#mask1 = imgRot[:, :, 3]
#mask_roi = 255 - cv2.merge((mask1, mask1, mask1))
#roi = back[y1:y2, x1:x2]
#tmp = cv2.bitwise_and(roi, mask_roi)
#tmp = cv2.bitwise_or(tmp, front_roi)
#back[y1:y2, x1:x2] = tmp #これでは透過できていなかった
back[y1:y2, x1:x2] = back[y1:y2, x1:x2] * (1-imgRot[:,:,3:]/255) + \
imgRot[:,:,:3] * (imgRot[:,:,3:]/255)
return back
if __name__=="__main__":
main()
背景画像をBG.pngにしているが、もし背景画像をnp.zerosで生成するなら
img_back=np.zeros((HEIGHT, WIDTH, 3),dtype=np.uint8)
img_back+=255 #(255,255,255)の色を生成して真っ白にする
とすること。引数の順番が (高さ,幅,ビット深さ) と、高さが先な点に注意。
または、RGB値を指定して任意色で塗りつぶすなら
color=(B,G,R)
img_back = np.full((HEIGHT, WIDTH, 3), color, np.uint8)
とすることもできる(もちろん(255,255,255)なら真っ白にもできる。)
いずれにせよ、dtype=np.uint8 はとてもとても大事なので注意すること