텍스트 이미지의 선명도를 좋게 하고 이미지의 용량도 줄여주는 Python Pillow 이미지 압축 및 선명도 개선 방법을 함께 코드를 통해 살펴보도록 하겠습니다.
목차
스캔한 이미지 확인
아래 그림 1은 인쇄물을 스캔한 이미지입니다. 작은 이미지로 보면 보는 데에는 별 문제가 없어보입니다.
![그림 1. 인쇄물 스캔 원본](https://osg.kr/wp-content/uploads/2024/05/image-71.png)
이번에는 크게 확대한 이미지를 보도록 하겠습니다. 글자 주변으로 흐린 회색의 얼룩들이 확인됩니다. 이미지 압축 과정에서 발생한 부분으로 보입니다.
![그림 2. 인쇄물 스캔 확대본](https://osg.kr/wp-content/uploads/2024/05/image-69.png)
그림 2와 같은 이미지는 픽셀별로 색상 값이 많이 다르기 때문에 이미지의 용량도 커지게 됩니다. 애매한 회색들을 싹 다 정리해서 흰색으로 만들어 주면 어떻게 될까요?
Python Pillow 이미지 압축 및 개선 전략
이미지 상태를 보면 글자 부분은 전반적으로 어두운 색입니다. RGB 값으로 #000000에 가까운 값을 갖게 됩니다. 그리고 글자 외의 부분은 전반적으로 밝은 색입니다. RGB 값으로 #FFFFFF에 가까운 값을 갖게 됩니다.
픽셀의 값이 일정 수준 이상의 값을 가지면 #FFFFFF를 이용하면 배경이 깨끗하게 정리될 것입니다. 뿐만 아니라 일정 수준 이하의 값을 가지면 #000000으로 변경해 주면 글자는 보다 선명해질 것입니다.
동일한 값이 많아지니 자연스레 용량도 줄어들고, 선명도는 올라가는 효과를 보게 됩니다. 여기에서는 그림 2의 확대된 이미지를 기준으로 작업을 해 보도록 하겠습니다.
![그림 3. 작업용 이미지 원본 크기](https://osg.kr/wp-content/uploads/2024/05/image-72.png)
우선 ls 명령어를 사용해 확인한 그림 2의 원본 이미지 크기는 45,288 바이트입니다. 약 45KB 정도 됩니다. 이미지 품질과 용량에서 얼마나 변화가 생길까요?
Python Pillow 이미지 선명도 높이기
그럼 이제 파이썬 코드로 이 작업을 어떻게 하는지 살펴보겠습니다. 사실 별다를 게 없습니다. 좌표 x, y를 차례대로 for loop으로 확인해서 모든 픽셀의 값을 한 번씩 확인하고 각 R, G, B 값이 모두 200을 넘는 경우 흰색(#FFFFFF)가 되도록 해 주는 작업입니다.
from PIL import Image
img = Image.open("test_clear1.png")
dat = img.load()
width = img.size[0]
height = img.size[1]
for x in range(width):
for y in range(height):
px = dat[x, y]
if px[0] > 200 and px[1] > 200 and px[2] > 200:
dat[x, y] = (255, 255, 255, 255)
img.save("output.png")
img.close()
Python우선 위의 코드를 실행한 결과 이미지 파일의 용량이 얼마나 줄어들었는지 보실까요? 약 24KB 정도로 줄었습니다. 약 47% 정도의 용량이 줄어들었습니다. 사람들은 이런 걸 이미지 압축이라고 표현하기도 합니다.
![그림 4. Python Pillow 선명도 개선(압축)된 이미지 파일 용량](https://osg.kr/wp-content/uploads/2024/05/image-73.png)
그럼 용량을 줄이는데 효과가 있는 것은 확인이 됐으니 이미지를 살펴보겠습니다. 글자와 배경 사이의 경계에 있는 값들이 일부 날아가면서 정리된 것을 볼 수 있습니다.
![그림 5. Python Pillow 이미지 압축 및 개선 결과](https://osg.kr/wp-content/uploads/2024/05/output.png)
여기에서 검은색 글자를 조금만 더 진하게 표현해 보도록 하겠습니다. 위의 코드와 달라진 부분은 아래의 13, 14 라인입니다.
from PIL import Image
img = Image.open("test_clear1.png")
dat = img.load()
width = img.size[0]
height = img.size[1]
for x in range(width):
for y in range(height):
px = dat[x, y]
if px[0] > 200 and px[1] > 200 and px[2] > 200:
dat[x, y] = (255, 255, 255, 255)
elif px[0] < 100 and px[1] < 100 and px[2] < 100:
dat[x, y] = (0, 0, 0, 255)
img.save("output.png")
img.close()
Python과연 이미지 용량은 얼마나 더 줄었을까요? 약 17KB 정도까지 줄어들었습니다. 약 63% 정도의 용량을 줄인 압축 효과가 나타났습니다.
![그림 6. Python Pillow 이미지 압축 최종 결과(용량)](https://osg.kr/wp-content/uploads/2024/05/image-74.png)
그러면 최종 이미지를 확인해 보도록 하겠습니다. 이미지 용량이 위와 같이 줄어들었음에도 이미지의 손실은 없는지 함께 살펴보겠습니다. 위의 그림 5에 비해 글자 부분이 좀 더 어두워진 것을 확인할 수 있습니다.
![그림 7. Python Pillow 이미지 압축 및 개선 최종 결과](https://osg.kr/wp-content/uploads/2024/05/output-1.png)
저는 임의의 값 200과 100을 기준으로 대충 밝은 색의 값과 어두운 색의 값을 나눴는데, 해당 값들을 좀 더 미세하게 조정한다면 원하는 이미지를 얻을 수 있으리라 생각합니다.
단순히 이미지의 픽셀 값을 바꿔 주었을 뿐인데 이미지 압축의 효과까지 보게 되었습니다. 그렇습니다. 일종의 이미지 압축 알고리즘이라고도 할 수 있겠네요. 하지만 매우 간단하고 효과는 큽니다. 물론 이와 같은 방식은 텍스트 이미지와 같은 경우에 가장 효과적일 수 있겠네요.
관련 자료
Pillow의 공식문서 PixelAccess Class 페이지를 참고했습니다.