勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

OpenCVやってみる-7. モルフォロジー変換

今回は、チュートリアル通り、モルフォロジー変換です。

モルフォロジー変換 — OpenCV-Python Tutorials 1 documentation

モルフォロジー

モルフォロジー(morphology)とは、

「一般に、形態、構造をいう。」

モルフォロジーとは - コトバンク

だそうです。
どゆこと?

OpenCVチュートリアルの説明によると、モルフォロジー変換は「画像上に写っている図形に対して作用するシンプルな処理」 とのこと。
チュートリアル内容を読みましたが、うまく端的な表現が思いつかない…
ひとまず、収縮(Erosion)、膨張(Dilation)という2種類の基本的な処理があり、これらを組み合わせてまた別の処理が定義されるようです。
また、処理対象は主に2値画像となります。

種類

チュートリアルでは、7種類の変換が紹介されていますが、今回シール点数集計で使えそうかな、というものだけやってみます。

  • 収縮(Erosion)
  • 膨張(Dilation)
  • オープニング(Opening)

まず2値化

前々回に2値化をやったので、これを元にモルフォロジー変換をやってみます。

その前に、resize関数で、fxfy引数を使っての縮小をやってみます。
今まではsize引数を用意してやっていましたが、ちょっと楽になります。

>>> import cv2
>>> img1 = cv2.imread('harupan_200317_1.jpg')
>>> img1 = cv2.resize(img1, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_AREA)
>>> cv2.imshow('image1', img1)
>>> cv2.waitKey(0)

f:id:nokixa:20210425220826p:plain

前々回の2値化。

>>> img1_hsv = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
>>> ret, img1_binary = cv2.threshold(img1_hsv[:,:,0], 160, 255, cv2.THRESH_BINARY)
>>> cv2.imshow('binary', img1_binary)
>>> cv2.waitKey(0)

f:id:nokixa:20210425222219p:plain

モルフォロジー変換

それでは本題に。

収縮(Erosion)

>>> import numpy as np
>>> kernel  = np.ones((5,5), np.uint8)
>>> img1_erosion = cv2.erode(img1_binary, kernel, iterations=1)
>>> cv2.imshow('erosion', img1_erosion)
>>> cv2.waitKey(0)

f:id:nokixa:20210425223409p:plain

何も考えずにやりましたが、これだとだめですね。
シールのピンク領域を白にした2値化画像ですが、どちらかというとピンク領域の内側の数字に着目したい。

反転させてやってみます。

>>> img1_inv = cv2.bitwise_not(img1_binary)
>>> cv2.imshow('inversion', img1_inv)

f:id:nokixa:20210425224955p:plain

>>> img1_erosion_inv = cv2.erode(img1_inv, kernel, iterations=1)
>>> cv2.imshow('erosion_inv', img1_erosion_inv)

f:id:nokixa:20210425225111p:plain

えらく数字の線が細い…
Erosionのカーネルサイズを小さくしてみるか?

>>> kernel = np.ones((3,3), np.uint8)
>>> img1_erosion_inv = cv2.erode(img1_inv, kernel, iterations=1)
>>> cv2.imshow('erosion_inv', img1_erosion_inv)

f:id:nokixa:20210425225303p:plain

数字が分かりやすくなりました。
シールより外側を除けばなお良い感じになったかも。

膨張(Dilation)

反転前の画像でいいかも。
カーネルサイズはさっきと同じ(3,3)で。

>>> img1_dilation = cv2.dilate(img1_binary, kernel, iterations=1)
>>> cv2.imshow('dilation', img1_dilation)

f:id:nokixa:20210425230126p:plain

単純にさっきの反転&収縮の反転になっているようです。

オープニング

ノイズ除去に効果的とのことですが、今回はシール上部の細かい文字を消せるのでは、と期待します。 収縮→膨張の処理をするとのことなので、反転画像に対して処理をかけてみます。
カーネルサイズはまたまたさっきと同じ(3,3)。

>>> img1_opening = cv2.morphologyEx(img1_inv, cv2.MORPH_OPEN, kernel)
>>> cv2.imshow('opening_inv', img1_opening)

f:id:nokixa:20210425230839p:plain

確かにさっきまでの処理画像を見ると少し細かい文字の点が残っていたので、その影響でカーネルサイズ(3,3)のドットが現れています。

(5,5)のカーネルでやってみます。

>>> kernel  = np.ones((5,5), np.uint8)
>>> img1_opening = cv2.morphologyEx(img1_inv, cv2.MORPH_OPEN, kernel)
>>> cv2.imshow('opening_inv', img1_opening)

f:id:nokixa:20210425232239p:plain

これはやりすぎか。
(4,4)のカーネルとかはできるのか?

>>> kernel  = np.ones((4,4), np.uint8)
>>> img1_opening = cv2.morphologyEx(img1_inv, cv2.MORPH_OPEN, kernel)
>>> cv2.imshow('opening_inv', img1_opening)

f:id:nokixa:20210425232408p:plain

処理自体はできましたが、0.5点の文字がうまく取れませんでした。

画像の解像度不足な気がするので、解像度を倍にして試してみる。
カーネルサイズは、今までの画像での(3,3)、(4,4)の中間になるよう、(7,7)にしてみます。

>>> img1 = cv2.imread('harupan_200317_1.jpg')
>>> img1 = cv2.resize(img1, None, fx=0.2, fy=0.2, interpolation=cv2.INTER_AREA)
>>> img1_hsv = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
>>> ret, img1_binary = cv2.threshold(img1_hsv[:,:,0], 160, 255, cv2.THRESH_BINARY)
>>> kernel = np.ones((7,7), np.uint8)
>>> img1_inv = cv2.bitwise_not(img1_binary)
>>> img1_opening = cv2.morphologyEx(img1_inv, cv2.MORPH_OPEN, kernel)
>>> cv2.imshow('opening_inv', img1_opening)

f:id:nokixa:20210425233144p:plain

やっぱり0.5点の文字が微妙です。細かい文字はいい感じにほぼ消えましたが。

ここまで

モルフォロジー変換はここまでにします。
色々と処理を組み合わせるといい感じの結果が出せそうです。

もう少しチュートリアル通りに進めて、次はエッジ検出をやってみたいと思います。