Skip to content

Repository files navigation

FrameFix

AI 생성 영상의 중복(정지) 프레임을 찾아내고 되살리는 도구. Find and repair duplicated (frozen) frames in AI-generated video.

Seedance, Dreamina, Hailuo 같은 영상 생성 모델은 결과물을 24fps라고 표시하지만, 실제로 새로 그려진 프레임은 그보다 훨씬 적은 경우가 있습니다. 모자란 자리는 바로 앞 프레임을 그대로 복사해 채워집니다. 눈에는 미세한 떨림으로 보이고, 편집 단계에서 한 장씩 골라내려면 시간이 끝없이 듭니다.

FrameFix는 폴더를 통째로 훑어 어떤 클립이 얼마나 상했는지 수치로 보여주고, 문제 있는 것만 골라 중복을 제거한 뒤 진짜 중간 프레임을 다시 그려 넣습니다.


이게 무슨 문제인가

실제로 제보받은 15초 클립을 361프레임 전부 계측한 결과입니다.

항목
선언 프레임레이트 24 fps
실효 프레임레이트 17.48 fps
중복 프레임 98 / 361 (27.1%)
정지 길이 예외 없이 2프레임(83ms), 3장 연속은 0건
중복 위치 98건 중 90건이 프레임번호 mod 3 == 1 자리

중복이 위상에 잠겨 있다는 건 출력단의 프레임레이트 변환이 원인이라는 뜻입니다. 보간 없이 그냥 복사한 것이라 그 지점에서 모션이 완전히 멈춥니다.

가장 중요한 원칙

중복 제거를 보간보다 먼저 하세요.

순서를 뒤집어 원본을 그대로 RIFE·Topaz·DaVinci Speed Warp에 넣으면, 보간기는 같은 그림 두 장 사이를 계산하게 됩니다. 결과는 모션이 0인 중간 프레임입니다. 떨림은 그대로 남고 화면만 뭉개집니다. FrameFix는 항상 제거를 먼저 합니다.


설치

A. 포터블 — 설치할 것이 없습니다 (권장)

Releases 에서 zip 을 받아 압축을 풀고 FrameFix.exe 를 더블클릭하면 끝입니다. Python 도 ffmpeg 도 따로 깔 필요 없습니다. Windows x64.

영상 담당자에게 그냥 폴더째 전달하면 됩니다.

B. 소스에서

git clone https://github.com/openerai/framefix.git
cd framefix

Windows 라면 setup.bat 을 더블클릭하세요. Python 확인 → 패키지 설치 → ffmpeg·Topaz 감지까지 한 번에 합니다. 수동으로 하려면:

pip install -r requirements.txt

Python 3.9 이상이면 됩니다. ffmpeg가 PATH에 있으면 그걸 쓰고, 없으면 imageio-ffmpeg가 함께 설치한 번들 바이너리를 자동으로 찾습니다.

받아야 할 AI 모델이 있나요?

없습니다. 이 도구는 학습 모델을 쓰지 않습니다 — 프레임 차분 계산과 ffmpeg 만으로 동작합니다. ffmpeg 의 minterpolate 도 고전 옵티컬 플로우입니다.

Topaz Video 가 설치·로그인된 PC 에서는 Topaz 의 AI 보간을 자동으로 씁니다. 그 모델도 Topaz 가 알아서 내려받으므로(tvai_fidownload 기본값이 1) 따로 준비할 것이 없습니다. Topaz 가 없으면 ffmpeg 보간으로 자동 전환되며, 결과는 거의 같습니다(다만 Topaz 쪽이 4배쯤 빠릅니다).

필요한 것 모델
검출·중복 제거 numpy, ffmpeg 없음
ffmpeg 보간 ffmpeg 없음
Topaz 보간 (선택) Topaz Video 라이선스 Topaz 가 자동 내려받음
NVENC 인코딩 (4K) NVIDIA GPU 없음

앱 창으로 쓰려면 Electron이 필요합니다(선택).

cd electron && npm install

이미 다른 곳에 Electron이 있다면 설치 없이 그걸 가리켜도 됩니다.

set ELECTRON_EXE=C:\path\to\electron.exe

