https://d226lax1qjow5r.cloudfront.net/blog/blogposts/use-a-green-screen-in-javascript-with-vonage-video/Blog_Greenscreen_1200x600.png

Verwendung eines Greenscreens in Javascript mit Vonage Video

Zuletzt aktualisiert am May 12, 2021

Lesedauer: 4 Minuten

Beim Erstellen eines Vonage Video-Publishers kann der Stream direkt von einer Benutzerkamera, von einem <video> Element oder einem HTML <canvas> Element stammen. Sobald die Pixel auf die Leinwand gezeichnet wurden, können sie leicht manipuliert werden, bevor sie in einer Video API-Sitzung verwendet werden.

In diesem Tutorial erfahren Sie, wie Sie einen Greenscreen entfernen und durch ein neues, benutzerdefiniertes Bild ersetzen, das Sie in Ihre Videoanrufe einfügen können.

Overview of the project componentsOverview of the project components

Damit das Projekt funktioniert, sind mehrere Komponenten erforderlich. Erstens, ein <video> Element einen Stream von der Kamera des Benutzers aufnehmen. Bei jedem Bild wird der Inhalt des Videoelements auf eine Leinwand gezeichnet, wo wir in einer Schleife die grünen Pixel entfernen. Auf einer zweiten Leinwand wird das Ersatz-Hintergrundbild gezeichnet und dann die nicht-grünen Pixel der ersten Leinwand darüber gelegt.

Mit unserer gewünschten Ausgabe auf einer Leinwand können wir die Leinwand als Quelle für einen Vonage Video API-Publisher verwenden, den wir in unseren Videositzungen mit Freunden einsetzen können.

Wenn Sie sich den fertigen Code ansehen möchten, finden Sie ihn unter https://github.com/nexmo-community/video-green-screen

Gerüst Markup

Erstellen Sie einen neuen Projektordner, gefolgt von einer neuen Datei index.htmlund füllen Sie diese Datei mit dem folgenden Code:

<!DOCTYPE html>
<html>
<head></head>
<body>
  <video id="v1" width="320" height="240" autoplay></video>
  <canvas id="c1" width="320" height="240"></canvas>
  <canvas id="c2" width="320" height="240"></canvas>
  <div id="opentok-publishers"></div>
  <div id="opentok-subscribers"></div>
  <script>
    // Create references to the video and canvas elements
    const v1 = document.getElementById('v1')
    const c1 = document.getElementById('c1')
    const c2 = document.getElementById('c2')

    // Get canvas context
    const c1Ctx = c1.getContext('2d')
    const c2Ctx = c2.getContext('2d')
  </script>
</body>
</html>

Außerdem benötigen Sie das Bild, das Sie als Ersatz für Ihren Greenscreen verwenden möchten, im Projektordner. In diesem Tutorial wird einer der Farbverläufe der Marke Vonage. Laden Sie das Bild, nachdem Sie den Canvas-Kontext hergestellt haben:

const backgroundImage = new Image()
backgroundImage.src = 'vonage-gradient.png'

Webcam Video abrufen

Setzen Sie die <video> Element auf den Stream von der Webcam des Benutzers. Dieses Snippet wählt die Standardkamera:

navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => { v1.srcObject = stream })

Erstellen Sie eine leere process() Funktion. Sobald das Videogerät des Benutzers bereit ist und "abspielt", führen Sie die Funktion bei jedem Bild aus:

v1.addEventListener('play', () => {
  setInterval(process, 0)
})

function process() {

}

Video-Stream auf die Leinwand zeichnen

Aktualisierung process():

function process() {
  c1Ctx.drawImage(v1, 0, 0, 320, 240)
  c2Ctx.drawImage(backgroundImage, 0, 0, 320, 240)
}

Aktualisieren Sie Ihre Seite, und Sie sollten Ihr <video> Element, Ihr erstes <canvas> mit einem doppelten Bild, und Ihr zweites <canvas> mit dem neuen Hintergrundbild. Das Ziel ist es, alle nicht-grünen Pixel über dem Hintergrund in der zweiten Leinwand zu erhalten.

Pixel durchschleifen

In einem Canvas wird das gesamte Bild in einem einzigen langen Array von Pixeln dargestellt. Auch wenn Sie auf den ersten Blick glauben, dass unser Bild mit den Maßen 320x240 76.800 Einträge in dem Array hat, liegen Sie falsch.

canvas pixelscanvas pixels

Jedes sichtbare Pixel besteht aus vier Array-Elementen - eines für seinen Rotwert, eines für Grün, eines für Blau und das letzte, um seine Deckkraft festzulegen. Diese Werte sind wichtig, wenn wir die Schleife aufbauen und verwenden.

Holen Sie sich das Pixel-Array dieses Frames innerhalb der process() Funktion ab und bilden die Schleife:

const frame = c1Ctx.getImageData(0, 0, 320, 240)
const pixels = frame.data

for(let i=0; i<pixels.length; i+=4) {

}

Beachten Sie, dass der Zähler so eingestellt ist, dass er bei jedem Durchlauf dieser Schleife um 4 erhöht wird, i der Array-Index des nächsten sichtbaren roten Pixelwerts sein.

HSL verstehen

Bevor grüne Pixel entfernt werden können, möchte ich Sie mit dem Farbformat Hue Saturation Lightness (HSL) vertraut machen.

Hue is a color wheel, sauturation is the amount of grey, lightness is a scale of black to whiteHue is a color wheel, sauturation is the amount of grey, lightness is a scale of black to whiteSie können sich den Farbton als ein Farbrad vorstellen - und die Position auf dem Rad verwenden, um eine Farbe von 0 bis 360 anzugeben. Der grüne "Bereich" kann für jede Person anders sein, aber 90 bis 200 funktioniert für mich gut.

