私は OpenCV-Python (cv2) で「数字認識 OCR」を実装しようとしています。これは学習目的です。OpenCV の KNearest と SVM の両方の機能を学びたいです。
各数字のサンプル(画像)が 100 個あります。それらを使ってトレーニングしたいと思います。
OpenCV サンプルにはサンプルletter_recog.py
が付属しています。しかし、まだ使い方がわかりません。サンプルや応答などが何なのかわかりません。また、最初に txt ファイルをロードしますが、最初は理解できませんでした。
後で少し検索してみると、cpp サンプルの中に letter_recognition.data が見つかりました。これを使用して、letter_recog.py のモデルで cv2.KNearest のコードを作成しました (テスト用):
import numpy as np
import cv2
fn = 'letter-recognition.data'
a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') })
samples, responses = a[:,1:], a[:,0]
model = cv2.KNearest()
retval = model.train(samples,responses)
retval, results, neigh_resp, dists = model.find_nearest(samples, k = 10)
print results.ravel()
サイズ 20000 の配列が返されましたが、それが何なのかわかりません。
質問:
1) letter_recognition.data ファイルとは何ですか? 独自のデータセットからそのファイルを構築するにはどうすればいいですか?
2) 何をresults.reval()
意味しますか?
3) letter_recognition.data ファイル (KNearest または SVM) を使用して、簡単な数字認識ツールを作成するにはどうすればよいでしょうか?
ベストアンサー1
さて、私は上記の問題を解決するために、自分の質問について自分で考えてみることにしました。私が望んでいたのは、OpenCV で KNearest または SVM 機能を使用して簡単な OCR を実装することです。以下は私が行ったことと方法です。(これは、簡単な OCR 目的で KNearest を使用する方法を学ぶためだけのものです)。
1)私の最初の質問は、OpenCV サンプルに付属するファイルについてでしたletter_recognition.data
。そのファイルの内容が知りたかったのです。
そこには文字と、その文字の 16 個の特徴が含まれています。
そしてthis SOF
見つけるのに役立ちました。これらの16の特徴は論文で説明されていますLetter Recognition Using Holland-Style Adaptive Classifiers
(最後にいくつかの機能は理解できませんでしたが)
2)わかってはいたのですが、すべての機能を理解していないと、その方法を実行するのは難しいです。他の論文もいくつか試してみましたが、どれも初心者には少し難しかったです。
そこで、すべてのピクセル値を特徴として採用することにしました。(精度やパフォーマンスについては気にせず、少なくとも最低限の精度で動作することだけを望んでいました)
トレーニング データとして以下の画像を使用しました。
(トレーニングデータの量が少ないことは承知しています。しかし、すべての文字が同じフォントとサイズなので、これを試してみることにしました)。
トレーニング用のデータを準備するために、OpenCV で小さなコードを作成しました。このコードは次のことを行います。
- 画像を読み込みます。
- 数字を選択します (当然のことながら、輪郭を検出し、文字の面積と高さに制約を適用して誤検出を回避します)。
- 1 つの文字の周囲に境界四角形を描画し、 を待ちます
key press manually
。今回は、ボックス内の文字に対応する数字キーを自分で押します。 - 対応する数字キーが押されると、このボックスのサイズが 10x10 に変更され、100 個のピクセル値すべてが配列 (ここではサンプル) に保存され、対応する手動で入力された数字が別の配列 (ここでは応答) に保存されます。
- 次に、両方の配列を別々の
.txt
ファイルに保存します。
数字の手動分類の最後に、トレーニング データ ( train.png
) 内のすべての数字が手動でラベル付けされ、画像は次のようになります。
以下は、上記の目的で使用したコードです (もちろん、それほどきれいではありません)。
import sys
import numpy as np
import cv2
im = cv2.imread('pitrain.png')
im3 = im.copy()
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
################# Now finding Contours ###################
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
samples = np.empty((0,100))
responses = []
keys = [i for i in range(48,58)]
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
cv2.imshow('norm',im)
key = cv2.waitKey(0)
if key == 27: # (escape to quit)
sys.exit()
elif key in keys:
responses.append(int(chr(key)))
sample = roismall.reshape((1,100))
samples = np.append(samples,sample,0)
responses = np.array(responses,np.float32)
responses = responses.reshape((responses.size,1))
print "training complete"
np.savetxt('generalsamples.data',samples)
np.savetxt('generalresponses.data',responses)
これからトレーニングとテストの部分に入ります。
テスト部分では、トレーニング フェーズで使用したのと同じ種類の文字を含む以下の画像を使用しました。
トレーニングでは次のように行います。
.txt
先ほど保存したファイルを読み込みます- 使用している分類器のインスタンスを作成します(この場合はKNearestです)
- 次にKNearest.train関数を使ってデータをトレーニングします。
テストの目的で、次のことを行います。
- テストに使用した画像をロードします
- 先ほどと同様に画像を処理し、輪郭法を使用して各数字を抽出します。
- 境界ボックスを描画し、サイズを 10x10 に変更して、先ほどと同様にピクセル値を配列に格納します。
- 次に、KNearest.find_nearest() 関数を使用して、指定した項目に最も近い項目を検索します。(運が良ければ、正しい数字が認識されます。)
最後の 2 つのステップ (トレーニングとテスト) を以下の 1 つのコードに含めました。
import cv2
import numpy as np
####### training part ###############
samples = np.loadtxt('generalsamples.data',np.float32)
responses = np.loadtxt('generalresponses.data',np.float32)
responses = responses.reshape((responses.size,1))
model = cv2.KNearest()
model.train(samples,responses)
############################# testing part #########################
im = cv2.imread('pi.png')
out = np.zeros(im.shape,np.uint8)
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
roismall = roismall.reshape((1,100))
roismall = np.float32(roismall)
retval, results, neigh_resp, dists = model.find_nearest(roismall, k = 1)
string = str(int((results[0][0])))
cv2.putText(out,string,(x,y+h),0,1,(0,255,0))
cv2.imshow('im',im)
cv2.imshow('out',out)
cv2.waitKey(0)
そしてそれはうまくいきました。以下は私が得た結果です:
ここでは 100% の精度で動作しました。これは、すべての数字が同じ種類で同じサイズであるためだと思います。
しかし、いずれにせよ、これは初心者にとって良いスタートとなるでしょう(そう願っています)。