반응형


Logistic Regression을 사용하여 직접 촬영한 손글씨 이미지로부터 숫자를 인식시켜 보았습니다.  





텐서플로우 2.0에서 텐서플로우 1.x 코드를 실행하는 방법을 설명합니다. 


Tensorflow 2.0에서 Tensorflow 1.x 코드 실행하기

https://webnautes.tistory.com/1393




1. A4용지에 0 ~ 9까지 숫자를 적은 후, 카메라로 찍었습니다. 그리고나서 이미지를 10개로 나누어서 저장했습니다.

이미지 크기는 똑같을 필요없지만 중앙에 숫자가 오도록 이미지를 잘라줘야 합니다.






2. 세션을 시작하면서 디스크에 저장했던 모델 파라미터를 가져옵니다. 이미지로부터 숫자를 예측할 때 사용되어집니다.


sess = tf.Session()

sess.run(tf.global_variables_initializer())


model_path = "/tmp/model.saved"
saver = tf.train.Saver()

saver.restore(sess, model_path)
print("Model restored from file: %s" % model_path)



모델 파라미터를 저장했다가 불러오는 것은 다음 포스팅을 참고하세요.



예제로 배우는 텐서플로우 강좌 - 7. 훈련된 모델 파라미터 저장하기

http://webnautes.tistory.com/1237





3. 흑백 이미지로 읽어서 28 x 28 크기의 이미지로 변환합니다. 현재 흰바탕에 검은 글씨입니다.


   gray = cv2.imread(str(no)+".png", 0)
   gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)




4. 검은색 바탕의 흰글씨로 이진화한 후, 숫자 영역외의 공백을 모두 제거합니다.


   (thresh, gray) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV  | cv2.THRESH_OTSU)

   while np.sum(gray[0]) == 0:
       gray = gray[1:]

   while np.sum(gray[:,0]) == 0:
       gray = np.delete(gray,0,1)

   while np.sum(gray[-1]) == 0:
       gray = gray[:-1]

   while np.sum(gray[:,-1]) == 0:
       gray = np.delete(gray,-1,1)

   rows,cols = gray.shape

 



5. 숫자가 중앙에 오도록 공백을 다시 추가합니다.


   if rows > cols:
       factor = 20.0/rows
       rows = 20
       cols = int(round(cols*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols,rows))
   else:
       factor = 20.0/cols
       cols = 20
       rows = int(round(rows*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols, rows))

   colsPadding = (int(math.ceil((28-cols)/2.0)),int(math.floor((28-cols)/2.0)))
   rowsPadding = (int(math.ceil((28-rows)/2.0)),int(math.floor((28-rows)/2.0)))
   gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')

   shiftx,shifty = getBestShift(gray)
   shifted = shift(gray,shiftx,shifty)
   gray = shifted





6. 이미지를  0 ~ 1 사이 값을 갖는 크기 784( = 28 x 28 )의 일차원 배열로 변환합니다.

   flatten = gray.flatten() / 255.0
   images[i] = flatten




7. 입력 이미지로부터 예측한 숫자를 출력합니다.


print (sess.run(tf.argmax(y_model,1), feed_dict={x: images}))



테스트 결과 8을 5로 인식하는 오류가 있습니다. Logistic regression을 사용하여 훈련시킨 모델의 예측 정확도가 91% 정도 이기 때문입니다.

실제로 해보면 잘 인식되는 손글씨도 있고 잘 인식안되는 손글씨도 있습니다.


좀 더 고급 알고리즘을 사용하면 정확도를 좀 더 올릴 수 있습니다.  텐서플로우 공부를 진행하며 개선해볼 예정입니다.


[0 1 2 3 4 5 6 7 5 9]




..


..


8. 전체 코드입니다. 모델을 훈련시키는 코드와 모델을 사용하여 이미지를 인식하는 두개의 코드로 구성됩니다.

이 코드를 먼저 실행하여 훈련 결과물인 모델 파라미터를 디스크에 저장합니다.


코드 설명은 다음 포스팅을 참고하세요.



예제로 배우는 텐서플로우 강좌 - 6. Logistic Regression( MNIST 데이터를 이용한 손글씨 인식 )

http://webnautes.tistory.com/1236




import tensorflow as tf



#---------------------------------------------------------------------------------------------------------- 입력
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)



#---------------------------------------------------------------------------------------------------------- 모델
x = tf.placeholder(tf.float32, [None, 784])

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

