본문으로 건너뛰기

비디톡스 (Videtox)

영상 URL과 프롬프트로 원하는 분량의 요약 글이나 요약 영상을 만드는 영상 요약 서비스

바이브 코딩으로 개발, 최적화는 실측으로 검증

개요

기간

2026.09

개요

개인 프로젝트

주요 기능

요약 글·요약 영상, 화면·말 분석(Florence-2 · PaddleOCR · Whisper), LLM 관련도 채점과 가우시안, 분량 맞추기

사용 기술

PythonFastAPIReactPyTorchHugging FaceWhisperFlorence-2PaddleOCRffmpegClaude API

아키텍처

구현 기능 및 개발 과정

AI 모델 네 개의 역할 분담 — 허깅페이스 Florence-2 · Whisper, PaddleOCR, Claude

화면 · 글자 · 말 통째로영상 파일Claude 요약토큰 낭비오래 걸림
처음에는 영상을 Claude에 통째로 넘겨 요약했고, 토큰 낭비가 심하고 시간도 오래 걸렸음.
yt-dlp컷마다16kHz 오디오8장씩자식 프로세스12,000자 조각 · 동시 6개pydantic영상 사이트source.mp4샷 대표 프레임Whisper 받아쓰기Florence-2 캡션PaddleOCR 글자컨텍스트Claude 채점점수 JSON
영상을 받아 샷과 소리로 나누고, 전용 모델이 글로 바꾼 컨텍스트만 Claude가 채점해 점수 JSON을 냄.
문제

영상에는 화면, 화면 속 글자, 말이 섞여 있어 LLM 하나에 영상을 통째로 맡기니 토큰 낭비가 심하고 시간도 오래 걸렸음.

분석

LLM이 영상을 판단하는 데 필요한 정보는 화면에 보이는 내용과 음성으로 하는 말임. 영상을 그대로 넘기면 같은 화면이 몇 초씩 이어져도 모든 프레임과 소리를 처리해야 함. 이를 줄이기 위해 전용 모델이 로컬 GPU에서 화면·글자·말을 먼저 짧은 글로 정리하도록 함.

해결

화면이 전환되는 지점을 기준으로 영상을 샷으로 나누고, 샷마다 대표 프레임 1장만 남김. 허깅페이스 transformers로 불러온 Florence-2-large로 각 프레임을 한 줄로 설명함. Whisper large-v3-turbo로 음성을 받아쓰고 말한 시각을 함께 기록함. PaddleOCR 한국어 모델로 화면 속 글자를 읽음.

해결

화면 설명·받아쓰기·화면 속 글자를 시간순으로 합쳐 컨텍스트를 구성함. Claude에는 영상 대신 이 글만 넘김. 글은 12,000자씩 나눠 동시에 보내고, 응답은 pydantic으로 정한 형식에 맞춰 JSON 점수 목록으로만 받음.

결과

16분 39초 영상(59,905프레임)이 대표 프레임 356장과 말 346구간으로 줄고, Claude는 이를 정리한 61,426자 글만 읽음.

*Florence-2 · Whisper · PaddleOCR: 이미지를 설명하는 모델 · 말을 받아쓰는 모델 · 화면 글자를 읽는 모델 *샷 · 대표 프레임: 컷과 컷 사이의 연속된 화면 구간과, 그 구간을 대표하는 이미지 1장 *컨텍스트: Claude가 판단할 때 참고하는 글 *JSON · pydantic: 항목과 값을 정해진 구조로 담는 데이터 형식과, 그 형식을 정의하고 검사하는 라이브러리

핵심 문장 기준 관련도 채점과 가우시안 점수 보정

LLM사용자 프롬프트 추가봉우리 앞뒤 끌어올림화면 JSON · 말 JSON컨텍스트핵심 문장 4~5개기준 글조각마다 동시 채점가우시안최종 점수
핵심 문장과 프롬프트로 기준 글을 작성하고 각 조각을 동시에 채점한 뒤, 점수가 높은 구간 주변의 점수도 올려 구간을 자연스럽게 이어 붙이도록 함.
5단계 컨텍스트. 화면·말·글자를 시간 순서대로 한 줄씩 나열하고, 말도 글자도 없는 샷은 `화면만:` 으로 표시함
문제

샷과 발화를 각각 채점해 점수가 높은 조각만 고르면 서로 떨어진 구간을 선택하게 됨. 떨어진 구간을 이어 붙이면 앞뒤 맥락이 끊겨 사용자 요청에 맞는 흐름을 만들기도 어려움.

