1. OCR이란?
Optical Character Recognition (광학 문자 인식)
이미지에서 text범위를 추출하여 text를 문자로 인식하는 기술
- 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를 받을 수 있다.
- 개발자 등록하는 링크 :
- REST API 키 받는 법
developers.kakao에는 개발자가 사용하기 유용한 다양한 api를 제공한다.
이중 난 OCR을 사용할 예정이다.
개발 가이드의 구현 예제 함수를 이용하면 쉽게 OCR API를 이용할 수 있다.
카카오 ocr 개발가이드 :
https://developers.kakao.com/docs/latest/ko/vision/dev-guide#ocr
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 이미지로 만들어 함수에 넣었다.
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를 이용하여 보여준다.
결과 사진
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.
번외) 손글씨 data 인식하기
생각보다 잘돼서 손글씨 data도 입력해보았다.
- 원본 이미지
- 인식된 글씨
- recognition_words
감탄 | 감튀 | 불일치 (1글자 오류) |
구운브리 치즈 | 구운 브리치즈 | 일치 |
고추치킨 | 고추치킨 | 일치 |
치즈 함 박 | 치즈 함박 | 일치 |
더덱(푸아그라 | 더덱푸아그라 | 일치 ( 더덕푸아그라 인가? ) |
쿠브스테이크 | 큐브스테이크 | 불일치 (1글자 오류) |
곶감 | 곶감 | 일치 |
복극치즈 | 블루치즈 | 불일치 (2글자 오류) |
연어 타1타즈 | 연어 타르타르 | 불일치 (2글자 오류) |
오리콩피 | 오리콩피 | 일치 |
크로크구수 | 크로크무슈 | 불일치 (1글자 오류) |
- 줄간격이 맞지않는 손글씨를 했음에도 불구하고 상당히 높은 정확도를 보여줌
전체 코드
https://github.com/gimkuku/Kakao_ocr
위의 코드에는 기울어진 메뉴판을 바르게 피는 작업이 빠져있다.
금방 어떻게 바르게 피는지에 대한 코드를 가지고 오도록 하겠다!~!