TesseractOCR, EasyOCR 실행과 학습

mminjg·2022년 5월 11일
3

프로젝트 개요

먼저 우리의 프로젝트를 간략하게 소개하자면, 티켓 정보를 바탕으로 하는 공연 후기 서비스이다. 따라서 유저의 티켓 정보를 필요로 하며, 이 글에서는 다음과 같은 티켓 사진에서 공연 일시와 장소, 좌석 정보를 가져오기 위한 OCR 작업을 소개하고자 한다.

TesseractOCR


Tesseract는 1985년에서 1994년 사이에 독점 소프트웨어로 개발되었고, 2006년부터 구글에서 후원하고 있다. 여러 언어를 지원하고, 오픈소스로 현재까지도 많이 사용되고 있다. Tesseract 버전3은 문자 패턴을 인식하여 작동하였으며, 버전 4부터 LSTM 기반 OCR 엔진 및 모델이 추가되었다. GPU는 사용이 불가능하다.

작업 환경 설정

Tesseract 학습에 있어서 windows 환경은 여러 제약이 있었고, 공식 홈페이지에서도 linux 환경을 추천하였기 때문에 도커 컨테이너에서 작업하는 것으로 결정하였다.

Dockerfile 작성

도커 이미지 생성을 위하여 Dockerfile을 작성하였다.

# Set docker image
FROM ubuntu:18.04

# Skip the configuration part
ENV DEBIAN_FRONTEND noninteractive

# Update and install depedencies
RUN apt-get update && \
    apt-get install -y wget unzip bc vim python3-pip libleptonica-dev git

# Packages to complie Tesseract
RUN apt-get install -y --reinstall make && \
    apt-get install -y g++ autoconf automake libtool pkg-config libpng-dev libjpeg8-dev libtiff5-dev libicu-dev \
        libpango1.0-dev autoconf-archive

# Set working directory
WORKDIR /app

# Copy requirements into the container at /app
COPY requirements.txt ./

# Getting tesstrain: beware the source might change or not being available
# Complie Tesseract with training options (also feel free to update Tesseract versions and such!)
# Getting data: beware the source might change or not being available
RUN mkdir src && cd /app/src && \
    wget https://github.com/tesseract-ocr/tesseract/archive/5.1.0.zip && \
	unzip 5.1.0.zip && \
    cd /app/src/tesseract-5.1.0 && ./autogen.sh && ./configure && make && make install && ldconfig && \
    make training && make training-install && \
    cd /usr/local/share/tessdata && \ 
    wget https://github.com/tesseract-ocr/tessdata_best/raw/main/eng.traineddata && \ 
    wget https://github.com/tesseract-ocr/tessdata_best/raw/main/kor.traineddata

# Setting the data prefix
ENV TESSDATA_PREFIX=/usr/local/share/tessdata

# Install libraries using pip installer
RUN pip3 install -r requirements.txt

# Set the locale
RUN apt-get install -y locales && locale-gen en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8

# Copy test.py and data files
RUN mkdir data
COPY ./src/test.py /app/src 
COPY ./src/__init__.py /app/src 
COPY ./data/. /app/data

# Git clone tesstrain
RUN cd /app/src && git clone https://github.com/tesseract-ocr/tesstrain.git

# Make model ground-truth directory
RUN mkdir -p /app/src/tesstrain/data/my-model-ground-truth

해당 레포를 참고하여 작성하였다.
tesseract 버전 변경과 한글 학습을 위하여 kor.traineddata 데이터를 추가하였으며, 이미지 만드는 시간을 단축시키기 위해 변경이 잦은 data 폴더를 copy하는 명령어는 아래쪽으로 이동하도록 수정하였다.

도커 이미지 생성

docker build -f Docker -t train-tesseract .

Dockerfile로 train-tesseract 이미지를 생성한다.

도커 컨테이너 실행 및 접속

Docker Volume vs Bind mounts

Tesseract 학습을 위해 학습 데이터 파일이 컨테이너에서 필요하다. 도커 이미지를 만들 때 데이터 전체를 복사하여 가져오는 방법도 있지만, 양이 많아 복사에 오랜 시간이 걸리고 학습 데이터를 계속 구하고 있는 상황이었기 때문에 호스트 시스템의 파일을 도커로 가져올 수 있으면 좋겠다고 생각하였다.

찾아보니 도커에는 컨테이너가 호스트의 시스템에 파일을 저장할 수 있는 VolumeBind mounts 방법이 있었다.

