Tensorflow와 OpenCV를 사용하여 웹캠에 비춘 손글씨 숫자를 인식시켜보았습니다. 



최초 작성 2019. 10. 1




관련 최근 포스트


[Machine Learning & Deep Learning/Tensorflow 강좌] - Keras와 OpenCV를 사용하여 손글씨 숫자 인식하기


CNN을 사용하여 인식 정확도가 좋아졌습니다.









01.py


손글씨 숫자를 인식을 위해 뉴럴 네트워크를 학습시키는 코드입니다. 

실행결과 가중치를 파일로 저장합니다. 


import tensorflow as tf


mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

model.save_weights('mnist_checkpoint')




02.py

 

학습된 뉴럴 네트워크를 사용하여 웹캠에 비춘 손글씨 숫자를 인식하는 코드입니다. 


import tensorflow as tf
import cv2
import numpy as np
import math


def process(img_input):

    gray = cv2.cvtColor(img_input, cv2.COLOR_BGR2GRAY)

    gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)


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


    h,w = img_binary.shape

 
    ratio = 100/h
    new_h = 100
    new_w = w * ratio

    img_empty = np.zeros((110,110), dtype=img_binary.dtype)
    img_binary = cv2.resize(img_binary, (int(new_w), int(new_h)), interpolation=cv2.INTER_AREA)
    img_empty[:img_binary.shape[0], :img_binary.shape[1]] = img_binary

    img_binary = img_empty


    cnts = cv2.findContours(img_binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


    # 컨투어의 무게중심 좌표를 구합니다.
    M = cv2.moments(cnts[0][0])
    center_x = (M["m10"] / M["m00"])
    center_y = (M["m01"] / M["m00"])

    # 무게 중심이 이미지 중심으로 오도록 이동시킵니다.
    height,width = img_binary.shape[:2]
    shiftx = width/2-center_x
    shifty = height/2-center_y

    Translation_Matrix = np.float32([[1, 0, shiftx],[0, 1, shifty]])
    img_binary = cv2.warpAffine(img_binary, Translation_Matrix, (width,height))


    img_binary = cv2.resize(img_binary, (28, 28), interpolation=cv2.INTER_AREA)
    flatten = img_binary.flatten() / 255.0

    return flatten



model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])

model.load_weights('mnist_checkpoint')


cap = cv2.VideoCapture(0)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))


while(True):

    ret, img_color = cap.read()

    if ret == False:
        break;

    img_input = img_color.copy()
    cv2.rectangle(img_color, (250, 150),  (width-250, height-150), (0, 0, 255), 3)
    cv2.imshow('bgr', img_color)

    img_roi = img_input[150:height-150, 250:width-250]


    key = cv2.waitKey(1)

    if key == 27:
        break
    elif key == 32:
        flatten = process(img_roi)
       
        predictions = model.predict(flatten[np.newaxis,:])

        with tf.compat.v1.Session() as sess:
            print(tf.argmax(predictions, 1).eval())

        cv2.imshow('img_roi', img_roi)
        cv2.waitKey(0)


cap.release()
cv2.destroyAllWindows()





포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

