Communitygithub.com

johnnyfekete/video-framing

Rahmt einen Talking-Head-Schnitt in DaVinci Resolve Studio wie ein professioneller Editor. Beschneidet jede Kameraeinstellung auf einen sauberen, zentrierten Bildausschnitt, entfernt unnötige Randobjekte und fügt Punch-Ins, Push-Ins sowie eine neue Timeline-Version hinzu.

Was ist video-framing?

Diese Fähigkeit für KI-Agenten automatisiert das professionelle Einrahmen von Talking-Head-Aufnahmen in DaVinci Resolve Studio. Sie analysiert jede Kameraeinstellung, identifiziert Gesichter, Augen, Hände und Gesten und erstellt saubere, zentrierte Bildausschnitte. Dabei werden störende Elemente wie Magnete, Lampen oder Stühle am Rand ausgeblendet, während gewünschte Requisiten wie Pflanzen erhalten bleiben. Die Funktion erkennt Sprungschnitte und fügt automatisch Punch-Ins hinzu, um sie zu überbrücken, sowie langsame Push-Ins bei wichtigen Aussagen. Der gesamte Ablauf erfolgt eigenständig: Der Agent bereitet die Daten vor, der Benutzer trifft die kreativen Entscheidungen, und die Software wendet sie in Resolve an. Unterstützt wird dies durch die resolve-bridge, ffmpeg, ffprobe und Swift (für Apple Vision) sowie Whisper für die Transkription. Das Ergebnis ist eine neue Timeline-Version mit einem detaillierten Bericht, der zur Überprüfung dient.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/johnnyfekete/resolve-video-skills/tree/HEAD/skills/video-framing

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht video-framing?

Führe dies nach video-tighten aus (es blendet die Sprungschnitte aus, die tighten erzeugt) und vor video-color. Erfordert die resolve-bridge-Fähigkeit, die neben dieser installiert ist (sie findet sie unter ../resolve-bridge, oder setze RVS_BRIDGE), sowie ffmpeg, ffprobe und swift (macOS bringt es mit; Apple Vision misst Gesichter und Hände). Das Transkript benötigt uv (lokal Whisper) oder einen Transkriptionsbefehl in RVS_TRANSCRIBE_CMD; falls die Transkription fehlschlägt, fährt prepare mit einer Warnung fort und das Transkript ist unvollständig.

Führe Skripte aus als python3 <Ordner dieser Fähigkeit>/scripts/framing.py <Befehl> --workdir <Ordner>. Befehle: prepare, solve, apply, pushes.

Die Skripte messen und rechnen. Du triffst die Entscheidungen (was ist Unordnung, welche Einstellungen sind schlicht, welche Zeilen erhalten einen Push-In). Frage niemals um Erlaubnis. Der Benutzer überprüft später. Bei einem unsicheren Aufruf gibst du trotzdem deine beste Antwort und der Build läuft weiter; füge es zur issues-Liste in decisions.json hinzu (siehe Wie man entscheidet), und apply macht daraus einen Checklist-Eintrag. Die reason-Felder erscheinen nur im Bericht.

