영수증 속 그림자 없애는 내용입니다.

계속 봐왔던 이미지 이지만,
영수증 접혀진 부분 봐보면, 그림자가 좀 보입니다.
원래는 이전 포스팅 에서 언급했던 것 처럼,
서로 비교해보려고 작업했던 걸 이진화 과정을 거친 후, 결과물을 확인했으나,
접혀진 부분의 그림자 때문에 잘 안 나왔습니다.
그래서 그림자 부터 확실하게 잡고 넘어가자는 의미로
작업 과정을 정리해봅니다.
전 포스팅에서 Binarization 내용 정리할때 살짝 언급하고 넘어갔었는데,
전에는 노이즈 제거 명분으로 언급했었지만,
여기서는 정규화를 위해 사용되었습니다.
bg_blur = cv2.GaussianBlur(image, (55, 55), 0)
normalized = cv2.divide(image, bg_blur, scale = 255)
필터 크기는 55 by 55로 맞춰줬는데,
크면 클수록 더 강한 블러 효과를 불러옵니다.
뒤에 0값은 표준 편차(σ)로, 블러 강도를 설정하는 값인데,
0으로 설정하면 자동으로 최적의 σ값을 게산합니다.
여기서, 검은 부분과 흰 부분의 대략적 패턴을 저장(이용)하기 위해서 가우시안 필터를 적용한 것이고,
뒤에 정규화(0~255값 변환) 과정을 통해서
밝은 영역은 더 밝게, 어두운 부분은 더 어둡게 할 수 있습니다.

왼쪽이 원래 이미지, 오른쪽이 정규화 후 출력 이미지 입니다.
확실히 그림자는 많이 감소된게 보입니다.
바로 전 내용에, Opening, Closing 연산을 거쳤었습니다.
열림과 닫힘 연산은 노이즈 제거, 작은 구멍 채우는 효과가 있다면,
Top-hat, Bottom-hat은
밝기 차이를 기반으로 특정 영역(텍스트, 그림자)을 강조
하는 효과를 가지고 있습니다.
세세하게 살펴보면,
이고,
Opening 연산이 작은 노이즈(검은 점)를 제거하는 효과가 있기 때문에,
결국 자연스레 밝은 부분이 강조되게 됩니다.
즉,
더 밝아진 상태에서 원본을 빼게 되면, 어두웠던 부분에 있어서 차이값이 남게 되고,
어두운 부분(ex. 글자, 그림자)이 강조되어 나타나게 됩니다.

왼쪽 그림은 Top hat 적용, 오른쪽은 Bottom hat 적용 결과 입니다.
적용 코드는
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
top_hat = cv2.morphologyEx(normalized, cv2.MORPH_TOPHAT, kernel)
bottom_hat = cv2.morphologyEx(normalized, cv2.MORPH_BLACKHAT, kernel)
이고,
필터는 텍스트 크기를 고려해서 10 by 10으로 지정했습니다.
이제 각각의 적용 결과는 봤고,
지금까지 했던 것을 잘 섞어 보도록 하겠습니다.
shadow_removed = cv2.add(normalized, top_hat)
위에서 정규화 거쳤던 거랑 Top hat 결과 더한 것 입니다.
정규화 과정을 거치면서, 밝은 영역은 더 밝게, 어두운 영역은 더 어둡게 만들어줬기 때문에,
여기서 Top hat 결과물을 더하게 되면 밝은 부분(텍스트)이 더더욱 강조되게 됩니다.
shadow_removed_final = cv2.subtract(shadow_removed, bottom_hat)
위 결과물에서 bottom hat 결과물 빼준 것인데,
강조된 어두운 부분(Bottom hat) 이 빠지게 되면서,
최정적으로 결국 그림자와 같은 어두운 부분이 사라지고
텍스트만 선명하게 남게 됩니다.

왼쪽 이미지는 정규화 이미지, 오른쪽 이미지는 최종 결과물 입니다.
단순 정규화만 거쳤을 때 보다, 확실히 남겨야 하는 글씨 부분이 잘 살아있고, 그림자도 획기적으로 사라진 것을 확인할 수 있습니다.
쭉 이미지 전처리를 해오면서,
아마 이 그림자가 가장 큰 걸림돌이지 않았나 싶습니다.
계속 하면서 느끼는건데,
OCR...
아.. 정말 쉬운게 아닌거 같습니다.
그냥 처음부터 추가학습으로 밀어붙여 볼껄 그랬나... 하는 생각도 듭니다 ㅎㅎ
지금 원래 계획했던 것에서 원하는 결과물이 안 나와 조금씩 미뤄지기는 하는데,
쯕 더 해야죠 뭐 어쩌겠습니까 ㅎㅎ
화이트 박스 모델, 블랙 박스 모델.
단순 추가학습으로 밀어붙였다면,
결과가 잘 나와도 이유는 쭉 모르는거고,
그래도 이건 어느정도 까지는 해석 가능한 것으로 남겨둘 수 있으니까
엉덩이 무겁게
다음 포스팅에서 뵙겠습니다.
감사합니당 ~ 🦾