Communitygithub.com

Istok-Media-Tool/zoom

Zoomen Sie gemeinsam mit dem Benutzer im Plan-Editor-Tool (klicken Sie auf ein Wort, siehe Plan-Editor-Fähigkeit) und fügen Sie dynamische Punch-In-Zoom-Cuts zu einem bereits geschnittenen Talking-Head-Video an inhaltlich motivierten Stellen hinzu. Die Zooms sind korrekt auf das Motiv zentriert und behalten den richtigen Kopfabstand bei – so wirken Schnitte zwischen verschiedenen Zoomstufen wie eine bewusste, filmische Kamerabewegung und nicht wie ein sichtbares Springen des Motivs im Bild.

Was ist zoom?

Diese Fähigkeit definiert einen zweistufigen Workflow zum Erstellen dynamischer Zooms in Talking-Head-Videos. In Schritt 1 arbeitet der Benutzer gemeinsam mit dem KI-Assistenten im Plan-Editor, um Wort-für-Wort über den Zoomverlauf zu entscheiden – basierend auf dem inhaltlichen Energieverlauf des Skripts. In Schritt 2 werden automatisch präzise Bildausschnitte berechnet, die das Motiv stets korrekt zentrieren und einen definierten Kopfabstand (~9%) einhalten. Dabei werden auch vermeintlich unveränderte ‚Weitwinkel‘-Szenen nachbearbeitet, um einen konsistenten Bildeindruck zu gewährleisten. Die Fähigkeit unterstützt die Plattformen Claude Code und Cursor und stellt sicher, dass Zooms immer auf dem tatsächlichen Wortschlüssel landen und nicht zufällig wirken.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Istok-Media-Tool/video-edit-skills/tree/HEAD/video-edit/skills/zoom

In Ihrer bevorzugten KI fragen

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

Dokumentation

$SKILLS unten ist der Ordner, der den Ordner dieser Fertigkeit enthält: das übergeordnete Verzeichnis des Basisverzeichnisses, das beim Laden der Fertigkeit angezeigt wird (der skills/-Ordner des Plugins).

Dynamischer Zoom

Warum diese Fähigkeit existiert

Das Hinzufügen eines „dynamischen“ Zooms zu einem geschnittenen Talking-Head-Video ist trügerisch einfach falsch zu machen – und zwar auf eine Weise, die sich erst zeigt, wenn man das Ergebnis tatsächlich ansieht, nicht nur aus den Zahlen allein. In einem realen Fall durchlief dasselbe Video mehrere korrigierte Feedback-Runden, bevor es richtig war – jede brachte eine Lektion, die es wert ist, nicht noch einmal von Grund auf neu gelernt zu werden:

  1. Ein naiver „Zoom bei jedem Schnitt“-Ansatz wirkte billig und aufgesetzt – echte Schnittentscheidungen sind sparsamer und überlegter als das.

  2. Ein symmetrisch auf Augenhöhe zentrierter Bildausschnitt hinterließ, statt auf der tatsächlichen Kopfoberkante zu verankern, nach dem Hochskalieren viel leeren Raum über dem Kopf (~17% Kopfabstand, wenn ~9% richtig aussahen).

  3. Die Wiederverwendung der gemessenen Kopfposition EINES Segments für die Berechnung des Bildausschnitts ANDERER Segmente derselben Aufnahme führte dazu, dass die Person in Segmenten sichtbar außermittig war, in denen sie sich Minuten früher oder später in der Aufnahme im Stuhl verschoben hatte – obwohl es eine einzige durchgehende Aufnahme ist.

  4. Die „Weitwinkel“-/nicht gezoomten Einstellungen völlig unbearbeitet zu lassen, während nur die gezoomten Einstellungen korrigiert wurden, führte dazu, dass jeder Schnitt zwischen einer Weitwinkel- und einer gezoomten Einstellung die Person immer noch seitlich springen ließ – die Weitwinkeleinstellungen benötigten dieselbe Neuzentrierung und denselben Kopfabstand, nur auf einem viel niedrigeren Zoomlevel, damit Schnitte zwischen ihnen wie eine kohärente Kamera wirken.

  5. Ein Zoomlevel pro Clip und dann ein mechanisch in der Mitte jedes Satzes platziertes Punch-In wirkten beide ZUFÄLLIG auf den Benutzer. Zoom muss dem Skript folgen: Jede Änderung landet auf dem spezifischen Wort oder der Phrase, die sie rechtfertigt (eine Enthüllung, ein Schlüsselbegriff, eine persönliche Frage, die Pointe), und Rückzooms landen auf Wörtern über Maßstab („Univerzum“, „Milijarde godina“). Änderungen erfolgen mitten im Clip, manchmal mehrmals pro Clip.

  6. Der Benutzer möchte den Zoomplan GEMEINSAM entscheiden, Wort für Wort, mit dem Plan-Editor-Tool (Schritt 1). Verwenden Sie es immer – für jedes Skript.

  7. Der Benutzer wählte die Zoomstufen: 1,0x, 1,5x, 1,7x, 2,0x, 2,2x (Planebenen 0-4). „1,0x“ (Ebene 0) ist NICHT der Rohframe: Gerendert als roher, unbeschnittener Frame ließ es die Person außermittig mit viel zu viel Platz über dem Kopf, und der Benutzer bat darum, es mit demselben Kopfabstand wie jede andere Einstellung zu zentrieren. Also ist Ebene 0 = der --baseline-Zuschnitt (der lockerste Zuschnitt, der die 9%-Kopfabstandsbedingung erfüllt, neuzentriert; in der Praxis ~1,2x) – build_zoom_selections.py macht das automatisch. Behalten Sie die Bezeichnung „1,0x“ im Tool bei; es bedeutet „am weitesten, zentriert".