y_model = tf.matmul(x, W) + b



#---------------------------------------------------------------------------------------------------------- 라벨
y = tf.placeholder(tf.float32, [None, 10])



#---------------------------------------------------------------------------------------------------------- loss와 optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=y_model))
optimizer = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # learning_rate = 0.01



#---------------------------------------------------------------------------------------------------------- 세션
sess = tf.Session()


sess.run(tf.global_variables_initializer())


for epoch in range(25):
   avg_cost = 0.

   total_batch = int(mnist.train.num_examples / 100)
   for i in range(total_batch):
       batch_xs, batch_ys = mnist.train.next_batch(100)
       _, c = sess.run([optimizer, cost], feed_dict={x: batch_xs, y: batch_ys})
       avg_cost += c / total_batch

   print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(avg_cost))


print("최적화 완료")


correct_prediction = tf.equal(tf.argmax(y_model, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print("Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}))


#---------------------------------------------------------------------------------------------------- 모델 파라미터 저장
model_path = "/tmp/model.saved" # 모델 파라미터를 저장할 경로와 파일 이름
saver = tf.train.Saver()

save_path = saver.save(sess, model_path)
print("Model saved in file: %s" % save_path)

sess.close()




9. 훈련된 모델을 사용하여 이미지로부터 숫자를 예측하는 코드입니다.


import tensorflow as tf
import cv2
import numpy as np
import math
from scipy import ndimage


def getBestShift(img):
   cy,cx = ndimage.measurements.center_of_mass(img)

   rows,cols = img.shape
   shiftx = np.round(cols/2.0-cx).astype(int)
   shifty = np.round(rows/2.0-cy).astype(int)

   return shiftx,shifty


def shift(img,sx,sy):
   rows,cols = img.shape
   M = np.float32([[1,0,sx],[0,1,sy]])
   shifted = cv2.warpAffine(img,M,(cols,rows))
   return shifted



#---------------------------------------------------------------------------------------------------------- 모델
x = tf.placeholder(tf.float32, [None, 784])

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

y_model = tf.matmul(x, W) + b

#---------------------------------------------------------------------------------------------------------- 라벨
y = tf.placeholder(tf.float32, [None, 10])



#---------------------------------------------------------------------------------------------------------- 세션
sess = tf.Session()

sess.run(tf.global_variables_initializer())



# 저장된 모델 파라미터를 가져옵니다.
model_path = "/tmp/model.saved"
saver = tf.train.Saver()

saver.restore(sess, model_path)
print("Model restored from file: %s" % model_path)




# 10개의 이미지를 위한 배열을 생성
images = np.zeros((10,784))

i = 0
for no in range(10): # 10개의 이미지를 입력 받음


   gray = cv2.imread(str(no)+".png", 0)
   gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)



   (thresh, gray) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV  | cv2.THRESH_OTSU)

   while np.sum(gray[0]) == 0:
       gray = gray[1:]

   while np.sum(gray[:,0]) == 0:
       gray = np.delete(gray,0,1)

   while np.sum(gray[-1]) == 0:
       gray = gray[:-1]

   while np.sum(gray[:,-1]) == 0:
       gray = np.delete(gray,-1,1)

   rows,cols = gray.shape

   cv2.imwrite("b_" + str(no) + ".png", gray)



   if rows > cols:
       factor = 20.0/rows
       rows = 20
       cols = int(round(cols*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols,rows))
   else:
       factor = 20.0/cols
       cols = 20
       rows = int(round(rows*factor))
       # first cols than rows
       gray = cv2.resize(gray, (cols, rows))

   colsPadding = (int(math.ceil((28-cols)/2.0)),int(math.floor((28-cols)/2.0)))
   rowsPadding = (int(math.ceil((28-rows)/2.0)),int(math.floor((28-rows)/2.0)))
   gray = np.lib.pad(gray,(rowsPadding,colsPadding),'constant')

   shiftx,shifty = getBestShift(gray)
   shifted = shift(gray,shiftx,shifty)
   gray = shifted


   cv2.imwrite("image_" + str(no) + ".png", gray)


   flatten = gray.flatten() / 255.0
   images[i] = flatten

   i += 1



print (sess.run(tf.argmax(y_model,1), feed_dict={x: images}))





참고한 코드


https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/2_BasicModels/logistic_regression.py


https://github.com/opensourcesblog/tensorflow-mnist/blob/master/mnist.py



마지막 업데이트 - 2018. 9. 5




문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts