画像の算術演算

目的

  • 画像の算術演算(足し算,引き算,ビット演算など)を学ぶ
  • 次の関数を学ぶ :cv2.add(), cv2.addWeighted()

画像の足し算

画像の足し算は,OpenCVの関数 cv2.add() を使う,もしくはNumpyの演算によって res = img1 + img2 とする。ここでimg2はスカラーか、2つの画像(img1, img2)がビット数もデータ型も同じでなければならない。

Note

OpenCVの足し算とNumpyの足し算は違いがある。OpenCVの cv2.add() 関数を使った場合,上限値を超える和の値は res = min(img1+img2, MAX)のように打ち切られる.それに対し、Numpyの足し算は和に対し、MAXでモジュロを取った値になる: res = (img1+img2) mod MAX

例によって違いを理解しよう:

>>> x = np.uint8([250])
>>> y = np.uint8([10])

>>> print(cv2.add(x,y)) # 250+10 = 260 => 255
[[255]]

>>> print(x+y)         # 250+10 = 260 % 256 = 4
[4]

この現象は2枚の画像を足し合わせた時によりはっきり確認できる.OpenCVの足し算を使った方がよい結果になることが多いので、OpenCVの関数を使う方が良い.

画像の混合(ブレンド)

画像の混合とは基本的に画像の足し算のことであるが,それぞれの画像に異なる重み付けをして足し算するため、画像を混ぜあわせたり透明感を出したりしているような感じが得られる.画像の混合は次のように計算される :

g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)

ここで、上の式の \alpha の値を 0 \rightarrow 1 に徐々に変えていくと,画像 f_{0} からもう一つの画像 f_{1} に段々と変化させることができる.

ここでは2枚の画像を混合させた.1枚目の画像の重みを0.7、2枚目の画像の重みを0.3に設定し, cv2.addWeighted() 関数を使って以下の計算を行っている.

dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma

なお次のコードでは \gamma の値を0に設定している: (コード, ml.png, opencv_logo.png)

img1 = cv2.imread('ml.png')
img2 = cv2.imread('opencv_logo.jpg')

dst = cv2.addWeighted(img1,0.7,img2,0.3,0)

cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

結果は以下のようになる:

Image Blending

ビット単位の処理

OpenCVが提供するビット単位の処理には AND, OR, NOT とXORがある.これらの関数は,(後に紹介するように)画像の中から特定の領域を抽出する時や矩形「でない」形の注目領域を定義したり処理する時などに特に役に立つ.以下の例では画像の特定領域だけを変える方法を示す.

この例では画像上にOpenCVのロゴを表示したい.2枚の画像を単純に足し算するとロゴの色が変わってしまうし,混合するとロゴが透けてしまう。ここではロゴを元画像を保ったまま半透明表示させたい.ロゴの領域が長方形であれば注目領域(ROI)を指定するだけで済むが,OpenCVのロゴはそうではない. そこでこのような時はビット単位での処理を用いる: (コード, messi5.jpg, opencv_logo.png)

# Load two images
img1 = cv2.imread('messi5.jpg')
img2 = cv2.imread('opencv_logo.png')

# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]

# Now create a mask of logo and create its inverse mask also
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)

# 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[0:rows, 0:cols ] = dst

cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

以下に上のコードを実行した結果を示す.左がマスク画像で,右が結果の画像である.よりよく理解したければ,最終に至るまでの中間画像(特に img1_bgimg2_fg )を表示してみるとよいだろう.

Otsu's Thresholding

補足資料

課題

  1. cv2.addWeighted 関数を使って2枚の画像の片方の画像からもう片方の画像に滑らかに遷移する画像群を生成せよ.なおこの画像群にシーケンシャルな番号をつけ、cv2.videoCapture関数に引数として与えるとスライドショーとして表示することができる(ただし、それにはちょっと工夫が必要. cv2.VideoCapture()関数の説明書を読むこと) 。