Como usar o movimento do rosto para interagir com a tipografia

Publicados: 2022-03-10
Resumo rápido ↬ Neste artigo, abordaremos como combinar Machine Learning, fontes variáveis ​​e grades CSS para criar layouts que respondam à proximidade, inclinação e número de faces dos usuários.

Web designers estão sempre procurando novas maneiras de melhorar a apresentação do conteúdo de uma página. Às vezes, isso pode levar a soluções engenhosas ou a interagir com tecnologias que muitas vezes são mantidas longe do campo do design. Neste artigo vamos colocar a tipografia em contato com a Inteligência Artificial, usando aprendizado de máquina para detectar coisas como a proximidade do rosto do usuário a fim de melhorar a legibilidade do texto.

Vamos experimentar como usar o reconhecimento facial com o Tensorflow para extrair algumas informações da câmera, como a distância entre a tela e o rosto do usuário ou a quantidade de pessoas lendo a página. Em seguida, passaremos esses dados ao CSS para adaptar a tipografia e ajustar o layout da página.

O que é Tensorflow?

O Tensorflow é uma plataforma de código aberto do Google para Machine Learning. Machine Learning é um campo da Ciência da Computação que estuda algoritmos que aprendem a reconhecer relações complexas e padrões recorrentes de imagens, faixas de áudio, séries temporais, texto natural e dados em geral. Esses algoritmos geram modelos matemáticos (também chamados de modelos treinados), que são uma espécie de esquema que pode ser usado para tomar decisões com base nos dados de entrada. Se você quiser abordar o assunto, Charlie Gerard escreveu sobre ML para desenvolvedores frontend aqui na Smashing Mag.

O Tensorflow fornece muitas ferramentas para desenvolvedores de IA, cientistas de dados, matemáticos, mas não entre em pânico se a análise de dados não for o seu pão de cada dia! A boa notícia é que você não precisa ser um especialista para usá-lo, desde que esteja usando modelos pré-construídos, assim como vamos fazer.

Os modelos do Tensorflow estão disponíveis para uso na Web com o SDK do JavaScript.

Configuração

Para começar a usar algoritmos de reconhecimento facial, precisamos seguir alguns passos:

  • carregue o SDK do Tensorflow.
  • carregue a biblioteca Facemesh que contém o modelo matemático.
  • acessar a câmera do usuário e transmiti-la para um elemento de vídeo HTML. O Facemesh analisará os quadros da tag de vídeo para detectar a presença de rostos.

Nestes projetos vamos usar o Tensorflow via CDN, mas também está disponível no NPM caso você prefira o modo bundler:

 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>

O Tensorflow não faz o truque em si, então precisamos adicionar o Facemesh, uma biblioteca que é construída no topo da estrutura de ML e fornece um modelo já treinado para reconhecimento facial:

 <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>

O próximo passo é configurar a biblioteca Facemesh para carregar o modelo treinado e definir a função que avaliará os dados de face de um stream de vídeo:

 // create and place the video const video = document.createElement('video'); document.body.appendChild(video); // setup facemesh const model = await facemesh.load({ backend: 'wasm', maxFaces: 1, }); async function detectFaces() { const faces = await model.estimateFaces(video); console.log(faces); // recursively detect faces requestAnimationFrame(detectFaces); }

Agora estamos prontos para pedir ao usuário permissão para acessar o stream da câmera usando uma tag de vídeo:

 // enable autoplay video.setAttribute('autoplay', ''); video.setAttribute('muted', ''); video.setAttribute('playsinline', ''); // start face detection when ready video.addEventListener('canplaythrough', detectFaces); // stream the camera video.srcObject = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'user', }, }); // let's go! video.play();

O método navigator.mediaDevices.getUserMedia solicitará a permissão e começará a transmitir a câmera para o elemento de vídeo. Uma vez aceita, a câmera começará a transmitir para a tag de vídeo, enquanto o console do navegador registrará as informações de rosto detectadas pelo Facemesh.

Observe que as permissões da câmera exigem uma conexão https segura ou localhost: você não pode simplesmente abrir o arquivo index.html. Se você não tiver certeza de como configurar um servidor local de check-out http-server para Node ou siga este guia para Python ou este para PHP.

Mais depois do salto! Continue lendo abaixo ↓

Caso 1. Ajuste a tipografia usando a câmera do smartphone

Navegamos na web em todos os lugares com nosso smartphone. Houve um tempo, não muito tempo atrás, em que costumávamos pegar trens ou ônibus lotados e mantínhamos o smartphone bem perto dos olhos porque não havia espaço. Em muitos momentos e lugares do nosso dia, muitas vezes mudamos a posição e a inclinação do smartphone, mesmo que estejamos assistindo ao mesmo site. A distância entre os olhos e o smartphone afeta nossas capacidades de leitura. Avaliando essa distância podemos ajustar a microtipografia de forma a otimizar os glifos para uma leitura mais próxima ou mais distante.