Schritt 1: Planen Sie den Zoom GEMEINSAM im Plan-Editor-Tool

Zwingend für jedes Video: Laden und befolgen Sie die Plan-Editor-Fähigkeit ($SKILLS/plan-editor/SKILL.md). Es ist das Standard-„Klick-auf-ein-Wort“-Tool des Benutzers, mit dem er den Zoom mit Ihnen entscheidet und über „Izmeni tekst“ den Untertiteltext in denselben Sätzen korrigiert. Rendern Sie niemals einen Zoom aus einem Plan, den der Benutzer dort nicht geformt hat.

Ihre Aufgabe in diesem Schritt ist der Startvorschlag: Bauen Sie ihn aus der Energiekurve des Inhalts auf – Einführung/ruhig = weit, eine Wendung oder Pointe verdient ein Hineinzoomen AUF SEINEM SCHLÜSSELWORT, ein erklärender Schlag holt zurück zum Durchatmen, der Höhepunkt bekommt den engsten Bildausschnitt; entscheidende Stufenunterschiede (kein schleichender Übergang); mindestens eine echte Rückkehr zur Weitwinkelaufnahme nach der Eröffnung – und geben Sie den Grund für jede Änderung an. Die Antwort des Benutzers (PLAN_JSON, SENTENCES_JSON) ist der bestätigte Plan; rendern Sie ihn genau so in Schritt 5.

Die Anwendung des Plans erfordert Wort-Zeitstempel (Schritt 5) und Motivpositionen pro Clip (Schritt 2).

examples/sample/ enthält einen echten bestätigten Satz (Sätze, Benutzerplan, Positionen, Schnitte) für ein seitlich aufgenommenes Video, das mit transpose=1 gedreht wurde – eine Referenz für Dateiformate und für die Art von Plan, die dieser Benutzer wählt.

Schritt 2: Messen Sie die Position des Motivs – PRO SEGMENT, jedes Mal

Verwenden Sie niemals die gemessene Kopfposition eines Segments für die Berechnung des Bildausschnitts eines anderen Segments, selbst innerhalb derselben durchgehenden Aufnahme – jemand kann sich im Laufe mehrerer Minuten einer langen Einstellung im Stuhl verschieben, und ein Bildausschnitt, der aus einem frühen Segmentframe berechnet wurde, kann das Motiv in einem späteren Segment, das ähnlich aussieht, aber nicht ganz gleich eingerahmt ist, dezentrieren oder bei engerem Zoom sogar abschneiden.

Für jedes Segment, das Sie zuschneiden möchten (einschließlich der „Weitwinkel“-Segmente – siehe Schritt 4):

ffmpeg -nostdin -y -ss <mid_timestamp_of_that_segment> -i source.mp4 -frames:v 1 ref.png
ffmpeg -nostdin -y -i ref.png -vf "drawgrid=w=54:h=96:t=1:[email protected]" grid.png

(54x96 ergibt ein 20x20-Raster auf einem 1080x1920-Frame – für andere Auflösungen proportional skalieren.) Betrachten Sie grid.png und lesen Sie ab:

  • cx – das x der Gesichts-/Nasenmitte
  • head_top_y – das y des obersten Haaransatzes (nicht die Augen – der Kopfabstand wird von der Oberkante des Kopfes gemessen)

