콘텐츠로 이동

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: 영상에서 팔레트(자주 쓰는 색 모음) 추출

ffmpeg -i input.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff" palette.png

옵션 풀이: - 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 옵션으로 앞부분만:

ffmpeg -y -t 14 -i input.mp4 ...
→ "처음 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가 황금 비율. 한 줄 명령으로 화질 잃거나 용량 폭발하지 말고, 미리 색 골라두는 한 단계만 더 추가하면 결과가 완전히 달라짐.