Volume

  • 파일이 도커에서 관리하는 호스트 파일 시스템 일부에 저장된다.
  • 볼륨은 도커에서 만들고 관리하며 호스트의 핵심 기능과 분리되는 장점이 있다.
  • 하나의 볼륨을 여러 컨테이너에서 동시에 사용할 수 있다.
  • 익명 볼륨을 만들 수 있다.

Bind Mounts

  • 파일을 호스트 시스템의 아무 곳에나 저장할 수 있다.
  • 호스트 시스템의 전체 경로로 참조된다.
  • 볼륨보다는 기능이 제한적이며, 성능이 우수하지만 호스트의 파일 시스템에 의존한다.

이미 데이터 파일을 특정 경로에 다운 받아 놓은 상황이라 볼륨 경로로 이동시키기에 부담이 있다고 판단하였고 바인드 마운트 방법을 선택하였다.

폴더 바인드 마운트 및 컨테이너 실행

docker run -d -it --name train-ocr --mount type=bind,source="<DATA PATH>",target="/app/src/tesstrain/data/my-model-ground-truth" train-tesseract

train-ocr 컨테이너의 /app/src/tesstrain/data/my-model-ground-truth<DATA PATH> 디렉토리가 마운트 된다.

예를 들면 Downloads/ground-truth 폴더에 학습 데이터가 있을 경우, 다음과 같이 작성하면 된다.

docker run -d -it --name train-ocr --mount type=bind,source="C:\Users\user\Downloads\ground-truth",target="/app/src/tesstrain/data/my-model-ground-truth" train-tesseract

셸 실행

docker exec -it train-ocr bash

Tesseract 실행해보기

cd /app/src
python3 test.py
import pytesseract
import cv2
from difflib import SequenceMatcher as SQ

config = ('-l kor+eng --oem 3 --psm 6')

img_path = '/app/data/validation/data1.tif'
txt_path = '/app/data/validation/data1.txt'

f = open(txt_path, 'r')
target = f.read()
f.close()

img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
gray = cv2.medianBlur(gray, 3)
raw_text = pytesseract.image_to_string(gray, config=config) 
print(f"Output:\n{raw_text}\n\nPercent coincidence:{round(SQ(None, target, raw_text).ratio()*100,2)}%")

test.py

  • pytesseract를 이용하여 ocr을 진행하였다.

  • data1의 이미지 파일과 정답 텍스트 파일을 읽어와서 OCR을 수행하고 OCR 결과 텍스트와 유사도를 출력한다. opencv를 이용한 이미지 전처리는 작성하지 않았다.

  • config psm: 4 혹은 6이 한글에 적합하다.

    0 = Orientation and script detection (OSD) only.
    1 = Automatic page segmentation with OSD.
    2 = Automatic page segmentation, but no OSD, or OCR. (not implemented)
    3 = Fully automatic page segmentation, but no OSD. (Default)
    4 = Assume a single column of text of variable sizes.
    5 = Assume a single uniform block of vertically aligned text.
    6 = Assume a single uniform block of text.
    7 = Treat the image as a single text line.
    8 = Treat the image as a single word.
    9 = Treat the image as a single word in a circle.
    10 = Treat the image as a single character.
    11 = Sparse text. Find as much text as possible in no particular order.
    12 = Sparse text with OSD.
    13 = Raw line. Treat the image as a single text line,
    bypassing hacks that are Tesseract-specific.

  • data1 이미지

  • 결과

Tesseract 학습

Tesseract 학습 방법

Tesseract 학습에는 다음 세 가지 방법이 있다.

  • Fine tuning
    기존에 학습된 언어에서 시작하여 특정 데이터를 추가로 학습하는 방법이며 적은 데이터로 학습할 수 있다.
  • Cut off the top layer (or some arbitrary number of layers) from the network and retrain a new top layer using the new data.
    fine tuning이 작동하지 않으면 차선책으로 사용한다. 새로운 언어나 스크립트를 훈련하는 데 작동할 수 있다.
  • Retrain from scratch
    학습데이터가 많지 않은 경우 어렵다. 과적합된 네트워크가 될 가능성이 높다.

Tesseract 학습은 두 가지 방법으로 학습데이터를 구축할 수 있다.

  • 폰트파일을 이용하여 자동으로 학습데이터 생성
  • 이미지와 정답 데이터를 이용한 학습데이터 생성

AI Hub 한국어 글자체와 자체 제작 데이터를 이용할 것이므로 두 번째 방법으로 진행하였다.

데이터 파일 전처리

