Zum Inhalt springen
Video2Any
2026-08-25

0.2.0: Was ein Webcam-Fenster mit der Folienerkennung macht

Wir haben heute Version 0.2.0 von video-slide-extractor auf npm veröffentlicht. Das Paket ist ein Detektor, der das Bild blockweise vergleicht: Er tastet das Video in festen Abständen ab und entscheidet Bild für Bild, ob der Inhalt auf dem Schirm zur nächsten Folie gewechselt hat. Es ist derselbe Detektor, den Video2Any in deinem Browser ausführt. Der Code ist offen, hat keine Abhängigkeiten, steht unter MIT-Lizenz und läuft im Browser wie unter Node.

Die im Juli veröffentlichte 0.1.x war eine Kopie des Produktcodes vom 16. Juli. Bei einer sauberen Bildschirmaufnahme entscheidet sie richtig, bei einem guten Teil der Aufnahmen, die Leute tatsächlich haben, falsch. Es gibt zwei Situationen, in denen sie versagt; das Produkt hat beide im folgenden Monat gelöst, das Paket kannte keine davon. Die 0.2.0 übernimmt diese beiden.

Im Folgenden erklären wir drei Dinge, in dieser Reihenfolge: wie die hochgeladenen Videos wirklich aussahen, welchen Befund der Benchmark schon vor jeder Änderung geliefert hat, und was wir nach der Änderung gemessen haben.

Auf dieser Seite

Wie die hochgeladenen Videos wirklich aussahen

Video2Any protokolliert Extraktionen seit dem 16. Juli. Bis heute haben 96 Personen 411 Durchläufe gestartet; 354 davon liefen durch und ergaben 291 Dateien: 188 PowerPoint-Dateien, 95 PDFs und 8 Bildarchive. Fast immer handelt es sich um lokale Dateien — in 398 der 411 Durchläufe hat die Person ein Video gewählt, das bereits auf demselben Rechner lag, auf dem der Tab geöffnet war. (Die zwei Tage, an denen unsere eigenen automatisierten Tests liefen, sind aus allen Zahlen dieses Artikels herausgerechnet.)

Bei den 106 Durchläufen, deren Datei die eigene Länge angab, war das Video im Mittel 53 Minuten lang. Das sind keine fünfminütigen Produktdemos, sondern Vorlesungen, Meetings und Kursaufzeichnungen. Gegen diese Art von Video war der Juli-Detektor nie gemessen worden.

Aufschlussreicher ist die Zahl der Folien pro Durchlauf. Abgeschlossene Durchläufe ergaben im Mittel 64 Folien — eine Zahl, die normal wirkt und die Verteilung verdeckt: 64 Durchläufe kamen über 100 Folien, sieben über 300, und vier lieferten genau 900. 900 ist die höchste Zahl an Stichproben, die der Durchlauf nehmen darf; in diesen vier Fällen wurde also jede Stichprobe als neue Folie gewertet und der Detektor hat keine einzige verworfen. Eines dieser Videos war 30 Minuten lang.

Auch die Ausbeute ist kein fester Wert: Bei gleicher Einstellung ergaben Videos unter einer Stunde etwa 6.5 Folien pro Minute, Videos über einer Stunde etwa 1.0. Ein Teil dieses Unterschieds ist echt, denn ein zweistündiges Meeting wechselt pro Minute seltener das Bild als eine kurze Demo. Das Problem ist, dass die Zahl allein nicht sagt, welche Seite recht hat. Die vier Durchläufe am Anschlag sagen es: Wenn alle Stichproben behalten werden, entscheidet der Detektor nichts.

Der Benchmark lieferte diesen Befund bereits

Noch vor jeder Korrektur hat die 0.2.0 dem Paket das Verzeichnis bench/ hinzugefügt. Die Testvideos entstehen aus einer bekannten Folienfolge, sodass die genaue Sekunde jedes Wechsels beim Rendern feststeht und niemand sie von Hand etikettieren muss. Jeder Foliensatz wird auf drei Arten gerendert: eine saubere Fassung, eine auf crf 45 komprimierte Fassung für Kompressionsrauschen, und eine mit einem animierten Webcam-Fenster in der Ecke.

Beim MIT-Foliensatz (46 Folien, alle 2 Sekunden eine Stichprobe in 160×90) erreichte die saubere Fassung ein F1 von 0.966. Mit darübergelegtem Webcam-Fenster fällt das F1 auf 0.538: Der Detektor holte 125 Aufnahmen aus einem Inhalt von 46 Folien, die Precision lag bei 0.368, und 63 % dessen, was er zurückgab, war eine bereits aufgenommene Folie.

