Communitygithub.com

johnnyfekete/video-framing

전문 편집자처럼 DaVinci Resolve Studio에서 토크 헤드 컷을 프레이밍합니다. 불필요한 가장자리 클러터를 제거하고, 선택한 세트 드레싱을 유지하며, 점프 컷에 펀치인을 추가하고, 주요 대사에 슬로우 푸시인을 적용합니다. resolve-bridge를 통해 새 타임라인 버전을 생성합니다.

video-framing란 무엇인가요?

이 스킬은 DaVinci Resolve Studio에서 토크 헤드 비디오의 프레이밍을 자동으로 개선하여 전문 편집자 수준의 결과물을 제공합니다. 'video-tighten'과 'video-color' 사이에 실행되며, 각 카메라 설정을 분석하여 깔끔하고 중앙에 맞춰진 프레임으로 크롭합니다. 점프 컷이 보일 때는 시선 위치를 유지하는 펀치인을, 단조로운 설정의 주요 대사에는 슬로우 푸시인을 적용합니다. 이 스킬은 resolve-bridge, ffmpeg, ffprobe, Swift(macOS 기본 제공) 및 Whisper(uv 필요) 또는 RVS_TRANSCRIBE_CMD로 지정된 트랜스크라이버를 필요로 합니다. 주요 기능으로는 프레임 준비(prepare), 결정 사항 적용(solve), 타임라인 적용(apply), 푸시인 키프레임 관리(pushes)가 있습니다. 사용자가 프레임이 지저분하거나, 점프 컷이 눈에 띄거나, 화자가 중앙에 있지 않거나, 펀치인/푸시인/정리 크롭을 요청할 때 유용하며, 스스로 판단하여 작업을 실행하고 결과 보고서를 생성합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/johnnyfekete/resolve-video-skills/tree/HEAD/skills/video-framing

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

video-framing은(는) 무엇을 하나요?

video-tighten 이후(video-tighten이 만든 점프 컷을 숨깁니다), video-color 이전에 실행하세요. resolve-bridge 스킬이 이 스킬 옆에 설치되어 있어야 하며(../resolve-bridge에서 찾거나 RVS_BRIDGE 설정), ffmpeg, ffprobe 및 swift(macOS 기본 제공, Apple Vision이 얼굴과 손을 측정합니다)도 필요합니다. 트랜스크립트는 uv(로컬 Whisper) 또는 RVS_TRANSCRIBE_CMD의 트랜스크라이버 명령이 필요합니다. 트랜스크립션이 실패하면 prepare가 경고와 함께 계속 진행되고 트랜스크립트는 불완전해집니다.

스크립트를 다음 명령으로 실행하세요: python3 <이 스킬의 폴더>/scripts/framing.py <명령어> --workdir <폴더>. 명령어: prepare, solve, apply, pushes.

스크립트는 측정 및 계산을 수행합니다. 당신이 판단을 내립니다 (무엇이 클러터인지, 어떤 설정이 단조로운지, 어떤 대사에 푸시인을 넣을지). 승인을 구하기 위해 멈추지 마세요. 사용자는 이후에 검토합니다. 확실하지 않은 판단에도 최선의 답을 주고 빌드를 계속 진행하세요. decisions.json의 issues 목록에 추가하고(결정 방법 참조), apply가 이를 체크리스트 이슈로 변환합니다. reason 필드는 보고서에만 사용됩니다.