data
├── dataset_info.json
├── 00000000.png
├── 00000001.png
├── 00000002.png
└── ...

AI Hub 한국어 글자체 데이터는 이미지 파일들과 1개의 json 파일로 구성되어있다.

ground-truth
├── 00000000.tif(.png/.bin.png/.nrm.png)
├── 00000000.gt.txt
├── 00000001.tif(.png/.bin.png/.nrm.png)
├── 00000001.gt.txt

학습을 위해서는 다음과 같은 형태로 저장되어야 한다.

import json

file_path = "./data_info.json"

with open(file_path, 'r', encoding="UTF8") as file:
  json_data = json.load(file)

for data in json_data['annotations']:
  if int(data['id']) > 532658:	# 끝 번호
    break
  f = open("<PATH>/ground-truth/"+data['id']+".gt.txt", 'w', encoding="UTF8")
  f.write(data['text'])
  f.close()

json 파일에서 id와 text 정보를 가져와 txt파일을 작성하여 저장한다.
이 때 이미지와 txt 파일들은 모두 도커 바인드 마운트를 진행한 폴더에 저장되어야 한다.

학습 시작

cd /app/src/tesstrain
make training MODEL_NAME=my-model START_MODEL=kor PSM=7 TESSDATA=/usr/local/share/tessdata
  • MODEL_NAME: 결과 모델의 이름. tesstrain/data/<MODEL_NAME>.traineddata에 저장된다.
  • START_MODEL: kor.traineddata를 초기 모델로 fine tuning을 진행한다.
  • PSM=7: 한 줄 텍스트 이미지에 대해 학습한다.

결과 확인

cp /app/src/tesstrain/data/my-model.traineddata /usr/local/share/tessdata/

새 모델을 Tesseract의 데이터 디렉토리로 이동시킨다.

test.py의 언어 config를 kor+eng에서 my-model 혹은 my-model+eng 으로 변경하여 결과를 확인한다.

음절데이터 약 53만개의 데이터를 학습하는데 약 3일 정도 소요되었으며, 기대한 학습결과는 나오지 않았다.

Github Repository

https://github.com/TCAT-capstone/train-tesseract
다음 레포에서 데이터 전처리 코드를 제외한 전체 코드를 확인할 수 있다.

Reference

https://github.com/guiem/train-tesseract
https://github.com/tesseract-ocr/tesstrain
https://docs.docker.com/storage
https://aihub.or.kr/aidata/133


EasyOCR

최근에 개발된 오픈소스 OCR로 Detection은 Clova AI의 CRAFT 알고리즘을 사용하고 있으며, Recongnition model은 CRNN이다. Tesseract와 달리 GPU를 지원한다.

Tesseract와 비교하여 좋았던 점은 한 번 실행에 여러 언어 인식을 제공한다는 점이었다. Tesseract도 kor+eng 모델로 실행을 할 수 있었으나, 하나의 모델로 실행할 때보다 인식률이 매우 낮았다. 우리가 하고자 하는 프로젝트에서는 두 가지 언어를 인식하는 점이 중요한데, 이 점에서 EasyOCR이 더 낫다고 판단하여 변경하게 되었다.

다만 텍스트 인식까지 오랜 시간이 소요된다는 점은 EasyOCR의 아쉬운 점인 것 같다.

EasyOCR 실행해보기

pip install easyocr

실행을 위해 easyocr을 설치한다.

import easyocr

reader = easyocr.Reader(['ko', 'en'])
result =  reader.readtext('./data1.jpg')

간단한 파이썬 코드를 작성하여 실행할 수 있다.

EasyOCR은 결과값으로 검출된 텍스트의 바운딩 박스 좌표를 제공해주는데, 이 정보로 위와 같이 이미지에 박스를 그려 확인할 수 있다.
인식 결과로는 학습 없이도 거의 정확한 결과를 보여주는 것을 알 수 있었다.

import cv2
import easyocr
import numpy as np
import cv2
import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image

reader = easyocr.Reader(['ko', 'en'])
result =  reader.readtext('./data.jpg')

img = cv2.imread('./data.jpg')

img = Image.fromarray(img)
font = ImageFont.truetype("malgun.ttf", 40)
draw = ImageDraw.Draw(img)

for i in result:
  x = i[0][0][0]
  y = i[0][0][1]
  w = i[0][1][0] - i[0][0][0]
  h = i[0][2][1] - i[0][1][1]

  draw.rectangle(((x, y), (x+w, y+h)), outline="blue", width=2)
  draw.text((int((x+x+w)/2), y-40), str(i[1]), font=font, fill="blue")

