OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
kNNを用いた手書き文字のOCR

目標

この章では:

  • kNNに関する知識を使って、基本的なOCR(光学文字認識)アプリケーションを構築する。
  • OpenCVに付属する数字とアルファベットのデータでアプリケーションを試す。

手書き数字のOCR

目標は、手書きの数字を読み取れるアプリケーションを構築することである。そのためには、いくつかの訓練データとテストデータが必要である。OpenCVには、5000個の手書き数字(各数字につき500個)を含む画像digits.png(opencv/samples/data/フォルダ内)が付属している。各数字は20x20の画像である。そこで最初のステップは、この画像を5000個の異なる数字画像に分割することである。次に、各数字(20x20の画像)について、400ピクセルからなる単一の行に平坦化する。これが特徴セット、すなわちすべてのピクセルの輝度値である。これは作成できる最も単純な特徴セットである。各数字の最初の250サンプルを訓練データとして使用し、残りの250サンプルをテストデータとして使用する。それではまず、それらを準備しよう。

import numpy as np
import cv2 as cv
img = cv.imread('digits.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# Now we split the image to 5000 cells, each 20x20 size
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]
# Make it into a Numpy array: its size will be (50,100,20,20)
x = np.array(cells)
# Now we prepare the training data and test data
train = x[:,:50].reshape(-1,400).astype(np.float32) # Size = (2500,400)
test = x[:,50:100].reshape(-1,400).astype(np.float32) # Size = (2500,400)
# Create labels for train and test data
k = np.arange(10)
train_labels = np.repeat(k,250)[:,np.newaxis]
test_labels = train_labels.copy()
# Initiate kNN, train it on the training data, then test it with the test data with k=1
knn = cv.ml.KNearest_create()
knn.train(train, cv.ml.ROW_SAMPLE, train_labels)
ret,result,neighbours,dist = knn.findNearest(test,k=5)
# Now we check the accuracy of classification
# For that, compare the result with test_labels and check which are wrong
matches = result==test_labels
correct = np.count_nonzero(matches)
accuracy = correct*100.0/result.size
print( accuracy )
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
Converts an image from one color space to another.

これで基本的なOCRアプリの準備ができた。この具体的な例では、精度91%が得られた。精度を向上させる選択肢の一つは、訓練用のデータを追加することであり、特に誤りの多かった数字についてデータを増やすとよい。

アプリケーションを起動するたびにこの訓練データを求める代わりに、それを保存しておけば、次回はこのデータをファイルから直接読み込んで分類を開始できる。これはnp.savetxt、np.savez、np.loadなどのいくつかのNumpy関数を使って行える。詳細についてはNumPyのドキュメントを参照してほしい。

# Save the data
np.savez('knn_data.npz',train=train, train_labels=train_labels)
# Now load the data
with np.load('knn_data.npz') as data:
print( data.files )
train = data['train']
train_labels = data['train_labels']

私のシステムでは、約4.4 MBのメモリを消費する。特徴として輝度値(uint8データ)を使用しているため、まずデータをnp.uint8に変換してから保存した方がよい。この場合はわずか1.1 MBしか消費しない。そして読み込み時にfloat32に戻せばよい。

英語アルファベットのOCR

次に、英語アルファベットについても同じことを行うが、データと特徴セットに少し変更がある。ここでは画像の代わりに、OpenCVにデータファイルletter-recognition.dataがopencv/samples/cpp/フォルダに付属している。これを開くと、一見ゴミのように見えるかもしれない20000行が表示される。実際には、各行の最初の列はラベルである文字を表す。それに続く次の16個の数字がさまざまな特徴である。これらの特徴は UCI Machine Learning Repository から取得されたものである。これらの特徴の詳細は このページ で確認できる。

20000サンプルが利用可能であるため、最初の10000を訓練サンプルとし、残りの10000をテストサンプルとする。文字は直接扱えないため、文字をASCII文字に変換する必要がある。

import cv2 as cv
import numpy as np
# Load the data and convert the letters to numbers
data= np.loadtxt('letter-recognition.data', dtype= 'float32', delimiter = ',',
converters= {0: lambda ch: ord(ch)-ord('A')})
# Split the dataset in two, with 10000 samples each for training and test sets
train, test = np.vsplit(data,2)
# Split trainData and testData into features and responses
responses, trainData = np.hsplit(train,[1])
labels, testData = np.hsplit(test,[1])
# Initiate the kNN, classify, measure accuracy
knn = cv.ml.KNearest_create()
knn.train(trainData, cv.ml.ROW_SAMPLE, responses)
ret, result, neighbours, dist = knn.findNearest(testData, k=5)
correct = np.count_nonzero(result == labels)
accuracy = correct*100.0/10000
print( accuracy )

これは93.22%の精度を与える。繰り返すが、精度を向上させたい場合は、反復的にデータを追加していけばよい。

追加リソース

  1. 光学文字認識に関するWikipediaの記事

演習

  1. ここではk=5を使用した。kに他の値を試すとどうなるか?精度を最大化する(誤りの数を最小化する)値を見つけられるか?