Compare commits
83 Commits
7b4a98d760
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fa40821319 | |||
| 68ec775916 | |||
| 3b42c5d018 | |||
| f3c3a9cfd4 | |||
| e966a4c321 | |||
| 45b5376cef | |||
| 4b3894a812 | |||
| 3ad2b51e56 | |||
| c16e46fb9d | |||
| 8ca6d4b696 | |||
| b771c6792b | |||
| 6bf3ab6626 | |||
| 9a5abd5312 | |||
| b2abdafc7a | |||
| 02e9fee982 | |||
| 5425939a84 | |||
| ed7b083dca | |||
| ae3c2b1b13 | |||
| 71117a8a3b | |||
| c1425003c1 | |||
| bcaf0417b3 | |||
| f63d65fcd2 | |||
| c08ba97d37 | |||
| a275b2efb6 | |||
| fab6c53698 | |||
| c5b7d61451 | |||
| acafe538b2 | |||
| 10e27afc8d | |||
| e335fffe92 | |||
| bdc9e4ab31 | |||
| 430a81a988 | |||
| 5611902eb5 | |||
| 4eeecca80d | |||
| 5407f08fbc | |||
| 0baedb3a17 | |||
| d83fced8d2 | |||
| 4fe1d35f1a | |||
| 730b5ef3c0 | |||
| f20f89b06b | |||
| 18c8c89ee6 | |||
| 9b524c9329 | |||
| 1e5ffffd91 | |||
| 8fd0442724 | |||
| 18a67387f6 | |||
| 7ffe4adc3b | |||
| 92a12276ee | |||
| 64b53c0e82 | |||
| 8096f9b4d8 | |||
| e960b1c080 | |||
| c972894972 | |||
| 72e22969b4 | |||
| 9d3c5d5afd | |||
| 0375580373 | |||
| e3a4c22b71 | |||
| c71ed2b701 | |||
| 49412c54a6 | |||
| 533ab49d62 | |||
| f1e9636a83 | |||
| cd10e2bc03 | |||
| c118428167 | |||
| 45769aa366 | |||
| 3b90905d07 | |||
| 07f47ebe2b | |||
| 2f8b0585e2 | |||
| d287952572 | |||
| e2c30d0062 | |||
| fab6aa9388 | |||
| 64e0132cc7 | |||
| 4afb438a4d | |||
| a7b103b4fe | |||
| 54d3f04616 | |||
| 223789eafc | |||
| 8aa6fe8323 | |||
| dbadc3fc26 | |||
| b70d7e11be | |||
| cc27208d2a | |||
| 2a3840e528 | |||
| 17db238ea3 | |||
| f57bd6a669 | |||
| a405df0ddb | |||
| 5a6ae2175c | |||
| 97a8f9e305 | |||
| 06a2326bf1 |
@@ -0,0 +1,6 @@
|
||||
* text=auto
|
||||
.gitattributes text eol=lf
|
||||
*.py text eol=lf
|
||||
*.md text eol=lf
|
||||
*.html text eol=lf
|
||||
*.ps1 text eol=crlf
|
||||
@@ -30,10 +30,14 @@ proxy/
|
||||
*.jpeg
|
||||
*.png
|
||||
|
||||
# IDE
|
||||
# IDE / editor
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.code-workspace
|
||||
|
||||
# Claude Code session data
|
||||
.claude/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
@@ -42,3 +46,15 @@ Thumbs.db
|
||||
# Secrets / local overrides
|
||||
.env
|
||||
config.local.toml
|
||||
|
||||
# Cutter-Report assets — these MUST be tracked so the remote repo can serve
|
||||
# the inline previews even without re-running the generator. Negated rules
|
||||
# must come after the broader ignores above.
|
||||
!output/
|
||||
output/*
|
||||
!output/cutter_stills/
|
||||
!output/cutter_stills/**
|
||||
!output/cutter_clips/
|
||||
!output/cutter_clips/**
|
||||
!output/report/
|
||||
!output/report/**
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
# Handover Notes
|
||||
|
||||
Stand: 2026-05-03 (Beat-20-Reparatur abgeschlossen).
|
||||
|
||||
## Zustand
|
||||
|
||||
- `pytest tests/ -q` → 52/52 grün.
|
||||
- `python cli.py match --beat 20 --vision` läuft erfolgreich durch und schreibt
|
||||
einen confirmed Match (Score 0.6632, scene 613, in=5284.706s, dur=0.88s).
|
||||
- Vorheriger Cache wurde nach `.cache/match_results.json.bak` gesichert.
|
||||
- Kein offener PR; lokale Änderungen sind committed (siehe letzter Commit).
|
||||
|
||||
## Was zuletzt geändert wurde und warum
|
||||
|
||||
### 1. `cli.py` — `realign_window` wählt das Action-Window pro Segment
|
||||
|
||||
In `_filter_semantically_invalid_vision_matches.realign_window`:
|
||||
|
||||
- **Vorher:** `find_action_window_in_scene(action_beat or check_beat, …)` — bei
|
||||
segmentierten Beats wurde immer der ganze Beat als semantischer Kontext
|
||||
benutzt. Das hat für Beat 20 die Source-Position auf die Kuss-Phase
|
||||
(5270 s) gelegt, obwohl das *sichtbare* Segment nur "approaching and pulling
|
||||
apart" zeigt — diese Phase liegt im Source erst um 5284 s.
|
||||
- **Jetzt:** Es werden zwei Fenster gesucht (Segment-Beschreibung *und* Beat-
|
||||
Beschreibung). Der Beat-Kontext gewinnt nur bei deutlichem (>0.06) Score-
|
||||
Vorsprung. Der Trailer-Offset-Shift (`visible_content_offset`) wird nur
|
||||
angewendet, wenn tatsächlich der Beat-Kontext benutzt wurde — sonst zeigt
|
||||
das Segment-Fenster bereits auf die richtige Phase.
|
||||
|
||||
Effekt für Beat 20: 5270.118 → 5284.706, Score 0.6449 (provisional) → 0.6632
|
||||
(confirmed).
|
||||
|
||||
### 2. `cli.py` — Filter-/Repair-Stufe ist crash-tolerant
|
||||
|
||||
`_filter_semantically_invalid_vision_matches` hat den Per-Result-Body in eine
|
||||
lokale Funktion `_filter_repair_one` herausgezogen und in einen try/except
|
||||
verpackt. Wenn die Reparatur abbricht (z. B. weil Vision-API mitten in der
|
||||
Antwort wegfällt), wird der bisher gecachte Treffer behalten statt komplett
|
||||
verworfen.
|
||||
|
||||
### 3. `src/llm/vision_cache.py` — Vision-Retry für Lesefehler
|
||||
|
||||
`_call_vision_model` fängt jetzt zusätzlich `TimeoutError`,
|
||||
`socket.timeout`, `ConnectionError` und `OSError` während des Antwort-Lesens
|
||||
und retryt mit demselben Backoff wie HTTP-/URL-Fehler. Die Auslöse-Bedingung
|
||||
war ein 24-h-DSL-Disconnect mitten im Lauf; davor wurde der Match-Lauf hart
|
||||
abgebrochen und der Cache stand auf "kein Match".
|
||||
|
||||
### 4. `README.md`
|
||||
|
||||
Zwei kurze Absätze ergänzt, die (1) die Segment-vs-Beat-Window-Auswahl und
|
||||
(2) das neue Crash-/Netzfehler-Verhalten beschreiben.
|
||||
|
||||
## Nicht angefasst, aber relevant für die Übergabe
|
||||
|
||||
- Der **vollständige FFmpeg-Vollscan** liefert für Beat 20 weiterhin keinen
|
||||
bestätigten Treffer (final score 0.419 < provisional 0.430). Den
|
||||
Confirmed-Match liefert die Action-Window-Reparatur. Das ist erwartet:
|
||||
das sichtbare Segment ist visuell sehr generisch (Two-Shot Profil mit
|
||||
unscharfem Hintergrund), die korrekte Phase fällt erst durch die
|
||||
semantische Aktionsbeschreibung auf.
|
||||
- Die `candidate_points`-Schleife in `realign_window` (lines ~700–765) sucht
|
||||
nur ±~2 s um `start_s` herum. Solange `start_s` jetzt aus dem Segment-
|
||||
Fenster kommt, liegt der korrekte Source-Punkt in diesem Bereich. Wenn
|
||||
künftig Beats mit längeren visiblen Inseln auftauchen, kann diese Range
|
||||
zu eng werden — dann den Suchradius erweitern statt das Window-Picking
|
||||
rückgängig machen.
|
||||
- Es gibt **keine Tests** für `_filter_semantically_invalid_vision_matches`
|
||||
oder `realign_window`. Wer das anfasst, sollte Beat 20 als Live-Smoke-Test
|
||||
benutzen (siehe unten).
|
||||
|
||||
## Reproduktion / Smoke-Test
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\Activate.ps1
|
||||
python cli.py match --beat 20 --vision
|
||||
```
|
||||
|
||||
Erwartet: `Beat 20: realigned semantically valid long scene by motion/action
|
||||
windows`, danach `is_confirmed: true` für Beat 20 in
|
||||
`.cache/match_results.json` mit `in_point_s ≈ 5284.7` und `match_score ≥ 0.65`.
|
||||
|
||||
Wenn das fehlschlägt:
|
||||
|
||||
1. `python -m pytest tests/ -q` — falls rot, ist die Codebasis selbst kaputt.
|
||||
2. `.cache/vision_descriptions.json` prüfen — die Schlüssel
|
||||
`beat:20:73.560:74.680:…` und `action_window:613:5282.390:5285.430:…` müssen
|
||||
existieren, sonst ruft Vision live ab (kostet Credits; braucht Netz).
|
||||
3. `match_results.json.bak` zurückspielen, falls der Cache zerschossen ist.
|
||||
|
||||
## Offene Risiken / Bekannte Schwächen
|
||||
|
||||
- Die Schwelle `0.06` für "Beat-Kontext gewinnt" in `realign_window` ist
|
||||
kalibriert an Beat 20. Andere Beats sollten auch durchlaufen werden, bevor
|
||||
weitere Beats angefasst werden — am besten ein voller `python cli.py match`
|
||||
ohne `--beat` und Diff der `match_results.json` gegen `.bak`.
|
||||
- Die Filter-/Repair-Stufe kann durch Vision-Calls minutenlang laufen. Das
|
||||
ist nicht neu, aber bei Netzproblemen sehr sichtbar.
|
||||
- Die `_filter_repair_one`-Funktion bekommt viele Argumente durchgereicht
|
||||
(closure-Variablen aus dem Parent). Bei einer nächsten Iteration könnte das
|
||||
in eine kleine Klasse umgebaut werden.
|
||||
|
||||
## Useful greps
|
||||
|
||||
- `find_action_window_in_scene` — semantische Action-Window-Suche (Vision).
|
||||
- `_reference_scoreable_segments` — bestimmt die sichtbaren Inseln eines
|
||||
Beats.
|
||||
- `estimate_usable_source_duration` — kürzt Match-Clips, wenn die Source
|
||||
vor Beat-Ende in eine andere Phase wechselt.
|
||||
- `_filter_semantically_invalid_vision_matches` — Eintrittspunkt der
|
||||
Repair-Stufe in `cli.py`.
|
||||
@@ -1,27 +1,71 @@
|
||||
# AI Trailer Generator v2
|
||||
|
||||
**Frame-accurate trailer reconstruction via pure Computer Vision**
|
||||
**Frame-genaues Nachbauen eines Trailers aus dem Quellfilm.**
|
||||
|
||||
> Gibt einen Reference Trailer und den dazugehörigen Quellfilm hinein — bekommt eine fertige FCPXML/EDL heraus, die den Trailer Frame-genau aus dem Quellfilm nachbaut.
|
||||
Du gibst zwei Videos rein — einen Referenz-Trailer und den dazugehörigen
|
||||
Spielfilm — und bekommst eine fertige FCPXML/EDL für deinen Schnittplatz, die
|
||||
den Trailer Beat für Beat aus dem Quellfilm nachbaut.
|
||||
|
||||
---
|
||||
|
||||
## Das Kernprinzip
|
||||
## Für den Cutter — was du wirklich brauchst
|
||||
|
||||
Standardmäßig kein LLM für visuelles Matching. Optional kann ein Vision-Layer
|
||||
gecachte 3-Frame-Beschreibungen als zusätzliche Suchanker liefern; der finale
|
||||
Match bleibt aber CV-verifiziert.
|
||||
Du musst dieses Tool **nicht selbst bedienen** und musst **kein Python können**.
|
||||
Was du bekommst sind zwei Dateien, mit denen du arbeitest:
|
||||
|
||||
| Phase | Was passiert | Technologie |
|
||||
|-------|-------------|-------------|
|
||||
| **0 — Prep** | Reference Trailer analysieren & Beats extrahieren | PySceneDetect + OpenCV |
|
||||
| **1 — Global Scan**| Gesamten Quellfilm via FFmpeg-Stream (2 FPS) gegen alle Beats scannen | FFmpeg Pipe + Luma-Histogramm |
|
||||
| **1b — Optional Vision Seeds** | Unsichere Top-K Szenen mit 3-Frame-Beschreibungen cachen | OpenAI-kompatibles Vision-LLM |
|
||||
| **2 — Refine** | Beste Treffer auf Frame-Ebene präzisieren | OpenCV `matchTemplate` |
|
||||
| **3 — Dramaturgie** | Narrative BeatType-Klassifikation aus Dialog-Text | OpenRouter LLM |
|
||||
| **4 — Export** | Timeline → FCPXML 1.10 oder CMX 3600 EDL | xml.etree + eigener Timecode-Layer |
|
||||
1. **`CUTTER_REPORT.md`** — die Tabelle für die manuelle Kontrolle und das
|
||||
Nachschneiden. Pro Beat steht drin:
|
||||
- der Trailer-Zeitcode (h:mm:ss:ff),
|
||||
- der vorgeschlagene Source-Zeitcode aus dem Spielfilm,
|
||||
- ein Status: `OK` (kann übernommen werden), `?` (bitte sichten) oder
|
||||
`MAN.` (kein Treffer, manuell setzen),
|
||||
- eine kurze Beschreibung, was im Trailer-Beat zu sehen ist (damit du
|
||||
die richtige Stelle im Source schneller findest).
|
||||
2. **`output/*.fcpxml`** und **`output/*.edl`** — die fertige Timeline für
|
||||
FCP / Premiere / Avid / Resolve. Beats mit Status `OK` sind dort schon
|
||||
richtig gesetzt; `?` und `MAN.` musst du im NLE prüfen bzw. selbst setzen.
|
||||
|
||||
**Text-Safe Crop:** Obere 15% und untere 30% des Frames werden vor jedem Vergleich ausgeblendet, um Title Cards, Logos und Letterbox zu ignorieren.
|
||||
**Workflow-Empfehlung:**
|
||||
|
||||
1. Öffne `CUTTER_REPORT.md` und arbeite die Tabelle von oben nach unten ab.
|
||||
2. Importiere die FCPXML/EDL ins NLE, lade Trailer und Spielfilm dazu.
|
||||
3. Bei `OK`-Beats nur stichprobenartig sichten.
|
||||
4. Bei `?`-Beats die beiden Vorschau-Stills im `CUTTER_REPORT.md` direkt
|
||||
vergleichen (Trailer-Frame neben Source-Frame). Wenn die Bewegungsphase
|
||||
nicht passt, im NLE den Source-In um wenige Frames vor/zurück verschieben.
|
||||
5. Bei `MAN.`-Beats selbst die passende Stelle im Spielfilm suchen — die
|
||||
Beschreibung im Report sagt dir was du suchst.
|
||||
|
||||
Für die visuelle Kontrolle ist zusätzlich **`CUTTER_REPORT.html`** relevant:
|
||||
er enthält die frame-locked Compare-Clips. Der alte `match_report.html` ist
|
||||
nicht mehr Teil des Workflows.
|
||||
|
||||
Alles andere unten ist Hintergrund für den Tool-Verantwortlichen.
|
||||
|
||||
---
|
||||
|
||||
## Wie das Tool die Treffer findet (Kurzfassung)
|
||||
|
||||
| Phase | Was passiert |
|
||||
|-------|--------------|
|
||||
| **0** | Trailer in Beats zerlegen (PySceneDetect). |
|
||||
| **1** | Schneller Vibe-Check: für jeden Beat die Top-K ähnlichsten Szenen aus dem Spielfilm vorauswählen (Histogramm + pHash). |
|
||||
| **2** | Optional: Vision-LLM beschreibt unsichere Szenen mit 3-Frame-Samples; die Beschreibungen liegen gecached vor. |
|
||||
| **3** | Frame-genaue Verfeinerung pro Beat (OpenCV-Templatematching, Bewegungsphasen-Vergleich). |
|
||||
| **4** | Phasen-Reparatur: bei segmentierten Beats wird die Bewegungsphase lokal um den gefundenen Inpoint saliency- und motion-gewichtet mit der sichtbaren Trailerphase abgeglichen. |
|
||||
| **5** | Recovery: Beats ohne Treffer werden via Vision-Phasensuche in den Top-K Szenen nochmal probiert. |
|
||||
| **6** | Export als FCPXML 1.10 oder CMX-3600-EDL plus `CUTTER_REPORT.md`. |
|
||||
|
||||
**Text-Safe Crop:** Obere 15 % und untere 30 % jedes Frames werden vor dem
|
||||
Vergleich ausgeblendet, damit Title-Cards, Logos und Letterbox die Treffer
|
||||
nicht verfälschen.
|
||||
|
||||
**Cutter-Report-Caching:** Vorhandene Compare-Clips werden wiederverwendet.
|
||||
Bei gezielten Rematches wird nur der betroffene Beat neu gerendert, damit der
|
||||
Report schnell aktuell bleibt und keine unnötigen Videoartefakte neu entstehen.
|
||||
|
||||
**Wichtig:** Auch wenn Vision aktiviert ist — der finale Match bleibt
|
||||
CV-verifiziert. Das LLM liefert nur zusätzliche Suchanker.
|
||||
|
||||
---
|
||||
|
||||
@@ -103,260 +147,33 @@ python cli.py rematch --beat 5 --threshold 0.50 # Schwelle anpassen (Globaler S
|
||||
python cli.py rematch --beat 5 --refine # Cached Match per lokalem Bildinhalt-Offset nachschärfen
|
||||
```
|
||||
|
||||
Der HTML-Report regeneriert seine Preview-Clips bei jedem Lauf mit genauer
|
||||
FFmpeg-Nachsuche und synchronisiert die beiden Video-Player pro Beat. Dadurch
|
||||
ist der Report zur Frame-Prüfung geeignet und zeigt keine alten gecachten
|
||||
Preview-Clips.
|
||||
Source-Previews bekommen bei Trailer-only-Tails denselben schwarzen Tail wie der
|
||||
Export, damit der Browser nicht einen zu kurzen Source-Clip gegen den längeren
|
||||
Referenzbeat weiterspult oder loopt.
|
||||
Zur Synchronprüfung rendert der Report ein einzelnes Frame-Locked-Compare-Video
|
||||
mit Referenz und Source in demselben MP4-Stream. Dieses Compare-Video ist
|
||||
maßgeblich, weil zwei getrennte Browser-Videoelemente nie zuverlässig
|
||||
framegenau synchron bleiben.
|
||||
### Cutter-Report neu erzeugen
|
||||
|
||||
Wenn ein Trailer-Beat am Ende eine Blende, Schwarzfläche oder Textkarte enthält,
|
||||
die im Source-Film nicht als normaler Shot vorhanden ist, endet der Source-Match
|
||||
am letzten stabil passenden Frame. Exportierte Timelines behalten trotzdem die
|
||||
volle Beat-Länge und fügen danach automatisch einen schwarzen Trailer-Tail mit
|
||||
Marker für Fade/Dissolve ein.
|
||||
`CUTTER_REPORT.md` wird bei jedem `match`-Lauf automatisch geschrieben.
|
||||
Manuell neu erzeugen (z. B. nach Edit eines einzelnen Beats):
|
||||
|
||||
Gezielte Ein-Beat-Matches nutzen zusätzlich vorhandene automatische Nachbarbeats
|
||||
aus dem Cache als zeitliche Suchanker. Das hilft bei aufeinanderfolgenden Shots,
|
||||
ohne manuelle Szenen oder Timecodes zu kuratieren.
|
||||
Bei `match --beat N` wird ein alter Cache-Treffer für genau diesen Beat entfernt
|
||||
und nur ein neu gefundener automatischer Treffer wieder eingetragen. Ein
|
||||
fehlgeschlagener neuer Lauf kann dadurch keinen alten falschen Report-Treffer
|
||||
stehen lassen.
|
||||
```powershell
|
||||
python scripts/generate_cutter_report.py # mit Vorschau-Stills
|
||||
python scripts/generate_cutter_report.py --no-stills # nur die Tabelle
|
||||
```
|
||||
|
||||
Der globale Bildvergleich arbeitet auf kontrast-normalisierten Luma- und
|
||||
Kantenfeatures statt auf rohen Farb-Pixeln. Dadurch bleiben Schwarzweiß- oder
|
||||
anders gegradete Trailerbilder mit dem Source-Material vergleichbar, während
|
||||
unähnliche Farbshots schlechter ranken.
|
||||
Die Inpoint-Feinjustage bestimmt den Versatz lokal aus dem Bildinhalt: Um einen
|
||||
groben Treffer herum werden mehrere Referenzframes gegen mehrere Source-Offsets
|
||||
verglichen, und der beste gemeinsame Offset wird übernommen. Das ist schneller
|
||||
als ein erneuter globaler Scan und vermeidet pauschale Frame-Prerolls.
|
||||
Zusätzlich wird die Bewegungsphase über Frame-zu-Frame-Differenzen verglichen.
|
||||
Dadurch kann der Matcher innerhalb derselben Source-Szene unterscheiden, ob
|
||||
zwei Figuren noch sprechen, sich annähern, bereits im Kontakt sind oder sich
|
||||
wieder voneinander lösen. Ein optisch ähnlicher Standbild-Treffer reicht damit
|
||||
nicht mehr aus, wenn der Bewegungsverlauf nicht zur Referenz passt.
|
||||
Schwarze Referenzframes aus Blenden oder Titel-Tails werden für diese
|
||||
Offset-Messung ausgelassen, damit echte Bildbewegung und nicht die Blende selbst
|
||||
den Inpoint bestimmt.
|
||||
`rematch --refine` nutzt denselben lokalen FFmpeg/Pillow-Aligner und schreibt
|
||||
den korrigierten Inpoint direkt zurück in `.cache/match_results.json`.
|
||||
Stills landen unter `output/cutter_stills/` und werden nur neu gerendert,
|
||||
wenn sich das zugrundeliegende Match geändert hat.
|
||||
|
||||
### Wenn ein Match falsch wirkt
|
||||
|
||||
| Symptom | Was tun |
|
||||
|---------|---------|
|
||||
| Source-Clip zeigt richtige Szene, aber falsche Bewegungsphase | `python cli.py rematch --beat N --refine` — schiebt den Inpoint frame-genau aus dem Bildinhalt. |
|
||||
| Score zu niedrig, andere Szene wäre richtig | `python cli.py match --beat N --vision` — vollständiger Re-Match nur für diesen Beat mit Vision-Phasenprüfung. |
|
||||
| Match offensichtlich falsche Szene | `python cli.py rematch --beat N --threshold 0.50` — Schwelle absenken, neuer globaler Scan nur für diesen Beat. |
|
||||
| Beat ist Schwarzbild / Logo / Titel und sollte gar nicht matchen | nichts tun, der Status `GFX` im `CUTTER_REPORT.md` ist korrekt. |
|
||||
|
||||
### Algorithmische Details
|
||||
|
||||
Tiefer in die Matcher-Logik (Phasen-Reparatur, segmentierte Beats,
|
||||
Vision-Seeds, Recovery-Stufe usw.) — siehe [`docs/ALGORITHM.md`](docs/ALGORITHM.md).
|
||||
|
||||
Zusätzlich werden aus den besten szenenweiten Luma/Histogramm-Kandidaten
|
||||
mehrere Inpoint-Suchanker erzeugt. Diese Scene-Seeds verwenden keine harte
|
||||
pHash-Sperre, weil pHash bei stark anders gegradeten Trailerbildern echte
|
||||
Matches zu früh ausschließen kann.
|
||||
Optional kann `python cli.py match --beat N --vision` einen Vision-Layer
|
||||
zuschalten. Dann werden pro Trailer-Beat und pro wenigen Scene-Level-Kandidaten
|
||||
je drei Frames (Anfang, Mitte, Ende) von einem visionfähigen OpenAI-kompatiblen
|
||||
Modell beschrieben. Die Beschreibungen liegen in
|
||||
`.cache/vision_descriptions.json` und werden wiederverwendet. Vision erzeugt
|
||||
nur zusätzliche Suchanker; der eigentliche Match muss weiterhin durch CV,
|
||||
Content-Reranking, Timing und Duration-Coverage bestätigt werden.
|
||||
Gecachte Szenenbeschreibungen zählen nur, wenn sie vom aktuell konfigurierten
|
||||
Vision-Modell stammen. Bei langen semantisch passenden Source-Szenen beschreibt
|
||||
der Vision-Layer zusätzlich wenige lokale Zeitfenster und cached auch diese
|
||||
Fenster, damit eine grob ähnliche Szene nicht automatisch mit dem falschen
|
||||
Bewegungs- oder Dialogmoment gleichgesetzt wird.
|
||||
Dieser lokale Fenster-Probe ist bewusst breiter als die finale Seed-Auswahl:
|
||||
Eine lange Dialogszene kann in der Gesamtbeschreibung nur als Gespräch
|
||||
erscheinen, aber an einer späteren Stelle trotzdem genau die gesuchte
|
||||
Aktionsphase enthalten.
|
||||
Für diese Probe wird deshalb die grobe Szenenähnlichkeit ohne harte
|
||||
Aktionsstrafe gerankt; die harte Aktionsprüfung greift erst auf den lokalen
|
||||
Fenstern und dem finalen Source-Zeitbereich.
|
||||
Nach dem CV-Match kann derselbe Vision-Layer den konkreten finalen Source-
|
||||
Zeitbereich nochmals gegen den Trailer-Beat prüfen. Starke Aktionsphasen wie
|
||||
Annäherung, Kuss/Stirnkontakt, Handbewegungen oder Schneiden müssen dann auch
|
||||
im Source-Fenster beschrieben sein; fehlt diese Aktionsphase, wird der Treffer
|
||||
nicht gespeichert, selbst wenn der Low-Level-CV-Score hoch ist.
|
||||
Wenn die Szene selbst plausibel ist, aber der konkrete Source-Zeitpunkt diese
|
||||
Aktionsphase verfehlt, sucht der Matcher automatisch dichter innerhalb derselben
|
||||
Source-Szene nach lokalen Vision-Fenstern mit der passenden Aktion und richtet
|
||||
den Inpoint mit der Motion-Phase-Prüfung darauf neu aus. Erst wenn auch diese
|
||||
In-Scene-Reparatur scheitert, wird der Treffer verworfen.
|
||||
Diese In-Scene-Reparatur läuft auch für semantisch gültige Treffer aus langen
|
||||
Source-Szenen. Dadurch kann ein grob passender Dialogmoment nicht bestehen
|
||||
bleiben, wenn ein anderes lokales Fenster derselben Szene die gesuchte
|
||||
Aktionsphase und Bewegung klarer trifft.
|
||||
Die Kandidatenbewertung dieser Reparatur vergleicht dabei zwei Kontexte:
|
||||
den ganzen Beat als semantischen Handlungsrahmen und das konkret sichtbare
|
||||
Beat-Segment als Phasenprüfung. Ein Source-Zeitpunkt muss also nicht nur
|
||||
"die Szene mit dem Kuss" enthalten, sondern auch zur aktuellen Bewegungsphase
|
||||
des sichtbaren Trailerabschnitts passen. Pro Kandidat fließt zusätzlich ein
|
||||
lokaler Content-/Motion-Frame-Score ein, damit cached Vision-Beschreibungen
|
||||
keinen sichtbar versetzten Bewegungsmoment überstimmen.
|
||||
Bei blendigen oder segmentierten Beats nutzt die semantische Action-Suche den
|
||||
ganzen Trailerbeat als Kontext. Die eigentliche Frame-Ausrichtung bleibt auf das
|
||||
sichtbare Segment begrenzt; der gefundene Source-Inpoint wird dabei um den
|
||||
Trailer-Offset des Segments verschoben. So geht die globale Aktionsbeschreibung
|
||||
eines Beats nicht verloren, nur weil der scorebare Teil erst nach einer Blende
|
||||
beginnt.
|
||||
Die Suche nach diesem Action-Window prüft pro Segment zwei Beschreibungen:
|
||||
zuerst die des konkret sichtbaren Segments (so trifft die Phasensuche genau die
|
||||
gerade gezeigte Bewegung), als Rückfall die des gesamten Beats. Der Beat-
|
||||
Kontext gewinnt nur, wenn er deutlich (>0.06) besser scort; sonst bleibt das
|
||||
Segment-Fenster die Wahl, weil die Beat-Beschreibung Aktionen aus Fade-Bildern
|
||||
mit aufnehmen kann, die im sichtbaren Segment nicht stattfinden.
|
||||
Der Trailer-Offset-Shift wird nur angewendet, wenn tatsächlich der Beat-Kontext
|
||||
benutzt wurde; bei segmentbasierter Wahl ist das gefundene Fenster bereits auf
|
||||
die sichtbare Aktionsphase ausgerichtet.
|
||||
Der Segment-Offset zählt dabei nur über vorherige scorebare Bildinseln, nicht
|
||||
über schwarze oder blendige Lücken. Nach dem Retiming wird die nutzbare
|
||||
Source-Dauer erneut geschätzt; läuft die Source am Ende in eine sichtbar andere
|
||||
Aktionsphase, wird der Clip gekürzt und der Rest bleibt Placeholder/Fade statt
|
||||
einen falschen Bewegungsmoment zu zeigen.
|
||||
Der gewichtete Vision-Seed-Pfad ersetzt standardmäßig keinen normalen
|
||||
FFmpeg-Vollscan. Vision-Beschreibungen sind semantische Hinweise, aber keine
|
||||
Beweise; der volle CV-Scan bleibt deshalb aktiv, damit falsch bewertete
|
||||
Vision-Szenen echte Treffer nicht verdrängen. Für schnelle Experimente kann
|
||||
`skip_coarse_scan_with_weighted_seeds = true` gesetzt werden.
|
||||
Gewichtete Vision-Seeds werden nicht zuerst durch den alten Midpoint-Template
|
||||
Refine verschoben; sie gehen direkt in die lokale Content-Alignment-Prüfung.
|
||||
Das schützt wiederholte Gesprächseinstellungen, bei denen ähnliche Momente
|
||||
mehrfach in derselben Szene vorkommen.
|
||||
Innerhalb der automatisch von Vision vorgeschlagenen Szenen läuft zusätzlich
|
||||
eine dichte lokale Bildsequenzsuche. Sie misst den Phasenversatz in kleinen
|
||||
Zeitschritten direkt am Bildinhalt und bevorzugt Kandidaten mit genügend
|
||||
Restdauer in derselben Source-Szene. Das ist kein manueller Override: Vision
|
||||
grenzt nur Suchbereiche ein, die Auswahl bleibt Content-, Timing- und
|
||||
Coverage-getrieben.
|
||||
Nach einem dichten Vision-Treffer darf der spätere lokale Aligner nur noch im
|
||||
Bereich dieses Scan-Schritts nachjustieren. So kann ein korrekt gefundener
|
||||
Bewegungsmoment nicht wieder um viele Frames in eine ähnlich aussehende Phase
|
||||
derselben Szene verschoben werden.
|
||||
Für Vision-Action-Fenster nutzt die finale Retiming-Prüfung eine gemeinsame
|
||||
Content-und-Motion-Suche pro Frame. Content und Bewegungsphase werden dabei
|
||||
nicht mehr als zwei getrennte Korrekturschritte angewendet; das verhindert,
|
||||
dass eine kurze Geste erst korrekt erkannt und anschließend in eine spätere
|
||||
ähnliche Körperhaltung verschoben wird.
|
||||
Wenn mehrere Vision-Kandidaten in derselben Source-Szene ähnlich gut scoren
|
||||
und die Beat-Dauer abdecken, bevorzugt der Matcher die frühere Phase. Das
|
||||
verhindert, dass ein späterer, minimal stärkerer Standbildtreffer die
|
||||
Bewegungsphase des Trailers sichtbar überholt.
|
||||
Enthält ein Trailerbeat selbst einen harten Umschnitt, werden Kandidaten an
|
||||
angrenzenden Source-Szenengrenzen zusätzlich als zusammenhängender Multi-Shot-
|
||||
Span geprüft. Ein Match darf dann über eine Source-Szenengrenze laufen, aber
|
||||
nur wenn die relative Source-Grenze zeitlich zu einem erkannten Trailer-Umschnitt
|
||||
passt. So kann ein Beat aus Frage/Antwort-Shots vollständig erfasst werden,
|
||||
ohne Szenen willkürlich zusammenzukleben.
|
||||
Auch der lokale Content-Aligner darf einen Inpoint nur noch übernehmen, wenn
|
||||
die feste Whole-Frame-/Spatial-Validation dadurch besser wird.
|
||||
Vor dem teuren Frame-Refine wird der gesamte Kandidatenpool mit einer schnellen
|
||||
festen Inhaltsprüfung neu sortiert. Dadurch können korrekte Treffer aus
|
||||
wiederholten Einstellungen einer Szene nach oben kommen, auch wenn ein freier
|
||||
Template-Peak an anderer Stelle numerisch stärker war. Suchanker bleiben im
|
||||
Pool erhalten, dürfen aber erst nach der Inhaltsprüfung nach oben rücken. Wenn
|
||||
ein Kandidat visuell plausibel ist, aber wegen Trailerblende oder kurzem
|
||||
Source-Span die normale Coverage knapp verfehlt, wird er als provisional Match
|
||||
behalten statt als `NO MATCH` verworfen.
|
||||
Dieses Reranking berücksichtigt zusätzlich die verbleibende Szenenlänge ab dem
|
||||
Kandidaten-Inpoint. Dadurch werden zu späte ähnliche Gesprächsphasen innerhalb
|
||||
derselben Szene nicht mehr vor frühere, tragfähigere Phasen sortiert.
|
||||
Das Inhalts-Reranking nutzt bewusst nur wenige repräsentative Referenzframes und
|
||||
eine begrenzte Kandidatenzahl. So bleiben wiederholte Szenen auffindbar, ohne
|
||||
dass der Lauf durch tausende Random-Seeks minutenlang festhängt.
|
||||
Confirmed Matches werden zusätzlich durch eine feste nahezu-Whole-Frame-Prüfung
|
||||
aus Luma, Kanten, Farbhistogramm und räumlichen 4x4-Farbhistogrammen gedeckelt.
|
||||
Dadurch kann ein freier Template-Hit mit ähnlicher Fenster-/Gesichtsstruktur
|
||||
nicht mehr als sicherer Match gelten, wenn die Gesamtkomposition oder die
|
||||
Bewegungsphase sichtbar eine andere Szene ist.
|
||||
Für gewichtete Vision-Kandidaten gibt es zusätzlich eine eigene Provisional-
|
||||
Bewertung aus Content-Score, Restdauer und Seed-Stärke. Dadurch können echte,
|
||||
aber durch Trailer-Grading/Crop numerisch schwache Treffer im Report landen,
|
||||
ohne als confirmed Match durchzugehen.
|
||||
Die Cache-Normalisierung für Report/Export verwendet dieselbe niedrigere
|
||||
Content-Untergrenze für nicht bestätigte Vision-Provisional-Treffer, damit ein
|
||||
gerade gefundener automatischer Match nicht beim Report-Aufbau wieder
|
||||
weggefiltert wird.
|
||||
Sie übernimmt auch die Multi-Shot-Coverage-Regel: gecachte Treffer, die passend
|
||||
zu internen Trailer-Umschnitten über angrenzende Source-Szenen laufen, werden
|
||||
nicht mehr auf die erste Source-Szene zurückgekürzt.
|
||||
Gezielte Einzel-Beat-Matches gewichten außerdem die automatisch aus Nachbarbeats
|
||||
abgeleiteten Continuity-Seeds. Wenn ein Beat direkt an einen bereits passenden
|
||||
Vorgänger anschließt, kann ein späterer ähnlich aussehender Moment derselben
|
||||
Dialogszene den erwarteten Anschluss nicht mehr nur wegen eines höheren
|
||||
Standbildscores verdrängen.
|
||||
Diese Continuity-Seeds sind aber nur Suchanker: in derselben Szene darf ein
|
||||
späterer Inpoint gewinnen, wenn die mehrframeige Content-Prüfung die
|
||||
Bewegungsphase klar besser trifft. Dadurch bleiben Anschlussmatches stabil,
|
||||
ohne Hand-/Kopfbewegungen auf einen falschen Zeitpunkt festzunageln.
|
||||
Continuity- und Vision-Seeds allein schalten den globalen FFmpeg-Scan
|
||||
standardmäßig nicht ab. Sie sind Suchanker, keine Beweise; der volle CV-Scan
|
||||
bleibt aktiv, damit semantisch plausible, aber falsche Vision-Treffer echte
|
||||
Bildmatches nicht verdrängen.
|
||||
Bei aktivierter Vision wird für gezielte Match-Läufe trotzdem zuerst ein
|
||||
schneller seed-basierter CV-Prepass ausgeführt. Er überspringt den vollen
|
||||
FFmpeg-Stream nur vorläufig und akzeptiert einen Treffer erst nach derselben
|
||||
Bild-/Phasenvalidierung wie der normale Matcher. Nur nicht gelöste Beats fallen
|
||||
danach auf den vollständigen Scan zurück. Die Qualitätsparameter für lokale
|
||||
Vision-Szenenscans und Refine-Kandidaten bleiben dabei erhalten; der Prepass ist
|
||||
eine Reihenfolge-Optimierung, kein Qualitätsdeckel.
|
||||
Provisional Treffer aus diesem schnellen Prepass sind nicht endgültig: wenn sie
|
||||
unterhalb der Confirmed-Schwelle bleiben, läuft zusätzlich der vollständige
|
||||
CV-Scan und darf den besseren oder bestätigten Treffer übernehmen.
|
||||
OpenRouter-/Vision-Rate-Limits werden mit progressiv längeren Pausen erneut
|
||||
versucht. Billing-, Credit- oder Token-Guthaben-Fehler werden dagegen sofort als
|
||||
echter Blocker gemeldet, weil Warten dort nicht hilft.
|
||||
Auch Netzfehler beim Lesen der Antwort (Timeouts, Verbindungsabbrüche während
|
||||
einer DSL-Trennung) werden als retrybar behandelt, nicht nur Verbindungsfehler
|
||||
beim Verbindungsaufbau. Schlägt die Vision-Verifikation während der finalen
|
||||
Filter-/Repair-Stufe trotzdem dauerhaft fehl, wird der bisherige gecachte
|
||||
Treffer für diesen Beat behalten statt verworfen — ein Netzproblem darf keinen
|
||||
schon korrekt gefundenen Match aus dem Cache löschen.
|
||||
Lange Trailerbeats werden nicht mehr automatisch über ihre gesamte Beat-Länge
|
||||
gegen einen einzigen Source-Clip validiert. Sobald nach einem sichtbaren
|
||||
Source-Abschnitt eine anhaltende Schwarzblende oder Titel-/Credit-Insel beginnt,
|
||||
endet der matchbare Referenzbereich dort; zwei aufeinanderfolgende dunkle
|
||||
Samples reichen dafür. Spätere Text-/Creditbilder im selben Beat gehen damit
|
||||
nicht mehr in Reranking, Validation oder Span-Schätzung ein.
|
||||
Wenn nach einer Blende wieder sichtbares, matchbares Material kommt, wird der
|
||||
Beat nicht mehr als „Source + schwarzer Tail“ behandelt. Der CLI-Match speichert
|
||||
zusätzliche `MatchSegment`-Einträge für jede automatisch erkannte sichtbare
|
||||
Insel; der HTML-Report setzt diese Source-Segmente frame-lockend zusammen und
|
||||
füllt nur echte Zwischenlücken mit Schwarz. Dadurch können per Blende verbundene
|
||||
Trailer-Einstellungen innerhalb eines Beats getrennt gematcht werden, ohne die
|
||||
globale Scene Detection aggressiver oder beat-spezifisch zu kuratieren.
|
||||
Beats mit mehreren sichtbaren Inseln werden direkt segmentiert gesucht, statt
|
||||
zuerst als ein künstlich zusammenhängender Source-Clip über den ganzen Film zu
|
||||
laufen. Jede Insel nutzt dieselbe gestufte Vision-/CV-Validierung wie ein
|
||||
normaler Beat; der zusammengesetzte Report bleibt beat-synchron. Wenn der
|
||||
schnelle validierte Vision-Prepass für eine Insel keinen Treffer liefert, darf
|
||||
diese Insel weiterhin in den vollständigen Scan fallen.
|
||||
Falls ein kompletter Beat keinen belastbaren Einzelclip ergibt, versucht der
|
||||
Matcher dieselbe Segmentlogik automatisch als Fallback: sichtbare Inseln werden
|
||||
einzeln global gesucht und anschließend wieder zu einem Beat-Ergebnis
|
||||
zusammengesetzt. Sehr kurze Inseln dürfen zusätzlich in den Source-Szenen
|
||||
benachbarter bereits gematchter Beats lokal nach ihrer Bewegungsphase suchen.
|
||||
Das ist weiterhin nur ein allgemeiner Continuity-Anker, kein manueller Override
|
||||
für bestimmte Beat-Nummern oder Szenen.
|
||||
Besteht ein Beat nach automatischer Fade-/Titel-Filterung nur aus einer
|
||||
einzigen sichtbaren Insel, wird diese Insel direkt als primäres Suchziel
|
||||
verwendet. Dadurch scannt der Matcher denselben Bildinhalt nicht erst als
|
||||
vollen Beat und danach noch einmal als Segment; der Report behält trotzdem die
|
||||
korrekte Beat-Position und füllt echte Randlücken mit Schwarz.
|
||||
Gecachte segmentierte Treffer werden ebenfalls gegen die automatisch sichtbare
|
||||
Referenzdauer normalisiert, nicht gegen Schwarz-/Blendränder des gesamten Beats.
|
||||
Ein korrekt gematchter kurzer Bildinhalt wird dadurch beim Report-Aufbau nicht
|
||||
nachträglich als zu kurz verworfen.
|
||||
Zusätzlich werden sehr dunkle, kontrastarme oder noch nicht sauber
|
||||
auf-/abgeblendete Referenzframes aus Score, Inhalts-Reranking,
|
||||
Phasen-Alignment und Motion-Templates herausgenommen. Blenden sollen bestimmen,
|
||||
wie der Clip später exportiert wird, aber nicht, ob der Bildinhalt als Match
|
||||
gilt.
|
||||
Sichtbare Fade-Rampen werden nur in eine matchbare Insel hinein erweitert, wenn
|
||||
sie strukturell stark zum ersten bzw. letzten scorebaren Frame derselben
|
||||
Einstellung passen. Doppelbelichtungen aus Cross-Dissolves bleiben dadurch
|
||||
Übergangsmaterial und werden nicht als einzelner Quellclip erzwungen.
|
||||
Treffer unter `provisional_content_threshold` werden gar nicht mehr gespeichert
|
||||
oder aus alten Cache-Ergebnissen übernommen. Das verhindert, dass offensichtlich
|
||||
falsche Szenen im Report als Match-Kandidat weiterleben.
|
||||
|
||||
### Log-Level
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[project]
|
||||
name = "AI Trailer Generator v2"
|
||||
version = "2.0.0"
|
||||
log_level = "INFO" # DEBUG | INFO | WARNING | ERROR
|
||||
log_level = "DEBUG" # DEBUG | INFO | WARNING | ERROR
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# [paths] — External video sources (read-only access)
|
||||
@@ -72,12 +72,12 @@ match_threshold = 0.65
|
||||
|
||||
# Store/report lower-confidence automatic candidates for visual review instead
|
||||
# of dropping them as "NO MATCH". Confirmed exports can still use match_threshold.
|
||||
provisional_match_threshold = 0.43
|
||||
provisional_match_threshold = 0.35
|
||||
|
||||
# Lower gate for entering temporal multi-frame refinement. The final decision
|
||||
# still uses sequence/span scoring; this only avoids rejecting real matches
|
||||
# because one midpoint frame is weak.
|
||||
coarse_candidate_threshold = 0.50
|
||||
coarse_candidate_threshold = 0.40
|
||||
|
||||
# Candidate ranking weights. Duration coverage matters when the same visual
|
||||
# shot appears multiple times: prefer the occurrence that can cover the beat.
|
||||
@@ -86,7 +86,10 @@ span_score_weight = 0.15
|
||||
coarse_score_weight = 0.10
|
||||
duration_score_weight = 0.20
|
||||
duration_tie_break_score_delta = 0.03
|
||||
min_duration_coverage = 0.65
|
||||
min_duration_coverage = 0.55
|
||||
# Every visible sub-shot in a multi-shot beat must pass this stricter gate.
|
||||
# A weak segment is left unmatched instead of being hidden by a strong neighbor.
|
||||
multi_shot_segment_threshold = 0.50
|
||||
continuity_seed_offsets_s = [-1.0, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0]
|
||||
scene_seed_top_k = 30
|
||||
scene_seed_points_per_scene = 6
|
||||
@@ -103,7 +106,7 @@ refine_step_seconds = 0.04 # ≈ 1 frame at 25 fps
|
||||
content_align_window_seconds = 0.48
|
||||
content_align_sample_step_s = 0.28
|
||||
content_validation_weight = 0.35
|
||||
provisional_content_threshold = 0.42
|
||||
provisional_content_threshold = 0.30
|
||||
|
||||
# When several adjacent frame offsets score almost the same, prefer the earlier
|
||||
# one. This avoids matches that are visually correct but start a few frames late.
|
||||
@@ -183,7 +186,7 @@ local_scan_step_s = 0.12
|
||||
local_scan_max_points_per_scene = 180
|
||||
local_scan_top_candidates = 36
|
||||
local_scan_tie_break_score_delta = 0.08
|
||||
multi_shot_cut_corr_threshold = 0.20
|
||||
multi_shot_cut_corr_threshold = 0.55
|
||||
multi_shot_boundary_tolerance_s = 0.20
|
||||
fullscan_fallback = false
|
||||
content_threshold = 0.22
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
# Algorithmus-Notizen
|
||||
|
||||
Detaillierte Verhaltensbeschreibung und Designentscheidungen des Matchers.
|
||||
Für die normale Bedienung reicht die [README](../README.md) — dieses Dokument
|
||||
ist die Referenz für den Tool-Verantwortlichen, wenn etwas im Verhalten
|
||||
unklar wird oder ein Match systematisch falsch landet.
|
||||
|
||||
## HTML-Report und Vorschauclips
|
||||
|
||||
Der HTML-Report regeneriert seine Preview-Clips bei jedem Lauf mit genauer
|
||||
FFmpeg-Nachsuche und synchronisiert die beiden Video-Player pro Beat. Dadurch
|
||||
ist der Report zur Frame-Prüfung geeignet und zeigt keine alten gecachten
|
||||
Preview-Clips. Source-Previews bekommen bei Trailer-only-Tails denselben
|
||||
schwarzen Tail wie der Export, damit der Browser nicht einen zu kurzen
|
||||
Source-Clip gegen den längeren Referenzbeat weiterspult oder loopt.
|
||||
|
||||
Zur Synchronprüfung rendert der Report ein einzelnes Frame-Locked-Compare-Video
|
||||
mit Referenz und Source in demselben MP4-Stream. Dieses Compare-Video ist
|
||||
maßgeblich, weil zwei getrennte Browser-Videoelemente nie zuverlässig
|
||||
framegenau synchron bleiben.
|
||||
|
||||
## Trailer-Tails ohne Source-Pendant
|
||||
|
||||
Wenn ein Trailer-Beat am Ende eine Blende, Schwarzfläche oder Textkarte
|
||||
enthält, die im Source-Film nicht als normaler Shot vorhanden ist, endet der
|
||||
Source-Match am letzten stabil passenden Frame. Exportierte Timelines behalten
|
||||
trotzdem die volle Beat-Länge und fügen danach automatisch einen schwarzen
|
||||
Trailer-Tail mit Marker für Fade/Dissolve ein.
|
||||
|
||||
## Targeted single-beat re-matches
|
||||
|
||||
Gezielte Ein-Beat-Matches nutzen zusätzlich vorhandene automatische Nachbarbeats
|
||||
aus dem Cache als zeitliche Suchanker. Das hilft bei aufeinanderfolgenden
|
||||
Shots, ohne manuelle Szenen oder Timecodes zu kuratieren. Bei `match --beat N`
|
||||
wird ein alter Cache-Treffer für genau diesen Beat entfernt und nur ein neu
|
||||
gefundener automatischer Treffer wieder eingetragen. Ein fehlgeschlagener
|
||||
neuer Lauf kann dadurch keinen alten falschen Report-Treffer stehen lassen.
|
||||
|
||||
## Bildvergleich auf Luma + Kanten
|
||||
|
||||
Der globale Bildvergleich arbeitet auf kontrast-normalisierten Luma- und
|
||||
Kantenfeatures statt auf rohen Farb-Pixeln. Dadurch bleiben Schwarzweiß-
|
||||
oder anders gegradete Trailerbilder mit dem Source-Material vergleichbar,
|
||||
während unähnliche Farbshots schlechter ranken.
|
||||
|
||||
Die Inpoint-Feinjustage bestimmt den Versatz lokal aus dem Bildinhalt: um
|
||||
einen groben Treffer herum werden mehrere Referenzframes gegen mehrere
|
||||
Source-Offsets verglichen, und der beste gemeinsame Offset wird übernommen.
|
||||
Das ist schneller als ein erneuter globaler Scan und vermeidet pauschale
|
||||
Frame-Prerolls.
|
||||
|
||||
## Bewegungsphasen-Vergleich
|
||||
|
||||
Zusätzlich wird die Bewegungsphase über Frame-zu-Frame-Differenzen verglichen.
|
||||
Dadurch kann der Matcher innerhalb derselben Source-Szene unterscheiden, ob
|
||||
zwei Figuren noch sprechen, sich annähern, bereits im Kontakt sind oder sich
|
||||
wieder voneinander lösen. Ein optisch ähnlicher Standbild-Treffer reicht damit
|
||||
nicht mehr aus, wenn der Bewegungsverlauf nicht zur Referenz passt.
|
||||
|
||||
Schwarze Referenzframes aus Blenden oder Titel-Tails werden für diese
|
||||
Offset-Messung ausgelassen, damit echte Bildbewegung und nicht die Blende
|
||||
selbst den Inpoint bestimmt. `rematch --refine` nutzt denselben lokalen
|
||||
FFmpeg/Pillow-Aligner und schreibt den korrigierten Inpoint direkt zurück in
|
||||
`.cache/match_results.json`.
|
||||
|
||||
## Vision-Layer (optional)
|
||||
|
||||
Optional kann `python cli.py match --beat N --vision` einen Vision-Layer
|
||||
zuschalten. Dann werden pro Trailer-Beat und pro wenigen Scene-Level-
|
||||
Kandidaten je drei Frames (Anfang, Mitte, Ende) von einem visionfähigen
|
||||
OpenAI-kompatiblen Modell beschrieben. Die Beschreibungen liegen in
|
||||
`.cache/vision_descriptions.json` und werden wiederverwendet. Vision erzeugt
|
||||
nur zusätzliche Suchanker; der eigentliche Match muss weiterhin durch CV,
|
||||
Content-Reranking, Timing und Duration-Coverage bestätigt werden.
|
||||
|
||||
Gecachte Szenenbeschreibungen zählen nur, wenn sie vom aktuell konfigurierten
|
||||
Vision-Modell stammen. Bei langen semantisch passenden Source-Szenen
|
||||
beschreibt der Vision-Layer zusätzlich wenige lokale Zeitfenster und cached
|
||||
auch diese Fenster, damit eine grob ähnliche Szene nicht automatisch mit dem
|
||||
falschen Bewegungs- oder Dialogmoment gleichgesetzt wird. Dieser lokale
|
||||
Fenster-Probe ist bewusst breiter als die finale Seed-Auswahl: eine lange
|
||||
Dialogszene kann in der Gesamtbeschreibung nur als Gespräch erscheinen, aber
|
||||
an einer späteren Stelle trotzdem genau die gesuchte Aktionsphase enthalten.
|
||||
|
||||
Für diese Probe wird die grobe Szenenähnlichkeit ohne harte Aktionsstrafe
|
||||
gerankt; die harte Aktionsprüfung greift erst auf den lokalen Fenstern und
|
||||
dem finalen Source-Zeitbereich.
|
||||
|
||||
## Aktionsphase-Verifikation nach dem CV-Match
|
||||
|
||||
Nach dem CV-Match kann derselbe Vision-Layer den konkreten finalen Source-
|
||||
Zeitbereich nochmals gegen den Trailer-Beat prüfen. Starke Aktionsphasen wie
|
||||
Annäherung, Kuss/Stirnkontakt, Handbewegungen oder Schneiden müssen dann auch
|
||||
im Source-Fenster beschrieben sein; fehlt diese Aktionsphase, wird der
|
||||
Treffer nicht gespeichert, selbst wenn der Low-Level-CV-Score hoch ist.
|
||||
|
||||
Wenn die Szene selbst plausibel ist, aber der konkrete Source-Zeitpunkt
|
||||
diese Aktionsphase verfehlt, sucht der Matcher automatisch dichter innerhalb
|
||||
derselben Source-Szene nach lokalen Vision-Fenstern mit der passenden Aktion
|
||||
und richtet den Inpoint mit der Motion-Phase-Prüfung darauf neu aus. Erst
|
||||
wenn auch diese In-Scene-Reparatur scheitert, wird der Treffer verworfen.
|
||||
Diese In-Scene-Reparatur läuft auch für semantisch gültige Treffer aus langen
|
||||
Source-Szenen.
|
||||
|
||||
Die Kandidatenbewertung dieser Reparatur vergleicht zwei Kontexte: den ganzen
|
||||
Beat als semantischen Handlungsrahmen und das konkret sichtbare Beat-Segment
|
||||
als Phasenprüfung. Ein Source-Zeitpunkt muss nicht nur „die Szene mit dem
|
||||
Kuss" enthalten, sondern auch zur aktuellen Bewegungsphase des sichtbaren
|
||||
Trailerabschnitts passen. Pro Kandidat fließt zusätzlich ein lokaler
|
||||
Content-/Motion-Frame-Score ein, damit cached Vision-Beschreibungen keinen
|
||||
sichtbar versetzten Bewegungsmoment überstimmen.
|
||||
|
||||
## Segmentierte Beats (Beats mit Blenden / Inseln)
|
||||
|
||||
Bei blendigen oder segmentierten Beats nutzt die semantische Action-Suche den
|
||||
ganzen Trailerbeat als Kontext. Die eigentliche Frame-Ausrichtung bleibt auf
|
||||
das sichtbare Segment begrenzt; der gefundene Source-Inpoint wird um den
|
||||
Trailer-Offset des Segments verschoben. So geht die globale Aktionsbeschreibung
|
||||
eines Beats nicht verloren, nur weil der scorebare Teil erst nach einer Blende
|
||||
beginnt.
|
||||
|
||||
Die Suche nach diesem Action-Window prüft pro Segment zwei Beschreibungen:
|
||||
zuerst die des konkret sichtbaren Segments (so trifft die Phasensuche genau
|
||||
die gerade gezeigte Bewegung), als Rückfall die des gesamten Beats. Der
|
||||
Beat-Kontext gewinnt nur, wenn er deutlich (>0.06) besser scort; sonst bleibt
|
||||
das Segment-Fenster die Wahl, weil die Beat-Beschreibung Aktionen aus
|
||||
Fade-Bildern mit aufnehmen kann, die im sichtbaren Segment nicht stattfinden.
|
||||
Der Trailer-Offset-Shift wird nur angewendet, wenn tatsächlich der Beat-
|
||||
Kontext benutzt wurde; bei segmentbasierter Wahl ist das gefundene Fenster
|
||||
bereits auf die sichtbare Aktionsphase ausgerichtet.
|
||||
|
||||
Der Segment-Offset zählt nur über vorherige scorebare Bildinseln, nicht über
|
||||
schwarze oder blendige Lücken. Nach dem Retiming wird die nutzbare Source-
|
||||
Dauer erneut geschätzt; läuft die Source am Ende in eine sichtbar andere
|
||||
Aktionsphase, wird der Treffer im Cutter-Report klar als phasenkritisch
|
||||
markiert. Schwarz/Placeholder wird nur für wirklich ungematchte Trailer-
|
||||
Bereiche oder Fades verwendet, nicht um sichtbare Kandidatenbewegung im Review
|
||||
zu verstecken.
|
||||
|
||||
Diese Span-Schätzung ist strenger als der grobe Suchscore: Ein fast stehender
|
||||
Anfang darf einen Match nicht retten, wenn spätere Frames sichtbar in eine
|
||||
andere Gestik, Körperposition oder eintretende Figur driften. Stabile
|
||||
Score-Plateaus dürfen nur verlängern, wenn sie noch nah genug am Anfangsniveau
|
||||
liegen; sonst bleibt der Treffer vorläufig und muss neu gesucht oder visuell
|
||||
geprüft werden. Der Review-Clip zeigt den Kandidaten weiterhin sichtbar, damit
|
||||
Phasenfehler nicht durch Schwarz verdeckt werden.
|
||||
|
||||
Für Multi-Shot-Beats gilt zusätzlich eine Segment-Schwelle pro sichtbarer
|
||||
Einstellung. Ein gutes erstes Segment darf kein zweites Segment mit schwachem
|
||||
Score mitziehen. Segmente unter `multi_shot_segment_threshold` werden nicht als
|
||||
stabile Wahrheit behandelt, sondern innerhalb derselben plausiblen Source-Scene
|
||||
nachjustiert. Die Nachjustierung nutzt eine saliency-gewichtete Mehrframe-Prüfung:
|
||||
Timecodes und statische Randbereiche werden entwertet, kontrastreiche und über
|
||||
mehrere Trailerframes unterscheidbare Bildbereiche zählen stärker. Dadurch kann
|
||||
eine schwache zweite Einstellung phasengenauer repariert werden, ohne den Fehler
|
||||
durch Schwarzbild zu verdecken oder einen Beat manuell zu kuratieren.
|
||||
|
||||
Der Cutter-Report verwendet Clip-Caching. Bereits vorhandene Compare-Clips werden
|
||||
wiederverwendet; bei gezielten Rematches wird nur der betroffene Beat neu gerendert
|
||||
(`CUTTER_REPORT_FORCE_BEATS`). So bleibt der Report aktuell, ohne alle Beats jedes
|
||||
Mal neu zu kodieren.
|
||||
|
||||
## Vision-Seeds vs. Vollscan
|
||||
|
||||
Der gewichtete Vision-Seed-Pfad ersetzt standardmäßig keinen normalen
|
||||
FFmpeg-Vollscan. Vision-Beschreibungen sind semantische Hinweise, aber keine
|
||||
Beweise; der volle CV-Scan bleibt aktiv, damit falsch bewertete Vision-Szenen
|
||||
echte Treffer nicht verdrängen. Für schnelle Experimente kann
|
||||
`skip_coarse_scan_with_weighted_seeds = true` gesetzt werden.
|
||||
|
||||
Gewichtete Vision-Seeds werden nicht zuerst durch den alten Midpoint-Template
|
||||
Refine verschoben; sie gehen direkt in die lokale Content-Alignment-Prüfung.
|
||||
Das schützt wiederholte Gesprächseinstellungen, bei denen ähnliche Momente
|
||||
mehrfach in derselben Szene vorkommen.
|
||||
|
||||
Innerhalb der automatisch von Vision vorgeschlagenen Szenen läuft zusätzlich
|
||||
eine dichte lokale Bildsequenzsuche. Sie misst den Phasenversatz in kleinen
|
||||
Zeitschritten direkt am Bildinhalt und bevorzugt Kandidaten mit genügend
|
||||
Restdauer in derselben Source-Szene. Das ist kein manueller Override: Vision
|
||||
grenzt nur Suchbereiche ein, die Auswahl bleibt Content-, Timing- und
|
||||
Coverage-getrieben. Nach einem dichten Vision-Treffer darf der spätere
|
||||
lokale Aligner nur noch im Bereich dieses Scan-Schritts nachjustieren. So
|
||||
kann ein korrekt gefundener Bewegungsmoment nicht wieder um viele Frames in
|
||||
eine ähnlich aussehende Phase derselben Szene verschoben werden.
|
||||
|
||||
Für Vision-Action-Fenster nutzt die finale Retiming-Prüfung eine gemeinsame
|
||||
Content-und-Motion-Suche pro Frame. Content und Bewegungsphase werden nicht
|
||||
mehr als zwei getrennte Korrekturschritte angewendet; das verhindert, dass
|
||||
eine kurze Geste erst korrekt erkannt und anschließend in eine spätere
|
||||
ähnliche Körperhaltung verschoben wird. Wenn mehrere Vision-Kandidaten in
|
||||
derselben Source-Szene ähnlich gut scoren und die Beat-Dauer abdecken,
|
||||
bevorzugt der Matcher die frühere Phase.
|
||||
Die Vision-Recovery läuft nicht nur für komplett fehlende Beats, sondern auch
|
||||
für schwache unbestätigte Treffer. Gerade Low-Light-Beats dürfen nicht an einem
|
||||
falschen dunklen CV-Treffer hängen bleiben, wenn der Cache semantisch eine
|
||||
bessere Handlungsphase kennt.
|
||||
Bei langen Source-Szenen prüft die Action-Window-Suche immer den Szenenanfang
|
||||
und mehrere frühe Fenster, bevor sie gleichmäßig über die ganze Szene sampelt.
|
||||
Damit gehen kurze Trailer-Aktionen am Anfang einer langen Szene nicht unter,
|
||||
wenn der Rest der Szene aus Credits, Schwarzbild oder ruhigen Folgeframes
|
||||
besteht.
|
||||
Wenn ein Action-Window die starke Beat-Aktion explizit enthält, darf es eine
|
||||
etwas niedrigere Textähnlichkeit haben; die Handlung zählt dann stärker als
|
||||
Nebenwörter zu Licht, Bildausschnitt oder Stimmung.
|
||||
Bereits gecachte Action-Windows einer Szene bleiben gültige Kandidaten, auch
|
||||
wenn sich das aktuelle Sampling-Raster ändert. So verliert der Matcher keine
|
||||
teuren Vision-Hinweise und muss dieselben Fenster nicht erneut beschreiben.
|
||||
Wenn neue Vision-Calls deaktiviert sind, darf die Recovery vorhandene Cache-
|
||||
Beschreibungen trotzdem lesen; das erzeugt keine API-Kosten und verhindert,
|
||||
dass alte schwache CV-Treffer stehen bleiben.
|
||||
Schlägt die CV-Feinjustierung bei einem semantisch klaren Low-Light-Fenster
|
||||
fehl, bleibt das Action-Window als provisorischer Treffer erhalten. CV darf
|
||||
einen dunklen Treffer verfeinern, aber nicht einen eindeutigen Cache-Hinweis
|
||||
komplett verwerfen.
|
||||
Zusätzlich kann Recovery vorhandene gecachte Action-Windows direkt über alle
|
||||
Szenen ranken. Dieser schnelle Pfad vermeidet einen teuren Vollscan, wenn der
|
||||
Cache bereits eine starke Aktion wie Hand-am-Mund, Kuss oder Blickwechsel
|
||||
enthält.
|
||||
Eindeutige Begriffe aus der Beat-Beschreibung wirken als harte Filter für
|
||||
Vision-Fenster: `mouth` muss im Kandidaten wiederkehren, `dark interior` darf
|
||||
nicht auf Outdoor-Material fallen, und markante Personenmerkmale wie `blonde`
|
||||
bleiben bindend.
|
||||
|
||||
Der zusätzliche Hi-Res-Phasenrefine bleibt lokal um den bereits validierten
|
||||
Inpoint und übernimmt nur klare Verbesserungen. Er darf keine ganze lange
|
||||
Dialogszene nach ähnlichen Layouts durchsuchen, weil sonst dieselbe Location
|
||||
mit anderer Gestik als falsche Phase gewinnen kann und die Laufzeit explodiert.
|
||||
Die lokale Retune-Wertung nutzt deshalb nicht nur den mittleren Frame-Score,
|
||||
sondern auch den schlechtesten Einzelvergleich, die ersten sichtbaren Frames
|
||||
und die Frame-zu-Frame-Bewegung. Dadurch gewinnt nicht mehr ein späteres
|
||||
Standbild derselben Einstellung, nur weil Fenster, Gesichter und Licht fast
|
||||
identisch aussehen.
|
||||
Unsichere Einzeltreffer ohne Segmentliste laufen ebenfalls durch diesen lokalen
|
||||
Phasen-Probe. Das repariert alte Cache-Einträge, deren Szene korrekt ist, deren
|
||||
Inpoint aber einige Frames in der Bewegung daneben liegt. Der Probe bleibt auf
|
||||
kleine lokale Shifts begrenzt und wird nicht für jeden bestätigten Treffer
|
||||
erzwungen, damit Report-Refreshes nicht zum Vollscan werden.
|
||||
Report-Clips werden zusätzlich an den bekannten Source-Szenenstart plus eine
|
||||
sehr kurze Ein-Frame-Guard-Zone geklemmt, damit ein knapp vor oder direkt auf
|
||||
der Schnittkante liegender Inpoint nicht mit Frames der vorherigen Einstellung
|
||||
beginnt. Die Guard-Zone bleibt bewusst klein, weil eine längere Korrektur die
|
||||
sichtbare Bewegungsphase innerhalb derselben Einstellung verschieben würde.
|
||||
|
||||
## Multi-Shot-Beats
|
||||
|
||||
Enthält ein Trailerbeat selbst einen harten Umschnitt, werden Kandidaten an
|
||||
angrenzenden Source-Szenengrenzen zusätzlich als zusammenhängender Multi-Shot-
|
||||
Span geprüft. Ein Match darf dann über eine Source-Szenengrenze laufen, aber
|
||||
nur wenn die relative Source-Grenze zeitlich zu einem erkannten Trailer-
|
||||
Umschnitt passt. So kann ein Beat aus Frage/Antwort-Shots vollständig erfasst
|
||||
werden, ohne Szenen willkürlich zusammenzukleben.
|
||||
|
||||
## Titel- und Grafikbeats
|
||||
|
||||
Dunkle Trailerkarten mit deutlich isoliertem Text werden im Cutter-Report als
|
||||
`GFX` markiert, wenn es keinen Source-Treffer gibt. Diese Beats sind keine
|
||||
fehlgeschlagenen Matches: Der Cutter soll die Trailer-Grafik beziehungsweise
|
||||
eine NLE-Titelkarte übernehmen und nicht im Spielfilm nach einem Bild suchen.
|
||||
|
||||
## Reranking-Pipeline
|
||||
|
||||
Vor dem teuren Frame-Refine wird der gesamte Kandidatenpool mit einer
|
||||
schnellen festen Inhaltsprüfung neu sortiert. Dadurch können korrekte Treffer
|
||||
aus wiederholten Einstellungen einer Szene nach oben kommen, auch wenn ein
|
||||
freier Template-Peak an anderer Stelle numerisch stärker war. Suchanker
|
||||
bleiben im Pool erhalten, dürfen aber erst nach der Inhaltsprüfung nach oben
|
||||
rücken. Wenn ein Kandidat visuell plausibel ist, aber wegen Trailerblende oder
|
||||
kurzem Source-Span die normale Coverage knapp verfehlt, wird er als
|
||||
provisional Match behalten statt als `NO MATCH` verworfen.
|
||||
|
||||
Dieses Reranking berücksichtigt zusätzlich die verbleibende Szenenlänge ab
|
||||
dem Kandidaten-Inpoint, nutzt bewusst nur wenige repräsentative Referenzframes
|
||||
und eine begrenzte Kandidatenzahl. Confirmed Matches werden zusätzlich durch
|
||||
eine feste nahezu-Whole-Frame-Prüfung aus Luma, Kanten, Farbhistogramm und
|
||||
räumlichen 4×4-Farbhistogrammen gedeckelt. Auch der lokale Content-Aligner
|
||||
darf einen Inpoint nur noch übernehmen, wenn die feste Whole-Frame-/Spatial-
|
||||
Validation dadurch besser wird.
|
||||
|
||||
Für gewichtete Vision-Kandidaten gibt es zusätzlich eine eigene Provisional-
|
||||
Bewertung aus Content-Score, Restdauer und Seed-Stärke. Die Cache-
|
||||
Normalisierung für Report/Export verwendet dieselbe niedrigere
|
||||
Content-Untergrenze für nicht bestätigte Vision-Provisional-Treffer und
|
||||
übernimmt die Multi-Shot-Coverage-Regel: gecachte Treffer, die passend zu
|
||||
internen Trailer-Umschnitten über angrenzende Source-Szenen laufen, werden
|
||||
nicht mehr auf die erste Source-Szene zurückgekürzt.
|
||||
|
||||
## Continuity-Seeds
|
||||
|
||||
Gezielte Einzel-Beat-Matches gewichten außerdem die automatisch aus
|
||||
Nachbarbeats abgeleiteten Continuity-Seeds. Wenn ein Beat direkt an einen
|
||||
bereits passenden Vorgänger anschließt, kann ein späterer ähnlich aussehender
|
||||
Moment derselben Dialogszene den erwarteten Anschluss nicht mehr nur wegen
|
||||
eines höheren Standbildscores verdrängen. Diese Continuity-Seeds sind aber
|
||||
nur Suchanker: in derselben Szene darf ein späterer Inpoint gewinnen, wenn
|
||||
die mehrframeige Content-Prüfung die Bewegungsphase klar besser trifft.
|
||||
|
||||
## Vision-Prepass für gezielte Match-Läufe
|
||||
|
||||
Bei aktivierter Vision wird für gezielte Match-Läufe zuerst ein schneller
|
||||
seed-basierter CV-Prepass ausgeführt. Er überspringt den vollen FFmpeg-Stream
|
||||
nur vorläufig und akzeptiert einen Treffer erst nach derselben Bild-/
|
||||
Phasenvalidierung wie der normale Matcher. Nur nicht gelöste Beats fallen
|
||||
danach auf den vollständigen Scan zurück.
|
||||
|
||||
Provisional Treffer aus diesem schnellen Prepass sind nicht endgültig: wenn
|
||||
sie unterhalb der Confirmed-Schwelle bleiben, läuft zusätzlich der
|
||||
vollständige CV-Scan und darf den besseren oder bestätigten Treffer
|
||||
übernehmen.
|
||||
|
||||
## Fehlertoleranz bei Vision-API
|
||||
|
||||
OpenRouter-/Vision-Rate-Limits werden mit progressiv längeren Pausen erneut
|
||||
versucht. Auch Netzfehler beim Lesen der Antwort (Timeouts,
|
||||
Verbindungsabbrüche während einer DSL-Trennung) werden als retrybar
|
||||
behandelt, nicht nur Verbindungsfehler beim Verbindungsaufbau.
|
||||
Billing-, Credit- oder Token-Guthaben-Fehler werden dagegen sofort als
|
||||
echter Blocker gemeldet, weil Warten dort nicht hilft.
|
||||
|
||||
Schlägt die Vision-Verifikation während der finalen Filter-/Repair-Stufe
|
||||
trotzdem dauerhaft fehl, wird der bisherige gecachte Treffer für diesen
|
||||
Beat behalten statt verworfen — ein Netzproblem darf keinen schon korrekt
|
||||
gefundenen Match aus dem Cache löschen.
|
||||
|
||||
## Phasen-Reparatur und Recovery
|
||||
|
||||
Die Phasen-Reparatur an gefundenen Treffern läuft nicht nur in „langen"
|
||||
Source-Szenen, sondern überall dort, wo die Szene mehr als nur das
|
||||
Segment-Fenster trägt. Eine korrigierte Position wird übernommen, sobald sie
|
||||
das Bildinhalt-Validate besteht UND nicht spürbar schlechter scort als das
|
||||
Original (≤ 0.02 Verlust). Bereits bestätigte Treffer in eng zugeschnittenen
|
||||
Szenen werden bewusst nicht angefasst, damit ein guter Match nicht durch
|
||||
eine nominell gleichwertige Alternative ausgetauscht wird.
|
||||
|
||||
Beats, die nach dem CV-Lauf weder als Vollmatch noch als Segmentmatch
|
||||
landen, durchlaufen anschließend eine Recovery-Stufe: Vibe-Check
|
||||
(Histogramm/pHash) liefert Top-K Kandidatenszenen, die semantische
|
||||
Action-Window-Suche prüft darin die Phase des sichtbaren Trailerbeat-
|
||||
Anteils, und der CV-Aligner setzt den Inpoint frame-genau. Übernommen wird
|
||||
nur ein Kandidat, der dieselbe Vision-Phasenvalidierung wie der Hauptpfad
|
||||
besteht. Beats ohne sichtbares Bildmaterial (Logos, Titel-Karten,
|
||||
durchgehende Fades) werden gar nicht erst gesucht — sie sind bewusst kein
|
||||
Match.
|
||||
|
||||
## Behandlung von Blenden und Schwarzfeldern
|
||||
|
||||
Lange Trailerbeats werden nicht automatisch über ihre gesamte Beat-Länge
|
||||
gegen einen einzigen Source-Clip validiert. Sobald nach einem sichtbaren
|
||||
Source-Abschnitt eine anhaltende Schwarzblende oder Titel-/Credit-Insel
|
||||
beginnt, endet der matchbare Referenzbereich dort; zwei aufeinanderfolgende
|
||||
dunkle Samples reichen dafür. Spätere Text-/Creditbilder im selben Beat
|
||||
gehen nicht mehr in Reranking, Validation oder Span-Schätzung ein.
|
||||
|
||||
Wenn nach einer Blende wieder sichtbares, matchbares Material kommt, wird
|
||||
der Beat nicht mehr als „Source + schwarzer Tail" behandelt. Der CLI-Match
|
||||
speichert zusätzliche `MatchSegment`-Einträge für jede automatisch erkannte
|
||||
sichtbare Insel; der HTML-Report setzt diese Source-Segmente frame-lockend
|
||||
zusammen und füllt nur echte Zwischenlücken mit Schwarz.
|
||||
|
||||
Beats mit mehreren sichtbaren Inseln werden direkt segmentiert gesucht,
|
||||
statt zuerst als ein künstlich zusammenhängender Source-Clip über den
|
||||
ganzen Film zu laufen. Falls ein kompletter Beat keinen belastbaren
|
||||
Einzelclip ergibt, versucht der Matcher dieselbe Segmentlogik automatisch
|
||||
als Fallback. Sehr kurze Inseln dürfen zusätzlich in den Source-Szenen
|
||||
benachbarter bereits gematchter Beats lokal nach ihrer Bewegungsphase
|
||||
suchen.
|
||||
|
||||
Besteht ein Beat nach automatischer Fade-/Titel-Filterung nur aus einer
|
||||
einzigen sichtbaren Insel, wird diese Insel direkt als primäres Suchziel
|
||||
verwendet. Gecachte segmentierte Treffer werden gegen die automatisch
|
||||
sichtbare Referenzdauer normalisiert, nicht gegen Schwarz-/Blendränder
|
||||
des gesamten Beats.
|
||||
|
||||
Sehr dunkle, kontrastarme oder noch nicht sauber auf-/abgeblendete
|
||||
Referenzframes werden aus Score, Inhalts-Reranking, Phasen-Alignment und
|
||||
Motion-Templates herausgenommen. Sichtbare Fade-Rampen werden nur in eine
|
||||
matchbare Insel hinein erweitert, wenn sie strukturell stark zum ersten
|
||||
bzw. letzten scorebaren Frame derselben Einstellung passen.
|
||||
|
||||
Treffer unter `provisional_content_threshold` werden nicht mehr gespeichert
|
||||
oder aus alten Cache-Ergebnissen übernommen.
|
||||
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |