
브라우저만으로 영화적인 피사계 심도 구현하기: Depth Anything V2 Small × WebGPU
요약
오픈 소스 영상 에디터 Timeline Studio에서 WebGPU와 Depth Anything V2 Small을 활용해 브라우저 기반의 영화적 피사계 심도(Cinematic Depth) 기능을 구현한 사례를 소개합니다. 서버 업로드 없이 브라우저 내에서 심도 추정부터 영상 편집까지 완결되는 설계를 다룹니다.
핵심 포인트
- WebGPU와 @huggingface/transformers를 이용한 브라우저 내 실시간 추론 구현
- 단순 세그멘테이션을 넘어 연속적인 심도 추정을 통한 정교한 배경 흐림 처리
- 모델 재사용을 위한 Worker 및 WebGPU 세션 유지 설계
- 심도 분석과 외관 조정을 분리하여 사용자 인터랙션 성능 최적화
브라우저 상에서 동작하는 오픈 소스 영상 에디터 Timeline Studio에, Depth Anything V2 Small을 사용한 「Cinematic Depth(영화적인 피사계 심도)」를 구현했습니다.
소재가 되는 이미지나 영상을 서버로 업로드하지 않고, 심도 추정(Depth Estimation)·미리보기·내보내기까지 브라우저 내에서 완결되도록 했습니다.
- GitHub: MartinDelophy/ai-video-editor
- 데모: Timeline Studio
이 기사에서는 단순히 모델을 구동하는 것뿐만 아니라, 영상 편집 기능으로서 성립시키기 위해 필요했던 설계를 정리합니다.
일반적인 배경 흐림(Background Blur)은 인물 세그멘테이션(Segmentation)을 사용하여 다음과 같이 처리합니다.
인물 영역 → 샤프(Sharp)
인물 이외의 영역 → 일괄적으로 흐리게 처리
비디오 회의에는 유효하지만, 실제 카메라의 피사계 심도(Depth of Field)와는 다릅니다.
화면 안에는 렌즈에 가까운 잎, 중앙의 인물, 그 뒤의 가구, 더 멀리 있는 건물 등 여러 개의 깊이(Depth)가 존재합니다. 인물 마스크를 통해 알 수 있는 것은 「사람인가, 그 외인가」뿐입니다.
Depth Anything V2 Small은 화면 전체의 연속적인 상대적 심도를 추정합니다. 이를 통해 전경(Foreground)·중경(Midground)·배경(Background)을 서로 다른 강도로 흐리게 처리할 수 있으며, 포커스 위치를 이동할 수 있게 됩니다.
브라우저 추론(Inference)에서는 정확도뿐만 아니라 다음 조건들도 중요합니다.
- 최초 다운로드 크기
- GPU 메모리와 메인 메모리
- WebGPU 대응 상황
- 초기화 시간
- 영상의 여러 프레임을 처리하는 비용
- 모바일 단말에서의 부하
- 캐시와 모델 업데이트 관리
그렇기 때문에, 이번에는 Depth Anything V2 Small의 Q4F16 구성을 채택했습니다.
실행에는 프로젝트에서 이미 이용하고 있던 @huggingface/transformers를 사용하며, 추론 디바이스(Inference Device)로 WebGPU를 지정하고 있습니다.
개념적인 초기화 코드는 다음과 같습니다.
import { pipeline } from "@huggingface/transformers";
let depthEstimator;
export async function getDepthEstimator(
...
중요한 것은 생성할 때마다 Pipeline을 다시 만들지 않는 것입니다. 초기화된 Worker와 WebGPU 세션을 유지하여, 2회차 이후의 분석에서 재사용합니다.
Cinematic Depth는 Effects 워크스페이스의 5번째 카드로 추가했습니다.
카드에는 다음과 같은 동작이 있습니다.
- 평상시에는 가공되지 않은 동일한 샷을 표시
- hover/focus 시 피사계 심도 버전으로 전환
- hover가 없는 터치 단말에서는 자동 미리보기
- 데스크톱에서는 전용 Effects 인스펙터(Inspector)를 열기
- 모바일에서는 단일 목적의 프로퍼티 드로어(Property Drawer)를 열기
사용자가 조정할 수 있는 파라미터는 다음과 같습니다.
- 포커스 거리
- 초점 범위
- 렌즈 흐림(Lens Blur)
- 보케 하이라이트(Bokeh Highlight)
- Fast/Standard/Fine의 분석 품질
- 활성화, 취소, 리셋
심도 분석과 외관 조정을 분리했기 때문에, 포커스 거리나 흐림 양을 변경해도 모델 추론은 재실행되지 않습니다.
각 픽셀의 심도와 포커스 위치의 차이로부터 흐림의 강도를 구합니다.
export function calculateBlurAmount({
depth,
focusDistance,
...
포커스 범위 내는 샤프하게 유지하고, 범위에서 멀어질수록 흐림을 강하게 합니다.
Canvas에는 「심도 값에 따라 별도의 흐림 반경을 적용하는」 단일 API가 없습니다. 따라서 구현에서는 여러 개의 흐림 레벨을 만든 뒤, 심도 마스크(Depth Mask)에 따라 합성합니다.
원본
├─ 가벼운 흐림
├─ 중간 정도의 흐림
...
심도 경계의 헤일로(Halo) 현상을 억제하기 위해, 마스크에는 평활화(Smoothing)와 페더(Feather) 처리도 가합니다.
정지 영상은 1회의 추론으로 완료됩니다. 하지만 영상에서는 인물도 카메라도 움직입니다.
첫 프레임만을 분석하면 몇 초 후에는 영상과 심도가 어긋나게 됩니다. 반면, 원본 영상의 모든 프레임에 심도 추론을 실행하면 브라우저에 부하가 너무 높습니다.
그래서 품질 설정에 따른 시간 샘플링(Time Sampling)을 채택했습니다.
영상 디코딩
↓
시간축에서 프레임을 샘플링
...
저장하는 데이터는 개념적으로 다음과 같은 형태입니다.
const depthFrames = [
{
time: 0.0, depth: depth0 },
{
time: 0.5, depth: depth1 },
...
재생 시점에 가장 가까운 심도 프레임을 선택하고, 필요에 따라 전후 결과를 보간(Interpolation)합니다.
심도 분석은 비용이 많이 들지만, 피사계 심도 (DOF) 파라미터 합성(Composition)은 비교적 가볍습니다.
따라서 다음의 변경 사항이 발생하더라도 분석 결과를 폐기하지 않습니다.
- 초점 거리 (Focus Distance) 변경
- 초점 범위 (Focus Range) 변경
- 블러(Blur) 양 변경
- 보케 하이라이트 (Bokeh Highlight) 변경
- 이펙트의 일시적인 ON/OFF
캐시를 무효화하는 경우는 소재, 분석 범위, 품질, 모델 리비전(Revision)이 변경되었을 때입니다.
function createDepthCacheKey({
assetId,
clipStart,
...
캐시는 선택 중인 Visuals 또는 Overlay 클립에 종속됩니다. Overlay의 피사계 심도 설정이 메인 트랙에 잘못 적용되지 않도록, 모든 처리에서 클립 ID를 명시적으로 다룹니다.
영상 편집 기능에서 중요한 것은 "에디터에서는 올바른데, 내보내기(Export)를 하면 결과가 다른" 상태를 피하는 것입니다.
이번 구현에서는 프리뷰와 익스포트에서 다음 정보를 공유합니다.
- 동일한 심도 프레임
- 동일한 클립 ID
- 동일한 초점 거리
- 동일한 초점 범위
- 동일한 렌즈 블러
- 동일한 보케 설정
- 동일한 활성화/비활성화 상태
현재는 다음 경로에 통합되어 있습니다.
- 메인 Visuals 트랙
- Overlay / Picture-in-Picture
- 실시간 프리뷰
- 프로젝트 저장 및 복원
- Deterministic Export
- Compatibility Export
프리뷰 전용의 근사 처리(Approximation)를 별도로 두지 않고, 가능한 한 동일한 합성 로직을 재사용하는 것이 핵심입니다.
브라우저 로컬 AI에서도 모델 자체는 처음에 다운로드해야 합니다.
중국 내륙을 포함한 네트워크 환경에 대응하기 위해, 모델 배포는 다음 순서로 전환합니다.
중국어 / 중국 내륙 환경
↓
ModelScope를 우선순위로 설정
...
단, 배포 URL을 그대로 캐시 키(Cache Key)로 사용하면, 동일한 모델을 서로 다른 소스로부터 중복 다운로드하게 됩니다.
따라서 프로바이더(Provider)에 의존하지 않는 공통 캐시 ID를 사용합니다. 두 미러(Mirror) 서버 모두 파일의 체크섬(Checksum)을 확인하며, 프로덕션 환경에서는 가변적인 main이 아니라 불변 리비전(Immutable Revision)으로 고정합니다.
이를 통해 다음과 같은 문제를 방지할 수 있습니다.
- 모델 업데이트로 인한 갑작스러운 비호환성
- 프로바이더 전환 시의 재다운로드
- 오래된 코드와 새로운 가중치(Weights)의 혼재
- 장애 재현이 불가능한 상태
영상 분석에서는 단일 로딩 표시만으로는 불충분합니다.
UI에서는 다음 단계를 구분합니다.
- 모델 준비
- WebGPU 세션 초기화
- 영상 프레임 심도 분석
- 완료된 프레임 수 및 비율
- 결과 확정
취소 시에는 다이얼로그를 닫는 것에 그치지 않고, AbortController와 Worker 메시지를 사용하여 후속 디코딩과 추론(Inference)을 중단합니다.
또한, Failed to fetch와 같은 저수준(Low-level) 에러를 그대로 표시하지 않고, WebGPU 미지원, 네트워크 에러, 모델 획득 실패, 사용자 취소 등으로 구분하여 로컬라이즈(Localization)합니다.
처리 시간은 다음 요소에 따라 크게 달라집니다.
- GPU
- 브라우저의 WebGPU 구현
- 영상 해상도
- 샘플링 밀도
- 품질 설정
- 모델의 캐시 여부
- WebGPU 세션 초기화 여부
첫 실행 시에는 "모델 다운로드 + 초기화 + 분석"이 이루어지며, 두 번째 실행부터는 주로 "캐시 로드 + 분석"이 진행됩니다.
따라서 특정 개발 장비에서 측정한 단일 수치를 모든 사용자를 위한 성능 지표로 제시하지는 않습니다.
제품으로서 중점을 둔 것은 모델을 단 한 번만 가져오는 것, 세션을 재사용하는 것, 파라미터 변경 시 재추론하지 않는 것, 품질을 선택할 수 있는 것, 그리고 긴 분석 과정을 실제로 취소할 수 있다는 점입니다.
시계열 심도 데이터는 피사계 심도 외에도 재사용할 수 있습니다.
- 전경·인물·배경을 분리한 2.5D 사진 애니메이션
- 인물의 앞뒤에 정확히 배치되는 텍스트나 스티커
- 근경에서 원경으로 진행되는 공간 트랜지션(Transition)
- 깊이감을 고려한 Smart Frame
- 초점 거리를 키프레임화한 랙 포커스(Rack Focus)
Cinematic Depth는 이러한 기능들의 기반이 됩니다.
모델을 브라우저에서 한 번 실행하는 것만이라면 비교적 짧은 코드로 구현할 수 있습니다. 하지만 실제 영상 편집 기능으로 만들기 위해서는 모델 배포(Model Delivery), 캐시(Cache), 영상 타임라인(Video Timeline), 취소(Cancel), 모바일 UI, 상태 저장(State Preservation), 그리고 내보내기(Export)까지 연결해야 합니다.
Timeline Studio에서는 Depth Anything V2 Small을 단순한 깊이 맵(Depth Map) 데모가 아니라, 조정·저장·재사용·내보내기가 가능한 편집 기능으로 통합했습니다.
도움이 되었다면 GitHub에서 Star를 눌러주시면 큰 힘이 됩니다. Issue나 개선 제안도 환영합니다.
본 기능은 권리를 보유한 이미지·영상의 합법적인 편집을 목적으로 합니다. 불법, 권리 침해, 허위·오인 유도 콘텐츠, 사칭 이용, 그리고 AI 생성·가공 영상을 실사로 제시하는 행위는 금지합니다. 이용에 따른 책임은 이용자 본인에게 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기