JOBKOREA x ALBAMON
명함 다 내놔! 잡코리아 신규앱 nooc(눜) 에서 명함 인식해보기
NEW JOB NEW ME —
저는 잡코리아의 대표(가 되고싶은) 링커 임동현입니다.
현재 Flutter 앱 개발자로 23년부터 잡코리아와 함께하고 있는데요.
입사하자마자 게임잡 앱을 Flutter로 리뉴얼 하고, (아직은 말할 수 없는) 어떤 앱도 Flutter로 리뉴얼을 준비하며 평온하게 지내던 와중에 충격적인 소식을 듣게됩니다.
‘오늘부터 nooc 이라는 앱에서 명함인식 개발 해줘 ’
예? 제가요? 왜요?
라고 하기에는 프로젝트의 기술 스택이 너무 맛있어보였습니다.
내 밥벌이로 하고있는 Flutter에다가, 영상처리와, OCR, 그리고 LLM 을 붙여볼 수 있다니. 평소 해보고싶었던 기술들의 종합선물(?) 같은 패키지 구성이라 참지 못하고 프로젝트에 합류하게 됩니다.
그런데 합류때까진 몰랐습니다.
제가 명함 인식에서 Flutter 부분과, 영상처리와, OCR과 LLM을 다 해야 한다는 것을요. (다행히 중반부터 LLM관련 서비스는 동료링커 승원님이 개발해주셨습니다. 큰일날뻔 했어요)
nooc이 뭔가요?? 먹는거에요?
네, 저도 4달 전까진 몰랐습니다.
nooc, 이놈은 내 손으로 만들었지만 엄청난 친구입니다.
(저보단 팀원들이 모두 에이스였기에 엄청난 친구로 거듭난거지만..!✨)
종이한장 나지 않는 한국에서, 디지털 명함으로 환경보호도 하고!
나만의 개성있는 명함도 만들 수 있고!
원터치로(사실 몇번 눌러야 함) 타인 명함도 10초만에 저장할 수 있고!
‘힙하게’ 다른사람 핸드폰에 NFC 카드를 가져다 대면 명함공유도 할 수 있고!
‘안 쓸 이유가 없다~ 이말이다’ 라는 겁니다 (앱에 대한 자세한 소개는 아래 링크를 통해 확인 가능합니다!)
그래서 nooc 앱에서는 정확히 어떤 방식과 기술들로 명함인식을 했나요?
nooc에서 진행하는 명함인식은 크게 여섯 단계로 나뉩니다.
프로세스에 대한 세부 설명은 아래와 같습니다.
1. 맨 먼저 사진을 찍거나, 갤러리에서 이미지를 불러옵니다.
2. 그렇게 불러온 이미지를 사전 학습시킨 모델로, Object detection 을 통해 깔끔하게 외곽선을 검출해줍니다
3. 검출한 포인트를 기반으로 원근 변환(Perspective Transform) 해줍니다. 한마디로, 3차원 공간을 찍으며 찌그러진 이미지를 2차원의 네모 모양으로 변환합니다.
👉 이 과정에서 소실된 부분에 대한 Interpolation 이 일어나므로, 과도하게 찌그러진 이미지는 화질이 저하될 수 있습니다.
4. 이후 이미지 전처리를 통해서 AI 모델이 ‘YUMMY’ 하게 데이터를 먹을 수 있을 때까지, 다양한 방식(Morphological Transformations, Thresholding, Sharpening)을 통해 이미지를 후처리(Post processing) 합니다.
👉 이 단계를 통해 더욱 선명한 Text를 만드는 게 주요 목적입니다.
👉 또한 Noise 제거를 통해 불필요한 캐릭터 제거를 합니다. (노이즈가 dot 혹은 comma, 특히 글자가 잘못 인식되는 현상( ‘ㅣ’ 가 ‘ㅏ’로 인식되는 등)을 방지)
5. 후처리된 이미지들을 들고, MLKit 을 통해 다중 OCR을 빠르게 돌려줍니다.
👉 같은 소스에서는 같은 OCR 결과만 얻을 수 있지만, 후처리를 이용해 이미지 특성을 바꾸면 다양한 특성을 가진 결과를 얻을 수 있습니다. 이를 이용해 MLKit의 Baseline 이상의 정확도를 기대할 수 있습니다.
👉 추후 더욱 빠르고 정확한 자사 모델로 교체하려 합니다. 도움! DevRel!
👉 정규화된 텍스트 세트와 텍스트의 정확도 (Confidence) 세트를 생성합니다
6. 이후 LLM 을 통해 명함 데이터를 분류하고 인식(NER)합니다!
👉 이름/전화번호/이메일/유선전화 등등의 데이터를 분류합니다
👉 참고로 저희의 LLM의 “엄청난” 처리속도는 아래와 같습니다.
그냥 넣지 왜 굳이 영상처리를 했나요??
텍스트 인식 모델은 아직 만능이 아니기에, 사진의 컨디션이 조금이라도 떨어지거나, 인쇄된 글자 자체의 상태가 좋지 않다면 좋은 결과를 얻을 수 없습니다.
또한 모바일 환경의 H/W 특성상, 경량화된 모델(TFLite)을 사용하기 때문에 정확도는 더 떨어지게 됩니다.
이런 상황을 최대한 극복하기 위해, OCR/NER 단계 이전에 사진 데이터를 Cleansing 해주는 과정이 필수적으로 들어가게 됩니다.
Cleansing 과정에서는 여러 Post-processing 과정을 거치며 대략적으로 아래와 같은 느낌의 처리를 합니다.
어떤 후처리 방식을 사용하느냐에 따라, 아래 사진처럼 오히려 노이즈가 강조되어 사용할 수 없는 이미지도 나올 수 있습니다.
위 사례들 처럼 다양한 방식을 시도해봤고, 현재는 저희가 사용하는 OCR 모델에서 가장 좋은 인식률을 보인 후처리방식을 골라서 사용하고 있습니다.
아래 차트는 저희 nooc의 런칭시점 명함 인식률입니다. 결과적으로는 오픈 시점까지 여러가지 튜닝기법을 거쳐 최초의 baseline에서 인식률을 약 10% 끌어올릴 수 있었습니다. 😄
구현상 다른 어려움은 없었나요??
Flutter 가 현재 어느정도 궤도에 올라 호환성 문제나, 라이브러리의 부재 등 많은 이슈들이 줄어들기는 했습니다.
하지만 기존 Android / iOS 를 네이티브로 구현하는 것 대비, 버그도 많고 라이브러리도 많이 부족한 상황입니다.
특히 이번 프로젝트에서는, OpenCV dart 라이브러리를 연동하며 메모리 누수 문제가 발생해 다른 개발자들 몰래(?) 메모리를 해제시키는 동기 타이밍을 넣어놨었는데, 다행히 프로젝트 종반부 라이브러리 자체의 메모리 누수문제가 해결되어 완벽하게 동작시킬 수 있었습니다.
또 nooc은 일반 상용 앱처럼, 상위 계층의 자원만을 이용하는게 아니라, NNAPI 등 하드웨어 종속적인 기능을 이용하게 됩니다.
그런데 단일 앱으로 모든 시나리오가 구성되어있는 flutter에서, Android와 iOS의 반응이 달라 환경을 세팅하는데 꽤 고생을 했던 기억도 있습니다.
結
한명의 Flutter 앱 개발자로서 참 재미있었던 프로젝트였습니다.
24년 들어 기획된 새로운 서비스였기 때문에, 가장 최신의 기술들을 제한 없이 사용할 수 있었고, 다양한 선택지 중 가장 빠르고 좋은 기술을 도입할 수 있었습니다.
최신 기술들을 한데 엮어 서비스를 제작한 점이 참 뿌듯하고, 이런 기회를 준 회사와 동료들에게 감사함을 느낍니다.