NEW TECH/OCR

[Kakao_ocr API] Google Colab으로 OCR해보기

김쿸후 2021. 5. 25. 21:22

1. OCR이란? 

Optical Character Recognition (광학 문자 인식)
이미지에서 text범위를 추출하여 text를 문자로 인식하는 기술

OCR Flow

- OCR은 다음과 같이 텍스트 범위 인식 -> 이미지 정규화 -> 텍스트 recognition 과정으로 이루어진다. 

 

 

2. Google Colab으로 이미지 불러오기 

colab에서 OCR을 진행하기 전, 이미지를 불러올 방법을 먼저 알아보자.

 

2.1 drive 가지고오기

from google.colab import drive
drive.mount('/content/drive')
import numpy as np
import cv2
from google.colab.patches import cv2_imshow​

- 다음과 같이 구글 드라이브를 가지고 오면 인증을 하라고 팝업이 뜬다.

- 당황하지 않고 로그인을 다시 한뒤 구글이 주는 토큰을 입력하면 된다.

 

2.2 drive file 주소를 가지고와 cv2_imshow

#같은 드라이브에서 이미지 불러오는 함수
def get_image():
    file='/content/drive/MyDrive/자신의drivefolder/drivefile'
    img=cv2.imread(file, cv2.IMREAD_COLOR)
    file= cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    cv2_imshow(img)
  
if __name__=='__main__':
    get_image()

결과 사진

 

 

3. Google Colab으로 Kakao OCR 이용하기

Kakao API를 이용하기 위해선 developers.kakao 에 등록을 한뒤, 내 프로젝트를 등록하여 rest api key를 받아야 한다. 

개발자 등록을 하면 내 애플리케이션 탭에서 애플리케이션을 추가한 뒤 api key를 받을 수 있다.

 

 - 개발자 등록하는 링크 :

https://developers.kakao.com/

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 - REST API 키 받는 법

 

 

developers.kakao에는 개발자가 사용하기 유용한 다양한 api를 제공한다.

이중 난 OCR을 사용할 예정이다. 

개발 가이드의 구현 예제 함수를 이용하면 쉽게 OCR API를 이용할 수 있다. 

카카오 ocr 개발가이드 :

https://developers.kakao.com/docs/latest/ko/vision/dev-guide#ocr

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

3.1 Kakao OCR 불러오기

#Kako OCR api
import json

import requests
import sys

LIMIT_PX = 1024
LIMIT_BYTE = 1024*1024  # 1MB
LIMIT_BOX = 40


def kakao_ocr_resize(image_path: str):
    """
    ocr detect/recognize api helper
    ocr api의 제약사항이 넘어서는 이미지는 요청 이전에 전처리가 필요.

    pixel 제약사항 초과: resize
    용량 제약사항 초과  : 다른 포맷으로 압축, 이미지 분할 등의 처리 필요. (예제에서 제공하지 않음)

    :param image_path: 이미지파일 경로
    :return:
    """
    image = cv2.imread(image_path)
    height, width, _ = image.shape

    if LIMIT_PX < height or LIMIT_PX < width:
        ratio = float(LIMIT_PX) / max(height, width)
        image = cv2.resize(image, None, fx=ratio, fy=ratio)
        height, width, _ = height, width, _ = image.shape

        # api 사용전에 이미지가 resize된 경우, recognize시 resize된 결과를 사용해야함.
        image_path = "{}_resized.jpg".format(image_path)
        cv2.imwrite(image_path, image)

        return image_path
    return None


def kakao_ocr(image_path: str, appkey: str):
    """
    OCR api request example
    :param image_path: 이미지파일 경로
    :param appkey: 카카오 앱 REST API 키
    """
    API_URL = 'https://dapi.kakao.com/v2/vision/text/ocr'

    headers = {'Authorization': 'KakaoAK {}'.format(appkey)}

    image = cv2.imread(image_path)
    jpeg_image = cv2.imencode(".jpg", image)[1]
    data = jpeg_image.tobytes()


    return requests.post(API_URL, headers=headers, files={"image": data})

main 함수 외의 나머지 함수는 다 그대로 사용하였다. 

 

3.2  ocr api 호출 : JSON data로 받기

개발 가이드에 있는 main 함수를 그대로 사용하였다. 아, 한국어가 아스키코드로 나오기 때문에 ensure_ascii = False 로 바꾸어 한국어가 나오도록 하였다. 

def main():
    if len(sys.argv) != 3:
        print("Please run with args: $ python example.py /path/to/image appkey")
    image_path, appkey = '/content/drive/MyDrive/필요한이미지.jpg', '자기가 받은 Rest API 키'

    resize_impath = kakao_ocr_resize(image_path)
    if resize_impath is not None:
        image_path = resize_impath
        print("원본 대신 리사이즈된 이미지를 사용합니다.")

    output = kakao_ocr(image_path, appkey).json()
    #아스키코드 해제해줘야 한국어로 나옴
    print("[OCR] output:\n{}\n".format(json.dumps(output, ensure_ascii=False,sort_keys=True, indent=2)))


if __name__ == "__main__":
    main()

 - 결과는 다음과 같이 JSON형식의 file로 나온다.

 - box 범위는 다음과 같이 Integer형식으로 나오는데 나는 이것을 사진에서 어떤 범위인지 사진으로 받고 싶었다.

 

3.3  ocr api 호출 : 이미지 범위 확인하기

이미지를 잘라서 확인하려하니, text범위가 사선으로 되어있는 경우 자르기가 애매한 경우가 많았다. 

따라서 사진의 왜곡을 바르게 편 후, 최대한 직사각형의 menu 이미지로 만들어 함수에 넣었다. 

왜곡을 최소화 한 menu 사진

