kazu22002の技術覚書

PHPer, Golang, AWS エンジニアの日々

python + opencvでカード認識 前処理+ノイズ除去

前回の記事でカード認識までの前処理と処理結果を載せてみましたが、ノイズが多くしっかりと認識できているか怪しい状態でした。

そのためノイズ除去かほかの方法できれいにカードを認識できるようにしてみます。

f:id:kazu22002:20210624033750p:plain

ノイズ除去

labs.eecs.tottori-u.ac.jp

画素の収縮 -> 膨張を利用して細かい部分を除去や、膨張 -> 収縮を利用して穴を埋めたりできます。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def find_card_pattern3(image):
    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
    # 閾値を同的に取得
    threshold = luminance_threshold(img_gray)

    # 二値化
    _, binarized = cv2.threshold(img_gray, threshold, 255, cv2.THRESH_BINARY)

    kernel = np.ones((9, 9), np.uint8)
    gray_img = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)
    gray_img = cv2.morphologyEx(gray_img, cv2.MORPH_CLOSE, kernel)


    # find
    contours, hierarchy = cv2.findContours(binarized, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    return contours, hierarchy


img = cv2.imread("sample.jpg")
find_card_pattern3( img )

ノイズ除去後のニ値化画像

f:id:kazu22002:20210624045608p:plain

抽出した輪郭を元の画像に上書き

f:id:kazu22002:20210624045618p:plain

ノイズ処理を行わない状態

f:id:kazu22002:20210624042004p:plain

細かい部分が取り除けたのがわかると思います。

面積で小さい内容を除去

import cv2
import numpy as np
import matplotlib.pyplot as plt

def find_card_pattern3(image):
    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
    # 閾値を同的に取得
    threshold = luminance_threshold(img_gray)

    # 二値化
    _, binarized = cv2.threshold(img_gray, threshold, 255, cv2.THRESH_BINARY)

    kernel = np.ones((9, 9), np.uint8)
    gray_img = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)
    gray_img = cv2.morphologyEx(gray_img, cv2.MORPH_CLOSE, kernel)


    # find
    contours, hierarchy = cv2.findContours(binarized, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    return contours, hierarchy


img = cv2.imread("sample.jpg")
contours, _ = find_card_pattern3( img )

list = []
for i, cnt in enumerate(contours):
    if (cv2.contourArea(cnt) > 250):
        list.append(cnt)

f:id:kazu22002:20210624051721p:plain

ノイズ除去ほどではありませんが、閾値を変更することで不要な内容を削ることができるようになりました。

閾値を求めるのがうまくできるかで精度が変わってきますが、手動で行う場合は値を調節して利用するのも可能だと思います。

ノイズ除去の段階でかなりうまくいっているので、使う機会は少ないと思いますが、考え方は簡単のため紹介します。

面積の最大サイズのみ取得

正直これが一番てっとり早いです。認識した輪郭の最大サイズの内容のみを抽出することにします。

今回の要件はカードが認識できればいいため一枚限定で動作する仕様にしてしまいます。

contours, hierarchy = cv2.findContours(img_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
card_cnt = max(contours, key=cv2.contourArea)

f:id:kazu22002:20210624043828p:plain

このあたりも参考にしたサイトに同じ処理があります。かなり有効な処理だと思います。

面積の最大は、輪郭で囲われた画素数が最大の内容を取得する方法です。

まとめ

面積の最大サイズを取得すればなんとなく綺麗に取れていそうなところまできました。

次は必要な部分のみ切り抜いて画像に保存してみます。

余談として、画像処理はいわゆる画素をもとにどういう処理をするか。というものだと思います。

隣接する画素をもとに処理をしたり、画素自体の値から値を変換したりします。

いろいろなアルゴリズムがあるので、事前知識が必要な分野ではありますが、ライブラリのおかげで実装まで考える必要がないのがありがたいです。

画像処理は数学の知識がふんだんに使われているためよく数式がでてきますが、このあたりも理解できるとかなり面白い分野です。数学で勉強したことってどこで役に立つの??と思っている人は、画像処理や3Dの世界を覗いてみると感心できると思います。暗号化の分野でも数学を利用していますし、奥が深い分野ですね。

前の記事

kazu22002.hatenablog.com

参考

qiita.com