Schritt 3: Berechnen Sie den Bildausschnitt

python3 scripts/compute_crop.py --cx <gemessenes_x> --head-top-y <gemessenes_y> --zoom 1.7
python3 scripts/compute_crop.py --cx <gemessenes_x> --head-top-y <gemessenes_y> --baseline
  • --zoom Z für eine explizite Hineinzoomstufe (z. B. 1.7 für eine 170%-Einstellung).
  • --baseline für die „Weitwinkel“-Einstellungen: Es findet den LOCKERSTEN Zuschnitt, der dennoch das Kopfabstandsziel erfüllt und horizontal neuzentriert – das ist es, was die Weitwinkeleinstellungen nur leicht zugeschnitten (nicht buchstäblich 100%/unberührt) macht, aber denselben optischen Mittelpunkt und Kopfabstand wie die gezoomten Einstellungen teilt, was tatsächlich verhindert, dass Schnitte aussehen, als würde das Motiv springen. Überspringen Sie nicht das Zuschneiden der Weitwinkeleinstellungen – sie roh zu lassen, während nur die gezoomten repariert werden, war beim Testen die größte Quelle für „irgendwas sieht falsch aus“, größer als jede einzelne falsche Zoomstufe.

Der standardmäßige Kopfabstand beträgt 9% der Höhe des zugeschnittenen Bildes, was sich beim Testen als korrekt erwies – darunter wirkte es beengt, und es ist nicht nur ästhetisch: Eine vertikale Plattform (Instagram/TikTok/Shorts) legt Benutzernamen/Untertitel/Fade-UI über den obersten Rand des Bildes, sodass dieser Rand funktionaler Raum ist, den die Plattform abdecken wird, kein zusätzlicher Platz. Überschreiben Sie mit --headroom, wenn ein bestimmtes Projekt etwas anderes erfordert, aber gehen Sie für Inhalte, die auf diese Plattformen gehen, nicht viel unter ~9%.

Die vollständige Begründung für all das oben Genannte befindet sich im Docstring von scripts/compute_crop.py – er ist kurz; lesen Sie ihn, wenn in einem bestimmten Fall etwas hier unklar ist.

Schritt 4: Überprüfen Sie die tatsächliche gerenderte Ausgabe vor der vollständigen Wiedergabe

Ein Bildausschnitt, der auf einem einzelnen Standbild richtig aussieht, kann das Motiv dennoch abschneiden oder dezentrieren, sobald Sie Bewegungen prüfen. Rendern Sie für jedes Segment (insbesondere dasjenige, das die meiste Körperbewegung zeigte – Vorbeugen, Kopfneigung – als Sie den Referenzframe ausgewählt haben):

  1. Rendern Sie nur das eine Segment mit seinem angewandten Bildausschnitt.
  2. Entnehmen Sie 3+ Frames, die über seine tatsächliche Dauer verteilt sind (nicht nur den, den Sie gemessen haben), z. B. ffmpeg -i clip.mp4 -vf fps=3 check_%02d.png.
  3. Betrachten Sie sie. Wenn der Kopf jemals abgeschnitten oder zu nah an einer Kante ist, lockern Sie den Zoom dieses Segments oder vergrößern Sie seinen Rand, bevor Sie fortfahren.

Erst wenn dies bestanden ist, sollten Sie alle Segmente rendern und zusammenfügen.

