Communitygithub.com

MBehtemam/montagent-skills

Montagent에서 실제 비디오 편집: 진행자 영상 및 인터뷰, 크로마키, 자막 및 단어별 캡션, 하단바 및 이름표, 화면 속 화면, 음성 아래 음악 덕킹. 녹화된 영상에서 Montagent 비디오가 시작될 때 사용합니다.

montagent-skills란 무엇인가요?

이 스킬은 Montagent의 비디오 편집 파이프라인에서 녹화된 영상을 기반으로 한 작품을 제작하기 위한 레시피 모음입니다. 주로 `montagent`의 빌드 단계에서 사용되며, 먼저 `montagent`를 로드해야 합니다. 진행자, 프리젠터 클립, 인터뷰 컷과 같은 콘텐츠를 위해 설계되었으며, 각 레시피는 구체적인 JSON 구성을 통해 그린스크린 키, 화자 단어 하이라이트 캡션, 하단바, 원형 화면 속 화면, 음성 아래 음악 덕킹 등 다양한 고급 편집 작업을 구현합니다. 이 스킬은 특정 편집 명령어와 함께 제공되는 Python 스크립트(`scripts/captions.py`, `scripts/duck.py`)를 활용하여 워크플로우를 자동화하며, `montagent-craft`를 통한 시각적 검증 단계를 포함합니다. 이를 통해 편집자는 반복 작업을 최소화하고 일관된 고품질 결과를 얻을 수 있습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MBehtemam/montagent-skills/tree/HEAD/skills/montagent-footage

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

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

문서

녹화된 비디오 편집

녹화된 영상을 기반으로 제작된 작품을 위한 레시피입니다. montagent의 빌드 단계 루프에 통합됩니다. 아직 로드하지 않았다면 먼저 montagent를 로드하세요. 진행자, 프리젠터 클립 또는 인터뷰 컷의 경우, 계획을 세우기 전에 진행자 구조를 여세요.

영상부터 시작하기

요소를 작성하기 전에 모든 클립의 크기, 프레임 속도, 길이 및 사운드 유무를 probe하세요. 그런 다음 음성을 기준으로 작품을 계획하세요. 단어 타이밍은 모든 캡션, 큐 및 덕킹이 기준으로 삼는 시계 역할을 하므로, 먼저 타이밍을 확인해야 합니다 (캡션 참조).

긴 클립(예: 화면 녹화 중 삽입할 구간)에서 특정 순간을 선택하려면, 클립을 새 프로젝트에 단독으로 넣고 frame --from 0 --to <길이> --infill-ceiling <ms>를 호출하세요. 이때 ceiling은 길이의 1/20로 설정합니다. 한 번 호출하면 몇 초마다 레이블이 지정된 타일이 생성됩니다. 원하는 타일에 대해 frame --at을 사용하여 더 가까이 확인할 수 있습니다.

레시피

각 레시피는 정답이 아닌 시작 범위를 제시합니다. 각 레시피는 확인 단계로 끝납니다: montagent-craft를 로드한 후, 단계에서 지시하는 대로 확인하고 진행하세요.

영상 배치

{
  "frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#FF5A36", "duration": 6000, "output": "out/presenter.mp4",
  "tracks": [
    {"name": "presenter", "layer": 10, "elements": [
      {"id": "presenter", "type": "video", "start": 0, "end": 6000, "source": "media/clip.mp4", "source_start": 0, "source_end": 6000, "x": 540, "y": 2160, "origin": "bottom-center", "width": 3413, "height": 1920, "fit": "cover", "effects": [{"name": "chroma", "color": "#00FF00", "tolerance": 0.2, "softness": 0.08, "spill": 0.9}]}
    ]}
  ]
}
  • 세로 프레임의 가로 영상: 요소를 프레임 높이만큼 높게 설정하고, 너비는 원본 종횡비에 따라 설정합니다(예: 16:9의 경우 1920 높이에 3413 너비). 그런 다음 화자를 중앙에 유지하기 위해 옆으로 슬라이드합니다.
  • 눈 위치: 프레임 상단에서 25-35% 아래. 요소의 bottom-center를 프레임 하단 가장자리 아래로 내려서 도달합니다. 다리가 잘리는 것은 정상입니다.
  • 머리 위 공간: 머리카락 위로 프레임 높이의 4-8%.
  • 작품보다 짧은 테이크: "overrun": "hold"는 마지막 프레임을 고정합니다. 고정 시간을 0.5초 미만으로 유지하거나 더 일찍 작품을 종료하세요.
  • 클립 내 프리즈 프레임, 속도 램프 또는 역재생: source_start 및 source_end 대신 source_time을 작성합니다. 리터럴 값은 한 순간을 고정하고, 소스 밀리초를 타임라인 t에 매핑하는 키는 램프를 생성합니다 (평평한 쌍은 고정, 감소하는 쌍은 역재생). 슬로우 모션(speed < 1.0) 또는 속도 램프(source_time) 클립에서 "frame_blend": "mix"를 설정하여 가장 가까운 프레임 사이를 전환하는 대신("none") 연속적인 소스 프레임을 크로스 디졸브합니다. 푸티지에 포스터라이즈 타임(스톱 모션) 효과를 내려면 여러 타임라인 프레임에 걸쳐 각 대상 소스 타임스탬프를 유지하는 계단식 source_time 키프레임을 사용하세요. source_time이 있는 클립은 volume 0에서 무음이므로, 사운드를 별도의 audio 요소에 넣으세요.
  • 테이크 자체 사운드: 음성은 volume 1.0으로 유지됩니다. 다른 모든 것은 그 아래에 믹싱됩니다.
  • 확인: 시작, 중간, 마지막으로 그려진 프레임에서 frame을 실행하고, 눈이 대역 내에 있고 중요한 부분이 프레임 측면에서 잘리지 않았는지 확인하세요.

