machine learning, image

OCR of Hand-written Data using SVM

qkqhxla1 2015. 9. 28. 14:38

http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_ml/py_svm/py_svm_opencv/py_svm_opencv.html


앞의 knn의 예제에서는 단순히 knn으로 분류하고 글자를 정하는것까지 다 했었는데, 이번에는 hog란걸로 특징점을 찾고, svm으로 글자들을 구분할 것이다. hog라는건 각각 글자들의 특징벡터를 찾아서 특징들을 기억해놓는거라고 보면 된다.


hog : https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients


hog와 svm의 조합을 사용해보면서 느낀점은, knn만 사용하는것보다 많이 정확도가 높다는것이다. knn이 단순히 픽셀의 위치중 가까운 위치들을 찾아서 같은지를 판별하는 알고리즘이라면, hog는 이미지의 특정한 부분을 찾아서 판별하기 때문에 저번의 http://qkqhxla1.tistory.com/362 여기서 다뤘던 SIFT였나 그 알고리즘처럼 글자가 살짝 우그러지더라도 더 판별을 잘하는것 같다.


그리고 knn은 픽셀 위치에서 가장 가까운 픽셀을 근거로 판단하기 때문에 트레이닝 데이터가 내가 실행할 테스트 데이터와 글자의 위치가 다르면(트레이닝 데이터는 20*20의 중앙에 글자가 있다고 하면 테스트 데이터는 글자가 오른쪽에 붙는다던가 등등) 그런것에서도 인식률이 떨어지는 것 같다. (knn의 속은 깊숙히 파보지 못해서 이게 맞는지는 모르겠는데 여러 이미지로 삽질해본 결과 이렇게 결론 내렸다.)


svm에 관련된 글을 가볍게 읽어봤다. http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_ml/py_svm/py_svm_basics/py_svm_basics.html#svm-understanding

가장 이해가 잘됬던 그림.

이 svm이란게 트레이닝을 많이하면 많이할수록 검출력이 높아지는데 그 검출력이란게 위의 그림에서 원과 사각형을 나누는 선분이 더 정교해진다는 소리이다. 그런데 저번에 wechall의 crackcha였나 그 문제를 삽질하면서 느낀건데 빅데이터,영상처리 등등 이쪽은 알고리즘 한번 확인을 위해서 트레이닝할 데이터들을 만들고, 트레이닝하고 판별하고 전체적으로 일반적인 코딩보다 상당히 많은 시간이 들어간다. 처음 하는 사람들은 코딩에 익숙하지 않으면 몇일동안 이것만 삽질해도 못하는 경우가 많을것같다.

참고를 위해 간단하게 하나 트레이닝후 판별하는 코드를 올리겠다.


트레이닝 데이터를 가지고 기본적으로 트레이닝하고, 비슷한 이미지를 찾는 예제.


1. 현재 파이썬 스크립트와 같은 디렉터리에 train이라는 폴더가 있으며, train폴더 내부에는 Sample001~Sample016 디렉터리가 있다. Sample001은 숫자 0, Sample002는 숫자 1, ~~Sample010은 숫자 9, Sample011은 영어 A, Sample016은 F가 여러가지 글자체로 들어있다.

# -*- encoding: cp949 -*-
#현재 파일 경로 : file_path = "%s%s" % (os.path.dirname(os.path.realpath(__file__))+"\\", filename)
import os
import Image
b = Image.new('RGB', (2000,2240), (255, 255, 255)) #한글자당 20*20크기, 100개를 트레이닝 시킬것이므로 X는 20*100인 2000, 글자가 16개이므로(0~9,A~F)면서 한글자당 7줄을 만들 것이므로 Y는 20*16*7인 2240.

cnt = 0
y = 0
for all_dirname, inner_dirnames, all_filenames in os.walk('train'):
    for i in inner_dirnames:
        for a,dir,f in os.walk('train\\'+i):
            x = 0
            for file in f:
                if file != 'Thumbs.db':
                    if x==100: 
                        x=0
                        y += 1
                    b.paste(Image.open('train\\'+i+'\\'+file),(x*20,y*20))
                    cnt += 1
                    if cnt==800:
                        cnt = 0
                        break
                x += 1
b.save('zero2.png') #zero2.png라는 이름으로 만든다.


요렇게 나누어져 있던것들이 zero2.png에는

이렇게 정렬되어 들어간다. 만들기 귀찮으면 이거 다운받아서 쓰길... (한글자당 20*20의 크기)


2. 이렇게 정렬된 사진들을 트레이닝해서 svm으로 처리해보자.

# -*- encoding: cp949 -*-
import cv2
import numpy as np

SZ=20
bin_n = 16 # Number of bins

svm_params = dict( kernel_type = cv2.SVM_LINEAR,
                    svm_type = cv2.SVM_C_SVC,
                    C=2.67, gamma=5.383 )

affine_flags = cv2.WARP_INVERSE_MAP|cv2.INTER_LINEAR

def deskew(img):
    m = cv2.moments(img)
    if abs(m['mu02']) < 1e-2:
        return img.copy()
    skew = m['mu11']/m['mu02']
    M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]])
    img = cv2.warpAffine(img,M,(SZ, SZ),flags=affine_flags)
    return img

def hog(img):
    gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
    gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
    mag, ang = cv2.cartToPolar(gx, gy)
    bins = np.int32(bin_n*ang/(2*np.pi))    # quantizing binvalues in (0...16)
    bin_cells = bins[:10,:10], bins[10:,:10], bins[:10,10:], bins[10:,10:]
    mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:]
    hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
    hist = np.hstack(hists)     # hist is a 64 bit vector
    return hist

img = cv2.imread('zero2.png',0)

cells = [np.hsplit(row,100) for row in np.vsplit(img,112)]

# First half is trainData, remaining is testData
train_cells = [ i[:50] for i in cells ]
test_cells = [ i[50:] for i in cells]

######     Now training      ########################

deskewed = [map(deskew,row) for row in train_cells]
hogdata = [map(hog,row) for row in deskewed]
trainData = np.float32(hogdata).reshape(-1,64)
responses = np.float32(np.repeat(np.arange(16),350)[:,np.newaxis])

svm = cv2.SVM()
svm.train(trainData,responses, params=svm_params)
svm.save('svm_data.dat')

######     Now testing      ########################

deskewed = [map(deskew,row) for row in test_cells]
hogdata = [map(hog,row) for row in deskewed]
testData = np.float32(hogdata).reshape(-1,bin_n*4)
result = svm.predict_all(testData)

#######   Check Accuracy   ########################
mask = result==responses
correct = np.count_nonzero(mask)
print correct*100.0/result.size

내가 만든 zero2.png를 반은 트레이닝하고 반은 테스트해봤는데 95%의 정확도가 나온다. 동일한 사진으로 knn이 70%정도?의 정확도가 나왔었는데(기억이 잘 안남) svm은 훨씬 정확도가 높고 믿음직하다. 


지금까지 공부하면서 생각한건데 knn은 비슷한 그림?을 찾는 느낌이고, svm이 ocr에 더 적합한 그런 알고리즘같다.