분석

컨텍스트(모델에게 주는 참고 자료)가 6만 자를 넘어 한 번에 보내기 어려움. 여러 조각으로 나눠도 조각끼리 점수를 비교할 수 있어야 하므로, 모든 조각을 같은 기준 글과 같은 점수표로 채점해야 함. 또 화면 설명(캡션)만 있는 샷은 확인할 근거가 적어, 설명 속 고유명사를 그대로 믿으면 안 됨.

해결

LLM이 컨텍스트에서 핵심 문장 4~5개를 뽑고, 여기에 사용자 프롬프트를 붙여 채점 기준 글을 만듦. 분석 결과 JSON은 12,000자씩 나눠 동시에 채점함. 이후 70점 이상인 구간을 중심으로 가우시안을 적용해 가까운 구간의 점수도 올림. 점수를 얼마나 멀리까지 올릴지 정하는 폭(σ)은 4초로 둠.

결과

16분 39초 영상의 JSON 702개를 조각 9개로 나눠 29초에 채점함. 점수가 내려간 JSON은 없고, 점수가 높은 구간 주변만 올라감.

분량 맞추기 — 말이 끊기지 않게 구간 다듬기

끝이 말 중간3초 미만다시 말 중간점수 높은 순구간 고르기말 끝까지 늘림앞뒤로 나눠 늘림그 말까지 늘림목표 분량 안
길이 문제는 점수 대신 길이를 조정해 해결함. 짧은 샷도 3초 이상 보여 주고 말은 중간에 끊기지 않도록 함.
문제

샷은 컷을 기준으로 나누므로 0.3초처럼 짧은 구간도 포함함. 측정한 영상에서는 전체 503개 샷 중 45%가 2초 미만임. 점수만으로 고르면 0.5초짜리 샷도 포함되어, 요약 영상을 볼 때 내용을 알아보기 어려움.

해결

고른 구간은 세 번 다듬음. 끝이 말 중간에 걸리면 말이 끝날 때까지 늘리고, 3초보다 짧으면 앞뒤로 늘림. 늘린 구간의 끝이 다시 말 중간에 걸리면 한 번 더 늘림.

해결

고른 구간은 ffmpeg로 다시 인코딩함(영상 H.264, 소리 AAC). 원본 순서대로 이어 붙이고, 이음새마다 40ms 동안 소리를 서서히 줄였다 키워(페이드) 소리가 튀지 않게 함.

결과

별도 시연에 사용한 38분 29초 영상을 목표 180초에 맞춰 줄여 구간 14개로 이루어진 163.7초 영상을 생성함. 이미 긴 구간은 길이를 유지함.

위스퍼 실행 전 무음 처리 — 말 없는 영상 건너뛰기와 긴 무음 제거

소리 나는 창 1% 미만1.5초 넘는 무음이어 붙인 시각16kHz 오디오25ms 창 RMS위스퍼 건너뜀무음 빼고 이어 붙임위스퍼원본 시각으로 되돌림말 JSON
소리 크기를 기준으로 말 없는 영상은 건너뛰고, 긴 무음은 잘라 낸 뒤 받아쓰고, 받아쓴 시각을 원본 시각으로 되돌림.
문제

소리 데이터는 있지만 실제로는 조용한 타임랩스 영상에서도 Whisper를 돌리는 데 65초가 걸렸지만 받아쓴 말은 없었음. 조용한 구간에서 같은 문장을 반복해 지어내는 환각(없는 내용을 만들어 내는 현상)도 발생함.

분석

오디오를 잘라 동시에 처리하는 방법과 여러 개를 묶어 처리하는 방법(배치)의 속도를 측정함. 속도는 몇 퍼센트 차이에 그치거나 오히려 느려졌고 받아쓰기 결과도 달라져 둘 다 쓰지 않음.

해결

25ms마다 소리 크기(RMS)를 재서, 소리가 나는 구간이 1% 미만이면 Whisper를 돌리지 않음. 받아쓸 때는 1.5초보다 긴 무음을 잘라 내고 나머지를 이어 붙여 넣은 뒤, 받아쓴 시각을 원본 영상의 시각으로 되돌림.

해결

