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

Utiliza una pantalla verde en Javascript con Vonage Video

Publicado el May 12, 2021

Tiempo de lectura: 4 minutos

Al crear un editor de Vonage Video, la transmisión puede provenir directamente de una cámara de usuario, de un elemento <video> o un elemento HTML <canvas> elemento. Una vez que los píxeles se dibujan en el lienzo, se pueden manipular fácilmente antes de utilizarlos en una sesión de Video API.

En este tutorial, aprenderás a eliminar una pantalla verde y sustituirla por una nueva imagen personalizada que podrás incluir en tus videollamadas.

Overview of the project componentsOverview of the project components

Para que el proyecto funcione se necesitan varios componentes. En primer lugar, un elemento <video> tomará una secuencia de la cámara del usuario. En cada fotograma, el contenido del elemento Video se dibujará en un lienzo, donde haremos un bucle a través de los píxeles para eliminar los que sean verdes. En un segundo lienzo, dibujaremos la imagen de fondo de sustitución y, a continuación, superpondremos los píxeles no verdes del primer lienzo.

Con nuestra salida deseada en un lienzo, podemos utilizar el lienzo como fuente para un editor de la Video API de Vonage, que podemos utilizar en nuestras sesiones de vídeo con amigos.

Si desea ver el código terminado, puede encontrarlo en https://github.com/nexmo-community/video-green-screen

Marcado de andamios

Cree una nueva carpeta de proyecto seguida de un nuevo archivo index.htmlpoblando este archivo con el siguiente código:

<!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>

También necesitará la imagen que desea reemplazar su pantalla verde dentro de la carpeta del proyecto. Este tutorial utilizará uno de los degradados de la marca Vonage. Después de obtener los contextos del lienzo, carga la imagen:

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

Obtener Video de Webcam

Establece la <video> a la transmisión de la cámara web del usuario. Este fragmento elegirá la cámara predeterminada:

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

Cree una función process() vacía. Una vez que el dispositivo de vídeo del usuario esté listo y 'reproduciendo', ejecuta la función cada fotograma:

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

function process() {

}

Dibujar una secuencia de Video en el lienzo

Actualización process():

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

Actualice su página y debería ver su elemento <video> su primer elemento <canvas> con una imagen duplicada, y el segundo <canvas> con la nueva imagen de fondo. El objetivo es conseguir cualquier píxeles no verdes en la parte superior del fondo en el segundo lienzo.

Bucle de píxeles

En un lienzo, toda la imagen representada en una única matriz larga de píxeles. Aunque inicialmente creas que nuestra imagen de 320x240 tendrá 76.800 entradas en la matriz, te equivocarías.

canvas pixelscanvas pixels

Cada píxel visible se compone de cuatro elementos de matriz: uno para su valor rojo, otro para el verde, otro para el azul y el último para establecer su opacidad. Estos valores son importantes a medida que construimos y utilizamos el bucle.

Obtenga la matriz de píxeles de este fotograma dentro de la función process() y construye el bucle:

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

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

}

Observe que el contador está configurado para incrementarse en 4. Cada vez que se ejecute este bucle, i será el índice de la matriz del valor rojo del siguiente píxel visible.

Comprender HSL

Antes de eliminar los píxeles verdes, me gustaría presentarte el formato de color Tono Saturación Luminosidad (HSL).

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 whitePuedes pensar en el matiz como en una rueda de colores, y utilizar la posición en la rueda para especificar un color, de 0 a 360. La "gama" verde puede variar según la persona, pero de 90 a 200 funciona bien para mí. La "gama" verde puede ser diferente para cada persona, pero de 90 a 200 me funciona bien.

Sin embargo, al leer y escribir píxeles en un archivo <canvas> debe utilizar el formato de color rojo, verde y azul (RGB). En la parte inferior de su <script>añada esta función RGBToHSL() función proporcionada en Trucos CSS

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]
}

Hacer transparentes los píxeles verdes

Dentro del bucle process() obtenga los valores RGB y HSL de cada píxel y establezca los píxeles verdes como transparentes:

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
}

Después del bucle, actualiza la imagen del lienzo:

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

Usted puede encontrar que 90 y 200 necesita actualizarse, dado el color de tu pantalla y la iluminación.

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

Dibujar los píxeles restantes en el fondo de sustitución

Por cada píxel que quede en el primer lienzo, dibújalo en el segundo. Después de la sentencia if en el bucle process() añada una else condición:

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)
}

En x y y son los píxeles visuales, por lo que el valor i debe dividirse por 4.

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

Incluir Canvas en la sesión de Video API

Crea un nuevo proyecto en tu Panel de Video de Vonage. Una vez creado, desplázate hasta Herramientas del proyecto y crea una nueva sesión enrutada. Toma el ID de sesión y crea un nuevo token.

En la parte superior de su <script>crea tres nuevas variables con los datos del cuadro de mandos del proyecto:

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

A continuación, copie la etiqueta <script> de la página página del Client SDK de la Video API de Vonage y colócala sobre tu etiqueta <script> existente.

En la parte inferior de tu etiqueta <script> obtén tu sesión básica de la Video API de Vonage inicializada y publícala desde el segundo lienzo:

// 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")
})

Ocultar elementos

En <video> y <canvas> son necesarios para que esto funcione, pero probablemente no quieras que sean visibles en tu página web. En su <head>añade el siguiente CSS para ocultarlos:

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

¿Cuáles serán sus antecedentes?

Esperamos que esta entrada del blog te haya resultado útil y que ahora puedas crear fondos personalizados a tu antojo. Aunque nos hemos centrado en las pantallas verdes, cualquier manipulación a nivel de píxel se puede hacer con el mismo enfoque.

Para llevar esto más lejos, puede optar por proporcionar a los usuarios controles que modifiquen los valores HSL que están "dentro del rango" a sustituir, o un selector de archivos para cambiar la imagen.

Puede encontrar el proyecto final en https://github.com/nexmo-community/video-green-screen

Como siempre, si necesitas ayuda, no dudes en ponerte en contacto con nosotros en la Slack de la comunidad de desarrolladores de Vonage. Esperamos verte allí.

Compartir:

https://a.storyblok.com/f/270183/400x400/c822f15b89/kevinlewis.png
Kevin LewisAntiguos alumnos de Vonage

Antiguo defensor de los desarrolladores de Vonage, donde su función era apoyar a la comunidad tecnológica local de Londres. Es un experimentado organizador de eventos, jugador de mesa y padre de un precioso perrito llamado Moo. También es el principal organizador de You Got This, una red de eventos sobre las habilidades básicas necesarias para una vida laboral feliz y saludable.