그린스크린 키

푸티지 요소의 chroma가 스크린을 키 아웃합니다. 여기서부터 시작하세요:

  • color: 스크린의 순수한 색상, 그린 스크린의 경우 #00FF00. 조명이 약간 파란색이나 노란색으로 치우친 스크린도 이러한 공차에서 동일하게 키되므로 샘플링할 필요가 없습니다.
  • tolerance: 0.2 (범위 0.18–0.25). 약 0.3 이상에서는 키가 검은 머리카락, 흰 옷깃, 어두운 옷을 잡아먹습니다.
  • softness: 0.08 (범위 0.05–0.1).
  • spill: 0.9 (범위 0.9–1.0).

다른 요소를 위에 추가하기 전에 확인하세요:

  1. 무손실 크롭 세 개. 머리카락, 어깨 또는 소매 가장자리, 그리고 손에서 배경 위에 대해 frame --crop <x,y,w,h> --png를 실행합니다. 크롭은 실제 크기로 반환되므로 200-400px 상자면 충분합니다.
  2. 이미 완료되었나요? 머리카락, 옷깃, 손이 단단하고 가장자리를 따라 최대 1-2px의 어둡거나 올리브색 또는 황갈색 선만 있는 경우: 키가 완료된 것이므로 다음 단계로 이동하세요. 이 선은 에지 플로어입니다. 0.12에서 0.3 사이의 모든 설정이 이 선을 남기며, spill이 이미 녹색을 황갈색으로 바꾸었으므로 녹색 픽셀 수는 0으로 읽힙니다.
  3. 그렇지 않은 경우, 결함당 하나의 수정. 머리카락, 옷깃 또는 어두운 옷을 통해 배경이 보이는 경우: tolerance를 0.05 낮춥니다. 에지 플로어보다 넓은 창백하거나 녹색의 후광이 있는 경우: tolerance를 0.03 높이거나 softness를 0.03 높입니다. 다시 크롭하고 2단계로 돌아갑니다. 하나의 키가 적용된 요소에 최대 세 가지 설정만 사용하세요. 영역별로 분할된 키 복사본은 렌더링 시간을 배가시키고 여전히 이음새를 남깁니다.
  4. 드리프트. 키가 적용된 요소를 measure합니다. 불투명 비율은 테이크 전체에 걸쳐 1-2포인트 이내로 유지되어야 합니다. 단계가 있다는 것은 그곳에서 조명이 변경되었음을 의미합니다. 단계 양쪽에 키프레임으로 tolerance를 키잉하거나, 해당 프레임에서 요소를 자르고 각 부분을 별도로 키잉하세요.
  • 확인: 화자가 가장 많이 움직이는 두 순간에 전체 그림을 frame하고, 윤곽이 유지되는지 확인하세요.

캡션: 화자 단어 하이라이트

캡션은 제공된 단어 타이밍을 통해 번들 스크립트로 생성되며, 수동으로 만들지 않습니다. python3 scripts/captions.py <project> <words> <spec> (사양은 --help로 확인)은 타이밍을 확인하고, measure로 단어를 한두 줄의 페이지로 나누고, 각 단어가 발화되는 동안 highlight 윈도우를 제공하며, 캡션이 추가된 프로젝트를 출력합니다. 모든 수정 사항을 stderr에 보고합니다. 해당 보고서를 읽으세요: 이것이 타이밍 확인입니다.

  • 확인하는 내용: 음성이 이미 시작된 후에 구를 시작하는 단어(전형적인 예: 110ms 늦고 20ms 긴 "Now"), 침묵 안에 있는 단어, 겹치는 단어, 너무 짧아 보이지 않는 단어. 짧은 단어는 min_word(150ms)로 길어집니다: 뒤의 일시 정지로, 그런 다음 다음 단어에서 가져옵니다. 침묵 안에 있는 것으로 보고된 단어는 해당 부분에서 파일이 잘못 정렬되었음을 의미합니다. 파일을 수정한 후 다시 실행하세요.
  • 크기: 1080 너비 프레임에서 56–72px, 굵게, 줄에는 밝은 색상 하나, 발화 중인 단어에는 브랜드 강조 색상.
  • 줄: 최대 두 줄, width는 프레임 너비의 약 80%. 페이지는 문장 끝이나 400ms 이상의 일시 정지에서 나뉩니다.
  • 배경: 가장 어두운 브랜드 색상의 pill을 80-85% 알파(CC-D9)로 사용하면 모든 영상 위에서 읽을 수 있습니다. 가로 28-36px, 세로 14-20px pad, radius 20-28.
  • 배치: 턱 아래, 세로 프레임 하단 15-20%는 플레이어 컨트롤이 표시되는 영역이므로 피하세요. 1080×1920에서 중심 y는 약 1350-1450입니다. 가로 프레임에서는 하단에서 10-14% 위에 중앙 정렬합니다.
  • 페이지는 다음 페이지가 시작될 때까지 또는 더 긴 일시 정지 후 마지막 단어 이후 1초까지 유지됩니다.

실제 캡션을 작성하므로, validate가 제기하는 캡션 관련 문제에 대해 조치를 취하세요.

  • 확인: 한 페이지에 대해 frame --from --to를 실행합니다: 각 강조된 단어는 자체 타일입니다. 각 타일이 정확히 하나의 단어를 말하는 순서대로 강조하고, 상자가 얼굴에서 떨어져 있는지 확인하세요. 그런 다음 가장 긴 일시 정지 후 첫 번째 프레임에서 frame --at을 실행하고, 새 페이지가 표시되고 아무것도 조기에 강조되지 않았는지 확인하세요.

영상 위 이름표

하단바가 나타났다 사라집니다. 영상은 평평한 색상이 아니므로 montagent-motion의 오클루더 와이프로 막대 자체를 드러낼 수 없습니다. 대신 막대가 가장자리에서 자라나고, 텍스트는 막대 자체 색상(평평함)의 오클루더에 의해 드러납니다.

{
  "frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#6E7B8B", "duration": 7000, "output": "out/namebar.mp4",
  "fonts": {"bold": [{"file": "fonts/Inter-Bold.ttf"}]},
  "fontVendor": {"fonts/Inter-Bold.ttf": {"licence": "OFL-1.1", "source": "https://github.com/rsms/inter", "sha256": "288316099b1e0a47a4716d159098005eef7c0066921f34e3200393dbdb01947f"}},
  "tracks": [
    {"name": "bar", "layer": 20, "elements": [
      {"id": "bar", "type": "rect", "start": 1000, "end": 6534, "x": 64, "y": 1160, "origin": "center-left", "width": 760, "height": 160, "fill": "#101418", "radius": 8, "scale": [{"t": 1000, "v": [0.0, 1.0]}, {"t": 1300, "v": [1.0, 1.0], "ease": [0.65, 0, 0.35, 1]}, {"t": 6266, "v": [1.0, 1.0], "ease": "linear"}, {"t": 6533, "v": [0.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]}
    ]},
    {"name": "bar-edge", "layer": 21, "elements": [
      {"id": "bar-edge", "type": "rect", "start": 1000, "end": 6534, "x": 64, "y": 1160, "origin": "center-left", "width": 12, "height": 160, "fill": "#FF5A36"}
    ]},
    {"name": "bar-name", "layer": 22, "elements": [
      {"id": "bar-name", "type": "text", "start": 1300, "end": 6266, "x": 108, "y": 1128, "origin": "center-left", "width": 400, "height": 70, "font": "bold", "size": 58, "color": "#F5F0E6", "runs": [{"text": "Montagent"}], "caption": false}
    ]},
    {"name": "bar-line", "layer": 22, "elements": [
      {"id": "bar-line", "type": "text", "start": 1300, "end": 6266, "x": 108, "y": 1196, "origin": "center-left", "width": 560, "height": 44, "font": "bold", "size": 34, "color": "#F5F0E6", "runs": [{"text": "비디오: 에이전트가 읽을 수 있음"}], "caption": false}
    ]},
    {"name": "bar-cover", "layer": 23, "elements": [
      {"id": "bar-cover-on", "type": "rect", "start": 1300, "end": 1700, "x": 800, "y": 1160, "origin": "center-right", "width": 716, "height": 140, "fill": "#101418", "scale": [{"t": 1300, "v": [1.0, 1.0]}, {"t": 1666, "v": [0.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]},
      {"id": "bar-cover-off", "type": "rect", "start": 6000, "end": 6266, "x": 84, "y": 1160, "origin": "center-left", "width": 716, "height": 140, "fill": "#101418", "scale": [{"t": 6000, "v": [0.0, 1.0]}, {"t": 6233, "v": [1.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]}
    ]}
  ]
}
  • 표시: 막대가 왼쪽 가장자리에서 250-350ms 동안 자라납니다, [0.65, 0, 0.35, 1]. 텍스트와 그 덮개는 막대가 완전히 자란 프레임부터 시작됩니다 (절대 더 일찍 시작하지 않음. 그렇지 않으면 덮개가 영상 위에 표시됨). 덮개는 먼 쪽 가장자리 주위로 300-400ms 동안 축소되어 텍스트를 읽는 순서대로 드러냅니다.
  • 사라짐, 큐에서 시작: 덮개가 200-300ms 동안 텍스트 위로 다시 자라난 후, 막대가 250-300ms 동안 가장자리로 다시 축소됩니다. 전체 막대는 큐 후 450-600ms 후에 사라집니다.
  • 크기: 1080 너비 프레임에서 막대 높이 130-180px, 이름 크기 52-64px, 두 번째 줄 30-38px. 앞쪽 가장자리에 강조 스트립 8-16px 너비.
  • 덮개: 정확히 막대의 fill과 동일, 막대 끝에서 20px 안쪽, 상단과 하단에서 10px 안쪽, 텍스트 위.
  • 배치: 프레임 가장자리에서 48-80px 안쪽, 캡션 상자와 화자 얼굴에서 떨어지며, 일반적으로 세로 프레임 높이의 55-65%에 위치.
  • 확인: 각 큐에서 1초 동안 frame --from --to --keyframes를 실행하고 어떤 타일에서도 덮개가 막대 너머로 표시되지 않는지 확인하세요. 정지 상태의 막대에서 frame --at을 실행하고 왼쪽 가장자리가 캡션 상자의 여백과 일치하는지 확인하세요.

원형 화면 속 화면

두 번째 클립을 원 안에 표시합니다: 원형 mask가 있는 비디오 요소, 뒤에 링, 오버슈트 베지어 팝.

{
  "frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#6E7B8B", "duration": 6000, "output": "out/pip.mp4",
  "tracks": [
    {"name": "pip-ring", "layer": 30, "elements": [
      {"id": "pip-ring", "type": "ellipse", "start": 1000, "end": 5500, "x": 800, "y": 900, "origin": "center", "width": 384, "height": 384, "fill": "#F5F0E6", "scale": [{"t": 1000, "v": [0.0, 0.0]}, {"t": 1400, "v": [1.0, 1.0], "ease": [0.34, 1.56, 0.64, 1]}, {"t": 5233, "v": [1.0, 1.0], "ease": "linear"}, {"t": 5466, "v": [0.0, 0.0], "ease": "ease-in"}], "effects": [{"name": "shadow", "dx": 0, "dy": 16, "radius": 40, "color": "#101418", "opacity": 0.35}]}
    ]},
    {"name": "pip", "layer": 31, "elements": [
      {"id": "pip", "type": "video", "start": 1000, "end": 5500, "source": "media/clip.mp4", "source_start": 10000, "source_end": 14500, "volume": 0, "x": 800, "y": 900, "origin": "center", "width": 640, "height": 360, "fit": "contain", "scale": [{"t": 1000, "v": [0.0, 0.0]}, {"t": 1400, "v": [1.0, 1.0], "ease": [0.34, 1.56, 0.64, 1]}, {"t": 5233, "v": [1.0, 1.0], "ease": "linear"}, {"t": 5466, "v": [0.0, 0.0], "ease": "ease-in"}], "effects": [{"name": "mask", "shape": "circle", "x": 140, "y": 0, "width": 360, "height": 360}]}
    ]}
  ]
}
  • 원의 사각형: 요소 높이만큼의 정사각형. x는 그림의 어느 부분이 표시될지 선택합니다. 중심은 (너비 - 높이) / 2이며, 이것을 사용해야 합니다. 팝은 요소의 origin을 기준으로 크기가 조정되므로, 중심에서 벗어난 원은 커지면서 옆으로 표류합니다. 그림의 다른 부분을 표시하려면 해당 부분이 중앙에 오는 source_start를 선택하거나, 요소를 더 크게 만들고 해당 부분이 원 안에 들어오도록 x를 설정한 다음, 원의 중심이 고정되도록 팝의 곡선에 x를 키잉하세요.
  • 크기: 프레임 너비의 30-40%. 링은 원보다 12-24px 더 넓으며, 배경의 가장 밝은 색상과 부드러운 shadow를 사용합니다.
  • 팝: 둘 다 [0.34, 1.56, 0.64, 1]로 350-450ms, 스케일 0에서 시작, 동일한 키에서. 종료는 200-300ms, ease-in, 다시 0으로.
  • 타이밍: 화자가 표시하는 것을 처음 언급할 때 나타나고, 종료 직전에 깔끔하게 닫히도록 한 박자 전에 사라집니다.
  • 배치: 화자 옆 가슴 높이, 얼굴과 캡션 상자에서 떨어지고, 프레임 가장자리에서 40px 이상 떨어짐.
  • 사운드: volume 0, 클립 자체 사운드가 요점이 아닌 한.
  • 확인: 팝의 정점(시작 + 길이의 40%)에서 frame을 실행하고 원이 얼굴과 프레임 가장자리를 벗어나는지 확인하세요. 정지 상태에서 frame을 실행하고 중요한 부분이 원 안에 있는지 확인하세요.

음성 아래 음악

음악은 음성이 말하는 동안 덕킹되고 일시 정지 중에 다시 볼륨이 올라갑니다. scripts/duck.py는 음성의 단어 타이밍(--words, .json 파일)에서 음악 요소에 일반적인 volume 키프레임으로 이를 작성합니다.

python3 scripts/duck.py <project> --words <words.json> [--music <stem>] [--mix <stem>]
  • 덕킹 깊이: mix 줄기의 나머지 부분 아래로 음악이 8-14dB 감소합니다.
  • 어택/릴리스: 음악은 음성 시작 50-100ms 전에 감소하기 시작하고, 음성이 멈춘 후 200-400ms 후에 복구됩니다.
  • 지속적인 음악이 아닌 경우: duck.py는 단어 타이밍과 일치하도록 음악 요소의 볼륨 키프레임을 작성하므로, 음악이 음성 아래에서만 재생되도록 하려면 end를 마지막 단어 이후 200ms로 설정하세요.
  • 확인: 음성이 있는 프레임과 없는 프레임에서 frame --at을 실행하고, 믹스가 균형 잡히고 음악이 음성을 압도하지 않는지 확인하세요.

일반적인 문제 해결

타이밍 불일치

단어 타이밍 파일이 편집 내용과 일치하지 않는 경우(예: 텍스트가 변경되었지만 타이밍이 업데이트되지 않음), scripts/captions.py는 불일치를 보고합니다. 보고서에 따라 타이밍 파일을 수정하거나, 가능한 경우 음성 길이에 맞게 편집을 조정하세요.

키 아티팩트

크로마키 후 미세한 녹색 또는 창백한 후광이 남을 수 있습니다. 이를 해결하려면 tolerance 또는 softness를 약간 높이거나 spill 보정을 적용하세요. 지속적인 문제는 잘못된 조명이나 그린 스크린 설정을 나타낼 수 있습니다.

오디오 덕킹 문제

음악이 제대로 덕킹되지 않거나 갑자기 튀는 경우, words.json 파일의 타이밍이 올바른지 확인하세요. 어택 및 릴리스 설정을 조정하거나 덕킹 깊이를 변경하세요. scripts/duck.py를 다시 실행하여 업데이트된 키프레임을 생성하세요.

성능

복잡한 프로젝트(많은 효과, 큰 비디오)는 렌더링이 느릴 수 있습니다. 각 요소의 source_start 및 source_end를 필요한 부분으로 제한하고, 불필요한 효과를 피하며, 미리보기에는 더 낮은 해상도를 사용하세요.

내보내기

최종 비디오를 내보내려면 montagent-render를 사용하세요. 렌더링 설정이 프로젝트 요구 사항(해상도, 코덱, 비트레이트)과 일치하는지 확인하세요. 렌더링 중 오류는 일반적으로 누락된 파일 또는 잘못된 경로로 인해 발생합니다.

추가 리소스

Individual skills in this repo

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

관련 스킬