A detecção de rosto significa, é claro, a detecção da posição dos olhos também. Podemos usar os dados fornecidos pelo Facemesh para calcular o tamanho do nosso rosto em relação a toda a imagem capturada pela câmera. Podemos supor que quanto maior o nosso rosto, mais perto estamos da tela. Podemos configurar uma escala de 0 (um braço distante - o rosto ocupa aproximadamente metade da câmera) a 1 (colado na tela) e detectar o valor atual com uma divisão de segmentos:

 async function detectFaces() { const faces = await model.estimateFaces(video); if (faces.length === 0) { // is somebody out there? return requestAnimationFrame(detectFaces); } const [face] = faces; // extract face surface corners let { bottomRight, topLeft} = face.boundingBox; // calculate face surface size let width = bottomRight[0] - topLeft[0]; let height = bottomRight[1] - topLeft[1]; let videoWidth = video.videoWidth; let videoHeight = video.videoHeight; let adjustWidth = videoWidth / 2; let adjustHeight = videoHeight / 2; // detect the ratio between face and full camera picture let widthRatio = Math.max(Math.min((width - adjustWidth) / (videoWidth - adjustWidth), 1), 0); let heightRatio = Math.max(Math.min((height - adjustHeight) / (videoHeight - adjustHeight), 1), 0); let ratio = Math.max(widthRatio, heightRatio); // recursively detect faces requestAnimationFrame(detectFaces); }
A representação do rosto do usuário por segmentos é colocada dentro da moldura de um smartphone para indicar quanta área da tela é ocupada pelo rosto.
Dois exemplos dos traços detectados pelo Facemesh, como a posição e inclinação dos olhos, nariz e boca. Podemos usar a área entre os pontos para calcular a proximidade da câmera do smartphone. (Visualização grande)

Agora que calculamos a ratio , é hora de fazer alguma mágica acontecer, passando o valor para a folha de estilo:

 document.documentElement.style.setProperty('--user-distance', ratio);

Com esse valor e um pouco de cálculo, poderíamos facilmente aplicar pequenas alterações no peso da fonte, tamanho e talvez também no estilo, mas podemos fazer algo ainda melhor. Usando uma fonte variável, uma fonte que possui formas e espaços parametrizados dos glifos, podemos ajustar a percepção de cada glifo atualizando sua variação de tamanho óptico.

Como cada fonte variável usa sua própria escala para valores de tamanho óptico, precisamos relacionar nosso valor de proporção a essa escala. Além disso, podemos querer mover apenas entre um subconjunto de tamanho óptico disponível, a fim de fornecer apenas pequenos aprimoramentos.

 .main-text { --min-opsz: 10; --max-opsz: 15; --opsz: calc(var(--min-opsz) + (var(--user-distance) * (var(--max-opsz) - var(--min-opsz)))); ... font-family: 'Amstelvar', serif; font-variation-settings: 'opsz' var(--opsz); }

Você pode ver ao vivo aqui. Observe que este exemplo é apenas uma demonstração de como a tecnologia funciona. As alterações tipográficas devem ser quase imperceptíveis aos olhos do usuário para realmente proporcionar uma melhor experiência ao leitor. Aqui aproveitamos as formas de glifos, mas usar cores para aumentar ou diminuir contrastes é apenas outra boa solução para tentar. Outro experimento foi detectar o ângulo do rosto para calcular a perspectiva da leitura, modificando ascendentes, descendentes e altura das letras:

Veja a Caneta [Facemesh e ascendentes/descendentes](https://codepen.io/smashingmag/pen/oNxrYop) de Edoardo Cavazza.

Veja a Pen Facemesh e ascendentes/descendentes de Edoardo Cavazza.

Caso #2: Ajustando um layout quando o número de pessoas olhando muda

Neste segundo caso, vamos alterar o layout com base no número de pessoas assistindo a tela. Podemos imaginar um ensaio exibido no Quadro Interativo no contexto de uma sala de aula do ensino médio. Esse cenário é discretamente diferente daquele detectado pela consulta de mídia de projeção obsoleta, pois queremos ajustar o layout da página se o número de alunos assistindo for menor ou maior que 10. Quando apenas alguns alunos estão na sala de aula, eles pode se aproximar do quadro com segurança, mas se toda a sala estiver presente, provavelmente o espaço não é suficiente e precisamos mudar o layout para mostrar menos (e maiores) coisas.

Precisamos apenas de algumas alterações no script anterior para detectar corretamente o número de rostos assistindo no quadro branco. Primeiro, precisamos instruir o Facemesh a detectar vários rostos:

 const model = await facemesh.load({ backend: 'wasm', maxFaces: 30, });

E então, temos que passar esse número para a folha de estilo:

 async function detectFaces() { const faces = await model.estimateFaces(video); document.documentElement.style.setProperty('--watching', faces.length); // recursively detect faces requestAnimationFrame(detectFace); }

Novamente, poderíamos usar esse valor para simplesmente aumentar o tamanho da fonte, mas nosso objetivo é fornecer um layout completamente diferente. Layouts de grade CSS podem nos ajudar nessa missão. Este documento projetado é um formulário longo com um aparte que contém imagens relacionadas:

 <section> <article> <h1>...</h1> <h2>...</h2> <p>...</p> </article> <aside> <img src="..." alt="..." /> </aside> </section>

E este é o seu layout padrão:

 section { display: grid; grid-template-columns: repeat(12, 1fr); grid-column-gap: 1em; width: 120ch; max-width: 100%; padding: 1em; } section article { grid-column: 1 / -5; } section aside { grid-column: 7 / -1; }
Esquerda: Quando mais de 10 pessoas estão assistindo, priorizamos o texto principal usando todas as colunas disponíveis e movendo as imagens após o texto. Direita: Um layout de página usando 12 colunas com guias ativos do inspetor de grade do Firefox. Todas as 12 colunas são usadas para o texto principal, 4 para imagens depois dele.
Esquerda : O layout padrão da página usa 8 de 12 colunas para o texto de formato longo e as 4 restantes para imagens. Direita : Quando mais de 10 pessoas estão assistindo, priorizamos o texto principal usando todas as colunas disponíveis e movendo as imagens após o texto. (Visualização grande)

Quando um grande número de pessoas está assistindo, precisamos privilegiar o contexto de leitura de formato longo, dando mais espaço à coluna principal, aumentando o tamanho da fonte e removendo elementos perturbadores. Para isso, aumentamos o número de colunas estendidas, movendo o lado abaixo do texto principal.

 :root { --watching: 10; } section { /** The maximum number of people watching for the default layout */ --switch: 10; /** The default number of columns for the text */ --text: 8; /** The default number of columns for the aside */ --aside: 4; grid-template-columns: repeat(calc(var(--text) + var(--aside)), 1fr); } section article { /** * Kinda magic calculation. * When the number of people watching is lower than --switch, it returns -2 * When the number of people watching is greater than --switch, it returns -1 * We are going to use this number for negative span calculation */ --layout: calc(min(2, (max(var(--switch), var(--watching)) - var(--switch) + 1)) - 3); /** * Calculate the position of the end column. * When --layout is -1, the calculation just returns -1 * When --layout is -2, the calculation is lower than -1 */ --layout-span: calc((var(--aside) * var(--layout)) + var(--aside) - 1); /** * Calculate the maximum index of the last column (the one "before" the aside) */ --max-span: calc(-1 * var(--aside) - 1); /** * get the max between --layout-span and the latest column index. * -1 means full width * --max-span means default layout */ --span: max(var(--max-span), var(--span)); grid-column-start: 1; grid-column-end: var(--span); }
  • Você pode ver ao vivo aqui →

Vice-versa, quando um pequeno grupo de alunos está experimentando o texto próximo ao quadro, poderíamos dar mais detalhes, como arquivos de mídia e gatilhos de ação interativos.

Além do reconhecimento facial

Os casos que enfrentamos () são apenas dois exemplos de como podemos usar a tecnologia de reconhecimento facial para layout ou escopos tipográficos. O Tensorflow fornece outros modelos e bibliotecas que podem transformar o stream da câmera em variáveis ​​para nossas páginas. Além disso, não devemos esquecer que em nossos smartphones existem muitos outros sensores que podemos explorar usando as APIs de sensores: GPS, acelerômetro, luz ambiente, etc.

Como o humor influencia a maneira como lemos, estudamos e buscamos informações, com o aprendizado de máquina também podemos analisar as expressões do usuário para mudar de layouts mínimos para detalhados de acordo com o espírito do usuário.

Por muitos anos, estamos acostumados a usar consultas CSS Media para web design responsivo. No entanto, o tamanho da janela de visualização é apenas uma das variáveis ​​da experiência do usuário. Recentemente, um novo tipo de consulta de mídia projetada para respeitar as preferências do usuário chegou aos navegadores, como o prefers-color-scheme e prefers-reduced-motion . Isso dá aos designers e desenvolvedores uma maneira de avançar nas práticas de web design, permitindo que a página da web se adapte a todo o ambiente em vez de apenas ao dispositivo do usuário. Na era do big data, temos a oportunidade de ir além do design responsivo e adaptável. Nossas páginas da web podem finalmente “sair da tela” e se tornar parte da experiência global do usuário. O design de interação vai envolver todas essas possibilidades, então continuar experimentando as possíveis combinações entre tecnologia e web design será crucial nos próximos anos.