plt.imshow(img)
plt.show()

EasyOCR 학습

EasyOCR 사용자 모델 학습 과정은 해당 블로그를 참고하였으며 프로젝트 구조는 다음과 같다.

├── /step3
│   ├── /training
│   │   └── /kordata
│   │       ├── data.lmdb
│   │       └── data.lmdb
│   ├── /validation
│   └── /test
│
├── /pre_trained_model
│   # DTRB 프로젝트에서 사용하게 될 Pre-trained 모델파일의 경로
│   └── korean_g2.pth
│
├── /user_network_dir
│   # EasyOCR 프로젝트에서 사용하게 될 사용자 모델 및 설정 파일의 저장 경로
│   ├── custom.pth
│   ├── custom.py
│   └── custom.yaml
│
└── /demo_images
    # 학습한 모델의 성능을 확인하기 위한 테스트 이미지
    ├── demo_01.png
    ├── demo_02.png
    └── ...

우리 프로젝트는 자체 데이터를 사용할 것이기 때문에 학습 데이터의 생성 과정은 생략하였다.

과정은 다음과 같다.

  1. 학습용 데이터 포맷 lmdb 형태로 변환
  2. 한글 클래스 적용
  3. EasyOCR 한글 모델 다운
  4. 모델 학습
  5. 모델 적용 및 테스트

학습은 Windows(GPU X) 환경에서 진행하였으며 이 때문에 몇 가지 코드 변경이 있다.
그러나 코드를 변경하지 않고 Google Colab에서 학습도 가능하다.

deep-text-recognition-benchmark 가져오기

git clone https://github.com/clovaai/deep-text-recognition-benchmark.git

먼저 학습을 위해 deep-text-recognition-benchmark 레포지토리를 clone 한다.

학습용 데이터 포맷 lmdb 형태로 변환

data
├── gt.txt
└── test
    ├── word_1.png
    ├── word_2.png
    ├── word_3.png
    └── ...

학습 데이터는 다음과 같이 저장하여야 create_lmdb_dataset.py를 이용하여 lmdb 형태로 변환할 수 있다.

test/word_1.png Tiredness
test/word_2.png kills
test/word_3.png A

gt.txt{imagepath}\t{label}\n의 형태로 작성하여야 한다.

# training 데이터 변환
create_lmdb_dataset.py --gtFile "<gt.txt path>" --inputPath "<images path>" --outputPath "<Path>\train-easyocr\step3\training\kordata"

# validation 데이터 변환
create_lmdb_dataset.py --gtFile "<gt.txt path>" --inputPath "<images path>" --outputPath "<Path>\train-easyocr\step3\validation\kordata"

# test 데이터 변환
create_lmdb_dataset.py --gtFile "<gt.txt path>" --inputPath "<images path>" --outputPath "<Path>\train-easyocr\step3\test\kordata"

deep-text-recognition-benchmark 프로젝트의 루트에서 명령을 실행한다.

한글 클래스 적용

deep-text-recognition-benchmark 프로젝트의 train.py 285라인에 아래 코드를 삽입한다.

