본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 31. 18:31

FFmpeg, PySceneDetect, 그리고 CLIP을 사용하여 자동으로 더 나은 비디오 썸네일 선택하기

요약

FFmpeg, PySceneDetect, OpenCLIP을 활용하여 비디오에서 최적의 썸네일을 자동으로 추출하는 Python 파이프라인 구축 방법을 소개합니다. 장면 전환 감지, 흐릿한 프레임 필터링, CLIP 기반 점수 산출 및 다양성 제약 조건을 통해 고품질의 썸네일을 선택하는 과정을 다룹니다.

핵심 포인트

  • FFmpeg와 PySceneDetect로 후보 프레임 추출
  • 라플라시안 분산을 이용한 흐릿한 프레임 필터링
  • OpenCLIP을 활용한 프롬프트 기반 프레임 점수화
  • 장면 다양성을 고려한 상위 K개 썸네일 선택

요약 (TL;DR)

우리는 어떤 비디오 파일이든 입력받아 FFmpeg와 PySceneDetect로 후보 프레임을 추출하고, OpenCV로 흐릿한 프레임을 필터링하며, OpenCLIP을 사용하여 작은 프롬프트 세트에 대해 각 후보의 점수를 매긴 뒤, 다양성 제약 조건을 적용하여 상위 K개의 썸네일을 선택하는 파이프라인을 구축할 것입니다. 약 200줄의 Python 코드로 구성되며, GPU 가속이 가능하고 완전히 로컬에서 동작합니다.

📦 코드: github.com/USER/auto-thumbnail-picker (발행 전 교체 필요)

인코더가 생성하는 기본 썸네일은 "중간 프레임"입니다. 대부분의 비디오에서 중간 프레임은 모션 블러 (motion blur), 장면 전환, 또는 누군가 눈을 깜빡이는 순간입니다. 약 한 시간 정도의 노력만 들인다면 훨씬 더 나은 결과를 얻을 수 있습니다. 여기 그 파이프라인이 있습니다.

버전

python 3.12
ffmpeg 7.1
pyscenedetect 0.6.x
...

파이프라인은 CPU에서 실행되지만, CLIP 단계에서는 눈에 띄게 느려집니다. 소비자용 GPU (또는 Apple Silicon MPS)를 사용하면 프레임당 인코딩 시간을 몇 밀리초(ms)로 단축할 수 있습니다.

우리가 만드는 것

  1. 후보 프레임 추출 (장면 경계 + 균등 샘플링).
  2. 라플라시안 분산 (Laplacian variance)을 사용하여 흐릿한 프레임 필터링.
  3. 긍정적/부정적 프롬프트 세트를 사용하여 OpenCLIP으로 남은 각 프레임의 점수 산출.
  4. 구조적 규칙 적용 (얼굴이 있는 프레임 선호).
  5. 장면 수준의 다양성을 고려하여 상위 K개 선택.

1. 설정

python -m venv .venv && source .venv/bin/activate
pip install --break-system-packages \
  open-clip-torch \
...

얼굴 탐지 (face detection)를 위해 OpenCV의 내장 Haar cascades를 사용하여 간단하게 유지하겠습니다. 작은 얼굴에 대해 더 높은 정확도가 필요하다면 나중에 insightface로 교체하세요.

2. 후보 프레임 추출

후보의 출처는 두 가지입니다: 장면 경계 (장면당 하나)와 균등 샘플링 (N초마다 하나). 이 둘을 결합한 다음 타임스탬프를 기준으로 중복을 제거합니다.

# extract.py
import subprocess, os, pathlib
from scenedetect import open_video, SceneManager, ContentDetector
...

💡 팁: -i _이전_에 -ss 플래그를 사용하면 ffmpeg가 인덱스를 사용하여 탐색합니다 (프레임 단위에서는 빠르지만 부정확합니다). 썸네일 품질의 스틸 이미지를 위해서는 정확한 탐색 (accurate seeking)이 필요하지만, 컷 이후 0.4초 정도의 여유는 인덱스 탐색 (index-seek)으로도 충분합니다.

실행하기:

$ python -c "from extract import build_candidates; print(len(build_candidates('clip.mp4', 'frames/')))"
27

6분 길이의 클립에 대해 27개의 후보가 추출되었습니다. 관리 가능한 수준입니다.

3. 흐릿한 프레임 필터링하기

CLIP은 흐릿함 (blur)에 대해 너무 관대합니다. 따라서 라플라시안 분산 (Laplacian variance)을 사용하여 먼저 가지치기를 합니다.

# sharpness.py
import cv2

...

1080p 소스의 경우 임계값 (threshold)을 80 정도로 설정하면 적당합니다. 720p의 경우 ~60으로 낮추세요. 보유하고 있는 라이브러리의 샘플 몇 개로 튜닝하십시오. 적절한 차단 지점 (cutoff)은 촬영에 사용하는 카메라에 따라 달라집니다.

