공부/AIFFEL

Going Deeper(CV)_DJ 12 : 직접 만들어보는 OCR

dong_dong_2 2021. 4. 23. 15:45

1. 들어가며
    - 이번에는 OCR을 직접 만들어보는 시간을 갖는다.
    - OCR을 처음부터 끝까지 만들기에는 시간이 많이 소요되므로 Detection은 keras-ocr을 활용하고 Recognition을 직접 만들고 학습해보도록 했다.
2. Overall structure of OCR


    - 오늘 만들고자 하는 OCR은 이미지 속에서 영문을 Boundong box로 찾아내고 그 Bounding box 내에 어떤 Text가 포함되는지 알 수 있는 시스템이다.
    - 이미지 속에서 문자 영역을 찾아내는 것인 Text Detection은 강의노드에서 봤던 방법 중 Segmentation 기반의 CRAFT를 활용한 keras-ocr을 활용할 예정이다.
    - Recognition 모델은 keras-ocr을 사용하지 않고 직접 만들어보도록 했다.
    - 물론 keras-ocr에서도 recognition을 지원한다.
    - keras-ocr은 Convolution layer와 RNN을 결합하고 CTC로 학습된 CRNN의 모델을 사용한다.
    - CNN과 RNN의 아이디어를 결합하여 Text Recognition의 초기 모델의 뼈대를 완성했던 CRNN 모델은 2015년에 나온 것이다.
    - 그 이후로도 다양한 모델들이 새로운 기법을 제시하며 조금씩 성능향상을 이뤄왔다.
    - 2019년에 발표된 Naver Clova의 논문에서 당시까지의 모델의 발전사를 잘 살펴볼 수 있다.
    - 해당 논문의 제목은 What Is Wrong With Scene Text Recognition Model Comparisons? Dataset and Model Analysis 이다.
    - 그럼 keras-ocr의 CRNN 기반 Recognition 모델과 위 논문에 소개된 Recognition에서 가장 높은 성능을 얻은 모델은 어떤 점이 다를까?
       1) 첫번째로 입력이미지 변환 단계에서는 모델의 앞에서 글자를 Thin plate spline Transformation을 해주는 TPS 모듈이 붙고
       2) 마지막 Text 출력 단계에서는 Bidirectional LSTM 뒤로 Attention decoder가 붙는다.
3. Dataset for OCR
    - OCR은 데이터셋에 필요한 텍스트 정보는 사람이 직접 입력을 해야하는 번거로움이 있다.
    - OCR 데이터는 대량으로 만들어내려면 큰 비용이 들게 된다.
    - 이런 데이터 문제를 해결하기 위한 방법 중 하나는 컴퓨터로 대량의 문자 이미지 데이터를 만들어내는 방법이다.
    - 직접 문자 데이터를 생성하게 되면 원하는 언어를 원하는 폰트 그리고 원하는 배치, 크기로 문자 이미지를 대량으로 만들어낼 수 있는 장점이 있다.
    - 위에서 소개한 논문과 같은 논문들에서는 Recognition model의 정량적인 평가를 위해서 MJ Synth와 SynthText라는 데이터셋을 활용한다.
    - Recognition model을 제안하는 다양한 논문들에서도 성능 비교를 위해 두 데이터를 활용함을 기억해두면 좋을 것 같다.
    - 앞으로 만들어볼 Recognition model을 학습하기 위해서 MJ Synth를 사용해보도록 한다.
    - 데이터는 20GB가 넘는 압축파일에 포함된 데이터 중에서 MJ 데이터만 활용할 예정이다.
    - 이 스텝에서 코드를 통해 데이터를 다운받을 차례지만, 20GB가 넘기 때문에 전날 저녁부터 다운 받았다.
    - 퇴근 전에 다운을 받아놓고 다음날 출근하니 다운로드가 끝나있었다. 그래서 다운로드 받는데 몇 시간이 소요되는지 모른다.
4. Recognition model (1)
    - Text recognition 모델을 직접 만들어보도록 한다.
    - Recognition 모델은 2015년에 발표된 An End-to-End Trainable Neural Network for Image-based SequenceRecognition and Its Application to Scene Text Recognition 논문에서 처음 소개된 CRNN 구조를 활용해서 만들어보도록 한다.


    - CRNN의 구조는 위 그림에서 아래부터 올라가는 순서로 보면 된다.
    - 입력 이미지를 Convolution Layer를 통해 Feature를 추출하고 추출된 Feature를 얻어낸다.
    - Recurrent Layer는 추출된 Feature의 전체적인 Context를 파악하고 다양한 output의 크기에 대응이 가능하다.
    - 끝으로 Transcription layer(Fully connected layer)는 step마다 어떤 character의 확률이 높은지 예측한다.
    - 아래의 표를 통해 정확한 구조를 확인할 수 있다.


    - 참고로 영어와 숫자를 구분하려면 총 37개의 class가 필요하다
       1) 숫자는 0 ~ 9까지 10개이다.
       2) 영어는 A부터 Z까지 26개이다.
       3) 숫자 + 영어는 36개이다.
       4) 문자가 없는 경우를 위해서 공백을 추가할 경우 총 class의 갯수는 37개가 된다.
    - 이번 노드에서 새롭게 필요한 패키지는 lmdb라는 패키지이다.
    - LMDB는 Symas에서 만든 Lightning Memory-Mapped Database의 약자이다.
    - 이 패키지가 필요한 이유는 오늘 다루는 데이터셋이 lmdb 포맷(mdb)의 파일로 이뤄져있기 때문이다.
    - 해당 패키지를 설치한 후 코드 실습을 통해 패키지를 로딩하고 데이터 경로를 설정했다.
