사진을 도트 그림으로 — 브라우저만으로 만든 픽셀 아트 변환기
서버 없이 Canvas API만으로 이미지 축소, 색상 양자화(median-cut), 디더링까지 처리하는 픽셀 아트 변환기를 만들었습니다.
목차
이 블로그가 도트(픽셀) 컨셉이다 보니, 아예 사진을 도트 그림으로 바꿔주는 도구가 있으면 어울리겠다 싶어 만들었습니다. 이미지를 올리면 픽셀 크기, 팔레트, 디더링을 조절해가며 결과를 바로 비교해볼 수 있습니다.
서버가 필요 없는 이미지 처리
이미지 변환 도구라고 하면 보통 업로드 서버부터 떠올리지만, 이 도구는 이미지가 브라우저 밖으로 나가지 않습니다. 파일을 선택하면(끌어다 놓기, Ctrl+V 붙여넣기도 됩니다) 브라우저의 Canvas API가 픽셀 데이터를 직접 읽고, 모든 계산이 사용자의 기기 안에서 끝납니다. 개인적인 사진을 변환해도 어디에도 전송되지 않고, 저는 서버 비용과 보안 걱정이 없으니 서로 좋은 구조입니다.
파이프라인은 세 단계입니다.
- 축소: 원본을 목표 크기(예: 가로 64픽셀)로 줄입니다. 이때 브라우저의 고품질 스무딩을 켜두면 주변 색이 평균으로 섞여 내려와서, 한 칸 한 칸이 그 영역의 대표색이 됩니다.
- 색 줄이기: 줄어든 픽셀들을 선택한 팔레트의 색으로만 다시 칠합니다.
- 확대: 결과를 다시 키울 때는 스무딩을 꺼서(최근접 확대) 픽셀 경계가 뭉개지지 않고 또렷한 사각형으로 남게 합니다.
이미지에서 대표색 뽑기 — median-cut
게임보이 4색이나 PICO-8 16색 같은 고정 팔레트도 재미있지만, 사진마다 어울리는 색은 다릅니다. 그래서 이미지에서 대표색을 자동으로 뽑는 median-cut 알고리즘을 넣었습니다.
원리는 단순합니다. 모든 픽셀을 하나의 상자에 담고, R·G·B 중 색 범위가 가장 넓은 축을 골라 중앙값에서 반으로 쪼갭니다. 이걸 원하는 색 수만큼 반복한 뒤, 각 상자의 평균색을 팔레트로 씁니다. 하늘이 많은 사진이면 파란 계열이 여러 단계로, 노을 사진이면 주황 계열이 풍부하게 뽑히는 식으로 이미지의 색 분포를 따라갑니다.
색이 부족할 때의 마법 — 디더링
색을 16개로 줄이면 그라데이션이 계단처럼 뚝뚝 끊깁니다. 옛날 게임기들이 이 문제를 해결한 방법이 디더링입니다. 두 색을 체커보드처럼 섞어 찍으면 멀리서 볼 때 중간색처럼 보이는 착시를 이용하는 거죠.
두 방식을 넣었습니다.
- 플로이드-스타인버그(오차 확산): 픽셀 하나를 팔레트 색으로 확정할 때 생기는 오차를 아직 처리하지 않은 이웃 픽셀들에게 나눠줍니다(오른쪽 7/16, 아래 5/16...). 사진처럼 부드러운 그라데이션에 강합니다.
- 바이엘(질서 디더): 4×4 임계값 행렬을 격자로 반복 적용합니다. 규칙적인 패턴이라 옛날 게임 특유의 질감이 나고, 결과가 픽셀 좌표만으로 결정되어 항상 같습니다.
같은 사진에 두 방식을 번갈아 적용해보면 차이가 확실히 느껴집니다.
만들면서 신경 쓴 것들
- EXIF 회전: 폰으로 찍은 사진은 회전 정보가 메타데이터에만 있는 경우가 있어서,
createImageBitmap의imageOrientation: "from-image"옵션으로 자동 보정했습니다. - 성능: 색 계산은 축소된 이미지(최대 가로 256픽셀)에만 수행하므로 워커 없이도 즉각 반응합니다. 아주 큰 원본은 중간 단계에서 한 번 줄여 비용을 고정했습니다.
- 비교 슬라이더: 원본과 결과를 겹쳐두고 경계선을 드래그해서 비교합니다. 투명한
range입력을 이미지 위에 깔아, 마우스 드래그와 키보드 화살표 조작이 같은 코드로 동작합니다.
직접 써보세요 — 여러분의 프로필 사진이 게임보이 화면에 들어가면 어떤 모습일지 궁금하지 않나요?
