やりたいこと
画像や動画の一部以外を黒く塗りつぶして切り抜きたい場合、画像のマスクを作成して、それを重ね合わせることで実現する。
失敗
Numpy を使って黒塗りしたい範囲は 0、切り抜きたい範囲は 1 とするマスクを作成。
行列の各要素をかけるとマスクできると思ったら、思ってたのと違う出力になった。
h_img, w_img = img.shape[:2]
mask = np.zeros((h_img, w_img, 3))
radius = int(h_img / 2)
center = radius
cv2.circle(mask, (center, center), radius, (1, 1, 1), thickness=-1, shift=0)
masked_img = img * mask
原因と解決策
Numpy で行列を定義するとき、 dtype を指定しないとデフォルトでは float64 型になる。一方で、opencv の imread で読み込んだ画像は、その輝度によって、CV_8U や CV_16F、 CV_32F で読み込まれる(通常は CV_8U)。 CV_8U だと Numpy のデータ型では np.uint8 に対応している。
つまり、失敗した例では CV_8U の画像に対して、 float64 型のマスクをかけている。
Numpy で行列を定義するときに、型を np.uint8 に指定してやる。
できた。
mask = np.zeros((h_img, w_img, 3), dtype=np.uint8) # 型を uint8 に指定
所感
データ型の不一致という初歩的なミスだが、最近 python ばかり使っていて、型を意識する機会が減っているせいか、気がつくのに時間がかかった。
そもそも、 np.zeros で定義せずに、np.zeros_like を使って image の型含めて行列定義すればよかった。