실행

방법 명령 비고
앱 창 FrameFix 앱.bat 권장. 드롭한 파일을 복사 없이 처리
브라우저 FrameFix 실행.bat Electron 없이 동작
직접 python framefix.py 브라우저가 열립니다
CLI python unstutter.py ... 배치·자동화용

macOS·Linux에서는 python framefix.py로 브라우저 UI를 쓰면 됩니다. .bat 실행기와 Topaz 연동은 Windows 전용입니다.


쓰는 법

  1. 폴더를 지정합니다 — [폴더 찾기] 버튼, 경로 붙여넣기(Ctrl+V), 또는 창 아무 곳에나 폴더를 끌어다 놓기.
  2. [스캔] — 클립마다 썸네일·실효 fps·잘못된 프레임 수·모션 그래프·판정이 나옵니다.
  3. [문제+경미] 버튼으로 손볼 것만 한 번에 고릅니다. Shift+클릭으로 범위 선택도 됩니다.
  4. [선택 복구] — 끝나면 완료 배너와 함께, 각 행에 재측정한 결과가 붙습니다.

체크된 것 = 실행될 것입니다. 스캔이 몰래 뭔가를 선택해 두지 않습니다. 선택한 것 중 일부가 '정상'이라 빠지게 되면 실행 전에 파일명까지 알려줍니다.

표 읽는 법

  • 실효 FPS — 실제로 새로 그려진 프레임의 초당 개수. 아래 가는 게이지는 선언값 대비 비율.
  • 잘못된 프레임 — 중복으로 판정된 개수. 회색(정상) / 주황(경미) / 빨강(문제).
  • 모션 — 프레임별 변화량. 주황 막대가 정지 지점입니다.
  • 판정 — 중복 2% 미만 정상 / 10% 미만 경미 / 그 이상 문제.
  • 결과 — 복구 후 같은 잣대로 자동 재측정한 값. 24.00fps · 중복 0%면 성공.

복구 방식

방식 잔여 중복 실효 fps 소요
원본 (처리 없음) 98 17.48
Topaz 단독 (rdt 기본값) 14 23.07 30s
정확검출 + ffmpeg 보간 0 24.00 65s
정확검출 + Topaz Chronos v2 0 24.00 15s
중복 제거만 (보간 없음) 0 17.48 6s

720p 15초 클립 기준.

검출은 이 도구가, 보간만 Topaz에 맡기는 조합이 가장 깨끗하고 가장 빠릅니다. Topaz가 361장이 아니라 263장만 처리하면 되기 때문입니다. Topaz가 없으면 ffmpeg 보간으로 사실상 동등한 결과가 나옵니다.

검출 방식

프레임 간 평균절대차를 구한 뒤, 주변 구간의 60분위를 모션 기준선으로 삼아 그 40% 미만이면 중복으로 봅니다. 절대 임계값이 아니라 상대값이라 어둡고 저대비인 컷에서도 그대로 동작합니다.

중앙값(50분위)을 기준선으로 쓰면 중복이 절반 가까운 클립에서 기준선 자체가 중복에 끌려 내려가 정작 중복을 놓칩니다. 12fps를 24fps로 늘린 테스트 클립에서 실제 50%인데 14.6%만 잡혔습니다. 60분위로 올리면 정확히 50%가 나옵니다.

ffmpeg의 mpdecimate는 절대 임계값 방식이라 이런 콘텐츠에서 "전부 삭제 아니면 하나도 삭제 안 함" 사이에 쓸 만한 구간이 없었습니다.


CLI

# 폴더 전수 진단 → CSV
python unstutter.py "clips/*.mp4" --analyze-only --csv report.csv

# 한 클립 복구 (중복 제거 + 24fps 보간, 오디오·길이 유지)
python unstutter.py shot_012.mp4 -o shot_012_fixed.mp4 --fps 24

# RIFE 등 외부 보간기에 넘길 경우 — 고유 프레임만 PNG 로 추출
#   _meta.json 의 true_fps 를 보간기 입력 프레임레이트로 쓰세요
python unstutter.py shot_012.mp4 --export-frames ./unique_frames