Der Grund ist arithmetisch, keine Frage von Pech. Die voreingestellte Bedingung lautet, dass sich mehr als 2 % der Blöcke ändern. Ein Webcam-Fenster, ein Mauszeiger oder ein in Schleife laufendes Logo belegen für sich allein mehr als 2 % der Blöcke, solange sie sich weiterbewegen. Damit überschreitet jedes abgetastete Bild die Schwelle, auch wenn keine einzige Folie gewechselt hat. Am Ende hängt die Zahl der Folien davon ab, ob der Vortragende die Kamera anhatte, und nicht davon, wie viele Seiten der Foliensatz hat.

Wir haben diese Zeile so veröffentlicht, wie sie ist. Die vier Durchläufe mit 900 Folien sind dieselbe Zeile, gesehen an echtem Material, und genau sie behebt die 0.2.0.

Was die 0.2.0 hinzufügt

Drei neu exportierte Funktionen, eine Option und die Typdeklarationen für all das.

  • buildActivityMask

    Diese Funktion nimmt die abgetasteten Bilder entgegen, findet die Blöcke, die sich in fast jedem Paar aufeinanderfolgender Bilder ändern — das Fenster, den Mauszeiger, die Uhr in der Ecke — und gibt eine Maske zurück, die diese Blöcke vollständig vom Vergleich ausnimmt. Würde die Maske den größten Teil des Bildes bedecken, gibt die Funktion null statt einer Maske zurück. Dieser Zweig ist wichtig: Wenn sich fast das ganze Bild bewegt, ist das Bewegte genau der Inhalt, auf den es ankommt, und ihn auszunehmen hieße, auf Erkennung zu verzichten.

  • chooseThreshold

    Ein und derselbe feste Schwellenwert kann nicht für einen Foliensatz und für eine sprechende Person in Großaufnahme taugen. Ein Foliensatz steht still, ein echter Wechsel hebt sich deutlich vom Rauschen ab. Kameramaterial bewegt sich ununterbrochen, und dort macht 0.02 aus jedem Bild eine neue Folie. Diese Funktion untersucht die Verteilung der Änderung zwischen aufeinanderfolgenden Bildern und gibt { changedRatio, mode } zurück. Das Feld mode ist absichtlich offengelegt: Es hat den Wert bimodal, static, motion oder default und benennt, für welche Art Material der Detektor sich entschieden hat. Fällt das Ergebnis schlecht aus, siehst du die Grundlage der Entscheidung, statt sie zu vermuten.

  • analyzeSamples

    Diese Funktion führt die beiden obigen Schritte über dieselben Bilder aus und gibt { mask, choice } zurück. Das ist es, was die meisten Aufrufer brauchen: Du hast Stichproben und willst wissen, mit welchen Parametern erkannt werden soll, ohne vorher herausfinden zu müssen, dass „mit welchen Parametern“ in Wahrheit zwei voneinander unabhängige Entscheidungen sind.

  • frameDiff({ collect: true })

    Mit dieser Option gibt frameDiff zusätzlich flags zurück: ein Byte pro Block, das angibt, an welchen Stellen sich dieses Bild geändert hat. Damit lassen sich die Bereiche zeichnen, die der Detektor als verändert gewertet hat. Zurückgegeben wird das Feld nur, wenn du collect übergibst, denn ein Feld, das normalerweise gar nicht existiert, lässt sich zuverlässiger verwenden als eines, das fast immer null ist.

In der Reihenfolge, in der du sie aufrufen würdest, ist das alles:

import { analyzeSamples, createSlideDetector } from 'video-slide-extractor';

// frames: RGBA-Stichproben, etwa alle 2 s, auf 160x90 verkleinert
const { mask, choice } = analyzeSamples(frames, 160, 90);
console.log(choice.mode);            // 'bimodal' | 'static' | 'motion' | 'default'

const detect = createSlideDetector(160, 90, {
  mask,
  changedRatio: choice.changedRatio
});

const kept = [];
frames.forEach((frame, i) => {
  if (detect(frame).keep) kept.push(i);   // Stichproben, die in voller Auflösung geholt werden
});

Was es bringt, gemessen

Gleiche Testvideos, gleiches Protokoll: MIT-Foliensatz, 46 Folien, Webcam-Fassung, alle 2 s eine Stichprobe in 160×90; eine Erkennung gilt als richtig, wenn sie weniger als ±2.5 s vom etikettierten Wechsel entfernt liegt.

MIT-Foliensatz, Fassung mit Webcam-Fenster, 46 tatsächliche Folien.
ErkennungAufnahmenPrecisionRecallF1Duplikatrate
0.1.x-Voreinstellungen (changedRatio 0.02)1250.3681.0000.5380.632
Mit Aktivitätsmaske540.8150.9570.8800.185
Mit Maske und automatischem Schwellenwert351.0000.7610.8640.000

Allein durch die Maske sinken die Aufnahmen von 125 auf 54, die Duplikate von 63 % auf 19 %, und das F1 steigt von 0.538 auf 0.880. Material, das sie nicht braucht, beeinflusst sie nicht: Bei der sauberen und der verrauschten Fassung gibt sie null zurück, diese beiden Zeilen liefern also genau das Ergebnis der 0.1.1.

