본문 바로가기

Theory/DataScience

OpenCV를 활용해서 내가 쓴 손글씨를 CNN으로 학습된 모델을 이용해서 인식해보기

손글씨 학습용 데이터인 MNIST 데이터 셋을 이용해서 텐서플로우를 이용해서 단층 신경망으로 손글씨 학습을 하던 초보스런 시작부터 갑자기 Keras로 갈아타서는 Keras로 CNN 모델을 이용해서 MNIST 손글씨를 학습했었네요. 이때는 목적이 있었죠. 바로 학습한 모델을 저장하고, 그걸 단지 불러와서 내 손글씨를 인식시켜 보겠다는 것이었죠. 그래서 이미 학습된 모델을 Keras를 이용해서 내 손글씨 인식하기를 했는데, 문제는 이때는 내가 쓴 손글씨를 모델에 적용하기 위해 사진을 자르고 사이즈를 강제로 맞추고, 뭐 그런 짓을 수동으로 했죠.ㅠㅠ. 

그걸 다시 자동(^^)으로 하기 위해.. 손을 댄 것이 바로 OpenCV였습니다. OpenCV는 최초 그냥 쉽게 사람 얼굴을 OpenCV로 인식해 보고자 시작했다가. 내가 쓴 손글씨에서 숫자 영역을 OpenCV로 추출하는 과정도 수행을 했었죠.^^ 와우~ 오늘 글은 여기서부터 이어서 가는 겁니다.^^. 직전 글인 내가 쓴 손글씨에서 숫자 영역을 OpenCV로 추출하는 과정에서 마지막 부분이

였죠. 그 때 저 반복문 안의 코드를 좀 바꿉니다.^^

img_result = []
img_for_class = img.copy()

margin_pixel = 60

for rect in rects:
    #[y:y+h, x:x+w]
    img_result.append(
        img_for_class[rect[1]-margin_pixel : rect[1]+rect[3]+margin_pixel, 
                      rect[0]-margin_pixel : rect[0]+rect[2]+margin_pixel])
    
    # Draw the rectangles
    cv2.rectangle(img, (rect[0], rect[1]), 
                  (rect[0] + rect[2], rect[1] + rect[3]), (0, 255, 0), 5) 

추가한 코드는 숫자로 인식된 이미지의 영역만 추출하는 거죠. 뭐 어차피 좌표를 아니까요...

plt.figure(figsize=(15,12))
plt.imshow(img);

그 결과야...

잘 추출된 영역이 위의 박스와 같을 겁니다.

plt.figure(figsize=(8,6))
plt.imshow(img_result[0]);

하나만 빼서 데리고 

시작하죠.  저 하나를 잘 인식시킬 수 있으면 나머진 반복문안에 넣어버리면 되니까요^^ 먼저 28*28로 사이즈를 변경해야 합니다.

plt.figure(figsize=(4,4))
plt.imshow(cv2.resize(img_result[0], (28,28)));

뭐... resize 명령으로 끝났네요^^

네.. 사진으로 봐서는 알수 없지만, 28*28로 되었습니다.^^

count = 0
nrows = 3
ncols = 4

plt.figure(figsize=(12,8))

for n in img_result:
    count += 1
    plt.subplot(nrows, ncols, count)
    plt.imshow(n, cmap='Greys', interpolation='nearest')

plt.tight_layout()
plt.show()

12개 모두를 뽑아보죠 

저게 원본이겠죠^^

count = 0
nrows = 3
ncols = 4

plt.figure(figsize=(12,8))

for n in img_result:
    count += 1
    plt.subplot(nrows, ncols, count)
    plt.imshow(cv2.resize(n,(28,28)), cmap='Greys', interpolation='nearest')

plt.tight_layout()
plt.show()

그걸 하나만 데리고 방금 한 것 처럼 통으로 resize를 걸죠.

넵.. 이쁘죠.. 이제.. 

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

import sys
import tensorflow as tf
import keras

from keras.models import load_model
model = load_model('MNIST_CNN_model.h5')

model.summary()

이전에 CNN으로 모델을 학습시키고 저장했던 것을 불러오던 때 처럼 그냥 부르죠. 주의해 주세요. 전 지금 그냥 저장된.. 학습이 완료도어 저장된 모델을 불러올 뿐이랍니다. 물론 이전에 CNN으로 학습을 시켰죠.

그때의 CNN 구조는 위와 같구요^^ 이제...

test_num = cv2.resize(img_result[2], (28,28))[:,:,1]
test_num = (test_num < 70) * test_num
test_num = test_num.astype('float32') / 255.

plt.imshow(test_num, cmap='Greys', interpolation='nearest');

test_num = test_num.reshape((1, 28, 28, 1))

print('The Answer is ', model.predict_classes(test_num))

하나만~ 확인하면 되죠~

네~ 2를 2라고 알아주네요^^ 그럼 뭐 다 돌려볼까요^^

count = 0
nrows = 3
ncols = 4

plt.figure(figsize=(12,8))

for n in img_result:
    count += 1
    plt.subplot(nrows, ncols, count)
    
    test_num = cv2.resize(n, (28,28))[:,:,1]
    test_num = (test_num < 70) * test_num
    test_num = test_num.astype('float32') / 255.
    
    plt.imshow(test_num, cmap='Greys', interpolation='nearest');
    
    test_num = test_num.reshape((1, 28, 28, 1))
    plt.title(model.predict_classes(test_num))
    
plt.tight_layout()
plt.show()

짠~ 네.. 그냥 12개 숫자를 다 보겠다는거죠^^

ㅎㅎㅎㅎ. 응? 흠.. 9를 3이라고 인식하는 오류를 범했네요ㅠㅠ. 흠.. 일단 이 부분을 개선하는 것은 좀 더 진행하는 걸로 하고.. 오늘은 계속 절차적인 면에서^^ 이제 인식도 할 수 있게 되었으니 최초의 이미지에 적용해 봐야죠... 즉...

이랬던 반복문 안에... 이 과정을 다 녹여줘야죠^^

img_result = []
img_for_class = img.copy()

margin_pixel = 60

for rect in rects:
    #[y:y+h, x:x+w]
    target_num = img_for_class[rect[1]-margin_pixel : rect[1]+rect[3]+margin_pixel,
                               rect[0]-margin_pixel : rect[0]+rect[2]+margin_pixel]
    test_num = cv2.resize(target_num, (28,28))[:,:,1]
    test_num = (test_num < 70) * test_num
    test_num = test_num.astype('float32') / 255.
    test_num = test_num.reshape((1, 28, 28, 1))
    predicted_num = model.predict_classes(test_num)
    
    # Draw the rectangles
    cv2.rectangle(img, (rect[0], rect[1]), 
                  (rect[0] + rect[2], rect[1] + rect[3]), (0, 255, 0), 5) 
    
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(img, str(predicted_num[0]), (rect[0],rect[1]), font, 4, (0,0,255), 10)

plt.figure(figsize=(15,12))
plt.imshow(img);

역시... 짧죠^^ 넵.. 완성하고 나면 참 아무것도 아닌게 되죠^^

결과는 위와 같습니다. 그놈의 3이라고 오류를 낸 9 빼고는 다 잘 되었네요^^ 이로써... 학습이 완료된 CNN 모델에서, 제가 그냥 연습장에 쓴 숫자에서 숫자 영역을 찾고, 그 개별 영역의 숫자를 인식해서 결과를 다시 원본 이미지 위에 쓰도록 했습니다.~~~^^

반응형