https://a.storyblok.com/f/270183/31138/ce7511249b/blog_greenscreen_1200x600.png

Como usar uma tela verde em JavaScript com o Vonage Video

Publicado em May 12, 2021

Tempo de leitura: 4 minutos

Ao criar um editor do Vonage Video, o fluxo de vídeo pode ser obtido diretamente da câmera do usuário, de um <video> elemento ou de um elemento HTML <canvas> . Assim que os pixels são desenhados na tela, eles podem ser facilmente manipulados antes de serem utilizados em uma sessão da Video API.

Neste tutorial, você aprenderá a remover uma tela verde e substituí-la por uma nova imagem personalizada que poderá incluir em suas videochamadas.

Overview of the project componentsOverview of the project components

São necessários vários componentes para que o projeto funcione. Em primeiro lugar, um <video> elemento irá captar um fluxo da câmera do usuário. A cada quadro, o conteúdo do elemento de Video será desenhado em uma tela, onde percorreremos os pixels em um loop para remover aqueles que forem verdes. Em uma segunda tela, desenharemos a imagem de fundo substituta e, em seguida, sobreporemos os pixels não verdes da primeira tela sobre ela.

Com o resultado desejado em uma tela, podemos usar essa tela como fonte para um publisher da Video API da Vonage, que podemos utilizar em nossas sessões de vídeo com amigos.

Se você quiser dar uma olhada no código final, pode encontrá-lo em https://github.com/nexmo-community/video-green-screen

Marcação de andaimes

Crie uma nova pasta de projeto e, em seguida, um novo arquivo index.html, preenchendo esse arquivo com o seguinte 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>

Você também precisará da imagem com a qual deseja substituir a tela verde na pasta do projeto. Este tutorial utilizará um dos gradientes da marca Vonage. Depois de obter os contextos da tela, carregue a imagem:

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

Obter Video da webcam

Defina a <video> fonte do elemento para o fluxo da webcam do usuário. Este trecho de código selecionará a câmera padrão:

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

Crie uma função vazia process() função vazia. Assim que o dispositivo de vídeo do usuário estiver pronto e em “reprodução”, execute a função a cada quadro:

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

function process() {

}

Desenhar o fluxo de Video na tela

Atualização process():

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

Atualize a página e você deverá ver o seu <video> elemento, o primeiro <canvas> com uma imagem duplicada e o seu segundo <canvas> com a nova imagem de fundo. O objetivo é fazer com que todos os pixels que não sejam verdes fiquem sobre o fundo na segunda tela.

Ciclo pelos pixels

Em uma tela (canvas), toda a imagem é representada por uma única matriz longa de pixels. Embora você possa inicialmente pensar que nossa imagem de 320x240 terá 76.800 entradas na matriz, você estaria enganado.

canvas pixelscanvas pixels

Cada pixel visível é composto por quatro elementos da matriz — um para o valor do vermelho, um para o verde, um para o azul e o último para definir sua opacidade. Esses valores são importantes à medida que criamos e utilizamos o loop.

Obtenha a matriz de pixels deste quadro dentro da process() função e crie o loop:

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

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

}

Observe que o contador está configurado para aumentar em 4. Cada vez que esse loop for executado, i será o índice na matriz do valor de vermelho do próximo pixel visível.

Entendendo o HSL

Antes de podermos remover os pixels verdes, gostaria de apresentar a vocês o formato de cor Matiz, Saturação e Luminosidade (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 whiteVocê pode pensar na matiz como uma roda de cores — e usar a posição nessa roda para especificar uma cor, de 0 a 360. O “intervalo” de verde pode variar de pessoa para pessoa, mas, para mim, de 90 a 200 funciona bem.

No entanto, ao ler e gravar pixels em um <canvas> , é preciso usar o formato de cor Vermelho, Verde, Azul (RGB). Na parte inferior da sua <script>, adicione esta RGBToHSL() função fornecida no 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]
}

Tornando os pixels verdes transparentes

Dentro do process() loop, obtenha os valores RGB e HSL de cada pixel e defina os pixels que forem 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
}

Após o loop, atualize a imagem na tela:

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

Você pode perceber que 90 e 200 precisa ser ajustado, considerando a cor da sua tela e a iluminação.

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

Desenhar os pixels restantes no plano de fundo substituto

Para cada pixel que ainda estiver na primeira tela, desenhe-o na segunda. Após a if instrução no process() loop, adicione uma else condição:

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

O x e y valores são os pixels visuais; portanto, o i valor deve ser dividido por 4.

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

Incluir o Canvas na sessão da Video API

Crie um novo projeto no seu Painel do Vonage Video. Após criá-lo, role a página até “Ferramentas do projeto” e crie uma nova sessão roteada. Anote o ID da sessão e crie um novo token.

Na parte superior do seu <script>, crie três novas variáveis com os dados do painel do projeto:

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

Em seguida, copie a <script> tag da página do Client SDK da Video API da Vonage e coloque-a acima da sua tag <script> .

Na parte inferior da sua <script> , inicialize sua sessão básica da Video API da Vonage e publique a partir da segunda tela:

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

O <video> e <canvas> são necessários para que isso funcione, mas você provavelmente não vai querer que eles fiquem visíveis na sua página da web. No seu <head>, adicione o seguinte CSS para ocultá-los:

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

Qual será a sua formação?

Espero que você tenha achado este post útil e que agora possa criar planos de fundo personalizados à vontade. Embora tenhamos nos concentrado em telas verdes, qualquer manipulação no nível do pixel pode ser feita com a mesma abordagem.

Para ir um pouco mais além, você pode optar por oferecer aos usuários controles que alterem os valores HSL que estão “dentro do intervalo” a ser substituído, ou um seletor de arquivos para trocar a imagem.

Você pode encontrar o projeto final em https://github.com/nexmo-community/video-green-screen

Como sempre, se precisar de ajuda, sinta-se à vontade para entrar em contato na Slack da Comunidade de Desenvolvedores da Vonage. Esperamos vê-lo por lá.

Compartilhar:

https://a.storyblok.com/f/270183/400x400/c822f15b89/kevinlewis.png
Kevin LewisEx-funcionários da Vonage

Ex-representante de desenvolvedores da Vonage, onde sua função era apoiar a comunidade tecnológica local em Londres. Ele é um experiente organizador de eventos, jogador de jogos de tabuleiro e pai de um cachorrinho fofo chamado Moo. Ele também é o principal organizador do You Got This — uma rede de eventos sobre as habilidades essenciais necessárias para uma vida profissional feliz e saudável.