def main():
    if len(sys.argv) != 3:
        print("Please run with args: $ python example.py /path/to/image appkey")
   image_path, appkey = '/content/drive/MyDrive/필요한이미지.jpg', '자기가 받은 Rest API 키'
    resize_impath = kakao_ocr_resize(image_path)
    if resize_impath is not None:
        image_path = resize_impath
        print("원본 대신 리사이즈된 이미지를 사용합니다.")

    #카카오 API에서 범위, 인식한 글씨 받기
    output = kakao_ocr(image_path, appkey).json()
    outputdata = json.dumps(output, ensure_ascii=False,sort_keys=True, indent=2)
    print("[OCR] output:\n{}\n".format(outputdata))

    #받은 데이터 array로 변환
    outputdata = json.loads(outputdata)

 - JSON 파일을 하나씩 분리해서 보려니 제약이 많아 json.loads 함수를 이용하여 함수를 분리할 수 있도록 하였다. 

 - 이후 반복문을 이용하여 각각의 box 위치를 추출하여 이미지를 crop 하였다. 

 

  for i in range(len(outputdata['result'])):
    #box 모양으로 잘라서 보여주기 
      x = outputdata['result'][i]['boxes'][0][0]
      y = outputdata['result'][i]['boxes'][0][1]
      w =  (outputdata['result'][i]['boxes'][1][0] -  outputdata['result'][i]['boxes'][0][0])
      h =  (outputdata['result'][i]['boxes'][2][1] -  outputdata['result'][i]['boxes'][0][1])
   #원본 이미지
      org_image = cv2.imread('/content/drive/MyDrive/Menu/menu_crop.png') 
   #자른 이미지
      img_trim = org_image[y:y+h, x:x+w]
   #자른 이미지 보여주기
      cv2_imshow(img_trim)

if __name__ == "__main__":
    main()

- 원본 이미지를 org_img에 넣은 후 파이썬의 slicing[ : ] 기능을 이용하여 잘라준다. 

- 원본 이미지는 cv2.imread (위에 import 한 cv2 라이브러리의 함수) 를 이용하여 불러왔다. 

- 자른 이미지는 imshow를 이용하여 보여준다. 

 

결과 사진

범위에 맞게 crop 된 이미지

 

3.4  ocr api 호출 : 이미지 범위 + recognition_word 함께 보기

- 위의 crop까지 하니 어떤 이미지의 인식률이 어떤지 한눈에 보기 어려웠다. 

- crop한 이미지와 recognition_word를 함께 보기위한 함수를 추가했다.

 

recognition_word를 보기위한 함수 한줄을 추가해주면 된다.

 print(outputdata['result'][i]['recognition_words'][0])

 

// 따라서 완성된 main 함수는 다음과 같다.

def main():
    if len(sys.argv) != 3:
        print("Please run with args: $ python example.py /path/to/image appkey")
   image_path, appkey = '/content/drive/MyDrive/필요한이미지.jpg', '자기가 받은 Rest API 키'
   
    resize_impath = kakao_ocr_resize(image_path)
    if resize_impath is not None:
        image_path = resize_impath
        print("원본 대신 리사이즈된 이미지를 사용합니다.")

    #카카오 API에서 범위, 인식한 글씨 받기
    output = kakao_ocr(image_path, appkey).json()
    outputdata = json.dumps(output, ensure_ascii=False,sort_keys=True, indent=2)
    print("[OCR] output:\n{}\n".format(outputdata))

    #받은 데이터 array로 변환
    outputdata = json.loads(outputdata)

    for i in range(len(outputdata['result'])):
    #box 모양으로 잘라서 보여주기 
      x = outputdata['result'][i]['boxes'][0][0]
      y = outputdata['result'][i]['boxes'][0][1]
      w =  (outputdata['result'][i]['boxes'][1][0] -  outputdata['result'][i]['boxes'][0][0])
      h =  (outputdata['result'][i]['boxes'][2][1] -  outputdata['result'][i]['boxes'][0][1])
   #원본 이미지
      org_image = cv2.imread('/content/drive/MyDrive/Menu/menu_crop.png') 
   #자른 이미지
      img_trim = org_image[y:y+h, x:x+w]
   #자른 이미지 보여주기
      cv2_imshow(img_trim)
      print(outputdata['result'][i]['recognition_words'][0])

if __name__ == "__main__":
    main()

 

결과 화면 1.

crop 된 이미지와 인식된 text가 함께 보여지는 모습

 

 

번외) 손글씨 data 인식하기 

생각보다 잘돼서 손글씨 data도 입력해보았다. 

 

 - 원본 이미지

 

- 인식된 글씨

 

- recognition_words

감탄 감튀 불일치 (1글자 오류)
구운브리 치즈 구운 브리치즈 일치
고추치킨 고추치킨 일치
치즈 함 박 치즈 함박 일치
더덱(푸아그라 더덱푸아그라 일치 ( 더덕푸아그라 인가? )
쿠브스테이크 큐브스테이크 불일치 (1글자 오류)
곶감 곶감 일치
복극치즈 블루치즈 불일치 (2글자 오류)
연어 타1타즈 연어 타르타르 불일치 (2글자 오류)
오리콩피 오리콩피 일치
크로크구수 크로크무슈 불일치 (1글자 오류)

- 줄간격이 맞지않는 손글씨를 했음에도 불구하고 상당히 높은 정확도를 보여줌


전체 코드

https://github.com/gimkuku/Kakao_ocr

 

gimkuku/Kakao_ocr

Contribute to gimkuku/Kakao_ocr development by creating an account on GitHub.

github.com

 

위의 코드에는 기울어진 메뉴판을 바르게 피는 작업이 빠져있다. 

금방 어떻게 바르게 피는지에 대한 코드를 가지고 오도록 하겠다!~!