Arbeitsablauf

  1. Wo sind wir. python3 <resolve-bridge>/scripts/rvs.py --workdir <Ordner> status. Übernimm zuerst die Timeline, falls noch nichts verfolgt wird (siehe resolve-bridge).

  2. Vorbereiten. Ändert nichts in Resolve:

    python3 scripts/framing.py prepare --workdir <Ordner>
    

    Falls Resolve geöffnet ist und die verfolgte Timeline manuelle Bearbeitungen enthält, die noch nicht aufgezeichnet wurden, zeichnet prepare sie zuerst als neue Version auf (manual, „Hand-Bearbeitungen vor dem Einrahmen“) über resolve-bridge und gibt dies bekannt. Informiere den Benutzer. Falls Resolve geöffnet ist und sein Skalierungsmodus dazu führen würde, dass apply sich weigert (siehe Was es mit der Timeline macht), gibt prepare eine Warnung zu Beginn aus, damit der Benutzer es vor dem langen Lauf ändern kann.

    Findet die On-Camera-Elemente, gruppiert sie in Kameraeinstellungen, misst Gesicht, Augen, Körper und Hände (etwa 4 Samples pro Sekunde) und schreibt:

    • generated/framing/setups/<id>.jpg: ein Standbild pro Einstellung mit einem beschrifteten 10%-Raster (0.0 bis 1.0 auf beiden Achsen);
    • .edit/framing/transcript.md: MM:SS.ss-MM:SS.ss <Einstellung> <Text>, eine Zeile pro Satz, der auf Kamera gehört wird, in Timeline-Zeit. Jedes On-Camera-Stück enthält nur die Wörter innerhalb seines eigenen Quellbereichs (Wortzeiten von Whisper), sodass nichts über Stücke hinweg wiederholt wird, und die Zeiten sind die des ersten und letzten Wortes. Wähle Push-In-Bereiche direkt aus diesen Zeilen;
    • .edit/framing/decisions.json: eine Vorlage mit jeder leeren Einstellung (eine erneute Ausführung behält Entscheidungen für Einstellungen, deren Elemente sich nicht geändert haben), context.json mit Budgets und Standbildern.

    Das erste prepare ist langsam: Das Messen dauert etwa 1,5 s pro Sekunde Quelle, und Whisper läuft einmal pro Quelldatei. Führe es im Hintergrund aus und rechne mit Dutzenden Minuten bei einem 5-minütigen Video. Es gibt eine Fortschrittszeile pro Element und pro Quelle aus (geflusht, sodass ein umgeleitetes Log es zeigt). Spätere Läufe werden gecached und sind schnell; ein Update der Fähigkeit, das die Messmethode ändert, misst einmal erneut.

    Es gibt eine Zeile pro Einstellung aus (Elemente, Zoom-Budget, Standbild) und die Anzahl der Elemente, die bereits manuelles Einrahmen tragen: diese werden exakt so belassen, wie sie sind, und die Liste findet sich in context.json und REPORT.md.

  3. Entscheiden. Betrachte jedes Einstellungs-Standbild und fülle decisions.json aus (siehe Wie man entscheidet). Wähle dann die Push-In-Zeilen aus.

  4. Lösen. Ein Probelauf, der nichts in Resolve ändert und sich nie damit verbindet. Er arbeitet immer vom aufgezeichneten Plan der aktuellen Version im verfolgten Zustand (eine Timeline, die über resolve-bridge übernommen wurde), sodass manuelle Bearbeitungen, die in Resolve seit der letzten aufgezeichneten Version vorgenommen wurden, nicht enthalten sind: prepare und apply zeichnen sie zuerst auf.

    python3 scripts/framing.py solve --workdir <Ordner>
    

    Schreibt generated/framing/REPORT.md, setups/<id>_before_after.jpg pro Einstellung und flags/<n>.jpg pro gekennzeichnetem Element sowie .edit/framing/solution.json. Eine ungültige decisions.json (fehlendes Label, Box außerhalb 0..1) stoppt mit Grund und Exit-Code 2.

  5. Überprüfe die Vorschauen selbst. Öffne jedes Vorher/Nachher-Bild und die Kennzeichnungsbilder. Jede Vorschau zeigt einen Frame: das erste Element der Einstellung oder das gekennzeichnete Element, in seiner Mitte. Eine Geste in einem anderen Element oder zu einem anderen Zeitpunkt ist nicht enthalten, überprüfe daher jedes gekennzeichnete Element-Vorschaubild, nicht nur die der Einstellung, und lies den Kennzeichnungstext. Bei einer Hand, die tief im Bild ist (in Richtung der Linse reichend, in der Nähe des unteren Randes gestikulierend), ziehe "bottom_hands": "gesture" in Betracht, selbst wenn der eine gezeigte Frame gut aussieht. Layout: die linke Hälfte ist die gesamte Quelle mit dem ausgelieferten Frame in Grün, Keep-Out-Boxen in Rot und Keep-In-Boxen in Blau (ein Push-In-Endframe in Cyan; bei einer Kennzeichnung zu einem übersprungenen Punch-In oder einem fallengelassenen Push-In der abgelehnte Versuch in Orange); die rechte Hälfte ist das Ergebnis, wie der Betrachter es sieht, mit Dritteln. Achte auf die Dinge, die die Regeln verbieten: eine Tangente entlang der Rahmenkante, eine abgeschnittene Hand (wenn eine Hand am unteren Rand abgeschnitten wird, während sie in Richtung der Linse reicht oder gestikuliert, nicht auf den Knien oder einem Tisch ruhend, setze "bottom_hands": "gesture" für diese Einstellung und löse erneut), ein Kopf zu nah am oberen Rand, Unordnung noch an einer Kante, eine ausgewählte Pflanze abgeschnitten. Wenn ein Bild falsch aussieht, korrigiere deine eigenen Entscheidungen (verschiebe eine Box, ändere ein Label, lösche einen Push-In) und löse erneut. Wiederhole dies, bis die Vorschauen richtig sind oder die verbleibenden Probleme nur ein Mensch beurteilen kann. Lies auch die Kennzeichnungen und fallengelassenen Push-Ins im Bericht: Der Bericht gibt unter jeder Kennzeichnung deren rohe Details an (Box-Koordinaten, Elementschlüssel); die Checklist-Einträge erhalten den kurzen Klartext.

  6. Anwenden. Benötigt geöffnetes Resolve mit geladenem Projekt.

    python3 scripts/framing.py apply --workdir <Ordner>
    

    Löst erneut, gabelt dann <Basis>_vNN_framing über resolve-bridge (Gradings, Effekte und manuelle Bearbeitungen bleiben), setzt Zoom, Pan und Tilt (ein Push-In-Clip erhält seinen Startframe), archiviert die vorherige Version und speichert das Projekt. Nachdem der Schritt aufgezeichnet wurde, wird jede Kennzeichnung und jeder Eintrag deiner issues-Liste zu einem Eintrag in EDIT_CHECKLIST.md, lokalisiert nach Clipname und Timecode. Kennzeichnungen werden zuerst zusammengeführt: alle übersprungenen Punch-Ins einer Einstellung werden zu einem Eintrag mit ihren Gründen zusammengezählt, und identische Kennzeichnungen auf einer Einstellung werden zu einer zusammengeführt. Informiere den Benutzer über den neuen Timelinenamen, die Issue-IDs und wo der Bericht liegt. Exit-Code 2 bedeutet einen erwarteten Fehler mit einer Nachricht (keine Timeline verfolgt, ein Skalierungsmodus, für den das Einrahmen nicht kalibriert ist, usw.).

  7. Keyframes für die Push-Ins im Inspector setzen. Die Scripting-API von Resolve kann keine Keyframes erstellen, daher belässt apply jeden Push-In-Clip auf seinem Startframe, schreibt .edit/framing/keyframes_todo.json und fügt einen Checklist-Eintrag pro Push-In mit den genauen Werten hinzu. Falls du Computerbenutzung hast, erledige sie jetzt; andernfalls teile dem Benutzer mit, dass die Issues bei ihm liegen (etwa eine Minute pro Clip), und sie werden durch pushes check nach der nächsten Synchronisierung manueller Bearbeitungen des Benutzers abgehakt.

    python3 scripts/framing.py pushes begin --workdir <Ordner>     # gabelt <Basis>_vNN_pushins, listet die Push-Ins auf
    python3 scripts/framing.py pushes goto 1 start --workdir <Ordner>
    python3 scripts/framing.py pushes finish --workdir <Ordner>    # überprüft die Keyframes, speichert, schließt die Issues
    

    Für jeden Push-In im Resolve Edit-Seite-Inspector (Video > Transform). Hintergrund-App-Steuerung kann nicht in Resolve tippen, daher wird Vollbildsteuerung benötigt:

    1. pushes goto N start. Das Skript bewegt den Abspielkopf, und der Inspector folgt ihm. Ein Klick auf einen Clip kann den Abspielkopf um einen Frame verschieben, klicke daher nicht danach auf die Timeline. Überprüfe, dass der Inspector den Clip benennt.
    2. Klicke die Keyframe-Diamanten von Zoom und Position rechts neben jeder Zeile. Sie werden rot. Die Werte sind bereits die des Startframes.
    3. pushes goto N end. Doppelklicke Zoom X, wähle dann alles aus, gib den End-Zoom ein und drücke die Eingabetaste (Zoom Y folgt, wenn verknüpft). Mache dasselbe für Position X und Y. Neue Keyframes erscheinen automatisch.
    4. Easing: Rechtsklicke auf jeden roten Diamanten. Wähle am Ende Ease In. Am Start, sobald das Ende existiert, wähle Ease In and Out. Das Menü eines Keyframes bietet nur die Easings an, die seine Nachbarn zulassen, und der Haken wird nicht immer aktualisiert. Überprüfe daher das Ergebnis: auf halbem Weg sollte Zoom nahe am Durchschnitt der beiden Werte sein, und bei einem Viertel sollte er unter dem linearen Wert liegen.
    5. Setze niemals eine Eigenschaft per Skript auf einem Clip mit Keyframes: Resolve löscht dann alle seine Keyframes.

    pushes finish liest die Keyframes zurück (über einen FCPXML-Export) und vergleicht Frames und Werte. Falls einer fehlt oder falsch ist, gibt es an, welcher, und lässt den Schritt offen. --partial speichert trotzdem und lässt diese Issues offen. pushes check führt denselben Check ohne Speichern durch. apply --pushes fusion erstellt stattdessen Fusion-Transform-Push-Ins (der alte Weg, vollständig skriptgesteuert).

