OpenCV で手本の文字と自分が書いた文字をマッチングしてみた(その1:特徴点マッチング)

OpenCV は初めて触った上、画像処理の知識がないので雑なまとめです。

はじめに

書道で使う手本は文字が小さく、手本は何十ページもあるため、後で自分が書いたものと手本を見返そうとしても探すのが大変だったりする。

f:id:nyamadori:20180603003552j:plainf:id:nyamadori:20180603003604j:plain
左: 手本をもとに自分が書いたもの、右: 手本の一部。手本の真ん中の行が書いた場所

自分が書いた文字と予めスキャンしておいた手本の中の文字とを自動でマッチングし、手本の文字と自分の文字をあわせた合成画像を生成できれば、あとで見返すのに便利そうである*1。例えば以下のような感じ。このように並べてみればニュアンスの違いが一目瞭然だし、一枚の画像に合成しておけば、スマホでいつでも確認できる。

f:id:nyamadori:20180603021624p:plain

OpenCV の特徴点マッチング

上のような合成画像を作るには、いろいろやることがありそうだが、ひとまず OpenCV の特徴点マッチングを用いて、手本の文字と自分の文字のマッチングを試みた。特徴点マッチングについては、以下を参照してほしい。雰囲気がつかめると思う。

特徴点マッチングはこのように実装した。このプログラムを実行すると、左側は自分が書いたもの、右側は手本に合成された一枚の画像が GUI で表示される。結果画像の左側と右側の間には、マッチした特徴点同士が線で結ばれる。

import numpy as np
import cv2 as cv
import matplotlib
from matplotlib import pyplot as plt

# 手本をもとに自分が書いたもの
origin1 = cv.blur(cv.imread('a_reproduce.jpg', cv.IMREAD_GRAYSCALE), (10, 10))

# 手本
origin2 = cv.blur(cv.imread('a_model.jpg', cv.IMREAD_GRAYSCALE), (10, 10))

# リサイズしないと処理速度的に厳しかった
resized_image_1 = cv.resize(origin1, None, fx=0.2, fy=0.2, interpolation=cv.INTER_CUBIC)
resized_image_2 = cv.resize(origin2, None, fx=0.2, fy=0.2, interpolation=cv.INTER_CUBIC)

# 二値画像に変換
ret1, preprocessed_image_1 = cv.threshold(resized_image_1, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
ret2, preprocessed_image_2 = cv.threshold(resized_image_2, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

# 特徴点検出器をインスタンス化
detector = cv.AKAZE_create()

# 特徴点を検出
kp1, des1 = detector.detectAndCompute(preprocessed_image_1, None)
kp2, des2 = detector.detectAndCompute(preprocessed_image_2, None)

bf = cv.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m,n in matches:
    if m.distance < 0.75 * n.distance:
        good.append([m])

img3 = cv.drawMatchesKnn(preprocessed_image_1, kp1, preprocessed_image_2, kp2, good, None, flags=2)

plt.imshow(img3) # 結果画像を表示
plt.show()

結果は次のようになった。ひと目で見て分かる通り、うまくマッチングできていない。うまくマッチングできていれば文字を繋ぐ線が分散せず、一つの対応する文字に集中する。

f:id:nyamadori:20180603010004p:plain

以下は正しくマッチできている箇所。周辺の空間に特徴があれば、精度高くマッチングできそう。

f:id:nyamadori:20180603010258p:plain

以下はミスマッチの箇所。エッジの角度やコーナーの形が類似する部分でミスマッチが多発しているっぽい。

f:id:nyamadori:20180603010308p:plain

考察

  • 特徴点マッチングは、エッジの角度や空間の形状をもとにしていると思われる
  • 文字は単純な線や空間で構成されているため、エッジの角度や空間の形状をもとにした特徴点マッチングでは、文字同士 をマッチングするのが難しい
  • 文字同士をマッチングするには、エッジの角度や空間の形状という細かい粒度ではなく、「文字」を塊として手本とマッチングする必要があると考えられる

おわりに

画像処理をちゃんと分かってなくてもそれっぽい結果が得られる OpenCV すごい。

次は、「文字」を塊として扱いやすいテンプレートマッチングを試してみたい。

*1:半紙一枚書くたびに写真を撮ればいいと思うかもしれないが、集中力が切れてしまう