마라톤너 앞에 붙인 번호표를 자동 인식하는
프로그램의 한 부분으로 숫자 하나씩 분류하는 프로그램이 필요합니다. 번호표에서 추출한 숫자 그림은 다음과 같고 이들 숫자를 인식해야 합니다. 필기체 보다는 정형화되어 있지만 번호표라서 뒤틀림이 약간
있고 기울기와 크기도 천차만별입니다.
여기서는 지도 학습(Supervised
Learning) 이 아닌 비지도학습(Unsupervised Learning)으로
숫자를 분류해보겠습니다. 먼저 크기가
서로 다른 파일이니 파일 크기를 모두 8X8로 조정합니다. 조정하는
이유 중에 하나는 인식 대상을 단순화하는 목적도 있습니다. 예를 들어 위 숫자 중에 적색 네모로 표시한 “5”를 세로와 가로 크기를 각각 8픽셀로 바꾼 그림이 아래에 있습니다.
다음은 여기에 동원된 프로그램입니다. bw에 폴더에 있는 모든 파일에 읽어 8X8
크기로 조정해서 resized 폴더에 같은 이름으로 저장합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import cv2 import os import numpy as np for root,dirs, files in os.walk("bw"): for fn in files: img = cv2.imread(root+"/"+fn, 0) h = img.shape[0] w = img.shape[1] if h < w: print("wrong size") size = (h, h, 1) blank = np.zeros(size, dtype="uint8") blank[:,:] = 255 offset = int((h - w) / 2) blank[0:h, offset:offset+w, 0]= img img8x8 = cv2.resize(blank, (8, 8)) cv2.imwrite("resized/" + fn, img8x8) |
라인 5과 6은 bw 폴더에 있는 파일을 찾은 과정이고 라인 7은
찾은 파일을 opencv를 통해 읽습니다. 라인8과 9는 이미지의 세로(h)와 가로(w)의 크기를 알아 내고
라인 10과 11에서 가로가 세로보다 큰지 점검합니다.
번호표에 나오는 숫자는 모두 길쭉한 모양이기 때문에
가로가 크면 숫자 인식 이전 분리 단계에서 오류가 있었다고 볼 수 있기 때문에 점검합니다. 라인 12,
13 그리고 14는 정사각형의 빈 이미지를 만들고 흰색으로 채웁니다.
여기서는 크기는 앞서 읽은 이미지의 세로 길이로 통일합니다. 라인 15과 16은 파일에서 읽은 이미지를 빈 이미지 중간에 복사합니다.
라인 18과 19는 합성한 이미지를
크기를 8 x 8로 줄여 resized 폴더에 저장합니다.
이렇게 크기를 줄인 숫자 이미지 파일이
resized
폴더 밑에 있고 이것을 비지도학습 방법 중에 하나인 K-means 알고리즘으로 자동
분류해 봅니다. 숫자가 10개이니 클러스터를 10개로 해보면 그렇게 성공율이 좋지 못합니다.
5과 6은 커녕 0과 4도 헷갈려 합니다. 그래서
클러스터 갯수를 30개로 늘려서 성공 확률을 높이고 나머지는 눈과 손으로 오류를 보완해서 후에 지도학습에
필요한 학습 데이터에 쓰려고 합니다. 즉, 학습 데이터 마련에 K-means 알고리즘을 활용합니다. 아래는 여기에 사용한 프로그램입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import numpy as np import cv2 import tkinter import os import shutil from sklearn import metrics from sklearn.cluster import KMeans from sklearn.preprocessing import scale from sklearn.decomposition import PCA data = [] for root, dirs, files in os.walk("resized"): for fn in files: img = cv2.imread(root+"/"+fn, 0) rev = cv2.bitwise_not(img) oneDim = np.reshape(rev, 64) data.append(oneDim) data = np.asarray(data) data = scale(data) est = KMeans(init='k-means++', n_clusters=30, n_init=10) est.fit(data) ind = 0 for root, dirs, files in os.walk("resized"): for fn in files: shutil.copy2("resized/"+fn, "classified/"+str(est.labels_[ind])) ind += 1 |
“resized” 폴더에 있는 모든 숫자 파일을 찾는 문장이 라인 13과 14입니다. 라인 15은 그 파일을 opencv를 통해 읽어 들이고 라인 16에서 이미지를 반전시킵니다. 이미지 파일에서는 흰색이 바탕이고 검정색이 숫자인데 데이터에서는 흰색을 0으로 표현하고 검정색은 255로 표현하려고 하기 때문입니다. 라인 17에서 2차원을 일차원으로 바꾸고 라인18에서 “data” 리스트에 추가합니다. 라인 20에서는 numpy의 array로 변환하고 라인 21에서는 sklearn.preprocessing.scale를 이용해서 데이터를 normalization시킵니다. 라인 24과 24는 scikit-learn의 K-means 를 이용해서 분류합니다. 결과는 “labels_” 리스트에 있는데 라인26부터 30는 그 내용에 따라 resized 폴더에 있는 파일을 결과 이름으로된 서브폴드에 복사합니다.
댓글 없음:
댓글 쓰기