Wie man entscheidet

decisions.json:

{
  "setups": {
    "A": {
      "items": ["..."],
      "label": "plain",
      "reason": "sitzend am Tisch, frontal, statisch. Mikrowelle mit Magneten am rechten Rand, Ersatzstuhl ganz links; die Pflanze bleibt.",
      "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": "auf der Theke sitzend, hoher Winkel", "keep_out": [], "keep_in": []}
  },
  "reclassify": {"V1@1234": "broll"},
  "push_ins": [
    {"start_s": 41.2, "end_s": 47.0, "text": "Writing is thinking.",
     "reason": "die These, zum ersten Mal schlicht ausgesprochen"}
  ],
  "issues": [
    {"text": "Pflanze am rechten Rand beschnitten mit den Couchtischen; unsicher, ob sie Unordnung war",
     "where": "S04_IMG_1234.MOV at 00:14.00"}
  ]
}

Boxen sind [links, oben, rechts, unten], normalisiert 0..1 auf dem Einstellungs-Standbild (lies sie von dessen Raster ab). start_s und end_s sind Timeline-Sekunden aus transcript.md. items wird von prepare ausgefüllt; belasse es.

  • Keep-Out-Boxen: alles, was niemand zeigen wollte, nahe den Rändern. Geräte, Magnete, Ersatzstühle, Lampen, Kabel, Thermostate, Türrahmen, ein Sofa-Eck. Der Löser beschneidet sie heraus, wo es möglich ist, ohne eine Geste abzuschneiden.
  • Keep-In-Boxen: von der sprechenden Person ausgewähltes Set-Dekor. Pflanzen, Blumen, eine Vase. Der Löser behält sie ganz oder schneidet sie deutlich heraus, niemals nur einen Streifen. Boxe den Teil, der wichtig ist (den Strauß, die Blätter der Pflanze), nicht bis zum Quellenrand: eine Box, die den Quellenrand berührt, kann niemals ganz innerhalb eines Bildausschnitts erhalten bleiben.
  • Label jede Einstellung als plain oder dynamic. plain ist frontal, auf Augenhöhe und statisch (sitzend an einem Tisch). dynamic ist ein hoher oder niedriger Winkel, eine ungewöhnliche Position (auf einer Theke sitzend) oder eine starke Diagonale. Dynamische Einstellungen erhalten keine Push-Ins.
  • Reason: eine Zeile pro Einstellung, immer. Sie geht in den Bericht.
  • Bottom hands (optional): "bottom_hands": "gesture" auf einer Einstellung. Standardmäßig zählen Hände, die den unteren Rand der Quelle berühren (von 93% ihrer Höhe abwärts), als ruhend: der Rahmen darf oberhalb von ihnen enden. Hände, die sich zur Linse hin ausstrecken, berühren ebenfalls den unteren Rand, und diese Regel würde sie abschneiden. Wenn eine Vorher/Nachher- oder Kennzeichnungsvorschau eine Hand zeigt, die am unteren Rand abgeschnitten ist, während sie sich ausstreckt oder gestikuliert, setze dies für diese Einstellung und löse erneut: diese Hände zählen dann vollständig, und der Rahmen ist weiter.
  • Issues: jeder unsichere Aufruf (Unordnung oder ausgewählt, welche von zwei Dingen behalten werden, eine Zeile, die du fast gewählt hättest), mit deiner besten Antwort angewendet. text sagt, was du entschieden hast und was eine Person überprüfen soll; where ist der Clip-Name und Timecode (aus context.json), zum Beispiel S04_IMG_1234.MOV at 00:14.00. Beide müssen nicht leer sein. Sie werden nach Abschluss von apply zu Checklist-Einträgen.
  • Reclassify nur zur Korrektur der Gruppierung: ein Element, das das Skript als On-Camera behandelt hat, aber eigentlich B-Roll ist (broll), oder umgekehrt (on_camera). Der Schlüssel ist V<Spur>@<Startframe> (z.B. V1@1234): die Schlüssel von items in .edit/framing/context.json, die auch Clip-Name und Timecode jedes Elements angeben. Ein Schlüssel, der auf kein Element passt, wird ignoriert, überprüfe ihn daher mit context.json. Eine Einstellung, deren Elemente alle zu broll neu klassifiziert wurden, benötigt kein Label.
  • Push-In-Zeilen stammen aus dem Skript und transcript.md: die These, eine Wendung im Argument, ein Höhepunkt, ein Aufruf zum Handeln. Keine Füllwörter, keine Einleitungssätze. Halte sie 3 bis 8 Sekunden lang und mindestens 30 Sekunden auseinander, platziere sie auf einfachen Einstellungen, wo kein B-Roll die sprechende Person verdeckt, und schreibe einen Grund für jede. Weniger ist besser als schwache. Der Löser verwirft alle, die die Regeln brechen, und gibt im Bericht an, welche Regel (und zeigt den abgelehnten Push-In in Orange auf seiner Kennzeichnungsvorschau an). Push-Ins werden in Timeline-Sekunden gehalten, daher überprüft solve den text jedes Push-Ins mit dem Transkript der aktuellen Timeline: wenn die Zeile nicht mehr in ihrem Bereich vorkommt (die Timeline hat sich seit deiner Auswahl geändert), wird der Push-In mit einem Issue fallen gelassen, um ihn neu auszuwählen. Kopiere text aus transcript.md, damit die Überprüfung ihn finden kann. Ohne ein gecachtes Transkript für den Bereich wird der Push-In beibehalten und der Bericht sagt, dass er nicht überprüft wurde.

Die Einrahmungsregeln

Diese werden von den Skripten durchgesetzt. Die Gründe sind hier angegeben, damit du Aufnahmen beurteilen kannst, die die Regeln nicht abdecken.

Basisrahmen, einer pro Einstellung

  • Augen auf oder knapp über der oberen Drittellinie, ein kleiner und gleicher Kopfraum für jeden Clip. Augen nahe der oberen Drittellinie wirken natürlich und engagiert; zu viel Wand darüber sieht amateurhaft aus. Ein Rahmen, der die Stirn abschneidet oder das Haar berührt, wirkt wie ein Fehler. Wenn tief im Rahmen befindliche Hände den Rahmen nach unten ziehen und die Augen über das Drittel drücken, kippt der Rahmen zurück, um etwa 10% der Rahmenhöhe über dem Kopf zu lassen (geschätzt aus Gesichtsgröße), bei gleichem Zoom; wo Gesten, Unordnung oder ein Gelenk keinen Raum lassen, entscheidet allein die Augenlinie.
  • Zentriert auf das Gesicht bei direkter Ansprache, Verschiebung um maximal 5% der Breite, um Unordnung fernzuhalten. Der direkte Blick in die Linse impliziert Symmetrie; außermittige Gesichter wirken wie ein Fehler, es sei denn, der Schuss ist eindeutig so komponiert.
  • Der untere Rand sitzt niemals auf einem Gelenk (Kinn, Hals, Ellbogen, Handgelenke, Taille,…

Individual skills in this repo

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

Verwandte Skills