# 폴더 전체 일괄 복구
python unstutter.py "clips/*.mp4" --fps 24

주요 옵션: --ratio 0.40 검출 민감도(느린 컷에서 과검출되면 0.30으로), --regular 보간 없이 카덴스만 정리, --fps 목표 프레임레이트.


알아두면 좋은 것들

4K 이상도 됩니다

3840×2160 클립(6초)으로 실측한 값입니다.

단계 시간 메모리
분석 0.6초 88 MB
중복 제거만 16초 2.3 GB
+ Topaz Chronos v2 50초 5.8 GB
+ ffmpeg 보간 136초 5.1 GB

분석은 320px로 줄여서 하므로 해상도와 거의 무관하게 빠릅니다. 프레임을 한 장씩 흘려보내는 구조라 영상이 길어져도 메모리는 늘지 않습니다.

번들 ffmpeg의 libx264는 4K + preset medium/slow에서 멀티스레드 버그로 죽습니다 (1440p 이하는 정상, -threads 1이면 통과). 그래서 600만 화소가 넘으면 하드웨어 인코더(h264_nvenc)를 자동으로 씁니다. 더 빠르기도 합니다.

폭이나 높이가 4096을 넘으면 이야기가 또 다릅니다. NVENC의 H.264는 4096이 한계라 인코더가 아예 열리지 않습니다. 실측 결과입니다.

해상도 h264_nvenc hevc_nvenc libx264
3840×2160
4096×2160
4320×1824
7680×4320

시네마스코프 4K(4320×1824)나 8K가 여기 걸립니다. FrameFix는 4096을 넘으면 libx264를 먼저 쓰고, 그것도 안 되면 hevc_nvenc로 넘어갑니다. 인코더가 실패하면 다음 후보로 자동 재시도하므로, 앞으로 모르는 한계에 부딪혀도 하드 실패 대신 조금 느린 성공이 됩니다.

컨테이너 타임베이스 함정

일부 생성물은 24fps 콘텐츠가 60fps 타임베이스(tbr 60) 컨테이너에 담겨 나옵니다. 이런 파일을 ffmpeg로 그냥 디코딩하면 ffmpeg가 60fps 격자에 맞추려고 스스로 프레임을 복제해서 내보냅니다. 361장이 901장이 되고, 없던 중복이 59.9%로 잡히며, 길이도 15초가 37.5초로 계산됩니다.

FrameFix는 -fps_mode passthrough로 코딩된 프레임만 읽고, 길이는 컨테이너 Duration과 대조해서 고릅니다. 직접 ffmpeg 스크립트를 쓰신다면 이 플래그를 꼭 붙이세요.

큰 화면에서 디코딩이 조용히 잘리는 문제

번들 ffmpeg의 h264 프레임 단위 멀티스레드 디코딩이 4K 초과 해상도에서 간헐적으로 죽습니다(0xC0000005). 같은 4320×1824 파일을 14번 디코딩한 결과입니다.

방식 성공 평균
기본 8/14 88·409·315·82 프레임에서 끊김
-thread_type slice 14/14 10.5초
-threads 1 14/14 12.7초

무서운 건 크래시 자체가 아니라 조용히 잘린다는 점입니다. 종료 코드를 확인하지 않으면 짧아진 결과를 정상으로 받아들이고, 그러면 복구가 엉뚱한 프레임을 지웁니다.

FrameFix는 큰 화면을 slice 스레딩으로 디코딩하고, 종료 코드와 프레임 수를 반드시 검증합니다. 어긋나면 스레딩을 끄고 재시도하며, 그래도 안 되면 조용히 넘어가지 않고 실패로 보고합니다.

ProRes 4444 같은 고품질 마스터는 손실 없이 처리합니다

VFX/컴포지팅용 마스터(ProRes 4444, DNxHR 등, 4:2:2 이상 채도·8bit 초과·알파 채널)를 넣으면 예전엔 무조건 H.264 · 8bit · 4:2:0 · .mp4 로 찍혀 나왔습니다. 파일이 작아진 건 정상이지만(중복 프레임을 뺐으니까), 그와 별개로 색 해상도가 강제로 절반이 되고 있었습니다 — 용량이 준 게 아니라 화질이 준 것이었습니다.

