Video mit Canvas manipulieren
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 in das angezeigte Video zu integrieren. Dieses Tutorial zeigt, wie Sie Chroma-Keying (auch bekannt als "Green-Screen-Effekt") mit JavaScript-Code durchführen können.
Der Dokumentinhalt
Das HTML-Dokument, das zum Rendern dieses Inhalts verwendet wird, ist unten dargestellt.
<!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" />
</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 wichtigsten Punkte, die Sie daraus mitnehmen sollten, sind:
- Dieses Dokument legt zwei
canvas
-Elemente fest, mit den IDsc1
undc2
. Das Canvasc1
wird verwendet, um das aktuelle Frame des Originalvideos anzuzeigen, währendc2
verwendet wird, um das Video nach der Durchführung des Chroma-Keying-Effekts anzuzeigen;c2
ist mit dem Standbild vorbeladen, 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 initial geladen wird. Die Aufgabe dieser Methode besteht darin, die für den Chroma-Key-Verarbeitungs-Code benötigten Variablen vorzubereiten und einen Event-Listener einzurichten, sodass wir erkennen können, 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();
},
false,
);
};
Dieser Code greift auf Referenzen zu den Elementen im HTML-Dokument zu, die von besonderem Interesse sind, nämlich das video
-Element und die beiden canvas
-Elemente. Er holt auch Referenzen auf die Grafik-Kontexte für jedes der beiden Canvas. Diese werden verwendet, wenn wir tatsächlich den Chroma-Keying-Effekt durchführen.
Dann wird addEventListener()
aufgerufen, um das video
-Element zu überwachen, damit wir eine Benachrichtigung erhalten, wenn der Benutzer die Wiedergabetaste im Video drückt. In Reaktion auf das Starten der Wiedergabe durch den Benutzer ruft dieser Code die Breite und Höhe des Videos ab, halbiert jeweils (wir werden die Größe des Videos halbieren, wenn wir den Chroma-Keying-Effekt durchführen), und ruft dann die Methode timerCallback()
auf, um das Video zu überwachen und den visuellen Effekt zu berechnen.
Der Timer-Callback
Der Timer-Callback wird initial aufgerufen, wenn das Video zu spielen beginnt (wenn das "play"-Ereignis eintritt), und übernimmt dann die Verantwortung, sich periodisch selbst aufzurufen, 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 tut, ist zu überprüfen, ob das Video überhaupt spielt; wenn nicht, kehrt der Callback sofort zurück, ohne etwas zu tun.
Dann ruft er die Methode computeFrame()
auf, die den Chroma-Keying-Effekt auf dem aktuellen Videoframe durchführt.
Das letzte, was der Callback tut, ist, setTimeout()
aufzurufen, um sich so schnell wie möglich erneut aufzurufen. In der realen Welt würden Sie dies wahrscheinlich basierend auf dem Wissen über die Bildrate des Videos planen.
Manipulation der Videoframe-Daten
Die Methode computeFrame()
, die unten gezeigt wird, ist dafür verantwortlich, 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 aktuellste Frame der Videodaten an, das so aussieht:
Dieses Videoframe wird in den Grafik-Kontext 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 in die Methode drawImage()
des Kontexts übergeben können, um das aktuelle Videoframe in den Kontext zu zeichnen. Das Ergebnis ist:
Der Aufruf der Methode getImageData()
auf dem ersten Kontext ruft eine Kopie der Roh-Grafikdaten für das aktuelle Videoframe ab. Dies liefert rohe 32-Bit-Pixelbilddaten, die wir dann manipulieren können. Wir berechnen dann die Anzahl der Pixel im Bild, indem wir die gesamte Größe der Bilddaten des Frames durch vier teilen.
Die for
-Schleife durchläuft die Pixel des Frames, zieht die roten, grünen und blauen Werte für jedes Pixel heraus und vergleicht die Werte mit vorbestimmten Zahlen, die verwendet werden, um den Green Screen zu erkennen, der mit dem aus foo.png
importierten Standbild ersetzt werden soll.
Jedes Pixel in den Bilddaten des Frames, das innerhalb der als Teil des Green Screens betrachteten Parameter gefunden wird, hat seinen Alpha-Wert auf null ersetzt, was darauf hinweist, dass das Pixel vollständig transparent ist. Dadurch wird das gesamte Green Screen-Bereich 100 % transparent, sodass, wenn es in den Zielkontext mit ctx2.putImageData
gezeichnet wird, das Ergebnis ein Overlay auf den statischen Hintergrund bildet.
Das resultierende Bild sieht so aus:
Dies wird wiederholt, während das Video abgespielt wird, sodass Frame für Frame verarbeitet und mit dem Chroma-Keying-Effekt angezeigt wird.