https://d226lax1qjow5r.cloudfront.net/blog/blogposts/snapchat-filters-opentok-tracking-js-dr/Blog_Snapchat-Style-Filters_1200x600.png

Filtros estilo Snapchat con Tracking.js y Vonage

Publicado el November 16, 2020

Tiempo de lectura: 20 minutos

Ser Batman o simplemente asustar a algunas personas. En esta guía, vas a añadir seguimiento facial utilizando Tracking.js y crear algunos filtros al estilo Snapchat para una secuencia de vídeo. ¡Qué guay!

API de Video de Vonage

API de Video de Vonage produce una plataforma basada en WebRTC para añadir vídeo en directo, voz y mensajería a aplicaciones web, móviles y de escritorio. Utilizaremos la API de Video de Vonage para conectarnos desde nuestra aplicación a una sesión de streaming de video. Cuando trabajes con la API de Video de Vonage puede que veas el nombre OpenTok, su nombre anterior, usado en código o documentación.

Seguimiento.js

Tracking.js es una librería ligera y potente de detección de caras y seguimiento de color en tiempo real que lleva diferentes algoritmos y técnicas al entorno del navegador. Usaremos tracking.js para rastrear caras y poder aplicar máscaras.

Requisitos previos

Esto es lo que necesitarás para seguir este tutorial.

Nodo y NPM

Para empezar vas a necesitar node y el gestor de paquetes node (npm) instalados. Vamos a comprobar si están instalados ejecutando:

node --version
npm --version

Si no tienes node y npm instalados, visita nodejs.org e instala la versión correcta para tu sistema operativo. Escribí este artículo con Node 11.8 y NPM 6.8.

Cuenta de Vonage

Visita Vonage para suscribirte a una prueba gratuita.

Git

Comprueba si tienes instalado Git.

git --version

Si no, Atlassian tiene una gran guía sobre cómo instalar Git. Escribí este artículo con Git 2.17 instalado.

Comenzar

Como base, he preparado una versión de la aplicación, a partir de muestras web de OpenTok para filtros de flujo, que puedes editar.

Echa un vistazo a mi versión de las muestras del filtro web OpenTok aquí ejecutando el siguiente comando en tu herramienta de línea de comandos preferida, o utilizando tu interfaz Git para consultar esta URL: https://github.com/nexmo-community/snapchat-filters-with-opentok-and-face-tracking.git

git clone git@github.com:nexmo-community/snapchat-filters-with-opentok-and-face-tracking.git

Nota: La rama master rama está en el punto de inicio, pero si eres impaciente (y te gusta saltar hasta el final como a mí) puedes git checkout demo-end y ejecutar la aplicación desde allí.

Configurar la aplicación

Por lo tanto, antes de comenzar a codificar, debes configurar la aplicación tal como está y tener una idea del punto de partida. Para ello necesitarás la Account de Vonage mencionada en los requisitos previos. Si no tienes una, dirígete a Vonage para registrarte ahora.

Crear un proyecto

Una vez que haya confirmado su dirección de correo electrónico e iniciado sesión, podrá hacer clic en Crear proyecto desde la resumen de cuenta. Crea un nuevo proyecto de Video API de Vonage haciendo clic en Crear proyecto personalizado. Dale un nombre a tu proyecto y haz clic en Crear.

Ahora, anote su CLAVE API y SECRETO para el paso de configuración.

Crear una sesión

Una vez creado el proyecto, se le redirigirá al panel de control del proyecto. Desplácese hacia abajo hasta Herramientas del proyecto y haga clic en Crear ID de sesión. También puede elegir entre Retransmitido y Enrutado. Básicamente, retransmitido intenta una conexión peer-to-peer pero utilizará servidores de retransmisión cuando los cortafuegos bloqueen peer-to-peer. Enrutado es el valor por defecto donde los flujos van al Media Router. Puede más información sobre sesiones en la documentación.

Anote el ID DE SESIÓN que se crea.

Generar un token

Antes de configurar la aplicación, necesitamos generar un token. En el panel de control del proyecto, en Herramientas del proyecto (la misma sección en la que creó su ID de sesión), introduzca el ID DE SESIÓN que acaba de generar y haga clic en Generar token.

Anote el TOKEN GENERADO para el siguiente paso.

Crear un archivo de configuración

Haga una copia de js/config.js.example y guárdala como js/config.js (en el directorio js) asegurándote de modificar los valores a los de tu cuenta de Vonage.

CLAVE API, ID DE SESIÓNy TOKEN se han creado anteriormente. Si está ejecutando la aplicación localmente utilizando npm startnecesitará una URL BASE de http://127.0.0.1:8080. Para algo como Heroku, es más como https://app-name.herokuapp.com.

Consejo rápido: Si estás ejecutando la aplicación habiendo ejecutado npm start puedes detenerla de nuevo con CTRL+C.

Su js/config.js terminará pareciéndose a esto.

// js/config.js
(function closure(exports) {
  exports.BASE_URL = 'base url';
  exports.API_KEY = 'api key';
  exports.SESSION_ID = 'session id';
  exports.TOKEN = 'token';
})(exports);

Instalar dependencias

Ahora usa npm para instalar tus dependencias. Ejecute esto desde dentro del directorio de su proyecto.

npm install

Ejecutar la aplicación

Puede iniciar la aplicación mediante npm start. Esta es una pequeña función inteligente. Si no configuras un script de inicio personalizado en package.jsonasumirá que se refiere a node index.js donde index.js es el archivo principal configurado en package.json. En nuestro caso, se ejecutará ./node_modules/bin/http-server para iniciar nuestro servidor web basado en nodos.

npm start

Una vez en marcha, puede ver la sesión localmente abriendo localhost:8080 en tu navegador favorito. Debería parecerse a esto, pero contigo en vez de conmigo.

Basic Vonage Video example up and runningBasic Vonage Video example up and running

Ahora, carga la misma URL en otra ventana del navegador para poder hablar contigo mismo.

Nota importante: No siempre parezco tan desaliñado :)

Basic Vonage Video conversation occuringBasic Vonage Video conversation occuring

Presentación de la tecnología

La plataforma Video API de Vonage facilita la incorporación de videos interactivos de alta calidad en tiempo real, mensajes, pantallas compartidas y mucho más en aplicaciones web y móviles. Aquí, específicamente, usaremos los SDK de cliente de JavaScript.

SDK de Video de Vonage

El Client SDK de JavaScript está haciendo el verdadero trabajo pesado. Va a utilizar credenciales codificadas para conectarse a una sesión utilizando un token que generamos en el portal de desarrolladores. A continuación, se suscribirá y publicará flujos de audio y vídeo en nuestro navegador.

Seguimiento.js

Tracking.js es una librería JavaScript ligera para rastrear posiciones faciales dentro de imágenes y Video. Vamos a recuperar las coordenadas en el lienzo.

La aplicación de demostración

La aplicación que has clonado e iniciado ya viene con algunos filtros. Estos filtros funcionan utilizando datos de imagen de la API Canvas y modificándolos. Los datos de la imagen están en un objeto que contiene datos de anchura, altura y píxeles. Los datos de los píxeles están contenidos en un array. Cada 4 elementos representan un píxel R, G, B y A respectivamente.

Así que aplicar efectos de imagen puede funcionar modificando los datos RGBA.

He aquí un ejemplo poco práctico. El código se explica mediante comentarios, pero en realidad no se ejecutará.

// The existing image data.
const imgData = new ImageData(existingData, width, height);

// An empty typed data array for our new modified image data
const dataArray = new Uint8ClampedArray(imgData.data.length);

// Loop over the data, skipping to every 4th item.
for (let i = 0; i < imgData.data.length; i += 4) {
  // i is R / red
  // i+1 is G / green
  // i+2 is B / blue
  // i+3 is alpha - which specifies the opacity of the color.

  // here, we're addoing 80 to the relative channel value, effectively lighting this channel
  dataArray[i] = imgData.data[i] + 80;
  dataArray[i + 1] = imgData.data[i + 1] + 80;
  dataArray[i + 2] = imgData.data[i + 2] + 80;
  // lightening every channel will result in a whitewashed/brightened effect

  dataArray[i + 3] = imgData.data[i + 3];
}

return new ImageData(dataArray, imgData.width, imgData.height);

Puede ver los filtros existentes que modifican los valores RGB en js/filters.js.

Creación de nuevos filtros

A continuación se muestran los pasos para editar la aplicación para la creación de dos nuevos filtros, para ajustar el brillo y el umbral de nuestro flujo.

Luminosidad

Ajustar el brillo de una imagen es una de las operaciones de procesamiento de imágenes más sencillas que se pueden realizar. Como se muestra en el ejemplo de código anterior, todo lo que necesita hacer es añadir el cambio deseado en el brillo a cada uno de los canales rojo, verde y azul.

Abra js/filters.js y encuentra la línea con el comentario // Brighten y reemplaza el comentario con una nueva función para nuestro filtro.

Consejo rápido: Comentarios como // ... y <!-- // ... --> muestran que existe más código pero se ha eliminado, en aras de la claridad, aquí en el blog.

// js/filters.js
(function closure(exports) {
  var Filters = {

    // ...

    brighten: function brighten(imgData) {
      // New data

      // Loop 

      // Return
    }
  };

  // ...
})(exports);

Los datos de la imagen en una matriz de 8 bits sin signo. Esto significa que tiene una longitud fija y los valores se fijan a valores sin signo entre 0-255. Perfecto para valores RGB. Así que tenemos que crear una nueva matriz de 8 bits vacía en la que almacenaremos los datos de nuestra imagen modificada. Reemplace // New data como se muestra aquí, donde estás estableciendo la longitud fija con la longitud de los datos existentes.

// js/filters.js
    // ...

    brighten: function brighten(imgData) {
      const res = new Uint8ClampedArray(imgData.data.length);

      // Loop 

      // Return
    }

    // ...

Ahora, haga un bucle sobre los datos existentes y desplace los valores para aplicar un efecto de brillo. Reemplace // Loop como se muestra aquí. Y, como se explicó anteriormente en el artículo, que está haciendo un bucle sobre los datos, saltando a cada índice 4 en la matriz.

// js/filters.js
    // ...

    brighten: function brighten(imgData) {
      const res = new Uint8ClampedArray(imgData.data.length);

      for (let i = 0; i < imgData.data.length; i += 4) {
        var inputRed = imgData.data[i];
        var inputGreen = imgData.data[i + 1];
        var inputBlue = imgData.data[i + 2];
        res[i] = inputRed + 80;
        res[i + 1] = inputGreen + 80;
        res[i + 2] = inputBlue + 80;
        res[i + 3] = imgData.data[i + 3];
      }

      // Return
    }

    // ...

Por último, para esta función, devolverá la nueva matriz de datos. Reemplace // Return como se muestra.

// js/filters.js
    // ...

    brighten: function brighten(imgData) {
      const res = new Uint8ClampedArray(imgData.data.length);

      for (let i = 0; i < imgData.data.length; i += 4) {
        var inputRed = imgData.data[i];
        var inputGreen = imgData.data[i + 1];
        var inputBlue = imgData.data[i + 2];
        res[i] = inputRed + 80;
        res[i + 1] = inputGreen + 80;
        res[i + 2] = inputBlue + 80;
        res[i + 3] = imgData.data[i + 3];
      }

      return new ImageData(res, imgData.width, imgData.height);
    }

    // ...

Una vez configurada la función, tienes que poder seleccionarla. Para ello, abra index.html y sustituye <!-- Brighten --> con una opción para seleccionar nuestra nueva función.

<!-- index.html -->
<!-- // ... -->
          <select id="filter">
            <option value="none">Filter: None</option>
            <option value="grayscale">Filter: Grayscale</option>
            <option value="sepia">Filter: Sepia</option>
            <option value="invert">Filter: Invert</option>
            <option value="brighten">Filter: Brightening</option>
            <!-- Threshold -->
          </select>
<!-- // ... -->

Ahora, inicie de nuevo la aplicación abriendo localhost:8080 y comprueba el nuevo filtro de brillo. El valor option es el mismo que el nombre de la función.

npm start

Vonage Video stream with a brightening filterVonage Video stream with a brightening filter

Umbralización

Lo siguiente es umbralizar la imagen.

Abra js/filters.js y busque la línea con el comentario // Threshold y reemplázala con la siguiente función. Esta vez, usted tiene la función completa.

El funcionamiento del umbral consiste en sustituir el color por el blanco o el negro mediante un algoritmo basado en la suma de los valores de los canales rojo, verde y azul. Una suma calculada por encima de 100 (&gt;= 100), que representa los colores más brillantes, se convierte en blanco. Todo lo demás se convierte en negro.

// js/filters.js
(function closure(exports) {
  var Filters = {

    // ...

    threshold: function threshold(imgData) {
      const res = new Uint8ClampedArray(imgData.data.length);
      for (let i = 0; i < imgData.data.length; i += 4) {
        var inputRed = imgData.data[i];
        var inputGreen = imgData.data[i+1];
        var inputBlue = imgData.data[i+2];
        var v = (0.2126 * inputRed + 0.7152 * inputGreen + 0.0722 * inputBlue >= 100) ? 255 : 0;
        res[i] = res[i+1] = res[i+2] = v;
        res[i + 3] = imgData.data[i + 3];
      }

      return new ImageData(res, imgData.width, imgData.height);
    }

  };

  // ...
})(exports);

Ahora añadir una opción para seleccionar el umbral. Así que abra index.html y reemplazar <!-- Threshold --> con una opción para seleccionar nuestra nueva función.

<!-- index.html -->
<!-- // ... -->
          <select id="filter">
            <option value="none">Filter: None</option>
            <option value="grayscale">Filter: Grayscale</option>
            <option value="sepia">Filter: Sepia</option>
            <option value="invert">Filter: Invert</option>
            <option value="brighten">Filter: Brightening</option>
            <option value="threshold">Filter: Thresholding</option>
          </select>
<!-- // ... -->

Ahora, inicie de nuevo la aplicación abriendo localhost:8080 y comprueba el nuevo filtro de umbralización.

npm start

Vonage Video stream with a thresholding filter!Vonage Video stream with a thresholding filter!

Seguimiento facial con Tracking.js

A continuación, añadirás Tracking.js y renderizarás una imagen sobre tu cara como si fuera una máscara.

Instalar Tracking.js

Usando npm, instala Tracking.js.

npm i tracking

Añada la biblioteca a su aplicación utilizando el método tradicional. Edita index.html y encuentra el comentario donde dice <!-- Scripts -->. Sustitúyelo por las nuevas etiquetas de script que se muestran a continuación. El primer archivo tracking-min.js es la librería minificada Tracking.js. El segundo archivo face-min.js son los datos utilizados por Tracking.js para rastrear rostros en los medios de comunicación. También están disponibles otros conjuntos de datos, como los rasgos faciales, incluidos los ojos, la nariz y la boca.

<!-- index.html -->
<!-- // ... -->
    
      var exports = {};
    
    <a href="http://node_modules/tracking/build/tracking-min.js">http://node_modules/tracking/build/tracking-min.js</a>
    <a href="http://node_modules/tracking/build/data/face-min.js">http://node_modules/tracking/build/data/face-min.js</a>
    <a href="http://js/config.js">http://js/config.js</a>
<!-- // ... -->

Ahora bien, como los filtros y las máscaras funcionan de forma ligeramente diferente, los he mantenido separados. Esto tiene que ver con los objetos disponibles dentro de la establecida js/filters.js establecido.

Cree un nuevo archivo js/masks.js y dale el siguiente contenido para empezar.

// js/masks.js
(function closure(exports) {
  var Masks = {
    none: function none(tracker, canvas, ctx) {}

    // Guy Fawkes

    // Batman
  };

  // Set the initial mask to none
  Masks.selectedMask = Masks.none;

  // When the mask selector changes we update the selectedMask
  var maskSelector = document.querySelector('#mask');
  maskSelector.addEventListener('change', function change() {
    Masks.selectedMask = Masks[maskSelector.value];
  });

  exports.Masks = Masks;
})(exports);

Abre y edita js/publish.js y busque el // Tracker.js comentario. Reemplace eso, como se muestra aquí.

Este rastreador de objetos es el que leerá los medios y reconocerá las caras. El rastreo funciona en general mediante el paso de una "forma" alrededor del medio hasta que encuentra una coincidencia cercana. La configuración es lo que establece la escala inicial de la "forma" y el tamaño del paso. Esto ayudará a afinar (y mejorar la velocidad) del rastreador.

Nota: Con mis cámaras web incorporadas y de 720p esta configuración solía funcionar bien.

// js/publish.js

    // ...
    var reqId;

    // Draw a box around face
    var tracker = new tracking.ObjectTracker('face');
    tracker.setInitialScale(10);
    tracker.setStepSize(2);
    tracker.setEdgesDensity(0.01);

    // ...

Y en el mismo archivo, busque el comentario // apply Mask comentario y reemplazarlo así.

// js/publish.js

      // ...
      ctx.putImageData(imgData, 0, 0);

      exports.Masks.selectedMask(tracker, canvas, ctx);

      // ...

Edita index.html y encuentra ambos <!-- Masks --> comentarios.

Para mantener las máscaras separadas, también creará un cuadro de selección separado. Dejaremos las selecciones vacías, por ahora.

También debe incluir aquí el nuevo archivo js/masks.js aquí.

<!-- index.html -->
<!-- // ... -->
    <div id="videos">
        <div id="subscriber"></div>
        <div id="publisher">
          <!-- // ... -->
          
            Mask: None
            <!-- Guy Fawkes -->
            <!-- Batman -->
          
        </div>
    </div>
    <!-- // ... -->
    <a href="http://js/filters.js">http://js/filters.js</a>
    <a href="http://js/masks.js">http://js/masks.js</a>
<!-- // ... -->

Antes de ejecutarlo, hay que aplicar algunos estilos al cuadro de selección de máscaras. Edite css/app.css y encuentra el comentario /* Masks menu css */ y reemplázalo con el siguiente CSS.

/* css/app.css */
/* // ... */
#mask {
  position: absolute;
  bottom: 0;
  left: 0;
  z-index: 102;
}

Aún no ha creado ninguna máscara, pero inicie de nuevo la aplicación abriendo localhost:8080 para comprobar que no has cometido ningún error. Si el flujo funciona, vas por buen camino.

npm start

La máscara de Guy Fawkes

La primera máscara será la de Guy. La máscara de Guy Fawkes se ha convertido en un símbolo de anonimato, ¡que es para lo que sirve llevar una máscara!

Abra js/masks.js y busque el comentario // Guy Fawkes. Sustituye el comentario por la nueva función.

// js/masks.js
// ...
(function closure(exports) {
  var Masks = {
    none: function none(tracker, canvas, ctx) {},

    guy: function guy(tracker, canvas, ctx) {
      // Load an image

      // Start tracking

      // Apply our image as a mask
    }

    // Batman
  };
// ...

Cargue la imagen que desea colocar sobre las caras encontradas por el rastreador, sustituyendo el // Load an image como se muestra aquí. ../images/guy.png debería estar disponible en el repositorio.

// js/masks.js

    // ...
    guy: function guy(tracker, canvas, ctx) {
      var mask = document.createElement("img");
      mask.src = '../images/guy.png';  

      // Start tracking

      // Apply our image as a mask
    }
    // ...

Ahora, activa el seguimiento de nuestros medios. Los medios de comunicación que está utilizando es el flujo de lienzo. Necesitas habilitar la cámara y el audio aquí. Reemplace el // Start tracking comentario como este.

// js/masks.js

    // ...
    guy: function guy(tracker, canvas, ctx) {
      var mask = document.createElement("img");
      mask.src = '../images/guy.png';

      tracking.track(canvas, tracker, { camera: true, audio: true });

      // Apply our image as a mask
    }
    // ...

Es necesario registrar un receptor de eventos contra el evento track del rastreador para que cuando encuentre una cara, pueda dibujar la máscara sobre la cara detectada.

// js/masks.js

    // ...
    guy: function guy(tracker, canvas, ctx) {
      var mask = document.createElement("img");
      mask.src = '../images/guy.png';  

      tracking.track(canvas, tracker, { camera: true, audio: true });

      tracker.on('track', function(event) {
        event.data.forEach(function(rect) {
          ctx.drawImage(mask, rect.x - 100, rect.y - 100, rect.width * 1.5, rect.height * 1.5);
        });
      });
    }
    // ...

Ahora, tienes que añadir la opción de la máscara de Guy a la aplicación. Edita index.htmlbusca el comentario <!-- Guy Fawkes --> y añade la opción como se muestra a continuación.

<!-- index.html -->
<!-- // ... -->
    <div id="videos">
        <div id="subscriber"></div>
        <div id="publisher">
          <!-- // ... -->
          
            Mask: None
            Mask: Guy Fawkes
            <!-- Batman -->
          
        </div>
    </div>
<!-- // ... -->

Crucemos los dedos, ¡todo funciona! Inicie de nuevo la aplicación abriendo localhost:8080. Selecciona tu primera máscara y compruébalo.

Vonage Video stream with a Guy Fawkes mask!Vonage Video stream with a Guy Fawkes mask!

Sé Batman

La máscara final y editar nuestro código será Batman. Abra js/masks.js y encuentra el comentario // Batman. Reemplace el comentario con esta nueva función.

// js/masks.js

    // ...

    batman: function batman(tracker, canvas, ctx) {
      var mask = document.createElement("img");
      mask.src = '../images/batman.png';  

      tracking.track(canvas, tracker, { camera: true, audio: true });
      tracker.on('track', function(event) {
        event.data.forEach(function(rect) {
          ctx.drawImage(mask, rect.x - 60, rect.y - 170, rect.width * 1.4, rect.height * 1.5);
        });
      });
    }
    // ...

Ahora añade la opción de la máscara de Batman a la aplicación. Edita index.htmlbusca el comentario <!-- Batman --> y añade la opción como se muestra a continuación.

<!-- index.html -->
<!-- // ... -->
    <div id="videos">
        <div id="subscriber"></div>
        <div id="publisher">
          <!-- // ... -->
          
            Mask: None
            Mask: Guy Fawkes
            Mask: Batman
          
        </div>
    </div>
<!-- // ... -->

¿Puedo ser Batman? Inicie de nuevo la aplicación abriendo localhost:8080. Selecciona la máscara y pruébala.

Vonage Video stream with a Batman maskVonage Video stream with a Batman mask

En resumen

Escribir filtros de Snapchat y usar máscaras en transmisiones de video es más fácil gracias a los SDK de Vonage Video. Vonage Video también te permite enviar tus transmisiones a plataformas como YouTube Live, Twitch y Facebook al mismo tiempo. Por lo tanto, además de poder crear tu propio sitio de transmisión en vivo, puedes llegar a la gente en otros servicios.

Compartir:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke OliffAntiguos alumnos de Vonage

Amable educador tecnológico, padre de familia, defensor de la diversidad, probablemente discuta demasiado. Anteriormente ingeniero de backend. Háblame de JavaScript (frontend o backend), el increíble Vue.js, DevOps, DevSecOps, cualquier cosa JamStack. Escritor en DEV.to