nyanpyou Note

主な目的は調べたり作ったりしたプログラミング備忘録(予定)

OpenCVとNumpyで比較明合成をしたい話2

以前OpenCVとNumpyで比較明合成を行うという話を書いた。

nyanpyou.hatenablog.com

この時は配列の要素に二重のfor文でアクセスする形になっていたため処理が遅く、その部分が課題だった。今回処理速度が改善できたので改めて書き残す。
今回は以下の単純な画像2枚を比較し、より明るい部分だけを残すことを目指す。

f:id:nyanpyou106:20200319231630p:plainf:id:nyanpyou106:20200319231643p:plain
今回合成する2枚の画像 左がimg1、右がimg2

そもそも

細かい所を気にしなければ、np.where()を使うと1行で書けることが分かった。

import numpy as np
import cv2

image1 = cv2.imread("image1.jpg")
image2 = cv2.imread("image2.jpg")

result = np.where(image1>image2, image1, image2)
cv2.imwrite("result.jpg", result)

例えばこの方法で前回使用した画像について合成を行うと、このような画像が得られる。

f:id:nyanpyou106:20200320113217j:plain
前回の画像にnp.where()を用いた結果
一見もうこれだけでいい気がするものの、この方法だといつも望み通りの画像が得られるわけではないことも分かった。今回のimg1とimg2を用いて実行すると、次のような画像が得られる。
f:id:nyanpyou106:20200319232512p:plain
今回の画像にnp.where()を用いた結果
より明るい部分を抜き出したいので、少なくとも左側は黄色になっていて欲しいのだが、ベージュになっている。
これはnp.where()が配列の全ての要素同士を比べるためで、その影響で色が変わってしまうことがあるようだ。当たり前だが、配列中の要素からBGR値をまとめて輝度に変換し、それを用いて比較するなんてことはやってくれない。

#np.where()の挙動の例
#ある画像の座標(x,y)のBGR値
a=np.array([255,0,255])
#別の画像の座標(x,y)のBGR値
b=np.array([0,255,0])
#np.where()で上記二つを比較して得られるBGR値
c=np.where(a>b, a, b)
print(c)
>>array([255, 255, 255])

やってくれないのであれば自分でそのように命令すればいいということで、上の結果を踏まえて、for文と配列要素への直接アクセスを使わずに比較明合成を行うコードを作ってみた。

比較明合成2

import cv2
import numpy as np

img1 = cv2.imread("img1.png")
img2 = cv2.imread("img2.png")

gray_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray_img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
#グレースケールの比較で作成したimg1用のマスク(img1の方が明るい画素を示す)
mask_img1 = np.where(gray_img1>gray_img2, 255, 0).astype(np.uint8)
#img2用のマスク(0と255を入れ替え)(img2の方が明るい画素を示す)
mask_img2 = np.where(mask_img1==255, 0, 255).astype(np.uint8)

#作成したマスクを使って元画像から抜き出し
masked_img1 = cv2.bitwise_and(img1, img1, mask=mask_img1)
masked_img2 = cv2.bitwise_and(img2, img2, mask=mask_img2)

img3 = masked_img1 + masked_img2

cv2.imwrite("imgresult.png", img3)

まず画像をグレースケールに変換し、これらを比較することでマスク画像を2枚作成する。その後マスク画像と元の画像でビット演算を行って明るい部分をRGBで抜き出し、加算する。
マスク画像は符号なし8ビット整数に変換しておく必要があり、忘れるとエラーが出てしまう。
masked_img1とmasked_img2は以下のようになる。

f:id:nyanpyou106:20200320120118p:plainf:id:nyanpyou106:20200320120127p:plain
masked_img1とmasked_img2
これらを加算して得られた結果が以下。
f:id:nyanpyou106:20200320125415p:plain
合成結果

まとめ

np.where()とcv2.bitwise_and()の使い方を学び、処理速度を向上した比較明合成のコードを作った。前回とは測定PCが異なるが、timeモジュールを使って実行時間を測定したところ、for文を使ったコードは3秒、今回のコードは0.2秒となり、15倍の高速化が出来た。やはりNumpyの関数を使って処理を行うと速度が段違い。これからも使える関数を増やしていきたい。
Numpyのよく使う関数とその使用例が載った参考書とかあれば欲しい。