지금은 소스의 픽셀 포맷·코덱을 읽어 고품질 마스터로 판단되면 ProRes로, .mov 컨테이너로 출력합니다. 코덱과 채도 서브샘플링이 원본과 같습니다.

원본 예전 결과 지금 결과
코덱 ProRes 4444 H.264 ProRes 4444
픽셀 포맷 yuv444p12le yuv420p yuv444p12le
컨테이너 .mov .mp4 .mov
오디오 AAC AAC 재인코딩 원본 그대로 복사

실제 4320×1824 ProRes 4444 12bit 마스터로 검증했습니다 — 코덱·픽셀포맷·오디오 스트림이 결과물에서 원본과 동일하게 나옵니다. 일반 소스(이미 H.264로 배포된 것 등)는 예전과 똑같이 H.264/.mp4 로 나갑니다 — 그쪽은 이미 8bit·4:2:0 이라 바꿀 이유가 없습니다.

한 가지는 정직하게 말씀드립니다. 내부적으로 프레임을 한 장씩 흘려보내는 파이프가 8bit RGB 를 거칩니다. 그래서 채도 손실(4:4:4→4:2:0)은 완전히 없앴지만, 비트뎁스는 이론상 여전히 8bit 지점을 지납니다. 같은 마스터로 실측한 결과, 8bit 경유 유무의 차이는 평균 0.14/255, 최대 2/255 수준이었습니다 — 이 소스 기준으로는 육안 차이가 나지 않을 정도로 작았지만, 극단적인 그러데이션이나 깊은 컬러 그레이딩 여유가 중요한 작업이라면 이 한계를 알아두세요. 진짜 비트단위 무손실 경로(16bit 원시 파이프)가 필요하시면 알려주세요 — 추가로 만들 수 있습니다.

Topaz Video 연동

설치되어 있으면 자동으로 감지합니다(Windows). 모델은 Chronos v2 / Chronos Fast / Apollo / Apollo Fast / Aion 중에 고를 수 있습니다.

Topaz에도 중복 제거 기능이 있습니다(tvai_fi 필터의 rdt 파라미터). 다만 문서 설명과 반대로 동작합니다 — 값을 올릴수록 결과가 나빠집니다.

rdt 잔여 중복 실효 fps
0.01 (기본) 14 23.07
0.05 36 21.61
0.12 94 17.75
0.20 (최대) 90 18.02

그래서 FrameFix는 rdt를 꺼두고(-0.01) 검출은 자체 알고리즘이 맡습니다.

⚠️ Topaz CLI는 라이선스 토큰이 담긴 인증 정보를 stdout으로 출력합니다. 직접 스크립트를 짜신다면 stdout을 로그 파일로 리다이렉트하지 마세요. FrameFix는 이를 버리도록 처리했습니다.

실제 라이브러리는 생각보다 멀쩡합니다

두 폴더를 훑어본 결과 27개 중 25개가 정상이었습니다. 전부 문제일 거라 가정하고 일괄 재인코딩하면 멀쩡한 클립의 화질만 깎입니다. 반드시 진단을 먼저 돌리고 판정된 것만 고르세요.

폴더 스캔은 이 도구가 만든 복구본(_fixed로 끝나는 파일)을 기본으로 제외합니다. 다만 파일을 콕 집어 끌어다 놓으면 이름이 _fixed로 끝나도 검사합니다 — 직접 지정한 것은 의도가 분명하니까요. 폴더 스캔에서 전부 제외되어 결과가 없으면 빈 화면 대신 이유를 알려줍니다.


요구사항

  • Python 3.9+, numpy
  • ffmpeg — PATH에 있거나 imageio-ffmpeg 번들
  • (선택) Electron — 앱 창
  • (선택, Windows) Topaz Video — AI 보간 백엔드
  • (선택) Pillow — --export-frames

라이선스

MIT. LICENSE 참고.

About

AI 생성 영상의 중복(정지) 프레임을 찾아내고 되살리는 도구 — Find and repair duplicated frames in AI-generated video (Seedance, Dreamina, Hailuo)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages