
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to
Filtros no estilo do Snapchat com Tracking.js e Vonage
Tempo de leitura: 20 minutos
Seja o Batman ou simplesmente assuste algumas pessoas. Neste guia, você vai adicionar o rastreamento facial usando o Tracking.js e criar alguns filtros legais no estilo do Snapchat para uma transmissão de Video. Legal!
Video API da Vonage
Video API da Vonage oferece uma plataforma baseada em WebRTC para adicionar vídeo ao vivo, voz e mensagens a aplicativos da web, móveis e de desktop. Usaremos a Video API da Vonage para conectar nosso aplicativo a uma sessão de streaming de vídeo. Ao trabalhar com a Video API da Vonage, você poderá encontrar o nome OpenTok, seu nome anterior, usado no código ou na documentação.
Tracking.js
Tracking.js é uma biblioteca leve e poderosa de detecção de rostos e rastreamento de cores em tempo real, que traz diversos algoritmos e técnicas para o ambiente do navegador. Usaremos o tracking.js para rastrear rostos, a fim de podermos aplicar máscaras.
Pré-requisitos
É isso que você vai precisar para seguir este tutorial.
Node e NPM
Para começar, você precisará ter o Node e o gerenciador de pacotes do Node (npm) instalados. Vamos verificar se eles estão instalados executando o seguinte comando:
Se você ainda não tiver o Node e o npm instalados, acesse nodejs.org e instale a versão correta para o seu sistema operacional. Escrevi este artigo usando o Node 11.8 e o npm 6.8.
Account da Vonage
Acesse Vonage para se inscrever para um teste gratuito.
Git
Veja se você tem o Git instalado.
Caso contrário, a Atlassian disponibiliza um ótimo guia sobre como instalar o Git. Escrevi este artigo com o Git 2.17 instalado.
Comece agora
Como base, preparei uma versão do aplicativo a partir de exemplos da web do OpenTok para filtros de stream, que você pode editar.
Confira minha versão dos exemplos de filtro da web do OpenTok aqui executando o seguinte comando na ferramenta de linha de comando de sua preferência ou usando sua interface do Git para acessar esta URL: https://github.com/nexmo-community/snapchat-filters-with-opentok-and-face-tracking.git
Observação: O
masterramificação está no ponto inicial, mas se você for impaciente (e gostar de pular direto para o final, como eu), podegit checkout demo-ende executar o aplicativo a partir daí.
Configurar o aplicativo
Portanto, antes de começar a programar, você precisa configurar o aplicativo tal como está e ter uma ideia do ponto de partida. Para isso, será necessária a Account da Vonage mencionada nos pré-requisitos. Se você não tiver uma, acesse Vonage para se cadastrar agora.
Criar um projeto
Depois de confirmar seu endereço de e-mail e fazer login, você poderá clicar em “Criar projeto” na visão geral da sua conta de Account. Crie um novo projeto da Video API da Vonage clicando em “Criar projeto personalizado”. Dê um nome ao seu projeto e clique em “Criar”.
Agora, anote sua chave de API e SECRETO para a etapa de configuração.
Criar uma sessão
Depois de criar seu projeto, você será redirecionado para o painel do projeto. Role a página para baixo até Ferramentas do projeto e clique em “Criar ID de sessão”. Você também pode escolher entre Reencaminhado e Roteado. Basicamente, o modo “Relayed” tenta uma conexão ponto a ponto, mas usará servidores de retransmissão quando os firewalls bloquearem a conexão ponto a ponto. “Routed” é a configuração padrão, na qual os streams são direcionados para o Media Router. Você pode ler mais sobre sessões na documentação.
Anote o ID DA SESSÃO que for gerado.
Gerar um token
Antes de configurarmos o aplicativo, precisamos gerar um token. No painel do seu projeto, na seção Ferramentas do Projeto (a mesma seção em que você criou seu ID de sessão), insira o ID DE SESSÃO que você acabou de gerar e clique em Gerar token.
Anote o TOKEN GERADO para a próxima etapa.
Criar um arquivo de configuração
Faça uma cópia de js/config.js.example e salve-a como js/config.js (no diretório js), certificando-se de modificar os valores para os da sua conta da Vonage.
CHAVE DA API, ID DA SESSÃOe TOKEN foram criados acima. Se você estiver executando o aplicativo localmente usando npm start, você precisará de uma URL BASE de http://127.0.0.1:8080. Para plataformas como o Heroku, é mais parecido com https://app-name.herokuapp.com.
Dica rápida: Se você estiver executando o aplicativo após ter executado
npm startvocê pode pará-lo novamente com CTRL+C.
O seu js/config.js ficará mais ou menos assim.
// 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 dependências
Agora use o npm para instalar suas dependências. Execute este comando dentro do diretório do seu projeto.
Executar o aplicativo
Você pode iniciar o aplicativo usando npm start. Essa é uma função bem prática. Se você não configurar um script de inicialização personalizado em package.json, o programa presumirá que você se refere a node index.js onde index.js é o arquivo principal configurado em package.json. No nosso caso, ele executará ./node_modules/bin/http-server para iniciar nosso servidor web baseado em nós.
Depois que estiver tudo funcionando, você poderá visualizar a sessão localmente abrindo localhost:8080 no seu navegador preferido. Deve ficar mais ou menos assim, mas com você no lugar de mim.
Basic Vonage Video example up and running
Agora, abra a mesma URL em outra janela do navegador para que você possa conversar consigo mesmo!
Observação importante: Nem sempre estou com essa aparência desleixada :)
Basic Vonage Video conversation occuring
Apresentando a tecnologia
A plataforma Vonage Video API facilita a integração de vídeo interativo em tempo real e de alta qualidade, mensagens, compartilhamento de tela e muito mais em aplicativos web e móveis. Neste caso específico, utilizaremos os SDKs de cliente em JavaScript.
SDK de Video da Vonage
O Client SDK JavaScript está fazendo o trabalho pesado. Ele vai usar credenciais codificadas para se conectar a uma sessão por meio de um token que geramos no portal do desenvolvedor. Em seguida, ele vai assinar e publicar fluxos de áudio e Video no nosso navegador.
Tracking.js
O Tracking.js é uma biblioteca leve de JavaScript para rastrear posições faciais em imagens e Video. Vamos recuperar as coordenadas na tela.
O aplicativo de demonstração
O aplicativo que você clonou e iniciou já vem com alguns filtros integrados. Esses filtros funcionam utilizando dados de imagem da API do Canvas e os modificam. Os dados da imagem estão em um objeto que contém largura, altura e dados de pixels. Os dados de pixels estão contidos em uma matriz. A cada 4 itens representam os pixels R, G, B e A, respectivamente.
Portanto, a aplicação de efeitos de imagem pode ser feita por meio da modificação dos dados RGBA.
Aqui está um exemplo um tanto improvisado. O código é explicado por meio de comentários, mas, na verdade, ele não vai funcionar.
// 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);
Você pode ver os filtros existentes modificando os valores RGB em js/filters.js.
Criando alguns novos filtros
A seguir, estão as etapas para editar o aplicativo com o objetivo de criar dois novos filtros: um para ajustar o brilho e limite do nosso fluxo.
Brilho
Ajustar o brilho de uma imagem é uma das operações de processamento de imagem mais simples que se pode realizar. Conforme mostrado no exemplo de código anterior, basta aplicar a alteração desejada no brilho a cada um dos canais vermelho, verde e azul.
Abra js/filters.js e encontre a linha com o comentário // Brighten e substitua o comentário por uma nova função para o nosso filtro.
Dica rápida: Comentários como
// ...e<!-- // ... -->mostram que há mais código, mas que foi removido, para maior clareza, aqui no blog.
// js/filters.js
(function closure(exports) {
var Filters = {
// ...
brighten: function brighten(imgData) {
// New data
// Loop
// Return
}
};
// ...
})(exports);Os dados da imagem estão em uma matriz de 8 bits sem sinal e com valores limitados. Isso significa que ela tem comprimento fixo e os valores são limitados a valores sem sinal entre 0 e 255. Perfeito para valores RGB. Portanto, precisamos criar uma nova matriz vazia de 8 bits na qual armazenaremos nossos dados de imagem modificados. Substitua // New data conforme mostrado aqui, onde você define o comprimento fixo com o comprimento dos dados existentes.
// js/filters.js
// ...
brighten: function brighten(imgData) {
const res = new Uint8ClampedArray(imgData.data.length);
// Loop
// Return
}
// ...Agora, percorra os dados existentes e desloque os valores para aplicar um efeito de clareamento. Substitua // Loop conforme mostrado aqui. E, conforme explicado anteriormente neste artigo, você está percorrendo os dados, pulando para cada quarto índice na 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 fim, nessa função, você retornará a nova matriz de dados. Substitua // Return conforme mostrado.
// 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);
}
// ...
Com a função configurada, agora você precisa poder selecioná-la. Então, abra index.html e substitua <!-- Brighten --> por uma opção para selecionar nossa nova função.
<!-- 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>
<!-- // ... -->Agora, reinicie o aplicativo abrindo localhost:8080 e confira o novo filtro de clareamento. O option valor é o mesmo que o nome da função.
Vonage Video stream with a brightening filter
Determinação do limiar
O próximo passo é aplicar o limiar à imagem.
Abra js/filters.js e encontre a linha com o comentário // Threshold e substitua-a pela função a seguir. Desta vez, você tem a função completa.
O funcionamento do limiar consiste em substituir a cor por branco ou preto com base em um algoritmo que envolve a soma dos valores dos canais vermelho, verde e azul. Uma soma calculada acima de 100 (>= 100), que representa as cores mais claras, é transformada em branco. Todo o restante é transformado em preto.
// 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);
Agora, vamos adicionar uma opção para selecionar o limiar. Então, abra index.html e substitua <!-- Threshold --> por uma opção para selecionar nossa nova função.
<!-- 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>
<!-- // ... -->Agora, reinicie o aplicativo abrindo localhost:8080 e confira o novo filtro de limiar.
Vonage Video stream with a thresholding filter!
Rastreamento facial com o Tracking.js
A seguir, você vai adicionar o Tracking.js e exibir uma imagem sobre o seu rosto, como se fosse uma máscara.
Instalar o Tracking.js
Use o npm para instalar o Tracking.js.
Adicione a biblioteca ao seu aplicativo usando a abordagem tradicional. Edite index.html e localize o comentário onde está escrito <!-- Scripts -->. Substitua-o pelas novas tags de script mostradas abaixo. O primeiro arquivo tracking-min.js é a biblioteca Tracking.js minimizada. O segundo arquivo face-min.js contém os dados utilizados pelo Tracking.js para o rastreamento de rostos em mídias. Outros conjuntos de dados, como características faciais — incluindo olhos, nariz e boca —, também estão disponíveis.
<!-- 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>
<!-- // ... -->Como os filtros e as máscaras funcionam de maneira um pouco diferente, eu os mantive separados. Isso tem a ver com os objetos disponíveis dentro do arquivo js/filters.js arquivo.
Crie um novo arquivo js/masks.js e insira o seguinte conteúdo para começar.
// 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);Abra e edite js/publish.js e localize o // Tracker.js comentário. Substitua-o, conforme mostrado aqui.
Este rastreador de objetos é o que irá analisar o material de mídia e reconhecer rostos. O rastreamento funciona, em geral, movendo uma “forma” ao redor do material de mídia até encontrar uma correspondência próxima. A configuração define a escala inicial da “forma” e o tamanho do passo. Esses parâmetros ajudarão a ajustar com precisão (e a melhorar a velocidade) do rastreador.
Observação: Com minhas webcams integrada e de 720p, essa configuração costumava funcionar muito bem.
// 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);
// ...E, nesse mesmo arquivo, encontre o // apply Mask comentário e substitua-o desta forma.
// js/publish.js
// ...
ctx.putImageData(imgData, 0, 0);
exports.Masks.selectedMask(tracker, canvas, ctx);
// ...Editar index.html e encontre os dois <!-- Masks --> comentários.
Para manter as máscaras separadas, você também criará uma caixa de seleção separada. Vamos deixar as opções em branco, por enquanto.
Você também precisa incluir o novo js/masks.js arquivo aqui.
<!-- 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 executá-lo, é preciso aplicar alguns estilos à caixa de seleção das máscaras. Edite css/app.css e localize o comentário /* Masks menu css */ e substitua-o pelo seguinte CSS.
/* css/app.css */
/* // ... */
#mask {
position: absolute;
bottom: 0;
left: 0;
z-index: 102;
}Você ainda não criou nenhuma máscara, mas reinicie o aplicativo abrindo localhost:8080 para verificar se não cometeu nenhum erro. Se o stream funcionar, você está no caminho certo.
A máscara de Guy Fawkes
A primeira máscara será a do Guy. A máscara de Guy Fawkes se tornou um símbolo do anonimato, que é justamente o objetivo de usar uma máscara!
Abrir js/masks.js e encontre o comentário // Guy Fawkes. Substitua o comentário pela nova função.
// 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
};
// ...Carregue a imagem que você deseja sobrepor a qualquer rosto identificado pelo rastreador, substituindo o // Load an image comentário conforme mostrado aqui. ../images/guy.png deve estar disponível no repositório.
// 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
}
// ...Agora, habilite o rastreamento em nossa mídia. A mídia que você está usando é o fluxo do canvas. Você precisa habilitar a câmera e o áudio aqui. Substitua o // Start tracking comentário da seguinte maneira.
// 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
}
// ...Você precisa registrar um ouvinte de evento no track evento do rastreador para que, quando ele detectar um rosto, possa desenhar a máscara sobre o rosto detectado.
// 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);
});
});
}
// ...Agora, você precisa adicionar a opção da máscara do Guy ao aplicativo. Edite index.html, procure o <!-- Guy Fawkes --> comentário e adicione a opção conforme mostrado abaixo.
<!-- index.html -->
<!-- // ... -->
<div id="videos">
<div id="subscriber"></div>
<div id="publisher">
<!-- // ... -->
Mask: None
Mask: Guy Fawkes
<!-- Batman -->
</div>
</div>
<!-- // ... -->Vamos torcer para que tudo funcione! Reinicie o aplicativo abrindo localhost:8080. Selecione sua primeira máscara e dê uma olhada nela.
Vonage Video stream with a Guy Fawkes mask!
Seja o Batman
A máscara final e a edição do nosso código serão o Batman. Abra js/masks.js e localize o comentário // Batman. Substitua o comentário por esta nova função.
// 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);
});
});
}
// ...Agora adicione a opção da máscara do Batman ao aplicativo. Edite index.html, procure o <!-- Batman --> comentário e adicione a opção conforme mostrado abaixo.
<!-- index.html -->
<!-- // ... -->
<div id="videos">
<div id="subscriber"></div>
<div id="publisher">
<!-- // ... -->
Mask: None
Mask: Guy Fawkes
Mask: Batman
</div>
</div>
<!-- // ... -->Posso ser o Batman?? Reinicie o aplicativo abrindo localhost:8080. Selecione a máscara e experimente.
Vonage Video stream with a Batman mask
Em resumo
A criação de filtros do Snapchat e o uso de máscaras em transmissões de vídeo ficam mais fáceis com os SDKs do Vonage Video. O Vonage Video também permite que você transmita simultaneamente para plataformas como o YouTube Live, o Twitch e o Facebook. Assim, além de poder criar seu próprio site de transmissões ao vivo, você ainda pode alcançar o público em outros serviços.
Compartilhar:
Educador de tecnologia com um jeito amigável, pai de família, defensor da diversidade, provavelmente discuto um pouco demais. Ex-engenheiro de backend. Fale comigo sobre JavaScript (front-end ou back-end), o incrível Vue.js, DevOps, DevSecOps e tudo relacionado ao JamStack. Escritor no DEV.to