워크플로우

  1. 현재 위치 확인. python3 <resolve-bridge>/scripts/rvs.py --workdir <폴더> status. 아직 추적 중인 항목이 없으면 먼저 타임라인을 적용하세요(resolve-bridge 참조).

  2. 준비(Prepare). Resolve에서 아무것도 변경하지 않습니다:

    python3 scripts/framing.py prepare --workdir <폴더>
    

    Resolve가 열려 있고 추적 중인 타임라인에 아직 기록되지 않은 수동 편집이 있으면, prepare가 먼저 resolve-bridge를 통해 새 버전(manual, "hand edits before framing")으로 기록하고 그 사실을 알립니다. 사용자에게 알리세요. Resolve가 열려 있고 스케일링 모드로 인해 apply가 거부할 경우(타임라인에 미치는 영향 참조), prepare가 시작 시 경고를 출력하므로 사용자가 긴 실행 전에 변경할 수 있습니다.

    카메라 내 아이템을 찾아 카메라 설정별로 그룹화하고, 얼굴, 눈, 몸, 손을 측정합니다(초당 약 4샘플). 다음을 기록합니다:

    • generated/framing/setups/<id>.jpg: 각 설정에 대해 레이블이 있는 10% 그리드(두 축 모두 0.0~1.0)가 포함된 스틸 이미지;
    • .edit/framing/transcript.md: MM:SS.ss-MM:SS.ss <설정> <텍스트>, 타임라인 시간 기준으로 카메라에서 들린 각 문장당 한 줄. 각 카메라 내 조각은 자체 소스 범위 내의 단어만 포함(Whisper의 단어 시간 기준)하므로 조각 간에 반복되지 않으며, 시간은 첫 번째와 마지막 단어의 시간입니다. 이 줄에서 직접 푸시인 범위를 선택하세요;
    • .edit/framing/decisions.json: 모든 설정이 비어 있는 템플릿(재실행 시 아이템이 변경되지 않은 설정의 결정 사항 유지), context.json(예산 및 스틸 포함).

    첫 번째 prepare는 느립니다: 측정하는 데 소스 1초당 약 1.5초가 소요되며, 소스 파일당 Whisper가 한 번 실행됩니다. 백그라운드에서 실행하고 5분 분량의 비디오에서 수십 분이 걸릴 것으로 예상하세요. 아이템별 및 소스별로 진행률 줄을 출력합니다(플러시되므로 리디렉션된 로그에 표시됨). 이후 실행은 캐시되어 빠릅니다. 스킬 업데이트로 측정 방식이 변경되면 다시 측정합니다.

    설정당 한 줄(아이템, 줌 예산, 스틸)과 이미 수동 프레이밍이 적용된 아이템 수를 출력합니다. 이러한 아이템은 그대로 두며, 목록은 context.json 및 REPORT.md에 있습니다.

  3. 결정(Decide). 모든 설정 스틸을 보고 decisions.json을 채웁니다(결정 방법 참조). 그런 다음 푸시인 대사를 선택합니다.

  4. 해결(Solve). Resolve에서 아무것도 변경하지 않고 연결하지 않는 드라이 런입니다. 항상 추적된 상태의 현재 버전 기록된 계획(resolve-bridge를 통해 적용된 타임라인)을 기반으로 작동하므로, 마지막 기록 버전 이후 Resolve에서 이루어진 수동 편집은 포함되지 않습니다. prepare와 apply가 먼저 기록합니다.

    python3 scripts/framing.py solve --workdir <폴더>
    

    generated/framing/REPORT.md, 설정당 setups/<id>_before_after.jpg, 플래그 아이템당 flags/<n>.jpg, .edit/framing/solution.json을 기록합니다. 유효하지 않은 decisions.json(레이블 누락, 박스가 0..1 범위를 벗어남)은 이유와 함께 종료 코드 2로 중지됩니다.

  5. 미리보기를 직접 확인하세요. 모든 before/after 이미지와 플래그 이미지를 여세요. 각 미리보기는 한 프레임(설정의 첫 번째 아이템 또는 플래그 아이템의 중간)만 보여줍니다. 다른 아이템이나 다른 순간의 제스처는 포함되지 않으므로, 설정 미리보기뿐만 아니라 플래그가 지정된 모든 아이템의 미리보기를 확인하고 플래그 텍스트를 읽으세요. 프레임 하단에 손이 낮은 경우(렌즈 쪽으로 뻗거나, 아래쪽 가장자리 근처에서 제스처), 표시된 한 프레임이 괜찮아 보이더라도 "bottom_hands": "gesture"를 고려하세요. 레이아웃: 왼쪽 절반은 전체 소스(녹색은 최종 프레임, 빨간색은 keep-out 박스, 파란색은 keep-in 박스, 푸시인의 종료 프레임은 청록색, 건너뛴 펀치인 또는 드롭된 푸시인에 대한 플래그는 주황색으로 거부된 시도 표시), 오른쪽 절반은 뷰어가 보는 결과물(3분할 포함)입니다. 규칙이 금지하는 사항(프레임 가장자리를 따라 있는 탄젠트, 잘린 손(바닥 가장자리에서 잘리면서 렌즈 쪽으로 뻗거나 제스처하는 경우, 무릎이나 테이블에 resting하지 않음), 헤드가 상단에 너무 가까움, 가장자리에 여전히 클러터가 있음, 선택한 식물이 잘림)을 찾으세요. 프레임이 잘못 보이면 자신의 결정을 수정(박스 이동, 레이블 변경, 푸시인 드롭)하고 다시 해결하세요. 미리보기가 올바르거나 남은 문제가 사람만 판단할 수 있는 문제일 때까지 반복하세요. 보고서의 플래그 및 드롭된 푸시인도 읽으세요. 보고서는 각 플래그의 원시 세부 정보(박스 좌표, 아이템 키)를 그 아래에 제공하며, 체크리스트 이슈는 간단한 일반 텍스트를 얻습니다.

  6. 적용(Apply). 프로젝트가 로드된 Resolve가 필요합니다.

    python3 scripts/framing.py apply --workdir <폴더>
    

    다시 해결한 다음, resolve-bridge를 통해 <base>_vNN_framing을 포크하고(등급, 효과, 수동 편집 유지), 줌, 팬, 틸트를 설정하고(푸시인 클립은 시작 프레임을 가짐), 이전 버전을 보관하고 프로젝트를 저장합니다. 단계가 기록된 후, 모든 플래그와 issues 목록의 각 항목은 클립 이름 및 타임코드로 찾을 수 있는 EDIT_CHECKLIST.md의 이슈가 됩니다. 플래그는 먼저 병합됩니다. 한 설정의 모든 건너뛴 펀치인은 이유가 계산된 하나의 이슈가 되고, 한 설정의 동일한 플래그는 하나가 됩니다. 사용자에게 새 타임라인 이름, 이슈 ID, 보고서 위치를 알리세요. 종료 코드 2는 메시지(타임라인이 추적되지 않음, 프레이밍에 대해 보정되지 않은 스케일링 모드 등)와 함께 예상된 실패를 의미합니다.

  7. Inspector에서 푸시인에 키프레임을 설정합니다. Resolve의 스크립팅 API는 키프레임을 생성할 수 없으므로, apply는 각 푸시인 클립을 시작 프레임에 그대로 두고 .edit/framing/keyframes_todo.json을 기록하며 푸시인당 하나의 체크리스트 이슈를 정확한 값과 함께 추가합니다. 컴퓨터 사용이 가능하다면 지금 수행하고, 그렇지 않으면 사용자에게 이슈가 그들의 몫임을 알리세요(클립당 약 1분 소요). 이후 사용자의 다음 수동 편집 동기화 시 pushes check로 확인합니다.

    python3 scripts/framing.py pushes begin --workdir <폴더>     # <base>_vNN_pushins를 포크하고 푸시인 목록 표시
    python3 scripts/framing.py pushes goto 1 start --workdir <폴더>
    python3 scripts/framing.py pushes finish --workdir <폴더>    # 키프레임 확인, 저장, 이슈 종료
    

    각 푸시인에 대해 Resolve 편집 페이지 Inspector(비디오 > 변환)에서 수행합니다. 백그라운드 앱 제어는 Resolve에 입력할 수 없으므로 전체 화면 제어가 필요합니다:

    1. pushes goto N start. 스크립트가 재생 헤드를 이동하고 Inspector가 따라옵니다. 클립을 클릭하면 재생 헤드가 한 프레임 움직일 수 있으므로 이후 타임라인을 클릭하지 마세요. Inspector가 클립 이름을 표시하는지 확인하세요.
    2. 각 행 오른쪽의 줌 및 위치 키프레임 다이아몬드를 클릭하세요. 빨간색으로 변합니다. 값은 이미 시작 프레임입니다.
    3. pushes goto N end. 줌 X를 두 번 클릭한 다음 모두 선택하고 종료 줌을 입력한 후 Return을 누르세요(줌 Y는 연결된 경우 따라옵니다). 위치 X 및 Y에 대해서도 동일하게 수행하세요. 새 키프레임이 자동으로 나타납니다.
    4. 이징: 각 빨간 다이아몬드를 마우스 오른쪽 버튼으로 클릭하세요. 종료 프레임에서 Ease In을 선택하세요. 시작 프레임에서 종료 프레임이 존재하면 Ease In and Out을 선택하세요. 키프레임 메뉴는 이웃이 허용하는 이징만 제공하며, 체크 표시가 항상 업데이트되지는 않으므로 결과를 확인하세요. 중간 지점에서 줌이 두 값의 평균에 가까워야 하며, 1/4 지점에서는 직선 값보다 낮아야 합니다.
    5. 키프레임이 있는 클립에는 스크립트로 속성을 설정하지 마세요. Resolve가 모든 키프레임을 삭제합니다.

    pushes finish는 키프레임을 다시 읽고(FCPXML 내보내기를 통해) 프레임과 값을 비교합니다. 하나라도 누락되거나 잘못되면 어떤 것인지 알리고 단계를 열어 둡니다. --partial은 저장하고 해당 이슈는 열어 둡니다. pushes check는 저장 없이 동일한 확인을 수행합니다. apply --pushes fusion은 대신 Fusion Transform 푸시인을 만듭니다(완전히 스크립트된 이전 방식).