Allerdings müssen Sie beim Lesen und Schreiben von Pixeln in einer <canvas> müssen Sie das Farbformat Rot-Grün-Blau (RGB) verwenden. Ganz unten in Ihrem <script>fügen Sie diese RGBToHSL() Funktion bereitgestellt auf CSS Tricks

function RGBToHSL(r, g, b) {
  r /= 255; g /= 255; b /= 255;
  let cmin = Math.min(r,g,b), 
      cmax = Math.max(r,g,b), 
      delta = cmax - cmin, 
      h = 0, s = 0, l = 0;
  if (delta == 0) h = 0;
    else if (cmax == r) h = ((g - b) / delta) % 6;
    else if (cmax == g) h = (b - r) / delta + 2;
    else h = (r - g) / delta + 4;
  h = Math.round(h * 60);
  if (h < 0) h += 360;
  l = (cmax + cmin) / 2;
  s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);
  return [h, s, l]
}

Grüne Pixel durchsichtig machen

Innerhalb der process() Schleife werden die RGB- und HSL-Werte für jedes Pixel ermittelt, und die grünen Pixel werden als transparent festgelegt:

const [r, g, b] = [pixels[i], pixels[i+1], pixels[i+2]]
const [h, s, l] = RGBToHSL(r, g, b)

if(h > 90 && h < 200) {
  pixels[i+3] = 0
}

Nach der Schleife wird das Bild auf der Leinwand aktualisiert:

frame.data = pixels
c1Ctx.putImageData(frame, 0, 0)

Sie werden feststellen, dass 90 und 200 aufgrund der Farbe Ihres Bildschirms und der Beleuchtung aktualisiert werden muss.

The first canvas has no background - appearing whiteThe first canvas has no background - appearing white

Verbleibende Pixel auf Ersatzhintergrund malen

Zeichnen Sie jedes Pixel, das auf der ersten Leinwand übrig bleibt, auf die zweite Leinwand. Nach der if Anweisung in der process() Schleife, fügen Sie eine else Bedingung:

if(h > 90 && h < 200) {
  pixels[i+3] = 0
} else {
  c2Ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`
  const x = (i/4) % 320
  const y = Math.floor((i / 4) / 320)
  c2Ctx.fillRect(x, y, 1, 1)
}

Die x und y Werte sind die visuellen Pixel, so dass der i Wert sollte durch 4 geteilt werden.

The second canvas now has the non-removed pixelsThe second canvas now has the non-removed pixels

Canvas in Video API-Sitzung einbeziehen

Erstellen Sie ein neues Projekt in Ihrem Vonage Video Dashboard. Scrollen Sie nach der Erstellung nach unten zu Projekttools und erstellen Sie eine neue geroutete Sitzung. Nehmen Sie die Sitzungs-ID und erstellen Sie ein neues Token.

Am Anfang Ihrer <script>erstellen Sie drei neue Variablen mit Daten aus dem Projekt-Dashboard:

const sessionId = 'YOUR_SESSION_ID'
const apiKey = 'YOUR_PROJECT_API_KEY'
const token = 'YOUR_TOKEN'

Als nächstes kopieren Sie das <script> Tag von der Vonage Video API Client SDK Seite und fügen Sie es über Ihr bestehendes <script> Tag ein.

Am unteren Ende Ihres <script> Tags wird Ihre grundlegende Vonage Video API-Sitzung initialisiert und im zweiten Canvas veröffentlicht:

// Initialize session
const session = OT.initSession(apiKey, sessionId)

// Create publisher
const publisher = OT.initPublisher("opentok-publishers", {
  videoSource: c2.captureStream().getVideoTracks()[0],
  width: 320,
  height: 240
})

// Once connected to session, publish the publisher
session.connect(token, () => {
  session.publish(publisher)
})

// Show other users' streams
session.on('streamCreated', event => {
  session.subscribe(event.stream, "opentok-subscribers")
})

Elemente ausblenden

Die <video> und <canvas> Elemente sind erforderlich, damit dies funktioniert, aber Sie wollen sie wahrscheinlich nicht auf Ihrer Webseite sehen. In Ihrer <head>fügen Sie das folgende CSS ein, um sie auszublenden:

<style>
  #v1, #c1, #c2 { display: none }
</style>

Was wird Ihr Hintergrund sein?

Wir hoffen, dass Sie diesen Blogbeitrag nützlich fanden und nun nach Herzenslust benutzerdefinierte Hintergründe erstellen können. Wir haben uns zwar auf Greenscreens konzentriert, aber jede Manipulation auf Pixelebene kann mit demselben Ansatz durchgeführt werden.

Darüber hinaus können Sie den Benutzern Steuerelemente zur Verfügung stellen, mit denen sie die zu ersetzenden HSL-Werte ändern können, oder einen Dateiselektor, um das Bild zu ändern.

Das Abschlussprojekt finden Sie unter https://github.com/nexmo-community/video-green-screen

Wie immer, wenn Sie Unterstützung benötigen, können Sie sich gerne an die Vonage Entwickler-Community Slack. Wir hoffen, Sie dort zu sehen.

Teilen Sie:

https://a.storyblok.com/f/270183/400x400/c822f15b89/kevinlewis.png
Kevin LewisVonage Ehemalige

Ehemaliger Developer Advocate bei Vonage, wo seine Aufgabe darin bestand, die lokale Tech-Community in London zu unterstützen. Er ist ein erfahrener Veranstaltungsorganisator, Brettspieler und Vater eines süßen kleinen Hundes namens Moo. Er ist auch der Hauptorganisator von You Got This - einem Netzwerk von Veranstaltungen zu den Kernkompetenzen, die für ein glückliches, gesundes Arbeitsleben erforderlich sind.