特徴点マッチングによる画像分類

OpenCV By Output不足なエンジニア

今回はDeepLearningを用いずに古典的な画像分類について紹介します。例えば様々な会社毎に異なる書類フォーマットでやり取りしていて、その書類をまとめてスキャンした場合は画像の振り分けが必要になります。画像分類と言えばCNNが圧倒的な精度を叩き出しますが、学習データを用意できない場合は古典的な画像処理で対応することも可能です。
帳票にQRコードとか付けれるならそれで一発解決ですが、なかなかそんな帳票は見かけないあたり、管理コードを埋め込むことは困難なのでしょう。。。



画像の特徴点について

画像の特徴点についてはこちらのQiita記事が分かりやすいので説明は割愛します。大雑把に言ってしまえばedgeという「画素が大きく変化している箇所」と、cornerという「edgeが集まる箇所」が特徴点の候補となります。あらかじめA社用契約書、B社用契約書、C社用○○書の画像を登録しておき、スキャンした画像と登録済み画像で特徴点のマッチングを行うことで、スキャン画像がどの画像か判断する寸法です。
2枚の画像で特徴点をマッチングした結果のイメージは下記になります。

本来は今まで本ブログで何度も登場しているアンケート調査表の特徴点マッチングした例を見せたいところですが、帳票って格子があったり特徴点が大量にあるのでおびただしい数の特徴点が出るので、以前参加したkaggleコンペで使ったクジラの尻尾の特徴点マッチングした画像を持ってきました。(この2枚は別の種で、尻尾の左側が右側にマッチングしちゃったりしてます)

特徴点の抽出

特徴点の抽出についてはAKAZEを使用します。SIFTやSURFのほうがより良い結果を返すと思いますが、特許技術なのでライセンス問題のないAKAZEを使います。下記のように簡単に抽出可能ですが、僅かですが時間がかかります。グレースケール画像でサイズも大きくなければさほどかからないと思いますが、大きな画像であれば抽出後pickle等で保存しておいたほうが良いかもしれません。抽出処理でC#を使う都合上そのままではpickleにできないので保存方法は別記事にします。

detector = cv2.AKAZE_create()

def __get_kp_and_des(img):
    '''img は画像を読み込んだnumpy配列'''
    global detector
    (kp, des) = detector.detectAndCompute(img, None)
    return (kp, des)

特徴点のマッチング

特徴点のマッチングはBFMatcherを使用します。下記のret が小さければ小さいほど類似度が高いことになります。

bf = cv2.BFMatcher(cv2.NORM_HAMMING)
# __get_kp_and_des は特徴点の抽出参照
(target_kp, target_des) = __get_kp_and_des(i1)
(comparing_kp, comparing_des) = __get_kp_and_des(i2)
try:
    matches = bf.match(target_des, comparing_des)
    dist = [m.distance for m in matches]
    ret = sum(dist) / len(dist)
except cv2.error:
    print(traceback.format_exc())

※抽出した特徴点を総当たりで比較するのでそれなりの負荷がかかります。比較結果は数値で返却されるので保存しておき、同じ画像ペアでの比較が発生しないような工夫が必要です。またはAnnoyなど近似的最近傍探索を実装したライブラリを使うと高速化が可能ですが、格子があったり特徴点が密集しているような帳票だとちょっと心配です。

フォーマットが違うレベルであれば結構大きな値になるのですが、実際の帳票だと人間でもパッと見ではわからないけど違うものってありますよね(レイアウトは一緒、文字を見れば違いがわかる)。そういうフォーマットはほぼ一緒だと値が小さくなりがちなので、分類は難しくなります。

github に比較して類似度のランキングをCSV出力するソースを公開しています。「images」の「known」は予め人の手で分類された画像用フォルダで、帳票種類ごとにフォルダを作成して「画像種類/画像」と格納しておくフォルダです。画像種類は階層を持てるので、A社向け/契約書、A社向け/請求書、B社向け/契約書のように階層を持たせることも可能です。
スキャンしてこれから分類する画像は「unknown」にフォルダを切らずに保存します。(readmeは追々作成します)


カテゴリー:OpenCV,python,画像処理

Output不足なエンジニア

統計が好きになれず、機械学習やったら必然的に統計が必要になるだろうと思ったら想像以上に機械学習にハマる。数学は芸術なので商売にするつもりはないけど、DeepLearningは数学じゃないし商売にしたいと思っているところ。画像処理がメイン。