4. OpenCLIP으로 점수 매기기

핵심 비결은 각 프레임의 점수를 positive_similarity - mean(negative_similarity)로 계산하는 것입니다. 이는 조명과 콘텐츠가 정규화(normalize)되기 때문에 절대적인 양수 점수 (absolute positive score)보다 더 견고 (robust)합니다.

# score.py
import torch, open_clip
from PIL import Image
...

⚠️ 주의: 모델을 한 번만 로드하고 재사용하십시오. 매 호출마다 모델을 다시 로드하는 것이 사람들이 "CLIP은 느리다"라고 생각하는 가장 흔한 이유입니다. 실제로 느린 것은 체크포인트 (checkpoint) 로드 과정입니다.

간단한 검증:

$ python -c "from score import Scorer; s = Scorer(); print(s.score('frames/f_0010_0030.50.jpg'))"
0.0432

0에서 0.1 사이의 숫자가 일반적입니다. 숫자가 클수록 좋습니다.

5. 얼굴 보너스

UGC (User Generated Content) 스타일의 콘텐츠의 경우, 적절한 썸네일에는 거의 항상 선명한 얼굴이 포함되어 있습니다. 얼굴이 존재할 때 점수를 높여줍니다.

# faces.py
import cv2

...

6. 오케스트레이션 (Orchestrate)

# pick.py
from extract import build_candidates
from sharpness import is_sharp
...

실행:

$ python pick.py clip.mp4
[
  { "t": 84.40, "path": "frames/f_0017_0084.40.jpg", "score": 0.1129 },
...

이제 다양하고, 선명하며, 점수가 매겨진 세 개의 후보 썸네일을 확보했습니다. 첫 번째 것을 기본값으로 사용하고, 나머지 두 개는 A/B 테스트용으로 보관하십시오.

7. (선택 사항) 종횡비에 맞춰 자르기

제품이 16:9 비율로 썸네일을 보여주는데 소스 영상이 9:16(세로형 UGC)인 경우, 또는 그 반대의 경우, 레터박스(letterbox)가 아닌 스마트 크롭(smart crop)이 필요합니다.

# crop.py
import cv2

...

세로형 콘텐츠에 대해 "상단 3분의 1(upper third)" 편향을 적용한 것은 의도적인 것입니다. 세로형 비디오에서 얼굴은 거의 항상 상단 3분의 1 지점에 위치하며, 중앙 정렬 크롭을 하면 얼굴을 놓치게 됩니다. 더 높은 품질을 원한다면, 5단계에서 얻은 바운딩 박스(bounding box)를 사용하여 이를 얼굴 인식 크롭(face-aware crop)으로 교체하십시오.

8. 파이프라인의 어느 단계에 배치할 것인가

단계시점
업로드 시 인라인(Inline) 처리가장 쉬운 방법입니다. "비디오 처리" 단계에 5~30초를 추가합니다.
...

관리형 비디오 API(managed video API)를 사용 중이라면 이미 "에셋 준비 완료(asset ready)" 웹훅(webhook)을 받을 수 있습니다. 해당 웹훅에 백그라운드 워커(background worker)를 연결하고, 선택된 썸네일을 CMS 행에 다시 기록하십시오. FastPix와 Mux 모두 에셋에 커스텀 썸네일 URL을 설정할 수 있도록 지원합니다.

다음 단계

복잡성을 감수할 만한 가치가 있는 세 가지 업그레이드 방법입니다:

  • 더 나은 얼굴 탐지기 (Face detector). 작은 얼굴에 대한 견고함을 위해 Haar를 insightface로 교체하십시오.
  • 학습된 스코어러 (Learned scorer). 클릭률(CTR) 데이터가 있다면, CLIP 임베딩(embeddings) 위에 작은 헤드(head)를 추가하여 미세 조정(fine-tune)하십시오. 수동으로 조정된 프롬프트(prompt)가 80%의 성과를 가져다준다면, 나머지 20%를 학습하는 것이 진정한 ROI(투자 대비 수익)입니다.
  • 시각-언어 스코어링 (Vision-language scoring). 코사인 유사도(cosine similarity) 대신 작은 VLM(시각-언어 모델)에게 질문하십시오 (예: "이 썸네일의 매력도를 1-10점으로 평가해줘"). 비용은 더 많이 들지만, 중요한 콘텐츠의 경우 그만한 가치가 있을 때가 있습니다.

이 파이프라인이 제대로 짚고 있는 핵심은 모델은 저렴한 부분이라는 점입니다. 진정한 레버리지는 후보 선택(candidate selection), 구조적 필터(structural filters), 그리고 다양성 제약(diversity constraint)에 있습니다. 일반적인 CLIP을 사용하여 이 요소들을 제대로 구현한다면, 이미 대부분의 플랫폼이 제공하는 기본 썸네일을 능가하고 있는 것입니다.

즐거운 썸네일 선택 되시길 바랍니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0