31. ffmpeg로 영상 → GIF 만들기 (팔레트 최적화)¶
블로그 본문에 짧은 영상 한 컷 넣고 싶다. mp4 그대로는 티스토리·SNS에서 안 돌아감. GIF로 바꿔야 하는데 그냥 만들면 색이 깨지거나 용량이 폭발. 2단계 팔레트 방식으로 가면 화질 살리면서 50% 압축.
ffmpeg가 뭐야?¶
영상·소리 파일을 자르고, 합치고, 변환하는 만능 칼. 무료, 오픈소스, 1996년부터 있는 도구. 유튜브·넷플릭스도 내부적으로 ffmpeg를 씀.
설치: winget install Gyan.FFmpeg (Windows) 또는 brew install ffmpeg (Mac)
비유로 이해하기¶
영상 → GIF 변환 = "동영상 한 토막을 움짤로 만드는 것". 한 단계로 만들면 ffmpeg가 256색 중에 아무거나 골라서 GIF 색을 정함 → 색이 튀고 무거움. 2단계 팔레트 방식은 미리 영상에서 "자주 쓰이는 색 256가지를 뽑아둔 다음" GIF를 그림. 옷 사진 찍기 전에 자주 입는 색 12가지를 미리 골라두는 것과 같음. 색감 살아나고 용량도 작아짐.
실전 패턴 - 2단계 팔레트 방식 ★¶
단계 1: 영상에서 팔레트(자주 쓰는 색 모음) 추출¶
옵션 풀이:
- fps=10: 초당 프레임 10장 (낮을수록 가벼움, 보통 8~12)
- scale=480:-1: 가로 480픽셀, 세로는 비율 유지 (-1)
- flags=lanczos: 리사이즈 알고리즘 (선명한 결과)
- palettegen=stats_mode=diff: 변화하는 부분 위주로 색 뽑기 (정적 배경 무시 → 더 정확)
단계 2: 그 팔레트로 GIF 그리기¶
ffmpeg -i input.mp4 -i palette.png \
-lavfi "fps=10,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
output.gif
옵션 풀이:
- paletteuse=dither=bayer:bayer_scale=5: bayer 디더링 (색 사이 빈자리를 점 패턴으로 메움 → 자연스러운 그라데이션)
- bayer_scale=5가 가장 미세한 패턴 (값 작을수록 거친 패턴)
Python으로 자동화¶
import subprocess, tempfile
def mp4_to_gif(src, dst, fps=10, scale=480):
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
palette = f.name
vf = f"fps={fps},scale={scale}:-1:flags=lanczos"
# 단계 1: 팔레트
subprocess.run(["ffmpeg", "-y", "-i", src,
"-vf", f"{vf},palettegen=stats_mode=diff",
palette, "-loglevel", "error"], check=True)
# 단계 2: GIF
subprocess.run(["ffmpeg", "-y", "-i", src, "-i", palette,
"-lavfi", f"{vf}[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5",
dst, "-loglevel", "error"], check=True)
mp4_to_gif("input.mp4", "output.gif")
용량 가이드 (실제 측정값)¶
19초짜리 풍경 영상 기준:
| 옵션 | 용량 | 화질 |
|---|---|---|
| 단순 변환 (한 단계) | 21MB | 색 깨짐, 무거움 |
| 480px / 8fps + 팔레트 | 8MB | 보통 |
| 480px / 10fps + 팔레트 | 10MB | 균형 (추천) |
| 540px / 10fps + 팔레트 | 12.5MB | 선명, 약간 무거움 |
| 600px / 10fps + 팔레트 | 21MB | 너무 무거움 |
| 640px / 12fps + 팔레트 | 9MB (7초 영상) | 짧은 영상에 적합 |
→ 블로그 업로드 한도 (티스토리 ~10MB) 안에 넣으려면 480~540px / 8~10fps 적당.
옵션 가이드¶
길이 자르기¶
영상이 너무 길면 GIF 용량 폭발. -t 옵션으로 앞부분만:
→ "처음 14초만 처리".-ss 5 -t 14는 "5초부터 14초간".
해상도 vs fps 트레이드오프¶
- 해상도 ↑ → 용량 빠르게 증가 (제곱으로)
- fps ↑ → 부드럽지만 용량 비례 증가
- 풍경/정적인 영상: 해상도 ↑, fps ↓ (8 정도)
- 빠르게 움직이는 영상: 해상도 ↓, fps ↑ (12~15)
디더링 옵션¶
dither=bayer:bayer_scale=5: 작은 패턴, 부드러움 (추천)dither=floyd_steinberg: 가장 자연스럽지만 용량 ↑dither=none: 패턴 없음, 용량 작지만 그라데이션 깨짐
활용 사례¶
블로그에 짧은 영상 GIF로 첨부¶
- 사용자가 황금산 후기에 "몽돌 차르락 소리" 영상 → GIF로 (8.8MB)
- 사진보다 임팩트 있는 풍경 (파도, 바람, 눈, 일몰)에 효과적
강의 자료 / SNS 미리보기¶
- PPT에 .gif 직접 삽입 가능 (자동 재생)
- 인스타·트위터에서 동영상 처리 까다로울 때 GIF가 호환성 좋음
사용 매뉴얼 / 튜토리얼¶
- 화면 녹화(.mp4) → GIF로 압축 → 매뉴얼에 단계별 첨부
- 클릭/입력 흐름이 GIF 한 컷에 다 들어가니 글 100줄보다 직관적
주의사항 / 함정¶
- 소리는 사라짐: GIF는 무음. 소리 있는 영상은 동영상으로 두는 게 나음 (또는 mp4로 별도 첨부)
- 해상도 너무 키우면 용량 폭발: 600px가 한계점. 그 이상은 화질 차이 미미한데 용량만 두 배.
- 15초 이상은 자르기 권장: 길어도 20초까지. 30초 GIF는 30MB 넘기 일쑤.
- 흰색 배경 영상은 색 손실 적어 GIF로 좋고, 노을/바다 같은 그라데이션 영상은 디더링 필수.
- palette.png는 임시 파일. 작업 후 삭제. (Python에서 tempfile로 자동 처리)
- 윈도우 한글 경로: ffmpeg는 대부분 됨. 안 되면 Python subprocess로 절대 경로 넘기기.
정리¶
GIF 만들 때는 2단계(팔레트 → 적용) + 480~540px / 10fps가 황금 비율. 한 줄 명령으로 화질 잃거나 용량 폭발하지 말고, 미리 색 골라두는 한 단계만 더 추가하면 결과가 완전히 달라짐.