결정 방법

decisions.json:

{
  "setups": {
    "A": {
      "items": ["..."],
      "label": "plain",
      "reason": "테이블에 앉아 정면을 보고 고정. 오른쪽 가장자리에 자석이 있는 전자레인지, 왼쪽 끝에 여분 의자; 식물은 그대로 둠.",
      "keep_out": [[0.00, 0.45, 0.12, 1.00], [0.86, 0.30, 1.00, 0.80]],
      "keep_in":  [[0.60, 0.20, 0.74, 0.62]]
    },
    "B": {"items": ["..."], "label": "dynamic", "reason": "카운터에 앉아 높은 각도", "keep_out": [], "keep_in": []}
  },
  "reclassify": {"V1@1234": "broll"},
  "push_ins": [
    {"start_s": 41.2, "end_s": 47.0, "text": "Writing is thinking.",
     "reason": "핵심 주제, 처음으로 명확하게 언급됨"}
  ],
  "issues": [
    {"text": "오른쪽 가장자리의 식물이 커피 테이블과 함께 잘림. 클러터인지 확실하지 않음",
     "where": "S04_IMG_1234.MOV at 00:14.00"}
  ]
}

박스는 설정 스틸에 대해 정규화된 0..1의 [left, top, right, bottom]입니다(그리드에서 읽음). start_s와 end_s는 transcript.md의 타임라인 초 단위입니다. items는 prepare에 의해 채워지며, 그대로 두세요.

  • Keep-out 박스: 아무도 보여주기로 선택하지 않은 항목(가장자리 근처). 가전제품, 자석, 여분 의자, 램프, 케이블, 온도 조절기, 문틀, 소파 조각. 솔버는 제스처를 자르지 않는 한도 내에서 이들을 크롭아웃합니다.
  • Keep-in 박스: 화자가 선택한 세트 드레싱. 식물, 꽃, 꽃병. 솔버는 이들을 온전히 유지하거나 명확하게 크롭아웃하며, 조각으로 남기지 않습니다. 중요한 부분(꽃다발, 식물 잎)을 박스로 지정하고 소스 가장자리까지 내려가지 마세요. 소스 가장자리에 닿는 박스는 크롭 내에서 절대 온전히 유지될 수 없습니다.
  • 각 설정에 plain 또는 dynamic 레이블을 지정하세요. plain은 정면, 눈높이, 고정(테이블에 앉음)입니다. dynamic은 높거나 낮은 각도, 비정상적인 위치(카운터에 앉음) 또는 강한 대각선입니다. Dynamic 설정에는 푸시인이 없습니다.
  • Reason: 설정당 한 줄, 항상. 보고서에 포함됩니다.
  • Bottom hands(선택 사항): 설정에 "bottom_hands": "gesture". 기본적으로 소스 하단(높이의 93%부터 아래)에 닿는 손은 resting으로 간주됩니다. 프레임이 그 위에서 멈출 수 있습니다. 렌즈 쪽으로 뻗는 손도 하단 가장자리에 닿으며, 해당 규칙이 손을 자를 수 있습니다. before/after 또는 플래그 미리보기가 손이 바닥 가장자리에서 잘리면서 뻗거나 제스처하는 것을 보여주면 해당 설정에서 이 값을 설정하고 다시 해결하세요. 그러면 해당 손이 완전히 계산되고 프레임이 더 느슨해집니다.
  • Issues: 확실하지 않은 모든 결정(클러터인지 선택된 것인지, 두 항목 중 무엇을 유지할지, 거의 선택한 대사)에 대해 최선의 답을 적용하세요. text는 결정한 내용과 사람이 확인해야 할 사항을 말하며, where는 context.json의 클립 이름 및 타임코드입니다(예: S04_IMG_1234.MOV at 00:14.00). 둘 다 비어 있지 않아야 합니다. apply가 완료되면 체크리스트 이슈가 됩니다.
  • Reclassify는 그룹화를 수정하는 데에만 사용하세요. 스크립트가 카메라 내로 처리했지만 실제로는 B-roll(broll)이거나 그 반대(on_camera)인 항목. 키는 V<트랙>@<시작 프레임>입니다(예: V1@1234): .edit/framing/context.json에서 items의 키이며, 각 항목의 클립 이름 및 타임코드도 제공합니다. 항목과 일치하지 않는 키는 무시되므로 context.json과 확인하세요. 모든 항목이 broll로 재분류된 설정은 레이블이 필요하지 않습니다.
  • Push-in lines는 스크립트와 transcript.md에서 가져옵니다: 핵심 주제, 논쟁의 전환, 펀치라인, 행동 촉구. 채우기나 설정 문장이 아닙니다. 길이는 3~8초로 유지하고 최소 30초 간격을 두고, B-roll이 화자를 덮지 않는 plain 설정에 배치하며, 각각 이유를 작성하세요. 약한 것보다 적은 것이 낫습니다. 솔버는 규칙을 위반하는 것을 드롭하고 보고서에 해당 규칙을 명시합니다(거부된 푸시인을 플래그 미리보기에서 주황색으로 표시). 푸시인은 타임라인 초 단위로 유지되므로, solve는 각각의 text를 현재 타임라인의 트랜스크립트와 확인합니다. 선택한 이후 타임라인이 변경되어 해당 범위에서 대사가 더 이상 언급되지 않으면 푸시인이 드롭되고 재선택을 위한 이슈가 생성됩니다. transcript.md에서 text를 복사하여 확인이 찾을 수 있도록 하세요. 해당 범위에 대한 캐시된 트랜스크립트가 없으면 푸시인이 유지되고 보고서에 확인되지 않았다고 표시됩니다.

