2018年1月4日木曜日

OpenCVのテンプレートマッチングを試す

以前にOpenCVを使って顔検出をやってみたが、今回はテンプレートマッチングで、
簡易的に数字を検出してみる。
  
下のページを参考にさせてもらいました。有り難うございました。
http://www.tech-tech.xyz/archives/3065942.html

本当は画像の中から文字を検出したかったのだが、意外と面倒そうだった。
手書きの文字やフォント不明の文字を認識したいわけではなく、予め用意した数字と
同じものさえ検出できればよかったので、テンプレート画像との一致を見つけることで
良しとした。

先ず、テンプレート画像を用意する。

上の画像から、検出したい数字の部分を切り取ってテンプレート画像を作る。
これら「1」「2」「3」のテンプレートを使って検出を行う。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cv2
import shutil

shutil.copyfile("test.jpg","result.jpg")

img = cv2.imread("result.jpg", 0)

for temp_num in [1, 2, 3]:
    temp = cv2.imread("temp" + str(temp_num) + ".png", 0)

    result = cv2.matchTemplate(img, temp, cv2.TM_CCOEFF_NORMED)

    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

    if max_val > 0.8:
        top_left = max_loc
        w, h = temp.shape[::-1]
        bottom_right = (top_left[0] + w, top_left[1] + h)

        result = cv2.imread("result.jpg")
        cv2.rectangle(result,top_left, bottom_right, (255, 0, 0), 2)
        cv2.imwrite("result.jpg", result)

※追記1(2018/01/04 20:20)※
  誤検出を減らすように、テンプレートマッチングの結果から
"max_val"(グレースケールで最も高い輝度の値)が0.8より大きい時だけ 
枠表示をするように変更しました。
何回か試したところ、正常に検出できた場合の"max_val"は、ほぼ0.9以上。
誤検出の場合は、0.7以下でした。
そこで「エイヤッ」と、0.8を設定しました。


今回は検出する画像がjpg、テンプレート画像がpngという変則コードになってしまったが
実行できれば問題ない(本当かよ?)

実際のカードを作って、それらを並べ替えてやってみた。
元画像を印刷した後、切り取って数字カードを作る。

それらのカードを適当に並べ替えて検出してみた。

更に、枠を外してやってみた。

<結果>
・影があったり、余分な物が写っている画像でも、正常に検出ができた。

<問題点>
・検出する画像の数字部分とテンプレート画像のサイズが(大体)合っていないと、
以下のようになる。
間違った検出。画像の数字に対してテンプレートが小さい
 ・検出する画像が大きいと時間が掛かる。