
파이썬 이미지 워터마크 작업은 생각보다 빨리 끝납니다. Pillow만 있으면 이미지에 글자를 얹고, 로고를 살짝 투명하게 올리고, PNG와 JPEG 저장 차이까지 한 번에 맛볼 수 있습니다.
이번 글은 거창한 편집 툴 대신, 작고 재밌지만 바로 써먹을 수 있는 미니 프로젝트 느낌으로 갑니다.
준비
먼저 Pillow를 설치합니다.
pip install pillow그리고 연습용 파일을 두 개 준비하면 좋습니다. input.jpg는 원본 이미지, logo.png는 투명 배경이 있는 로고입니다. 로고가 아직 없다면 텍스트 워터마크만 먼저 붙여도 충분합니다.
파이썬 이미지 워터마크
- 원본 이미지를 연다
- 복사본을 만든다
- 텍스트나 로고를 올린다
- 저장 포맷에 맞게 저장한다
초보자에게 가장 중요한 습관은 원본을 바로 덮어쓰지 않는 것입니다. 실수해도 다시 돌아갈 수 있게 copy()한 뒤 작업하는 편이 안전합니다.
텍스트 워터마크
제일 먼저 해볼 것은 텍스트 워터마크입니다. 사진 구석에 블로그 이름이나 아이디를 살짝 올리는 방식입니다.
from PIL import Image, ImageDraw, ImageFont
base = Image.open("input.jpg").convert("RGBA")
image = base.copy()
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("/Library/Fonts/Arial Unicode.ttf", 36)
text = "bscodelab.com"
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
margin = 24
x = image.width - text_width - margin
y = image.height - text_height - margin
draw.text(
(x, y),
text,
font=font,
fill=(255, 255, 255, 140)
)
image.save("watermarked_text.png")- RGBA로 바꿔서 반투명 색을 쓰기 쉽게 만든다
- textbbox()로 글자 크기를 재서 오른쪽 아래 위치를 계산한다
- fill=(255, 255, 255, 140)처럼 마지막 값으로 투명도를 준다
처음에는 글자를 무작정 (20, 20)에 놓기 쉽습니다. 하지만 이미지 크기가 바뀌면 바로 어색해집니다. 그래서 오른쪽 아래 기준 + margin으로 배치하는 습관이 꽤 유용합니다.
로고 워터마크
이번에는 로고를 얹어보겠습니다. 텍스트보다 조금 더 이미지 편집 같은 느낌이 납니다.
from PIL import Image
base = Image.open("input.jpg").convert("RGBA")
image = base.copy()
logo = Image.open("logo.png").convert("RGBA")
max_logo_width = image.width // 5
ratio = max_logo_width / logo.width
new_size = (int(logo.width * ratio), int(logo.height * ratio))
logo = logo.resize(new_size)
logo.putalpha(90)
margin = 24
x = image.width - logo.width - margin
y = image.height - logo.height - margin
image.paste(logo, (x, y), logo)
image.save("watermarked_logo.png")초보자가 자주 놓치는 부분은 paste()의 세 번째 인자입니다. image.paste(logo, (x, y), logo)처럼 마지막에 logo를 한 번 더 넣어야, 로고의 투명 영역을 마스크처럼 같이 써서 자연스럽게 합성됩니다. 이 한 줄이 빠지면 로고 뒤 배경이 네모 상자처럼 보일 수 있습니다.
둘 다 넣기
실전에서는 텍스트와 로고를 같이 쓰는 경우도 많습니다. 예를 들면 왼쪽 위에는 작은 로고, 오른쪽 아래에는 사이트 주소를 둘 수 있습니다.
from PIL import Image, ImageDraw, ImageFont
base = Image.open("input.jpg").convert("RGBA")
image = base.copy()
logo = Image.open("logo.png").convert("RGBA")
overlay = Image.new("RGBA", image.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(overlay)
font = ImageFont.truetype("/Library/Fonts/Arial Unicode.ttf", 28)
logo = logo.resize((120, 120))
logo.putalpha(100)
overlay.paste(logo, (24, 24), logo)
draw.text(
(image.width - 220, image.height - 50),
"bscodelab.com",
font=font,
fill=(255, 255, 255, 150)
)
result = Image.alpha_composite(image, overlay)
result.save("watermarked_all.png")이 방식의 장점은 깔끔하다는 점입니다. 투명한 overlay를 하나 만들고 거기에 요소를 올린 뒤 마지막에 합성하면, 워터마크를 여러 개 넣을 때도 수정이 편합니다.
위치 잡기
- 오른쪽 아래: 가장 무난함
- 왼쪽 위: 로고 넣기 좋음
- 가운데: 샘플 이미지 보호용으로는 강하지만 보기에는 거슬릴 수 있음
사진 전체를 망치고 싶지 않다면, 가장자리에서 16~32px 정도 띄우고, 핵심 피사체 위는 피하고, 너무 큰 로고보다 작고 옅은 워터마크를 먼저 시도해보는 편이 좋습니다. 워터마크는 잘 보이는 것보다 덜 거슬리는 것이 더 중요할 때가 많습니다.
투명도
워터마크가 실패하는 가장 흔한 이유는 보통 둘 중 하나입니다. 너무 진해서 사진을 망치거나, 너무 연해서 아무도 못 보는 경우입니다.
- 텍스트: 알파 100~160
- 로고: 알파 70~120
숫자가 낮을수록 더 투명해집니다. 완벽한 정답을 한 번에 정하려 하지 말고, 세 버전 정도 저장해 비교하면 감이 빨리 옵니다.
def apply_logo_alpha(logo, alpha):
logo = logo.copy()
logo.putalpha(alpha)
return logo저장 포맷
PNG와 JPEG는 결과가 꽤 다릅니다. PNG는 투명도 표현에 유리하고, JPEG는 파일이 가벼운 편이지만 투명도를 저장하지 못합니다. 그래서 작업 중간 파일이나 로고 자산은 PNG가 편하고, 최종 결과를 사진용으로 가볍게 저장하고 싶다면 JPEG를 많이 씁니다.
다만 RGBA 이미지를 JPEG로 바로 저장하면 원하는 결과가 안 나올 수 있습니다. 이럴 때는 RGB로 바꿔 저장하면 됩니다.
result = image.convert("RGB")
result.save("watermarked.jpg", quality=92)- 투명도 작업 중에는 RGBA
- JPEG 저장 직전에는 필요하면 RGB
빠른 실수
글자가 잘 안 보일 때
배경이 밝은 사진인데 흰 글자를 쓰면 거의 사라집니다. 이럴 때는 검은색 반투명 글자나 그림자를 한 번 더 그리는 방식이 낫습니다.
로고 배경이 네모일 때
대부분 logo.png가 아니라 배경이 박힌 파일을 쓰거나, paste()에서 마스크 인자를 빼먹은 경우입니다.
색이 이상할 때
중간에 RGBA와 RGB를 섞는 과정에서 생기는 경우가 많습니다. 특히 JPEG 저장 직전 변환 흐름을 다시 보면 금방 잡힙니다.
바로 써먹는 버전
from PIL import Image, ImageDraw, ImageFont
def add_text_watermark(input_path, output_path, text):
image = Image.open(input_path).convert("RGBA")
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("/Library/Fonts/Arial Unicode.ttf", 32)
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
margin = 20
x = image.width - text_width - margin
y = image.height - text_height - margin
draw.text((x, y), text, font=font, fill=(255, 255, 255, 140))
image.convert("RGB").save(output_path, quality=92)
add_text_watermark("input.jpg", "output.jpg", "bscodelab.com")이 정도만 익혀도 블로그용 이미지, 샘플 이미지, 간단한 포트폴리오 썸네일에 충분히 응용할 수 있습니다.
같이 보면 좋은 글
이미지를 다루는 재미 프로젝트 흐름으로는 파이썬 QR 코드 생성기 만들기를 같이 보면 좋습니다.
작은 자동화 감각을 더 넓히고 싶다면 파이썬 랜덤 비밀번호 생성기 만들기, 복사 습관 때문에 헷갈린 적이 있다면 파이썬 얕은 복사와 깊은 복사도 이어서 보기 좋습니다.
공식 기준이 궁금하면 Pillow 튜토리얼과 ImageDraw 문서, Image 문서를 함께 보면 됩니다.
마무리
Pillow로 이미지를 열고, 텍스트나 로고를 올리고, 투명도와 저장 포맷만 이해하면 워터마크 자동화의 첫 단추는 이미 끝난 것입니다.
처음에는 오른쪽 아래 텍스트 워터마크 하나만 붙여보세요. 그다음 로고를 얹고, 투명도를 바꾸고, PNG와 JPEG 결과를 비교해보면 금방 손에 익습니다. 이런 작은 프로젝트가 의외로 재밌고, 꽤 쓸모도 있습니다.