Depth Anything V2 Small과 WebGPU를 활용한 브라우저 내 시네마틱 깊이(Cinematic Depth) 구현
요약
WebGPU와 Depth Anything V2 Small 모델을 활용하여 브라우저 내에서 실시간 시네마틱 깊이 효과를 구현하는 기술적 방법을 소개합니다. 단순한 배경 흐림을 넘어 공간적 구조를 반영한 피사체 심도(DOF) 조절 기능을 로컬 환경에서 구현하는 엔지니어링 과정을 다룹니다.
핵심 포인트
- WebGPU를 통한 브라우저 로컬 환경에서의 모델 실행 및 데이터 처리
- Depth Anything V2 Small 모델을 활용한 연속적 상대 깊이 추정
- 단순 세그멘테이션을 넘어선 정교한 피사체 심도(DOF) 효과 구현
- 브라우저 제약 사항을 고려한 Q4F16 구성 및 워커 재사용 최적화
서론
우리는 최근 오픈 소스 브라우저 비디오 편집기인 Timeline Studio에 시네마틱 깊이 (Cinematic Depth) 기능을 추가했습니다.
이 기능은 WebGPU를 통해 Depth Anything V2 Small을 실행하여 이미지와 비디오 프레임을 로컬에서 분석하고, 결과로 나온 깊이 데이터(depth data)를 조절 가능한 피사체 심도 (depth-of-field) 효과로 변환합니다. 소스 미디어는 사용자의 기기를 떠날 필요가 없습니다.
- GitHub: MartinDelophy/ai-video-editor
- 라이브 데모: Timeline Studio
이 포스트는 단순히 회색조의 깊이 맵 (grayscale depth map)을 출력하는 데모가 아니라, 깊이 추정 모델을 실제 편집 기능으로 전환하는 데 필요한 엔지니어링 작업에 초점을 맞춥니다.
배경 흐림 (Background blur)은 피사체 심도 (depth of field)가 아닙니다
전형적인 배경 흐림 기능은 인물 세그멘테이션 (person segmentation)을 사용합니다:
인물 픽셀 -> 선명하게 유지
그 외 모든 것 -> 하나의 블러 반경 적용
이는 화상 회의에는 유용하지만, 장면의 공간적 구조를 모델링하지는 못합니다.
하나의 프레임에는 카메라와 가까운 나뭇잎, 중간에 있는 사람, 사람 뒤의 가구, 그리고 멀리 있는 건물이 포함될 수 있습니다. 인물 마스크 (person mask)는 오직 "사람인가 아닌가"만을 알려줍니다. 각 영역이 렌즈로부터 얼마나 멀리 떨어져 있는지는 알려줄 수 없습니다.
Depth Anything V2 Small은 프레임 전체에 걸쳐 연속적인 상대적 깊이 (continuous relative depth)를 추정합니다. 이를 통해 편집기는 다음과 같은 작업을 수행할 수 있습니다:
- 전경 (foreground)과 배경 (background)을 다르게 흐리게 처리;
- 설정 가능한 깊이 범위를 선명하게 유지;
- 장면을 통해 초점을 이동;
- 애니메이션 랙 포커스 (rack focus)를 위한 기반 구축.
왜 Small 모델인가?
브라우저 런타임 (runtime)은 GPU 서버에는 없는 제약 사항을 가지고 있습니다:
- 초기 다운로드 크기;
- GPU 및 시스템 메모리;
- WebGPU 가용성;
- 초기화 지연 시간 (initialization latency);
- 많은 비디오 프레임을 처리하는 비용;
- 모바일 하드웨어;
- 캐시 및 모델 버전 관리.
우리는 실용적인 균형을 위해 Depth Anything V2 Small의 Q4F16 구성을 선택했습니다.
프로젝트에서 이미 @huggingface/transformers를 사용하고 있었으므로, 개념적인 초기화 경로는 간단합니다:
import { pipeline } from "@huggingface/transformers";
let depthEstimator;
...
중요한 프로덕션 디테일은 재사용입니다. 우리는 매 분석마다 파이프라인 (pipeline)을 다시 구축하는 대신, 초기화된 워커 (worker)와 WebGPU 세션을 계속 유지합니다.
모델 데모가 아닌 편집 기능 설계
Cinematic Depth는 에디터의 Effects 워크스페이스에서 다섯 번째 카드로 나타납니다.
해당 카드는 다음과 같이 동작합니다:
- 기본적으로 처리되지 않은 샷 (untreated shot)을 보여줍니다.
- 호버 (hover) 또는 포커스 (focus) 시 명확한 피사체 심도 (depth-of-field) 미리보기로 전환됩니다.
- 호버 기능이 없는 터치 디바이스에서는 자동으로 미리보기를 제공합니다.
- 데스크톱에서는 전용 Effects 인스펙터 (inspector)를 엽니다.
- 모바일에서는 집중된 속성 드로어 (property drawer)를 엽니다.
사용자는 다음 항목을 조정할 수 있습니다:
- 초점 거리 (focus distance)
- 초점 범위 (focus range)
- 렌즈 블러 (lens blur)
- 보케 하이라이트 (bokeh highlights)
- Fast, Standard 또는 Fine 분석 품질
- 활성화, 취소 및 초기화
깊이 분석 (depth analysis)과 시각적 스타일링 (visual styling)은 별개의 단계입니다. 초점이나 블러 슬라이더를 움직이면 기존의 깊이 데이터를 재합성 (re-composites)할 뿐, 모델을 다시 실행하지는 않습니다.
깊이를 블러로 변환하기
각 픽셀에 대해, 선택된 초점 평면 (focus plane)으로부터 해당 픽셀의 깊이가 얼마나 떨어져 있는지 계산합니다:
export function calculateBlurAmount({
depth,
focusDistance,
...
초점 범위 내의 픽셀은 선명하게 유지됩니다. 깊이가 해당 범위를 벗어날수록 블러 (blur)가 증가합니다.
Canvas는 모든 픽셀에 서로 다른 블러 반경 (blur radius)을 할당하는 단일 연산을 제공하지 않습니다. 실용적인 구현 방식은 소스(source)의 블러 처리된 버전을 여러 개 생성하고, 이를 깊이 마스크 (depth masks)와 합성하는 것입니다:
Original
├── light blur
├── medium blur
...
또한 깊이 불연속 지점 (depth discontinuities) 주변의 헤일로 (halos) 현상을 줄이기 위해 마스크에 스무딩 (smoothing)과 페더링 (feathering) 처리가 필요합니다.
이것은 완전한 물리적 렌즈 시뮬레이션은 아니지만, 이진 방식의 인물/배경 마스크보다 훨씬 더 설득력 있는 공간적 전환을 만들어냅니다.
비디오에는 하나의 깊이 맵만으로는 부족하다
정지 영상은 한 번의 추론 (inference)이 필요합니다. 하지만 비디오는 시간에 따라 변화합니다.
클립 전체에 대해 첫 번째 프레임의 깊이(depth)를 재사용하면, 움직임으로 인해 깊이 맵과 소스 프레임이 빠르게 어긋나게 됩니다. 하지만 모든 원본 프레임에 대해 추론 (inference)을 실행하는 것은 많은 브라우저 디바이스에 너무 큰 비용이 듭니다.
우리는 품질에 따른 시간적 샘플링 (temporal sampling)을 사용합니다:
비디오 디코딩 (Decode video)
↓
선택된 클립 범위에 따라 프레임 샘플링
...
결과 데이터는 개념적으로 다음과 같습니다:
const depthFrames = [
{ time: 0.0, depth: depth0 },
{ time: 0.5, depth: depth1 },
...
렌더링 시점에 에디터는 현재 클립 상대 시간과 일치하는 깊이 프레임을 선택합니다. 인접한 샘플 간의 보간 (interpolation)을 통해 전환을 더 부드럽게 만들 수 있습니다.
클립 범위의 깊이 캐싱 (Clip-scoped depth caching)
깊이 추론 (depth inference)은 비용이 많이 듭니다. 이미 계산된 깊이를 재합성 (re-compositing)하는 것은 상대적으로 저렴합니다.
따라서 이러한 변경 사항들은 분석 (analysis)을 무효화해서는 안 됩니다:
- 초점 거리 (focus distance);
- 초점 범위 (focus range);
- 블러 강도 (blur strength);
- 보케 (bokeh) 설정;
- 효과를 일시적으로 비활성화하는 경우.
우리는 소스, 분석된 범위, 품질 또는 모델 버전이 변경될 때만 캐시를 무효화합니다.
function createDepthCacheKey({
assetId,
clipStart,
...
캐시는 정확한 Visuals 또는 Overlay 클립에 바인딩됩니다. 모든 미리보기 및 내보내기 경로는 명시적인 클립 ID를 포함하므로, Overlay 효과가 실수로 메인 트랙을 변경할 수 없습니다.
미리보기와 내보내기는 일치해야 함
AI 편집 기능은 에디터에서는 올바르게 보이지만 내보내기(export) 중에 변하는 경우가 많습니다.
이를 방지하기 위해 미리보기와 내보내기는 다음을 공유합니다:
- 동일한 타임스탬프가 찍힌 깊이 프레임;
- 동일한 클립 식별자 (identity);
- 동일한 초점 거리 및 범위;
- 동일한 렌즈 블러 (lens blur);
- 동일한 보케 (bokeh) 설정;
- 동일한 활성화 상태.
이 기능은 다음 항목들과 통합되어 있습니다:
- 메인 Visuals 트랙;
- Overlay / PIP (picture-in-picture) 클립;
- 실시간 미리보기;
- 프로젝트 저장 및 복구;
- 결정론적 내보내기 (deterministic export);
- 호환성 내보내기 (compatibility export).
우리는 별도의 "미리보기 근사치 (preview approximation)"를 유지하는 대신, 가능한 한 동일한 합성 (composition) 로직을 재사용합니다.
ModelScope 및 Hugging Face 미러 (mirrors)
로컬 추론 (Local inference)을 위해서는 여전히 초기 모델 다운로드가 필요합니다.
다양한 네트워크 환경의 사용자를 지원하기 위해, 런타임은 중국 및 국내 세션의 경우 ModelScope를 우선적으로 사용하고, Hugging Face를 폴백 (fallback)으로 사용할 수 있습니다.
중국 / 국내 세션
↓
ModelScope 시도
...
여기에는 중요한 캐시 (cache) 문제가 있습니다. 동일한 모델이라도 제공자 (provider)마다 URL이 다르다는 점입니다. 만약 URL이 캐시 식별자 (cache identity)가 된다면, 제공자를 전환할 때 동일한 파일을 두 번 다운로드하게 됩니다.
우리는 제공자에 의존하지 않는 캐시 식별자를 사용합니다. 두 미러 (mirrors)에 있는 파일들은 체크섬 (checksum)으로 검증되며, 프로덕션 URL은 가변적인 main 브랜치가 아닌 불변의 리비전 (immutable revisions)에 고정됩니다.
이를 통해 다음과 같은 상황을 방지합니다:
- 제공자 전환 후 중복 다운로드;
- 리포지토리 (repository) 업데이트 후 갑작스러운 호환성 문제;
- 오래된 코드와 새로운 가중치 (weights)의 혼용;
- 재현 불가능한 배포 (deployments).
진행 상황, 취소, 그리고 유용한 에러 메시지
비디오 분석에는 단순한 스피너 (spinner) 이상의 것이 필요합니다.
UI는 다음을 구분하여 표시합니다:
- 모델 준비 (model preparation);
- WebGPU 세션 초기화 (WebGPU session initialization);
- 비디오 프레임 분석 (video-frame analysis);
- 완료된 프레임 수 및 백분율;
- 결과 확정 (result finalization).
취소 기능 또한 실질적으로 작동합니다. 단순히 대화 상자를 숨기는 것이 아니라, AbortController와 워커 (worker) 메시지를 통해 추가적인 디코딩 (decode) 및 추론 (inference) 작업을 중단합니다.
Failed to fetch와 같은 저수준 (low-level) 메시지는 다음과 같이 실행 가능한 현지화된 에러로 변환됩니다:
- WebGPU 사용 불가;
- 네트워크 오류;
- 모델 다운로드 실패;
- 사용자 취소.
10초 분량의 클립은 얼마나 걸리나요?
기기 성능과 무관하게 적용되는 정직한 수치는 존재하지 않습니다.
처리 시간은 다음 요소에 따라 달라집니다:
- GPU 하드웨어;
- 브라우저의 WebGPU 구현 방식;
- 비디오 해상도;
- 시간적 샘플링 밀도 (temporal sampling density);
- 분석 품질;
- 모델 캐시 여부;
- WebGPU 세션의 초기화 여부.
첫 번째 실행에는 다운로드, 초기화 및 분석이 포함됩니다. 이후 실행은 주로 캐시 접근과 분석만 필요로 합니다.
가장 중요한 제품 최적화 요소는 다음과 같습니다:
- 모델을 한 번만 다운로드합니다.
- 초기화된 세션 (session)을 재사용합니다.
- 품질에 따라 비디오를 샘플링합니다.
- 미리보기와 내보내기(export) 간에 깊이 프레임 (depth frames)을 재사용합니다.
- 스타일만 변경되는 경우에는 추론 (inference)을 다시 실행하지 않습니다.
- 실제 진행 상황 (progress)과 취소 (cancellation) 기능을 노출합니다.
시간에 따라 변하는 깊이(time-varying depth)로 또 무엇을 할 수 있을까요?
시네마틱 깊이 (Cinematic Depth)는 이 데이터의 첫 번째 활용 사례일 뿐입니다.
동일한 타임스탬프가 찍힌 깊이 프레임은 다음과 같은 기능을 지원할 수 있습니다:
- 전경/배경 시차 (parallax)를 활용한 2.5D 사진 애니메이션
- 피사체의 앞 또는 뒤에 텍스트와 스티커를 올바르게 배치
- 근거리에서 원거리로의 공간 전환 (spatial transitions)
- 깊이 인식 스마트 프레임 (depth-aware Smart Frame)
- 키프레임 기반의 랙 포커스 (rack focus)
재사용 가능한 시간적 깊이 표현 (temporal depth representation)은 일회성 효과가 아닌 편집의 기본 단위 (editing primitive)가 됩니다.
마치며
브라우저에서 모델을 한 번 실행하는 것은 짧은 데모가 될 수 있습니다. 이를 편집 기능으로 전환하려면 모델 전달, 캐싱 (caching), 시간적 매핑 (temporal mapping), 취소 (cancellation), 반응형 UI, 지속적인 상태 (persistent state), 그리고 내보내기 일관성 (export consistency)이 필요합니다.
Timeline Studio는 사용자가 한 번 분석하고, 반복적으로 조정하며, 저장, 복구 및 내보낼 수 있는 요소로서 Depth Anything V2 Small을 통합합니다.
- GitHub: https://github.com/MartinDelophy/ai-video-editor
- Live demo: https://video-editor.ai-creator.top/
프로젝트가 유용하다고 생각하신다면, GitHub star를 눌러주시면 감사하겠습니다. 이슈 (Issues) 및 구현에 대한 피드백은 언제나 환영합니다.
이 기능은 사용자가 사용할 권한이 있는 미디어를 합법적으로 편집하는 용도로만 의도되었습니다. 불법적, 침해적, 허위, 오도 또는 신원 사칭 콘텐츠에 사용해서는 안 되며, AI로 생성되거나 편집된 결과물을 실제 영상인 것처럼 제시해서는 안 됩니다. 오용에 대한 책임은 사용자에게 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기