Beim Schwellenwert gibt es dagegen Vor- und Nachteile, und das erklärt man am besten anhand der Zeile, die schlecht ausfällt: Beim sauberen MIT-Foliensatz ist der berechnete Schwellenwert vorsichtiger als die alte Konstante, und der Recall fällt von 0.935 auf 0.761. Genau deshalb hat sich DEFAULTS in dieser Version nicht geändert. analyzeSamples ist für den Fall gedacht, dass du nicht weißt, welche Art Material dir vorliegt. Weißt du es, sind die Konstanten weiterhin vorhanden, und du entscheidest.

Beide neuen Zeilen haben wir mit dem Benchmark-Werkzeug auf dem Commit der Veröffentlichung gemessen. Die veröffentlichte RESULTS.md berichtet noch ausschließlich den Methodensatz der 0.1.x; die Maske dort als eigenständige Methode aufzunehmen, ist der nächste Schritt in bench/.

Was wir bewusst weggelassen haben

Das globale Zusammenlegen von Duplikaten und das Glätten von Übergängen stecken nicht im Paket und kommen auch nicht hinzu. Beides setzt voraus, das ganze Video auf einmal zu sehen: welche Einstellung später wiederkam, oder ob drei Aufnahmen in Wahrheit aus derselben Überblendung stammten. Das ist Aufgabe der Verarbeitungskette. Ein Detektor, der nur beantwortet, ob sich das Bild geändert hat, sollte nicht zusätzlich das ganze Video aufbewahren.

Die Foliendichte gehört ebenfalls nicht hierher: Wie viele Folien eine 90-minütige Vorlesung ergeben sollte, ist eine Produktentscheidung und keine Erkennungsfrage. Video2Any hat dazu ein Kriterium und bietet einen Regler an. Eine Bibliothek muss diese Entscheidung nicht für dich treffen; es genügt, wenn sie dir sagt, wo sich das Bild geändert hat.

Drei verwandte Texte:

Was als Nächstes kommt

  • Eine Haltezeit. Neuer Inhalt muss künftig ein paar Sekunden stillstehen, bevor er als Folie zählt. Das beendet die Durchläufe mit 900 Folien wirklich: Die Maske entfernt das Fenster, aber eine schnelle Überblendung oder ein Sprung in der Zeitleiste lösen die Bedingung weiterhin aus. In Video2Any funktioniert es bereits. Ins Paket kommt es, sobald die Schnittstelle geklärt ist, denn eine Haltezeit bedeutet ohne Abtastrate nichts, und das Paket weiß bewusst nicht, in welchem Abstand du abgetastet hast.
  • Das synthetische Material durch eine echte Kamera ersetzen. Das derzeitige Testvideo ist eine gerenderte Animation in einer Ecke. Eine Webcam, die einen echten Menschen aufnimmt, ist der schwierigere und wirklichkeitsnähere Test, und daran sollte die Maske gemessen werden.
  • Der Fehler, den keine der beiden Methoden löst. Unterscheiden sich zwei aufeinanderfolgende Folien nur um eine hinzugefügte Aufzählungszeile, bleibt dieser Unterschied bei 160×90 am Mindestwert der Bedingung kleben, sodass schrittweise aufgebaute Folien vollständig verloren gehen. Der Benchmark zeigt das sogar bei synthetischem Neurendering. Es ist ein Problem der Auflösung und der Bewertungsregel, und docs/evaluation.md macht die Zählweise solcher Aufbauten nun zu einem Parameter, den man angeben und berichten muss, statt sie der Auslegung der einzelnen Auswertenden zu überlassen.
  • Wo kalibriert werden sollte. Video2Any verteilt das Video auf vier Worker, und jeder kalibriert seinen Schwellenwert auf seinem eigenen Abschnitt; dieselbe Datei ergibt daher auf Rechnern mit unterschiedlicher Kernzahl leicht verschiedene Foliensätze. Das ist keine wünschenswerte Eigenschaft. Die naheliegende Lösung wäre, einmal global zu kalibrieren. Wir haben sie gebaut und gemessen: Bei einem 29-minütigen Meeting, in dem ffmpeg sieben echte Schnitte zählt, fand die abschnittsweise Kalibrierung sechs und die globale nur vier. Global zu kalibrieren ist nicht zwangsläufig besser, deshalb wird das Paket es nicht zur Voreinstellung machen, solange es keine Fassung gibt, die es ist.

Ausprobieren

Die Installation ist ein einziger Befehl, es gibt keine Abhängigkeiten, und es läuft auf jeder Aufnahme, die bei dir herumliegt:

npm i video-slide-extractor

Wenn du etwas damit baust oder Material hast, an dem es scheitert, schick es ans Repository. Ein Video, das den Detektor ratlos zurücklässt, ist uns mehr wert als ein Stern — so ist das Testvideo mit Webcam überhaupt entstanden.

Video umwandelnBlog