[OpenCV] 08-1-2. OCR of Hand-written Data using kNN
🐍Python/OpenCV

[OpenCV] 08-1-2. OCR of Hand-written Data using kNN

728x90
반응형

< 8-1-2. OCR of Hand-written Data using kNN >

이번 장에서는

  • OCR 기능을 만들기 위해서 kNN에 대한 지식을 이용해보고
  • OpenCV와 함께 제공되는 숫자와 알파벳 데이터를 사용해볼 것이다.

OCR of Hand-written Digits

목표는 손글씨 숫자를 읽을 수 있는 기능을 만드는 것이다. 이를 위해 train_data와 test_data가 필요하다. OpenCV는 5000개의 손글씨 숫자인 digits.png 이미지(0~9까지 500개씩)를 사용한다. 각 숫자는 20X20 크기의 이미지이다. 먼저 첫 번째로, 이미지를 5000개의 다른 숫자로 나눠야한다.



각 숫자에 대해, 펼쳐서 400 픽셀의 한 줄로 만든다. 이것이 우리의 feature set이다. 예를 들어 모든 픽셀들의 intensity 값들이다. 이것이 우리가 feature set을 만들 수 있는 가장 간단한 방법이다. 먼저 250개의 샘플을 학습데이터로, 나머지 250개의 샘플을 테스트 데이터로 사용하자. 그러면 준비를 해보자!!

import numpy as np
import cv2
import matplotlib.pyplot as plt

img = cv2.imread("./images/digits.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# 5000개의 셀로 쪼개야 한다. 각각 20x20 사이즈로!
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]

# np.array로 만들자 (50,100,20,20)
x = np.array(cells)

# train / test split
# 각 클래스별 50개씩
train = x[:,:50].reshape(-1,400).astype(np.float32)  # (2500,400)
test = x[:,50:100].reshape(-1,400).astype(np.float32) # (2500,400)

# label 만들기
label = np.arange(10)
# np.newaxis : 배열에 대해 차원만 1차원 증가시키는 경우에 사용
# (2500,) -> (2500,1) 로 라벨 만들어짐
train_labels = np.repeat(label,250)[:,np.newaxis]
test_labels = train_labels.copy()

# kNN 만들기, 데이터를 학습하고 k=1에 대해 테스트 해보자
knn = cv2.ml.KNearest_create()
# cv2.ml.ROW_SAMPLE : 배열의 길이을 전체 길이 1로 간주한다는 것
knn.train(train,cv2.ml.ROW_SAMPLE,train_labels)
ret,results,neighbours,dist = knn.findNearest(test,k=5)

# 분류 정확도 확인하기
matches = results == test_labels
correct = np.count_nonzero(matches)
accuracy = correct*100 / results.size
print(accuracy)

기본적인 OCR은 준비가 되었다. 이렇게 간단하지만 91.76%의 정확도를 보였다. 성능을 높이기 위해서는 특히 잘못된 데이터들을 많이 추가해주는 것이 좋다. 그리고 매번 학습시키기 보다는 저장을 해서 다음에는 분류만 해주면 된다. Numpy의 np.savetxt, np.savez, np.load등을 사용하면 된다.

# 저장
np.savez('knn_data.npz',train=train,train_labels=train_labels)

# 불러오기
with np.load('knn_data.npz') as data:
    print(data.files)
    train = data['train']
    train_labels = data['train_labels']

용량을 줄이려면 uint8로 변환시키고 저장하는 것이 도움이 된다. 사용할때는 float32로 바꿔서 써줘야한다.

OCR of English Alphabets

다음으로 알파벳에 똑같이 적용할 것이지만, 데이터와 특성에 좀 다른 변화가 있다. 먼저 이미지를 보자. 열어보면, 처음에는 20000개가 쓰레기처럼 보일 수 있다. http://archive.ics.uci.edu/ml/datasets/Letter+Recognition 여기서 다운로드 할 수 있다.(‘letter-recognition.data’ 파일)


반반 나눠서 10000개는 학습, 10000개는 테스트에 사용해보자. 알바벳을 직접 가지고 할 수 없기 때문에 알파벳을 아스키 문자로 바꿔줘야한다.

# A가 65로 소문자가 더 커서 빼주는 것, 시작점 맞추기 위해
data = np.loadtxt('letter-recognition.data',dtype='float32',delimiter=',',
              converters = {0: lambda ch:ord(ch)-ord('A')})
# train test split
train,test = np.vsplit(data,2)

# 라벨과 특성을 나누기
# [1]은 앞에 하나만 짜르고 뒤는 다 이어서 split
train_labels, trainData = np.hsplit(train,[1])
test_labels, testData = np.hsplit(test,[1])

# kNN 개시!
knn = cv2.ml.KNearest_create()
knn.train(trainData,cv2.ml.ROW_SAMPLE,train_labels)
ret, results, neighbours, dist = knn.findNearest(testData,k=5)

correct = np.count_nonzero(results == test_labels)
accuracy = correct*100/results.size
print(accuracy)

정확도는 93.06%정도 나왔다. 반복하면 에러가 줄게된다.

솔직히 이렇게 간단하게 실행을 해서 얻은 결과가 큰 신뢰가 가지는 않는다…! Overfitting일 수도 있고! validation data도 사용하지 않아서 더 검증이 필요해보인다..!

728x90
반응형