프레이밍 규칙

이 규칙은 스크립트가 적용하는 규칙입니다. 이유는 규칙이 다루지 않는 샷을 판단할 수 있도록 여기에 있습니다.

베이스 프레임, 설정당 하나

  • 눈은 위쪽 3분할 선 위 또는 바로 위에, 모든 클립에 대해 작고 동일한 헤드룸. 눈이 위쪽 3분할 근처에 있으면 자연스럽고 집중된 것으로 읽힙니다. 위에 벽이 너무 많으면 아마추어처럼 보입니다. 이마를 자르거나 머리카락에 닿는 프레임은 실수처럼 보입니다. 프레임 하단에 손이 낮아 프레임을 아래로 끌어내리고 눈을 3분할 위로 밀어내면, 프레임이 다시 기울어져 머리 위에 약 10%의 프레임 높이를 유지합니다(얼굴 크기에서 추정). 같은 줌 수준에서 제스처, 클러터 또는 관절이 공간을 남기지 않으면 눈 선만으로 결정합니다.
  • 직접 대화 시 얼굴에 중앙 정렬, 클러터를 피하기 위해 너비의 최대 5%까지 중앙에서 이동. 렌즈를 똑바로 응시하는 것은 대칭을 암시합니다. 중앙에서 벗어난 얼굴은 샷이 명확하게 그렇게 구성되지 않은 한 실수로 읽힙니다.
  • 하단 가장자리는 관절(턱, 목, 팔꿈치, 손목, 허리, 무릎)에 위치하지 않습니다. 관절에 위치하면 화자가 절단된 것처럼 보이므로 관절 위나 아래로 프레임을 조정합니다.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