FFmpeg로 영상을 GIF로 변환하는 원리 — 팔레트 생성과 디더링 완벽 가이드
영상을 GIF로 변환할 때 원본의 생생한 색감이 뭉개지거나 이상하게 변색되는 경험을 하셨을 겁니다. 이는 FFmpeg가 수행하는 팔레트 생성과 디더링 과정에서 비롯됩니다. GIF 형식은 최대 256색만 표현할 수 있는 인덱스 컬러 방식을 사용하기 때문에, 카메라로 촬영한 영상의 수백만 개 색상을 어떻게 256색으로 압축하느냐가 화질을 결정합니다. 이 글에서는 FFmpeg가 내부적으로 어떻게 팔레트를 생성하고 디더링을 적용하는지, 그리고 이 과정을 직접 제어해 최고 품질의 GIF를 만드는 방법을 설명합니다.
GIF 형식의 색상 제한과 팔레트의 필요성
GIF는 1987년 CompuServe에서 개발한 형식으로, 당시 제한된 메모리와 통신 속도를 고려해 최대 256색(8비트)만 저장하도록 설계되었습니다. 반면 일반적인 디지털 카메라나 스마트폰 영상은 RGB 24비트 색공간을 사용해 약 1,677만 개의 색상을 표현합니다. FFmpeg가 MP4, MOV, AVI 같은 트루컬러(True Color) 영상을 GIF로 변환할 때 가장 먼저 해야 할 일은 이 거대한 색상 집합에서 가장 중요한 256개만 골라내는 것입니다. 이 과정을 팔레트 생성이라고 부릅니다.
팔레트는 단순히 256개의 임의 색상이 아니라, 원본 영상의 색상 분포를 최대한 정확히 반영하도록 수학적으로 계산됩니다. 예를 들어 푸른 하늘을 배경으로 한 영상이라면 팔레트의 많은 슬롯을 파란색 계열로 할당해야 하고, 검은색 옷을 입은 인물이 있다면 짙은 검은색과 어두운 회색도 포함되어야 합니다. FFmpeg는 기본적으로 k-means 클러스터링이나 중앙값 절단(median cut) 알고리즘을 사용해 원본 영상에서 추출한 모든 픽셀의 색상 정보를 분석하고 가장 대표성 있는 256개 색상을 선정합니다.
FFmpeg의 팔레트 생성 알고리즘 — 중앙값 절단과 클러스터링
FFmpeg에서 기본적으로 사용하는 팔레트 생성 알고리즘은 중앙값 절단(median cut)입니다. 이 알고리즘은 다음과 같이 작동합니다. 먼저 원본 영상의 모든 픽셀을 RGB 색공간 상의 점으로 생각합니다. 그런 다음 이 점들을 포함하는 가장 작은 상자(정육면체)를 그립니다. 이 상자를 가장 긴 축(보통 빨강, 초록, 파랑 중 하나)을 따라 두 개의 동일한 부분으로 나눕니다. 그 과정을 반복해 상자를 계속 분할하면, 결국 256개의 작은 상자가 생기고 각 상자에 포함된 모든 픽셀의 평균 색상이 그 상자를 대표하는 팔레트 색상이 됩니다.
중앙값 절단 알고리즘의 장점은 계산이 빠르고 대부분의 경우 균형 잡힌 팔레트를 생성한다는 점입니다. 하지만 영상의 특성에 따라 최적은 아닐 수 있습니다. 예를 들어 회의실 영상처럼 회색 톤이 매우 많은 경우, 중앙값 절단이 생성한 팔레트는 회색에 너무 많은 슬롯을 할당할 수 있고, 실제로는 덜 중요한 색상들이 부족하게 됩니다. 이런 이유로 FFmpeg는 버전 4.1 이후 더 정교한 팔레트 생성 옵션을 제공합니다. palettegen 필터를 사용하면 중앙값 절단 외에 다른 색상 공간(예: Oklab)에서의 클러스터링도 수행할 수 있으며, 프레임별 가중치를 조정해 장면 전환이 있는 영상에서 색상 일관성을 유지할 수 있습니다.
디더링 — 256색 팔레트로 표현되지 않는 색상을 재현하는 방법
팔레트가 생성되고 나면 원본 영상의 모든 픽셀을 가장 가까운 팔레트 색상으로 변환해야 합니다. 예를 들어 원본의 빨간색 픽셀이 팔레트의 어떤 색상과도 정확히 일치하지 않는다면, FFmpeg는 가장 유사한 빨간색을 선택합니다. 하지만 이렇게 단순히 가장 가까운 색상으로 치환하면 급격한 색상 변화가 생기고 이미지가 뭉개진 것처럼 보입니다. 이를 개선하는 기법이 디더링(dithering)입니다.
디더링은 인접한 픽셀들 사이에 의도적으로 미세한 색상 차이를 삽입해, 인간의 눈이 평균 색상을 인식하도록 속이는 기법입니다. 예를 들어 원본에 밝은 회색이 있는데 팔레트에 흰색과 검은색만 있다면, 디더링 없이는 회색을 흰색 또는 검은색 중 하나로 일괄 변환해야 합니다. 하지만 디더링을 적용하면 흰색과 검은색을 적절한 비율로 체커보드 패턴으로 배치해 원거리에서 봤을 때 회색으로 보이게 합니다. 이 기법은 1960년대 신문사에서 흑백 인쇄로 사진을 표현할 때부터 사용된 역사 깊은 방법입니다.
FFmpeg에서 주로 사용되는 디더링 알고리즘은 Floyd-Steinberg 디더링입니다. 이 알고리즘은 각 픽셀을 팔레트 색상으로 변환할 때 발생하는 오차(원본 색상과 선택된 팔레트 색상의 차이)를 계산하고, 이 오차의 일부를 인접한 픽셀들에 분산시킵니다. 예를 들어 현재 픽셀의 오차가 8이라면, 오른쪽 픽셀에 7/16, 왼쪽 아래에 3/16, 아래에 5/16, 오른쪽 아래에 1/16을 분배합니다. 이렇게 오차를 전파하면 전체적으로 색상 재현이 훨씬 자연스러워집니다. Floyd-Steinberg 외에도 Bayer 매트릭스 디더링, Sierra 디더링, Jarvis-Judice-Ninke 디더링 등 여러 변형이 있으며, 각각은 속도와 화질의 트레이드오프가 다릅니다.
FFmpeg 명령어로 팔레트 생성과 GIF 변환 실행하기
이론을 알았으니 이제 FFmpeg를 직접 사용해 봅시다. FFmpeg로 GIF를 만드는 권장 방법은 두 단계를 거칩니다. 첫 번째 단계에서 palettegen 필터로 최적화된 팔레트를 생성하고, 두 번째 단계에서 paletteuse 필터로 그 팔레트를 적용해 GIF를 만듭니다.
1단계: 팔레트 생성
기본 명령어는 다음과 같습니다:
ffmpeg -i input.mp4 -vf "fps=10,scale=640:-1:flags=lanczos,palettegen" palette.png
이 명령어의 각 옵션 의미는 다음과 같습니다. fps=10은 초당 10프레임으로 설정해 파일 크기를 줄입니다(기본값은 모든 프레임). scale=640:-1:flags=lanczos는 영상을 가로 640픽셀로 리사이징하고 세로는 종횡비를 유지하도록 하며, Lanczos 고품질 필터를 사용합니다. palettegen은 최종적으로 조정된 색상 팔레트를 생성합니다. 기본값으로 256색을 생성하지만 다음처럼 색 개수를 조정할 수 있습니다:
ffmpeg -i input.mp4 -vf "fps=10,scale=640:-1,palettegen=nbcolors=128" palette.png
nbcolors=128로 설정하면 팔레트를 128색으로 제한합니다. 이 값을 줄이면 파일 크기가 더 줄어들지만 색감이 떨어집니다. 2단계: 팔레트를 적용해 GIF 생성
ffmpeg -i input.mp4 -i palette.png -filter_complex "[0:v]fps=10,scale=640:-1:flags=lanczos[v];[v][1:v]paletteuse=dither=floyd_steinberg" output.gif
이 명령어에서 paletteuse 필터가 핵심입니다. dither=floyd_steinberg는 Floyd-Steinberg 디더링을 적용합니다. 다른 디더링 옵션도 있습니다:
- dither=none: 디더링 미적용, 파일 크기 가장 작음, 색감 최악
- dither=bayer: Bayer 매트릭스 디더링, 빠름, 중간 수준의 색감
- dither=sierra2_4a: Sierra 디더링 변형, Floyd-Steinberg보다 느리지만 더 나은 화질
- dither=floyd_steinberg: 가장 인기 있는 선택, 좋은 화질과 합리적인 속도의 균형
디더링을 적용하면 파일 크기가 증가할 수 있지만, 화질 개선 효과가 매우 큽니다. 일반적으로 웹에서 사용할 GIF는 Floyd-Steinberg 디더링이 권장됩니다.
팔레트 생성 최적화 — 특정 장면에 맞춘 색상 할당
기본 palettegen은 전체 영상을 분석해 팔레트를 생성하지만, 영상에 색상 변화가 큰 장면이 여러 개 있으면 어느 장면도 완벽하게 표현되지 않을 수 있습니다. 예를 들어 처음 5초는 푸른 하늘, 다음 5초는 빨간 건물, 마지막 5초는 초록색 숲이라면, 단일 팔레트로는 세 장면을 모두 만족시킬 수 없습니다.
이런 경우 stats 필터를 사용해 프레임별 색상 통계를 수집하고, palettegen에 이를 반영할 수 있습니다:
ffmpeg -i input.mp4 -vf "fps=10,scale=640:-1,split[a][b];[a]palettegen=stats_mode=diff[p];[b]fifo[c];[p][c]paletteuse=dither=floyd_steinberg" output.gif
또한 특정 시간 구간만 대상으로 팔레트를 생성할 수도 있습니다. 예를 들어 영상의 첫 10초만으로 팔레트를 생성하려면:
ffmpeg -i input.mp4 -t 10 -vf "fps=10,scale=640:-1,palettegen" palette.png
-t 10은 최초 10초만 처리합니다. 이렇게 중요한 장면만 선택해 팔레트를 생성하면, 그 장면의 색감이 매우 살아나게 됩니다.
파일 크기와 화질의 실전 트레이드오프
지금까지 팔레트와 디더링의 이론을 설명했으니, 실제로 파일 크기에 어떤 영향을 미치는지 정리해 봅시다. 같은 영상에서 10초, 640×480 해상도로 GIF를 생성했을 때의 예상 결과입니다:
| 옵션 조합 | 예상 파일 크기 | 색감 품질 | 용도 |
|---|---|---|---|
| fps=5, scale=320:-1, dither=none | ~1.2MB | 낮음 (색 띠 현상) | 매우 제한된 대역폭, 극단적 최적화 |
| fps=5, scale=640:-1, dither=bayer | ~2.5MB | 중간 | 모바일 앱 내 썸네일 |
| fps=10, scale=640:-1, dither=floyd_steinberg | ~4.2MB | 높음 (권장) | 소셜 미디어, 일반 웹사이트 |
| fps=15, scale=1280:-1, dither=sierra2_4a | ~8.5MB | 매우 높음 | 고품질 아카이브, HD 디스플레이 |
일반적으로 소셜 미디어(Twitter, Discord, Slack)에 올릴 GIF는 fps=10, scale=640:-1, Floyd-Steinberg 조합이 가장 적절합니다. 이 설정은 2~4MB 범위의 합리적 크기를 유지하면서도 충분한 화질을 제공합니다. Instagram이나 Facebook에 올릴 때는 더 큰 해상도가 필요하면 scale=1080:-1로 늘릴 수 있지만, fps=10을 유지해 총 크기를 제어해야 합니다.
편집 도구로 추가 최적화하고 바로 쓰기
FFmpeg로 완벽한 GIF를 생성해도, 시간이 오래 걸리거나 명령어가 복잡할 수 있습니다. editpixel의 MP4·영상 편집 도구를 사용하면 FFmpeg의 복잡한 설정 없이 웹 브라우저에서 바로 영상을 GIF로 변환할 수 있습니다. 클릭 몇 번으로 팔레트 최적화와 디더링을 자동으로 적용해 주며, 변환된 GIF는 즉시 다운로드해 사용할 수 있습니다. 특히 GIF 크기 조정, 프레임 속도 설정 같은 추가 편집이 필요할 때는 FFmpeg 명령어를 몇 번이나 반복 실행하는 것보다 훨씬 빠르고 직관적입니다.
FFmpeg로 팔레트와 디더링을 깊이 있게 이해했다면, editpixel 같은 도구가 내부적으로 어떻게 작동하는지도 쉽게 파악할 수 있습니다. 화질이 필요한 경우에는 FFmpeg로, 빠르고 편한 변환이 필요한 경우에는 웹 도구로 적절히 선택해 사용하시기 바랍니다.