본문 바로가기
데이터 어쩌구/기술 써보기

[기록] OCR 변환한 한글을 DB 내 가까운 결과로 반환하기

by annmunju 2023. 12. 7.

1. 배경

  • 텍스트를 포함한 이미지(실시간 혹은 사진)에 나온 내용을 DB와 가장 가까운 결과로 반환하고자 한다.

2. 문제

  • OCR 결과가 이상하게 나오는 경우가 존재
  • 정확하지 않은 문장도 찾을 수 있게 만들고 싶다.

3. 해결

초안 : word2vec을 이용하여 문맥상 가장 가까운 결과 도출

처음에는 한글 단어를 벡터화 하여 거리를 계산하는 방식을 사용하려고 했다.

[과정]

  1. 이미지에서 OCR을 이용해 텍스트 변환. 특수문자, 영문이 포함된 잘못 나타난 결과를 전처리 (정규식 사용하여 특수문자 제거, 조사와 어미 등은 뒤에 띄어쓰기를 더하여 올바른 형태에 가까운 문장으로 변형.)
  2. 한글 단어를 벡터화 하기위해서 kor2vec(https://github.com/naver/kor2vec) 사용하고자 함
  3. 훈련이 필요하여 오픈 소스로 korpora(https://github.com/ko-nlp/Korpora) 패키지를 사용해 한국위키피디아 데이터를 활용
  4. 훈련...

[문제점]

  • OCR이 잘못 검출된 경우는 "문맥"으로 가까운 것을 찾는 것 보다 자모 분리된 하나하나의 단위로 가까운 것을 찾는 것이 더 합리적이다.
  • 해당 방법은 맥락이 비슷한 것을 찾는 방법으로 적합하지 않다.

대안 : hgtk 패키지를 사용해서 자모를 분리하고 edit distance로 가까운 거리를 계산

[과정]

  1. 기존과 동일
  2. 한글만 남았는지 점검하고 자모 분리
  3. edit distance 계산한 후 검색어 자모음 개수를 나눠 DB에 있는 전체 한글 단어들간의 절대적 점수를 계산.
  4. 절대적 점수를 min-max scailing 하여 상대적 점수도 계산
  5. 전체적으로 절대적, 상대적 점수가 정해놓은 각각의 Threshold를 넘는지 판단하여 데이터를 보여줌 (가까운 순으로)

[코드]

def extract_img_to_text(img_array:np.ndarray) -> str:
    '''
       이미지에서 easyocr을 이용하여 텍스트를 읽고 전처리하는 함수
    '''
    reader = easyocr.Reader(['ko','en']) # Korean, English

    result = reader.readtext(img_array)
    txt_result = ''
    for r in result:
        txt_result += r[-2] 

    kor_str = re.sub(r"[^ㄱ-ㅣ가-힣\s]", "", txt_result).strip()

    return kor_str


def search_txtimg_to_artwork_df(img_pil:Image.Image, tmp_df:pd.DataFrame) -> pd.DataFrame:
    '''
    텍스트 이미지 PIL와 데이터프레임을 넣으면 가장 가까운 단어를 찾아주는 함수
    '''
    sentents = extract_img_to_text(np.array(img_pil))

    if len(sentents) == 0:
        return pd.DataFrame()
    else:
        cv_split = ''.join([''.join(hgtk.letter.decompose(res)) for res in sentents if (res != ' ') and hgtk.checker.is_hangul(res)])
        search_idx = tmp_df['작품명_검색'].apply(lambda x : editdistance.eval(cv_split, x))
        tmp_df['거리'] = search_txt_dist = search_idx/len(cv_split)
        tmp_df['거리_scaled'] = np.round((np.array(search_txt_dist)-min(search_txt_dist))/(max(search_txt_dist)-min(search_txt_dist))*100)
        search_df = tmp_df[(tmp_df['거리'] < 15) & (tmp_df['거리_scaled'] < 20)] #각 숫자가 Threshold

        return search_df.sort_values('거리')

[결과] 

- 데이터 수 100여개인 데이터 프레임에서 오탈자가 있어도 문제 없이 잘 찾아준다. 
- 코드 최적화 하면 더 좋겠지만 우선 시간 급하다 급해

728x90