잘라 낸 구간의 이음새에 걸친 말은 원래 구간이 끝나는 지점까지만 포함함. 무음을 잘라도 길이가 10% 미만으로만 줄어들면, 시각을 되돌릴 때 생길 수 있는 오류를 피하려고 원본 오디오를 그대로 씀.

결과

16분 39초 오디오를 0.02초 만에 판정함. 말이 있는 영상은 소리가 나는 구간의 비율이 81.0%로 기준과 80배 차이가 나, 잘못 건너뛸 일이 없음.

트러블 슈팅

Florence-2가 한국어 자막 대신 '방탄소년단'을 출력함

356샷 중 198샷한글 토큰 0개 · 생성 모델torch 와 cuDNN DLL 충돌Florence-2 OCR없는 글자 방탄소년단토크나이저 확인글자 없음을 말 못함PaddleOCR 한국어모델별도 환경 워커프로세스환각 0샷
없는 글자를 지어내던 Florence-2의 글자 읽기를 글자 위치부터 찾는 PaddleOCR로 바꾸고, torch와 충돌하는 실행 환경은 별도 프로그램으로 분리함.
3단계에서 생성하는 샷 JSON. PaddleOCR이 대표 프레임의 한국어 글자(자료 화면 · 채팅)를 읽어 `ocr` 에 저장하고, 글자가 없으면 빈 문자열이 됨. 긴 글자는 앞부분만 표시함
문제

화면을 분석하는 3단계에서 Florence-2의 글자 읽기(`<OCR>`)를 쓰자, 356개 샷 중 198개(56%)에서 영상에 없는 '방탄소년단'이 나옴. 356개 샷 모두 무언가를 출력해, 글자가 없는 화면도 구분하지 못함.

분석

원인을 찾으려 Florence-2가 글자를 쪼개는 방식(토크나이저)을 확인함. 한글 전용 단위가 0개이고 글자를 바이트 조각으로 쪼개는 방식(byte-level BPE)이라, 학습 때 자주 본 한국어 문자열을 화면과 상관없이 내놓음. 또 글을 만들어 내는 생성 모델이라 '글자 없음'이라고 답하지 못함.

분석

PaddleOCR을 같은 환경에 설치하자 paddle과 torch(둘 다 딥러닝 라이브러리)가 충돌함. 두 라이브러리가 같은 이름의 `cudnn_cnn64_9.dll` 파일을 서로 다르게 만들어 써서, 나중에 불러오는 쪽이 실패함. CUDA를 13으로 맞춰도 해결되지 않았고, 둘이 요구하는 GPU 라이브러리(cuDNN·cuBLAS) 버전도 달라 한 환경에서 함께 쓸 수 없었음.

해결

글자 위치를 먼저 찾고 그 글자를 읽는 PaddleOCR 한국어 모델로 바꿈. `uv run --script`로 PaddleOCR 전용 실행 환경을 따로 만들고 별도 프로그램으로 돌림. 모델은 18MB로 작고, Florence-2가 GPU를 쓰지 않는 틈에 함께 돌아감.

결과

환각 샷 198개 → 0개. 글자가 없으면 빈 문자열이 되며, 프로젝트 의존성은 한 줄도 바뀌지 않음.

컷 검출 146초 — 병목은 판단이 아니라 색 변환이었음

측정측정252초flags=neighbor2단계 146초프레임 펼치기 13초BGR 변환 · 할당 90초ffmpeg 안에서 256px축소PyAV 백엔드컷 354개 일치2단계 46초
처리별로 나눠 측정하니 시간 대부분이 원본 크기의 프레임을 색 변환하는 데 쓰였음. ffmpeg에서 먼저 축소해 결과는 그대로 두고 시간만 줄임.
같은 16분 39초 영상의 최적화 전·후 단계별 시간 (잡 폴더 status.json 실측)
문제

컷을 찾는 2단계는 PySceneDetect(컷 검출 라이브러리)를 쓰며, 16분 39초 영상에 146초가 걸림. 256px로 줄인 화면만 보는데도 오래 걸려, 어디서 시간이 드는지 확인함.

분석

처리별로 시간을 나눠 측정함. 프레임을 읽기만 하는 `cap.grab()`은 13.1초, 파이썬에서 쓸 수 있게 이미지로 꺼내는 `cap.read()`는 102.9초로 89.8초 차이가 남. 이 차이는 OpenCV가 매 프레임을 원본 크기 그대로 색 변환하고 메모리를 확보해 이미지(BGR 배열)로 만드는 데서 생김.

분석