Schritt 5: Wandeln Sie den bestätigten Plan in wortverankerte Schnitte um und setzen Sie ihn zusammen

  1. Holen Sie sich Wort-Zeitstempel aus einer Wiedergabe desselben Schnitts (ein einfacher Schnitt oder eine frühere Zoomwiedergabe – die Auffüllung muss übereinstimmen) und notieren Sie die Dauern pro Clip dieser Wiedergabe (ffprobe jedes clip_NNN.mp4 in seinem Arbeitsverzeichnis):
    python3 scripts/word_times.py cut_render.mp4 words.json --language sr
    
  2. Erstellen Sie die Auswahldatei. cuts.txt = die ungefüllten start<TAB>end des Basisskills pro Clip; pos.json = [[cx, head_top_y], ...] pro Clip aus Schritt 2 (gemessen am gedrehten Frame, wenn --pre-filter dreht):
    python3 scripts/build_zoom_selections.py --cuts cuts.txt --positions pos.json \
      --plan plan.json --sentences sentences.txt --words words.json \
      --durations 6.28,6.08,... [--pre-filter transpose=1] > selections.txt
    
    Lesen Sie den von ihm ausgegebenen Übereinstimmungsbericht: Jedes geplante Wort sollte exact (oder eine sinnvolle fuzzy~-Schreibvariante – Whisper kann Namen anders oder in Kyrillisch buchstabieren) sagen. Jede proportional (no match!)-Zeile muss von Hand korrigiert werden, bevor gerendert wird. Die Änderung erfolgt 0,08 s vor dem Wort (--lead). Clips mit mehreren Stufen werden als ein durchgehender Clip gerendert (geteilt + zeitlich überlagert), sodass ein Zoom mitten im Satz nie einen Schnitt oder eine Audio-Naht hinzufügt.
  3. Überprüfen Sie stichprobenartig Frames 0,2 s vor und nach jeder Zoomänderung (Schritt-4-Logik: Kopf nie abgeschnitten, Änderung deutlich sichtbar), dann rendern Sie:
scripts/cut_and_concat.sh "<source_video>" selections.txt final_output.mp4 "$PWD/build"

Übergeben Sie das Arbeitsverzeichnis als ABSOLUTEN Pfad – mit einem relativen Pfad schlägt der letzte Concatenate-Schritt fehl, concat_list.txt zu finden.

Die gerenderten Clip-Dauern können um einige Hundertstelsekunden von denen der vorherigen Wiedergabe abweichen. Wenn also Untertitel (Untertitel-Fähigkeit) gegen eine frühere Wiedergabe getimed wurden, verschieben Sie jeden Untertitel um den neuen Start seines Clips, bevor Sie ihn einbrennen.

Lesen Sie den Kommentarblock oben in cut_and_concat.sh, bevor Sie ihn anpassen – er dokumentiert ffmpeg/bash-Fallen (stdin-Diebstahl in Schleifen, Fade-Outs, die die letzte Silbe eines Schnitts auffressen, warum Clips vor dem Concat eine individuelle Neucodierung benötigen), die sonst leicht stillschweigend wieder eingeführt werden.

Beispiel-Auswahldatei, die ein Baseline- (Weitwinkel-)Segment und zwei Punch-Ins mischt:

63.82	69.57	crop=908:1614:152:304,scale=1080:1920
80.37	85.90	crop=636:1130:288:348,scale=1080:1920
97.25	100.81	crop=908:1614:152:304,scale=1080:1920

Wenn die Quelle auch eine Drehungs-/Ausrichtungsfixierung benötigt, wird das separat gehandhabt (nicht Teil dieser Fähigkeit, da es nicht immer benötigt wird) – stellen Sie selbst einen entsprechenden Rotationsfilter jeder Zeile voran, falls er hier zutrifft.

Auslieferung

Wenn die Master-Ausgabe die Dateigrößenbeschränkung eines Tools überschreitet, erstellen Sie eine sekundäre komprimierte Kopie, anstatt den Master zu komprimieren:

ffmpeg -nostdin -y -i final_output.mp4 -c:v libx264 -preset slow -crf 24 -pix_fmt yuv420p -c:a aac -b:a 128k final_output_share.mp4 -loglevel error

Objekte, die der Sprecher zeigt, müssen in jedem Zoom vollständig bleiben

In einem echten Reel zeigt der Sprecher Fotos auf einem Tablet neben sich; Bildausschnitte, die auf das Gesicht zentriert sind, schneiden dessen linken Rand ab („ne sme tablet da bude isečen ovako nigde"). Listen Sie vor dem Rendern alles im Bild auf, um das es in dem Video GEHT (ein Tablet, ein Produkt, eine hochgehaltene Seite) und messen Sie seine Box auf dem Raster. Jeder Zoom-Ausschnitt muss diese Box mit ~30px Luft enthalten: Verschieben Sie das x/y des Ausschnitts in seine Richtung (das Gesicht darf bis zu ~70% seitlich sitzen), und wenn das Gesicht dann den Rand berühren würde, verwenden Sie für diese Änderung eine lockerere Stufe (1,7x -> 1,6x in einem echten Reel), anstatt eines von beiden abzuschneiden. Betrachten Sie einen Frame jedes Zoombereichs, um zu bestätigen, dass beide vollständig sind.

Individual skills in this repo

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

Verwandte Skills