Manipulation von Video mit Canvas
Indem Sie die Fähigkeiten des video
-Elements mit einem canvas
kombinieren, können Sie Videodaten in Echtzeit manipulieren, um eine Vielzahl von visuellen Effekten auf das angezeigte Video anzuwenden. Dieses Tutorial zeigt, wie man mit JavaScript-Code Chroma-Keying (auch bekannt als "Green Screen-Effekt") durchführt.
Der Dokumentinhalt
Das HTML-Dokument, das für die Darstellung dieses Inhalts verwendet wird, ist unten gezeigt.
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>Video test page</title>
<style>
body {
background: black;
color: #cccccc;
}
#c2 {
background-image: url("media/foo.png");
background-repeat: no-repeat;
}
div {
float: left;
border: 1px solid #444444;
padding: 10px;
margin: 10px;
background: #3b3b3b;
}
</style>
</head>
<body>
<div>
<video
id="video"
src="media/video.mp4"
controls
crossorigin="anonymous"></video>
</div>
<div>
<canvas id="c1" width="160" height="96"></canvas>
<canvas id="c2" width="160" height="96"></canvas>
</div>
<script src="processor.js"></script>
</body>
</html>
Die Schlüsselstellen, die Sie beachten sollten, sind:
- Dieses Dokument enthält zwei
canvas
-Elemente, mit den IDsc1
undc2
. Canvasc1
wird verwendet, um das aktuelle Frame des Originalvideos anzuzeigen, währendc2
dazu genutzt wird, das Video nach der Anwendung des Chroma-Keying-Effekts darzustellen;c2
wird mit dem statischen Bild vorab geladen, das verwendet wird, um den grünen Hintergrund im Video zu ersetzen. - Der JavaScript-Code wird aus einem Skript namens
processor.js
importiert.
Der JavaScript-Code
Der JavaScript-Code in processor.js
besteht aus drei Methoden.
Initialisierung des Chroma-Key-Players
Die Methode doLoad()
wird aufgerufen, wenn das HTML-Dokument zunächst geladen wird. Die Aufgabe dieser Methode ist es, die für den Chroma-Keying-Prozess benötigten Variablen vorzubereiten und einen Ereignislistener einzurichten, um festzustellen, wann der Benutzer beginnt, das Video abzuspielen.
const processor = {};
processor.doLoad = function doLoad() {
const video = document.getElementById("video");
this.video = video;
this.c1 = document.getElementById("c1");
this.ctx1 = this.c1.getContext("2d");
this.c2 = document.getElementById("c2");
this.ctx2 = this.c2.getContext("2d");
video.addEventListener("play", () => {
this.width = video.videoWidth / 2;
this.height = video.videoHeight / 2;
this.timerCallback();
});
};
Dieser Code erfasst Referenzen zu den Elementen im HTML-Dokument, die von besonderem Interesse sind, nämlich das video
-Element und die beiden canvas
-Elemente. Darüber hinaus werden Referenzen zu den Grafikkontexten für jedes der beiden Canvas abgeholt. Diese werden verwendet, wenn wir tatsächlich den Chroma-Keying-Effekt durchführen.
Dann wird addEventListener()
aufgerufen, um das video
-Element zu beobachten, damit wir eine Benachrichtigung erhalten, wenn der Benutzer die Wiedergabetaste des Videos drückt. Als Reaktion auf den Beginn der Wiedergabe durch den Benutzer ruft dieser Code die Breite und Höhe des Videos ab, halbiert jede (wir werden die Größe des Videos halbieren, wenn wir den Chroma-Keying-Effekt durchführen), und ruft dann die Methode timerCallback()
auf, um zu beginnen, das Video zu beobachten und den visuellen Effekt zu berechnen.
Der Timer-Callback
Der Timer-Callback wird zunächst aufgerufen, wenn das Video zu spielen beginnt (wenn das "play"-Ereignis eintritt), übernimmt dann die Verantwortung, sich so zu etablieren, dass er regelmäßig aufgerufen wird, um den Keying-Effekt für jedes Frame zu starten.
processor.timerCallback = function timerCallback() {
if (this.video.paused || this.video.ended) {
return;
}
this.computeFrame();
setTimeout(() => {
this.timerCallback();
}, 0);
};
Das erste, was der Callback macht, ist zu überprüfen, ob das Video überhaupt läuft; wenn nicht, gibt der Callback sofort zurück, ohne etwas zu tun.
Dann ruft es die Methode computeFrame()
auf, die den Chroma-Keying-Effekt auf das aktuelle Videoframe anwendet.
Das Letzte, was der Callback macht, ist setTimeout()
aufzurufen, um sein erneutes Aufrufen so bald wie möglich zu planen. In der realen Welt würden Sie dies wahrscheinlich basierend auf dem Wissen über die Framerate des Videos zeitlich planen.
Manipulation der Videoframedaten
Die Methode computeFrame()
, die unten dargestellt ist, ist verantwortlich dafür, tatsächlich ein Frame von Daten abzurufen und den Chroma-Keying-Effekt durchzuführen.
processor.computeFrame = function () {
this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
const frame = this.ctx1.getImageData(0, 0, this.width, this.height);
const data = frame.data;
for (let i = 0; i < data.length; i += 4) {
const red = data[i + 0];
const green = data[i + 1];
const blue = data[i + 2];
if (green > 100 && red > 100 && blue < 43) {
data[i + 3] = 0;
}
}
this.ctx2.putImageData(frame, 0, 0);
};
Wenn diese Routine aufgerufen wird, zeigt das Videoelement das neueste Frame der Videodaten an, das folgendermaßen aussieht:
Dieses Videoframe wird in den Grafikkontext ctx1
des ersten Canvas kopiert, wobei als Höhe und Breite die zuvor gespeicherten Werte angegeben werden, um das Frame in halber Größe zu zeichnen. Beachten Sie, dass Sie das Videoelement an die Methode drawImage()
des Kontexts übergeben können, um das aktuelle Videoframe in den Kontext zu zeichnen. Das Ergebnis ist:
Das Aufrufen der Methode getImageData()
auf dem ersten Kontext holt eine Kopie der Rohgrafikdaten für das aktuelle Videoframe. Dies liefert rohe 32-Bit-Pixeldaten, die wir dann manipulieren können. Wir berechnen dann die Anzahl der Pixel im Bild, indem wir die Gesamtgröße der Bilddaten des Frames durch vier teilen.
Die for
-Schleife durchsucht die Pixel des Frames, extrahiert die Rot-, Grün- und Blau-Werte für jedes Pixel und vergleicht die Werte mit vordefinierten Zahlen, die verwendet werden, um den grünen Bildschirm zu erkennen, der durch das statische Hintergrundbild aus foo.png
ersetzt wird.
Jedes Pixel in den Bilddaten des Frames, das innerhalb der Parameter gefunden wird, die als Teil des grünen Bildschirms betrachtet werden, erhält einen neuen Alphawert von Null, was bedeutet, dass das Pixel vollständig transparent ist. Daher ist das Endbild im gesamten Bereich des grünen Bildschirms zu 100% transparent, sodass, wenn es mit ctx2.putImageData
in den Zielkontext gezeichnet wird, das Ergebnis eine Überlagerung auf den statischen Hintergrund ist.
Das resultierende Bild sieht folgendermaßen aus:
Dies wird wiederholt, während das Video abgespielt wird, sodass Frame für Frame verarbeitet und mit dem Chroma-Key-Effekt angezeigt wird.
Sehen Sie sich den vollständigen Quellcode für dieses Beispiel an.