다른 방법으로도 시간을 측정함. PyAV는 원본 크기 프레임을 넘겨 252초가 걸렸고, GPU 영상 해제 기능(NVDEC)은 결과를 다시 CPU 메모리로 옮겨야 해 빨라지지 않음. 일부 프레임 건너뛰기(frame_skip)는 컷 판단 자체가 4.5초뿐이라 효과가 작아 쓰지 않음.

분석

화면을 줄이는 방식에 따라 컷 검출 결과가 달라지는지도 비교함. 주변 픽셀을 섞는 bilinear·bicubic은 컷 352개를 찾아, 기존 컷 6개를 놓치고 4개를 새로 만듦. 가장 가까운 픽셀을 그대로 쓰는 neighbor만 기존과 같은 354개를 찾음.

해결

프레임을 파이썬으로 넘기기 전에 ffmpeg에서 먼저 줄이도록 순서를 바꿈(`-vf scale`). 줄인 프레임은 파이프로 바로 넘기고, 컷 판단은 기존 AdaptiveDetector를 그대로 씀.

결과

컷 검출 108.8초 → 21.2초(5.2배), 2단계 전체 146초 → 46초. 컷 354개는 프레임 번호까지 동일함.

3단계 366초 — 사용하지 않는 물체 좌표와 부정확한 긴 캡션

좌표 전부 버림블라인드 평가 12장3단계 366초물체 추출 29%긴 캡션 440자물체 추출 제거없는 것 말함 11/12한 줄 캡션3단계 66초
사용하지 않는 좌표 토큰과 환각이 잦은 긴 캡션을 빼서 처리 시간과 컨텍스트 길이를 함께 줄임.
문제

화면 분석을 맡는 3단계에서는 샷마다 긴 캡션, 물체 정보, 화면 글자를 모두 뽑음. 16분 39초 영상에서 이 단계에만 366초가 걸림. 컨텍스트도 21만 자까지 늘어나 이후 LLM 처리 시간도 늘어남.

분석

물체 찾기(`<OD>`)는 위치 좌표까지 만들어 냈지만 실제로는 좌표를 버리고 물체 이름만 썼음. 이 작업이 3단계 시간의 29%를 차지했고, 찾은 물체의 93%는 캡션에 이미 들어 있었음.

분석

프레임 12장으로 긴 캡션과 한 줄 캡션을 비교하되, 어느 설정인지 가리고 평가함. 상황을 맞게 설명한 프레임은 둘 다 12장 중 9장임. 화면에 없는 내용을 넣은 프레임은 긴 캡션이 12장 중 11장, 한 줄 캡션이 12장 중 4장임. 긴 캡션은 화면 글자까지 읽으려다 없는 내용을 지어냄.

해결

물체 찾기를 빼고, 긴 설명(`<MORE_DETAILED_CAPTION>`) 대신 한 줄 설명(`<CAPTION>`)을 씀. 말도 화면 글자도 없는 샷에는 `화면만:` 표시를 달고, LLM에게 이 줄의 고유명사는 그대로 믿지 말고 일반명사로 읽으라고 지시함.

결과

3단계 소요 시간은 366초 → 66초로 단축됨. 없는 내용을 담은 캡션이 11/12 → 4/12로 줄어 분석에 쓰이는 잘못된 설명이 감소함. 남은 오류는 캡션보다 OCR과 발화 내용을 우선해 걸러냄. 1~5 분석 단계의 총시간은 621초 → 209초로 단축됨.

결과 및 회고

결과

URL과 프롬프트로 요약 글·요약 영상을 만드는 15단계 파이프라인을 완성함

같은 16분 39초 영상에서 분석 단계(1~5) 총시간을 621초 → 209초로 줄임

한국어 화면 글자 환각('방탄소년단' 198샷)을 0샷으로 없앰

단계 모듈 테스트 27건이 통과함

회고

최적화 전에 처리별로 시간을 측정함. 컷 검출의 병목은 판단이 아니라 색 변환이었음

속도를 높이는 변경을 할 때마다 결과가 유지되는지(컷 354개 일치, 받아쓰기 일치율) 함께 확인함

글을 만들어 내는 모델은 '없음'이라고 답하지 못함. 비어 있을 수 있는 값은 먼저 찾고 나서 읽는 모델에 맡김

문제에 맞는 해결책을 적용함. 짧은 샷 문제는 점수 대신 길이를 조정해 해결함

라이선스