1. ホーム
  2. python

[解決済み] OpenCV-Pythonによる簡単な数字認識OCR

2022-03-24 09:55:30

質問

OpenCV-Python(cv2)で、quot;Digit Recognition OCR"を実装しようと思っています。あくまで学習用です。OpenCVでKNearestとSVMの両方の特徴を学習したいと思います。

各桁のサンプル(=画像)が100枚ずつあります。それを使って学習したいのですが。

あるサンプル letter_recog.py OpenCVのサンプルに付属しているものです。しかし、その使い方がまだわからない。何がサンプルなのか、何がレスポンスなのか等、よくわかりません。また、最初に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のどちらか)を書くにはどうしたらよいか?

どのように解決するのですか?

さて、私は上記の問題を解決するために、自分自身を鍛錬することにしました。私がしたかったのは、OpenCVでKNearestやSVMの特徴を使った単純なOCRを実装することです。そして、以下は私が行ったことと方法です。( これは、単純なOCRの目的のためにKNearestを使用する方法を学習するためのものです。).

1) 最初の質問は、OpenCVのサンプルに付属しているletter_recognition.dataファイルについてです。そのファイルの中に何があるのか知りたかったのです。

文字と、その文字の16の特徴量が入っています。

そして this SOF を見つけるのに役立ちました。これら16の機能については、論文で解説しています Letter Recognition Using Holland-Style Adaptive Classifiers . ( 最後にいくつかの機能を理解できませんでしたが)

2) やっぱり、その特徴を全部理解しないと、あのやり方は難しいですね。他の論文も試しましたが、どれも初心者には少し難しいものでした。

So I just decided to take all the pixel values as my features. (精度や性能は気にせず、最低限動けばいいと思っていました)

学習用データとして、以下の画像を撮影しました。

(学習データ量が少ないことは承知しています。しかし、すべての文字が同じフォントとサイズであるため、これで試してみることにした)。

学習用のデータを準備するために、OpenCVで小さなコードを作りました。これは以下のようなことをする。

  1. 画像を読み込みます。
  2. 数字を選択する(明らかに輪郭抽出と、誤検出を避けるための文字の面積と高さの制約を適用している)。
  3. 一文字を囲む矩形を描画し、その上で key press manually . 今回は 自分で数字キーを押す ボックス内の文字に対応する
  4. 対応する数字キーが押されると、このボックスを10x10にリサイズし、100ピクセル分の値を配列(ここではsamples)に、手動で入力した対応する数字を別の配列(ここではrespons)に保存します。
  5. 次に、両方の配列を別々の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)


ここからは、トレーニングとテストのパートに入ります。

テスト用の画像は、学習用と同じ種類の文字が描かれた以下の画像を使用しました。

<イグ

トレーニングは次のように行います。 :

  1. 先ほど保存したtxtファイルを読み込む
  2. 使用する分類器のインスタンスを作成します(ここでは KNearest とします)。
  3. そして、KNearest.train 関数を用いてデータを学習させる。

テスト用には、以下のようにします。

  1. テストに使用した画像を読み込みます
  2. 先程と同様に画像を処理し、輪郭法を用いて各桁を抽出する
  3. バウンディングボックスを描画し、10x10にリサイズして、そのピクセル値を先程と同様に配列に格納する。
  4. 次に、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%の精度で動作しました。これは、すべての桁が同じ種類で同じ大きさであるためだと思われます。

しかし、いずれにせよ、これは初心者が行くには良いきっかけになる(といいのだが)。