opt.character = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~가각간갇갈감갑값강갖같갚갛개객걀거걱건걷걸검겁것겉게겨격겪견결겹경곁계고곡곤곧골곰곱곳공과관광괜괴굉교구국군굳굴굵굶굽궁권귀규균그극근글긁금급긋긍기긴길김깅깊까깎깐깔깜깝깥깨꺼꺾껍껏껑께껴꼬꼭꼴꼼꼽꽂꽃꽉꽤꾸꿀꿈뀌끄끈끊끌끓끔끗끝끼낌나낙낚난날낡남납낫낭낮낯낱낳내냄냉냐냥너넉널넓넘넣네넥넷녀녁년념녕노녹논놀놈농높놓놔뇌뇨누눈눕뉘뉴늄느늑는늘늙능늦늬니닐님다닥닦단닫달닭닮담답닷당닿대댁댐더덕던덜덤덥덧덩덮데델도독돈돌돕동돼되된두둑둘둠둡둥뒤뒷드득든듣들듬듭듯등디딩딪따딱딴딸땀땅때땜떠떡떤떨떻떼또똑뚜뚫뚱뛰뜨뜩뜯뜰뜻띄라락란람랍랑랗래랜램랫략량러럭런럴럼럽럿렁렇레렉렌려력련렬렵령례로록론롬롭롯료루룩룹룻뤄류륙률륭르른름릇릎리릭린림립릿마막만많말맑맘맙맛망맞맡맣매맥맨맵맺머먹먼멀멈멋멍멎메멘멩며면멸명몇모목몰몸몹못몽묘무묵묶문묻물뭄뭇뭐뭣므미민믿밀밉밌및밑바박밖반받발밝밟밤밥방밭배백뱀뱃뱉버번벌범법벗베벤벼벽변별볍병볕보복볶본볼봄봇봉뵈뵙부북분불붉붐붓붕붙뷰브블비빌빗빚빛빠빨빵빼뺨뻐뻔뻗뼈뽑뿌뿐쁘쁨사삭산살삶삼상새색샌생서석섞선설섬섭섯성세센셈셋션소속손솔솜솟송솥쇄쇠쇼수숙순술숨숫숲쉬쉽슈스슨슬슴습슷승시식신싣실싫심십싱싶싸싹쌀쌍쌓써썩썰썹쎄쏘쏟쑤쓰쓸씀씌씨씩씬씹씻아악안앉않알앓암압앗앙앞애액야약얇양얗얘어억언얹얻얼엄업없엇엉엌엎에엔엘여역연열엷염엽엿영옆예옛오옥온올옮옳옷와완왕왜왠외왼요욕용우욱운울움웃웅워원월웨웬위윗유육율으윽은을음응의이익인일읽잃임입잇있잊잎자작잔잖잘잠잡장잦재쟁저적전절젊점접젓정젖제젠젯져조족존졸좀좁종좋좌죄주죽준줄줌줍중쥐즈즉즌즐즘증지직진질짐집짓징짙짚짜짝짧째쨌쩌쩍쩐쪽쫓쭈쭉찌찍찢차착찬찮찰참창찾채책챔챙처척천철첫청체쳐초촉촌총촬최추축춘출춤춥춧충취츠측츰층치칙친칠침칭카칸칼캐캠커컨컬컴컵컷켓켜코콜콤콩쾌쿠퀴크큰클큼키킬타탁탄탈탑탓탕태택탤터턱털텅테텍텔템토톤톱통퇴투툼퉁튀튜트특튼튿틀틈티틱팀팅파팎판팔패팩팬퍼퍽페펴편펼평폐포폭표푸푹풀품풍퓨프플픔피픽필핏핑하학한할함합항해핵핸햄햇행향허헌험헤헬혀현혈협형혜호혹혼홀홍화확환활황회획횟효후훈훌훔훨휘휴흉흐흑흔흘흙흡흥흩희흰히힘"

EasyOCR 한글 모델 다운

EasyOCR Model Hub에서 학습을 위한 Pre-trained 모델을 다운로드한다.
korean_g2 모델을 다운받으면 되고, 프로젝트\pre_trained_model에 저장한다.

모델 학습 - Windows

GPU가 없는 Windows 환경에서 프로젝트의 train.py를 실행하게 되면 수많은 에러 메세지가 나오게 된다. 실행을 위해 몇 가지 코드 수정이 필요하다.

create_lmdb_dataset.py 수정

env = lmdb.open(outputPath, map_size=1099511627776)
↓수정↓
env = lmdb.open(outputPath, map_size=1073741824)

디스크의 용량이 충분하지 않은 경우 에러가 발생한다.
stackoverflow

train.py 수정

model.load_state_dict(torch.load(opt.saved_model), strict=False)
↓수정↓
model.load_state_dict(torch.load(opt.saved_model, map_location=device), strict=False)

gpu가 없기 때문에 cpu에서 불러와야 한다.
tutorials.pytorch
만약 위에 한글 클래스 적용 후 인코딩 오류가 뜬다면 상단에 # -*- coding: utf-8 -*-도 추가하여야 한다.

학습 실행

train.py --train_data "<Path>\train-easyocr\step3\training" --valid_data "<Path>\train-easyocr\step3\validation" --select_data / --batch_ratio 1 --Transformation None --FeatureExtraction "VGG" --SequenceModeling "BiLSTM" --Prediction "CTC" --input_channel 1 --output_channel 256 --hidden_size 256 --saved_model "<Path>\train-easyocr\pre_trained_model\korean_g2.pth" --FT --workers 0
  • --workers 0: gpu가 없기 때문에 추가하였다.

모델 학습 - Colab

조금 더 빠른 학습을 위해 Google Colab을 이용할 수도 있다. 그러나 무료 버전의 경우 12시간 세션 제한이 있는 점은 알아두어야 한다.