5. Recognition model (2) Input Image
    - 이번에는 데이터셋 안에 있는 이미지를 시각화 하여 어떻게 생겼는지 보고, lmdb를 통해 훈련데이터 이미지 일부를 열어서 shape가 어떻게 생겼는지, 이미지나 라벨은 어떻게 구성되어있는지 확인했다.
    - 코드 실습을 통해 진행되었다.
6. Recognition model (3) Encode
    - 위에서 데이터를 살펴봤을 때 Label이 우리가 읽을 수 있는 평문 Text로 이뤄져있는 것을 확인했다.
    - 이것은 모델을 학습하기 위해서 적절한 형태가 아니다.
    - 따라서 각 Character를 calss로 생각하고 이를 step에 따른 class index로 변환해서 encode를 해줘야 한다.
    - 이를 해줄 수 있는 LabelConverter 클래스를 작성해본다.
       1) __init__()에서는 입력으로 받은 text를 self.dict에 각 character들이 어떤 index에 매핑되는지 저장한다. 이 character와 index 정보를 통해 모델이 학습할 수 있는 output이 만들어진다. 만약 character = "ABCD"라면 "A"의 label은 1, "B"의 label은 2가 될 것이다.
       2) 공백(blank) 문자를 지정한다. 여기서는 공백 문자를 뜻하기 위해 '-'를 활용하며, label은 0으로 지정한다.
       3) decode()는 각 index를 다시 character로 변환한 후 이어주어 우리가 읽을 수 있는 text로 바꿔준다.
    - 위 조건을 보고 입력받은 text를 모델이 학습할 수 있는 label로 만드는 encode() 메소드를 구현했다.
    - 단, 같은 글자가 연속으로 이어지는 경우에는 이어지는 그 사이에 공백 문자의 label을 포함해야 한다.
    - OCR 모델 학습데이터에 왜 공ㅂ개 문자가 포함되어야 하는지는 밑에서 설명한다.
    - 코드를 통해 구현했고, HELLO를 Encode한 후 Decode가 정상적으로 동작하는지 확인했다. 이 때 동일한 글자 L이 연속될 때, 그 사이에 공백 문자가 포함된 것을 확인할 수 있었다.
7. Recognition model (4) Build CRNN model
    - 이제 입력과 출력을 준비했으니 모델을 만들어볼 차례다.
    - Keras에서 제공하는 keras.backend.ctc_batch_cost()를 활용해서 loss를 계산하도록 ctc_lambda_func이라는 함수를 만들었다. (이미 만들어져있었다.)
    - 이 함수에 대해서 잠시 짚고 넘어갔다. (이는 프로젝트 파일에서 정리했다.)
    - 그럼 위에서 만들어졌던 함수인 keras.backend.ctc_batch_cost()를 활용하여, image_input을 입력으로 그리고 마지막 Label을 output이라는 이름으로 출력하는 레이어를 갖도록 모델을 만드는 함수를 구현했다.
    - 코드를 통해서 구현했다.
8. Recognition model (5) Train & Inference
    - 이제 앞에서 정의한 MJDatasetSequence로 데이터를 적절히 분리하여 구성된 데이터셋을 통해 학습을 시켜본다.
    - 우리는 시간이 많지 않아서 적절한 Epoch를 설정하여 모델을 학습시켜봤다. 1Epoch에 10초 정도 소요되어서 100Epoch를 했다.
    - 그리고 학습된 모델을 테스트셋에서 inference해보고 이를 눈으로 확인했다.
    - 코드로 실습해서 확인했다.
9. 프로젝트 : End-to-End OCR
    - keras OCR의 Detector class를 이용해서 이미지 내의 문자 위치를 찾아내는 함수를 만들어본다.
    - 이를 위해서 keras-ocr를 설치해주고 Detector를 만들어야 한다.
    - 그리고 sample 사진을 한 장 줬다.
    - 여기서 준 sample 사진 내의 문자를 찾아주는 함수를 만들고 시각화하는 함수를 만든다.
    - 방금 얻어낸 이미지 내의 단어 이미지에서 Recognition model로 인식하는 함수를 직접 작성하고 결과를 출력하는 함수를 만든다.