Python Pillow 이미지 압축(60%) 및 선명도 개선

텍스트 이미지의 선명도를 좋게 하고 이미지의 용량도 줄여주는 Python Pillow 이미지 압축 및 선명도 개선 방법을 함께 코드를 통해 살펴보도록 하겠습니다.

스캔한 이미지 확인

아래 그림 1은 인쇄물을 스캔한 이미지입니다. 작은 이미지로 보면 보는 데에는 별 문제가 없어보입니다.

그림 1. 인쇄물 스캔 원본
그림 1. 인쇄물 스캔 원본

이번에는 크게 확대한 이미지를 보도록 하겠습니다. 글자 주변으로 흐린 회색의 얼룩들이 확인됩니다. 이미지 압축 과정에서 발생한 부분으로 보입니다.

그림 2. 인쇄물 스캔 확대본
그림 2. 인쇄물 스캔 확대본

그림 2와 같은 이미지는 픽셀별로 색상 값이 많이 다르기 때문에 이미지의 용량도 커지게 됩니다. 애매한 회색들을 싹 다 정리해서 흰색으로 만들어 주면 어떻게 될까요?

Python Pillow 이미지 압축 및 개선 전략

이미지 상태를 보면 글자 부분은 전반적으로 어두운 색입니다. RGB 값으로 #000000에 가까운 값을 갖게 됩니다. 그리고 글자 외의 부분은 전반적으로 밝은 색입니다. RGB 값으로 #FFFFFF에 가까운 값을 갖게 됩니다.

픽셀의 값이 일정 수준 이상의 값을 가지면 #FFFFFF를 이용하면 배경이 깨끗하게 정리될 것입니다. 뿐만 아니라 일정 수준 이하의 값을 가지면 #000000으로 변경해 주면 글자는 보다 선명해질 것입니다.

동일한 값이 많아지니 자연스레 용량도 줄어들고, 선명도는 올라가는 효과를 보게 됩니다. 여기에서는 그림 2의 확대된 이미지를 기준으로 작업을 해 보도록 하겠습니다.

그림 3. 작업용 이미지 원본 크기
그림 3. 이미지 원본 크기

우선 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 선명도 개선(압축)된 이미지 파일 용량
그림 4. Python Pillow 이미지 압축 결과(용량)

그럼 용량을 줄이는데 효과가 있는 것은 확인이 됐으니 이미지를 살펴보겠습니다. 글자와 배경 사이의 경계에 있는 값들이 일부 날아가면서 정리된 것을 볼 수 있습니다.

그림 5. Python Pillow 이미지 압축 및 개선 결과

여기에서 검은색 글자를 조금만 더 진하게 표현해 보도록 하겠습니다. 위의 코드와 달라진 부분은 아래의 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 이미지 압축 최종 결과(용량)
그림 6. Python Pillow 이미지 압축 최종 결과(용량)

그러면 최종 이미지를 확인해 보도록 하겠습니다. 이미지 용량이 위와 같이 줄어들었음에도 이미지의 손실은 없는지 함께 살펴보겠습니다. 위의 그림 5에 비해 글자 부분이 좀 더 어두워진 것을 확인할 수 있습니다.

그림 7. Python Pillow 이미지 압축 및 개선 최종 결과
그림 7. Python Pillow 이미지 압축 및 개선 최종 결과

저는 임의의 값 200과 100을 기준으로 대충 밝은 색의 값과 어두운 색의 값을 나눴는데, 해당 값들을 좀 더 미세하게 조정한다면 원하는 이미지를 얻을 수 있으리라 생각합니다.

단순히 이미지의 픽셀 값을 바꿔 주었을 뿐인데 이미지 압축의 효과까지 보게 되었습니다. 그렇습니다. 일종의 이미지 압축 알고리즘이라고도 할 수 있겠네요. 하지만 매우 간단하고 효과는 큽니다. 물론 이와 같은 방식은 텍스트 이미지와 같은 경우에 가장 효과적일 수 있겠네요.

관련 자료

Pillow의 공식문서 PixelAccess Class 페이지를 참고했습니다.

같이 읽으면 좋은 글

Leave a Comment