Colab은 Windows에서와 달리 프로젝트 코드의 수정 없이 실행하면 된다.
구글 드라이브에 학습 데이터를 저장하고, 드라이브 마운트를 통해 path를 설정해주고 똑같이 실행하면 된다.

모델 적용 및 테스트

모델 적용을 위한 custom.py 모듈 파일을 생성하고 run.py로 테스트를 실행한다. 위 두 코드는 여기서 확인할 수 있다.

실행결과는 역시나, 기대한 결과는 나오지 않았으며 테스트 데이터 등 여러 변수들을 고려하여 학습 수정 중에 있다.

Reference

https://github.com/JaidedAI/EasyOCR
https://github.com/clovaai/deep-text-recognition-benchmark
https://davelogs.tistory.com/94


OpenCV 이미지 전처리

사실 EasyOCR은 배경이 있거나, 왜곡이 있어도 인식을 잘 하는 편이다.
그러나 EasyOCR의 결과를 개선시키는 방법에서 사진 크기 조정, 기울기 조정 등은 기본적으로 소개되는 방법이기 때문에 진행하였다.

모든 상황에 적용이 되진 않을 수도 있지만, 나름대로 진행한 과정을 작성하겠다.

과정

테스트 이미지

전처리 과정을 소개하기 위한 테스트 이미지이다.

이미지 가져오기

src = cv2.imread('./data/data1.jpg', 1)

data폴더의 data1.jpg 이미지를 가져온다.

흑백 변환, 노이즈 제거

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)

이미지를 흑백으로 변환하고, Gaussian Blur로 노이즈를 없앤다.

canny 엣지 검출

canned = cv2.Canny(gray, 150, 300)

canny로 엣지를 검출한다.

엣지 연결

kernel = np.ones((10,1),np.uint8) # 가로 1 세로 10
mask = cv2.dilate(canned, kernel, iterations = 20)

dilate은 이미지를 팽창시킨다. dilate 인자에는 kenel과 iterations이 들어간다.
kenel matrix가 이미지 matrix를 이동하며 효과를 적용하며, iterations은 반복 횟수이다. 엣지들을 세로로 연결해주기 위해 10,1 행렬을 kernel로 설정하였다.

가장 큰 contours 찾아 외곽 사각형 그리기

# contours 찾기
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 가장 큰 contours 찾기
biggest_cntr = None
biggest_area = 0
for contour in contours:
    area = cv2.contourArea(contour)
    if area > biggest_area:
        biggest_area = area
        biggest_cntr = contour

# 외곽 box
rect = cv2.minAreaRect(biggest_cntr)
box = cv2.boxPoints(rect)
box = np.int0(box)

cv2.findContours로 가장 큰 면적을 가진 contour를 찾아 cv2.minAreaRect로 주어진 점을 감싸는 최소 크기 회전된 사각형을 찾는다.

사진 기울기 조정

# angle 계산
angle = rect[-1]
if angle > 45:
    angle = -(90 - angle)

# 기울기 조정
rotated = src.copy()
(h, w) = rotated.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(rotated, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

위에서 찾은 사각형의 angle에서 보정하기 위해 angle을 다시 계산하고 cv2.getRotationMatrix2D로 중앙을 기준으로 이미지를 회전시킨다.

이미지 자르기

# 회전된 박스 좌표 찾기
ones = np.ones(shape=(len(box), 1))
points_ones = np.hstack([box, ones])
transformed_box = M.dot(points_ones.T).T

y = [transformed_box[0][1], transformed_box[1][1], transformed_box[2][1], transformed_box[3][1]]
x = [transformed_box[0][0], transformed_box[1][0], transformed_box[2][0], transformed_box[3][0]]

y1, y2 = int(min(y)), int(max(y))
x1, x2 = int(min(x)), int(max(x))

# crop
crop = rotated[y1:y2, x1:x2]

회전된 사각형의 꼭짓점 좌표를 계산하여 이미지를 자르고 흑백 처리를 한다.

Github Repository

https://github.com/TCAT-capstone/ocr-preprocessor
다음 레포에서 이미지 전처리 전체 코드를 확인할 수 있다.

Reference

https://docs.opencv.org/4.x/da/d22/tutorial_py_canny.html
https://deep-learning-study.tistory.com/232
https://theailearner.com/tag/angle-of-rotation-by-cv2-minarearect
https://stackoverflow.com/questions/30327659/how-can-i-remap-a-point-after-an-image-rotation

0개의 댓글