유튜브 구독하기


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

  1. 1234 2020.02.20 13:54

    잘 동작 합니다.

    그런데 제가 글씨를 잘 못쓰나봐요

  2. 1234 2020.02.20 14:15

    Mac에서 돌려보니... 모델에서는 잘 되는데 실제로 동작할때 이미지 인식이 좋지 않네요

    모델이

    Epoch 1/5
    60000/60000 [==============================] - 2s 37us/sample - loss: 0.2692 - accuracy: 0.9180
    Epoch 2/5
    60000/60000 [==============================] - 2s 33us/sample - loss: 0.1256 - accuracy: 0.9617
    Epoch 3/5
    60000/60000 [==============================] - 2s 33us/sample - loss: 0.0937 - accuracy: 0.9708
    Epoch 4/5
    60000/60000 [==============================] - 2s 34us/sample - loss: 0.0745 - accuracy: 0.9766
    Epoch 5/5
    60000/60000 [==============================] - 2s 33us/sample - loss: 0.0626 - accuracy: 0.9800
    10000/10000 [==============================] - 0s 25us/sample - loss: 0.0974 - accuracy: 0.9702

    요정도인데 ..

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.02.20 14:49 신고

      이미지를 전처리하는 단계에서 이진화가 잘되었는지 여부에 따라 결과가 달라질듯 합니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.02.20 14:50 신고

      이진화때문에 조명에.따라 인식 결과가 달라집니다.

  3. daehakseng 2020.03.18 19:33

    웹카메라나 앱카메라에서 가운데 부분에 정사각형의 윤곽선을 만들고 그 사이에 사진을 찍게 하고 싶은데 어떻게 해야할지를 모르겠습니다. 어떤 코드가 영상에서의 빨간 윤곽선 형태인지 가르쳐주세요ㅠ

  4. JO 2020.04.13 15:46

    h,w = img_binary.shape


    ratio = 100/h
    new_h = 100
    new_w = w * ratio

    img_empty = np.zeros((110,110), dtype=img_binary.dtype)
    img_binary = cv2.resize(img_binary, (int(new_w), int(new_h)), interpolation=cv2.INTER_AREA)
    img_empty[:img_binary.shape[0], :img_binary.shape[1]] = img_binary

    img_binary = img_empty


    cnts = cv2.findContours(img_binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    이부분 이해가 가지않습니다 ㅠ MNIST로 처리하기위해 (28, 28) 로 resize후 img_empty (110, 110,)를 사용해서 무슨 작업을 하는지 감이 오지 않습니디 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.13 15:57 신고

      숫자가 이미지 정중앙에 오도록 큰이미지를 만들어서 중앙에 숫자이미지를 배치합니다

  5. 7484 2020.05.13 10:10

    if ret == False:
    break;

    img_input = img_color.copy()
    cv2.rectangle(img_color, (250, 150), (width-250, height-150), (0, 0, 255), 3)
    cv2.imshow('bgr', img_color)

    img_roi = img_input[150:height-150, 250:width-250]


    key = cv2.waitKey(1)

    if key == 27:
    break
    elif key == 32:
    flatten = process(img_roi)

    predictions = model.predict(flatten[np.newaxis,:])

    with tf.compat.v1.Session() as sess:
    print(tf.argmax(predictions, 1).eval())

    cv2.imshow('img_roi', img_roi)
    cv2.waitKey(0)


    cap.release()
    cv2.destroyAllWindows()

    이 부분이 무엇을 나타내는지 알려주실 수 있나요?ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.13 22:47 신고

      # 원본 이미지에서 숫자를 인식할 ROI를 지정합니다.
      img_roi = img_input[150:height-150, 250:width-250]


      key = cv2.waitKey(1)

      if key == 27:
      break
      elif key == 32: # 스페이스바를 누르면
      flatten = process(img_roi) # ROI를 입력으로 전처리를 합니다. 인식이 잘되도록 하기 위해서입니다.

      predictions = model.predict(flatten[np.newaxis,:]) # 전처리된 이미지를 입력으로 예측을 합니다.

      # 예측 결과로부터 숫자를 추출합니다.
      with tf.compat.v1.Session() as sess:
      print(tf.argmax(predictions, 1).eval())

  6. 서윤 2020.05.19 13:53

    파이썬 명령프롬프트로 해도 작동이 가능한가요?

  7. 서윤 2020.05.19 14:02

    1번은 완료됬는데 02.py에서
    >> import cv2
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    ImportError: No module named 'cv2'
    이렇게 뜨는데 뭐가 문제일까요 ㅠ?

  8. Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.19 14:03 신고

    pip opencv-python로 opencv를 설치하세요

    • 서윤 2020.05.19 14:05

      cmd창에 설치하나요? 파이썬 프롬프트에설치를 하나요 ㅠ?

    • 서윤 2020.05.19 14:06

      명령프롬프트 창에 설치하면 C:\Users\Administrator>pip opencv-python
      ERROR: unknown command "opencv-python"이렇게 뜨고 파이썬으로 설치하면 >>> pip opencv-python
      File "<stdin>", line 1
      pip opencv-python
      ^
      SyntaxError: invalid syntax이렇게 뜹니다 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.19 14:07 신고

      아 pip install opencv-python 입니다

  9. Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.19 14:05 신고

    텐서플로우 설치한 곳에서 실행하세요

    • 서윤 2020.05.19 14:09

      다운됩니다 감사합니다 ㅎㅎ!

  10. 서윤 2020.05.19 15:21

    >>> def process(img_input):
    ...
    File "<stdin>", line 2

    ^
    IndentationError: expected an indented block
    >>> gray = cv2.cvtColor(img_input, cv2.COLOR_BGR2GRAY)
    File "<stdin>", line 1
    gray = cv2.cvtColor(img_input, cv2.COLOR_BGR2GRAY)
    ^
    IndentationError: unexpected indent
    >>>
    >>> gray = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)

    이렇게 떠서
    인터넷에 검색하니까 들여쓰기 오류라는데 어떻게 해결해야할지 모르겠어요 ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.19 19:59 신고

      폅집기를 사용하세요.

      Sublime Text 3와 함께 Python 프로그래밍(Windows / Ubuntu)
      https://webnautes.tistory.com/454

  11. 1111 2020.06.20 16:54

    model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation=tf.nn.relu),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation=tf.nn.softmax)
    ])
    이부분에서 자꾸 ValueError: The first layer in a Sequential model must get an `input_shape` argument. 이러한 에러가 발생하여서
    저부분 없애고
    model = load_model('model.h5')
    이렇게 불러서 사용하려 했는데요
    계속해서 이부분에서
    predictions = model.predict(flatten[np.newaxis,:])
    다음과 같은 에러가 발생하는데..
    ValueError: Error when checking : expected conv2d_1_input to have 4 dimensions, but got array with shape (1, 784)
    이유 알 수 있을까요...?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.20 17:35 신고

      텐서플로우가 버전업되면서 바뀐부분이 있군요. 아래 링크를 참고하여 수정하세요

      https://www.tensorflow.org/learn?hl=ko

  12. 1111 2020.06.21 16:27

    빈 여백이나 배경을 비출때 쓰레기 값 숫자를 인식하는 것은 어쩔 수 없는건가요..?
    아니면 혹시 학습이 부족해서 그런건가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.21 19:34 신고

      무조건 학습한 것중 하나를 출력하도록 되어 있어 어쩔수 없습니다.

      예측된 수치값이 얼마 이상인 경우에만 제대로 검출된걸로 필터링해야 할듯합니다

+ Recent posts