Cómo usar Face Motion para interactuar con la tipografía

Publicado: 2022-03-10
Resumen rápido ↬ En este artículo, cubriremos cómo combinar Machine Learning, fuentes variables y cuadrículas CSS para crear diseños que respondan a la proximidad, la inclinación y la cantidad de rostros de los usuarios.

Los diseñadores web siempre buscan nuevas formas de mejorar la presentación del contenido de una página. A veces, esto puede conducir a soluciones ingeniosas o a interactuar con tecnologías que a menudo se mantienen alejadas del campo del diseño. En este artículo pondremos la tipografía en contacto con la Inteligencia Artificial, utilizando el aprendizaje automático para detectar cosas como la proximidad de la cara del usuario para mejorar la legibilidad del texto.

Experimentaremos sobre cómo usar el reconocimiento facial con Tensorflow para extraer información de la cámara, como la distancia entre la pantalla y la cara del usuario o la cantidad de personas que leen la página. Luego, pasaremos esos datos a CSS para adaptar la tipografía y ajustar el diseño de la página.

¿Qué es Tensorflow?

Tensorflow es una plataforma de código abierto de Google para Machine Learning. El aprendizaje automático es un campo de la informática que estudia algoritmos que aprenden a reconocer relaciones complejas y patrones recurrentes a partir de imágenes, pistas de audio, series temporales, texto natural y datos en general. Estos algoritmos generan modelos matemáticos (también llamados modelos entrenados), que son una especie de esquema que se puede usar para tomar decisiones basadas en datos de entrada. Si desea abordar el tema, Charlie Gerard escribió sobre ML para desarrolladores frontend aquí en Smashing Mag.

Tensorflow proporciona muchas herramientas para desarrolladores de IA, científicos de datos, matemáticos, ¡pero no se asuste si el análisis de datos no es su pan de cada día! La buena noticia es que no tienes que ser un experto para usarlo, siempre y cuando estés usando modelos preconstruidos, tal como lo haremos nosotros.

Los modelos de Tensorflow están disponibles para usarse en la Web con su SDK de JavaScript.

Configuración

Para comenzar a usar algoritmos de reconocimiento facial, debemos seguir algunos pasos:

  • carga el SDK de Tensorflow.
  • cargue la biblioteca Facemesh que contiene el modelo matemático.
  • acceda a la cámara del usuario y transmítala a un elemento de video HTML. Facemesh analizará los fotogramas de la etiqueta de video para detectar la presencia de rostros.

En estos proyectos vamos a utilizar Tensorflow a través de CDN, pero también está disponible en NPM si prefiere la forma de paquete:

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

Tensorflow no hace el truco por sí mismo, por lo que debemos agregar Facemesh, una biblioteca que se basa en la parte superior del marco ML y proporciona un modelo ya entrenado para el reconocimiento facial:

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

El siguiente paso es configurar la biblioteca Facemesh para cargar el modelo entrenado y definir la función que evaluará los datos faciales de una transmisión de video:

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

Ahora estamos listos para pedirle al usuario permiso para acceder a la transmisión de su cámara usando una etiqueta de video:

 // 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();

El método navigator.mediaDevices.getUserMedia solicitará el permiso y comenzará a transmitir la cámara al elemento de video. Una vez aceptado, la cámara comenzará a transmitir a la etiqueta de video, mientras que la consola del navegador registrará la información del rostro detectada por Facemesh.

Tenga en cuenta que los permisos de la cámara requieren una conexión https segura o localhost: no puede simplemente abrir el archivo index.html. Si no está seguro de cómo configurar un servidor local, consulte el servidor http para Node o siga esta guía para Python o esta para PHP.

¡Más después del salto! Continúe leyendo a continuación ↓

Caso 1. Ajuste la tipografía usando la cámara del teléfono inteligente

Navegamos por la web en todas partes con nuestro teléfono inteligente. Hubo un tiempo, no hace mucho, en el que tomábamos trenes o autobuses llenos de gente y teníamos el smartphone muy cerca de los ojos porque no había espacio. En muchos momentos y lugares de nuestro día, solemos cambiar la posición y la inclinación del smartphone, aunque estemos viendo el mismo sitio. La distancia entre los ojos y el teléfono inteligente afecta nuestras capacidades de lectura. Evaluando esa distancia podemos ajustar la microtipografía para optimizar los glifos para una lectura más cercana o más lejana.

La detección de rostros significa, por supuesto, también la detección de la posición de los ojos. Podemos utilizar los datos proporcionados por Facemesh para calcular el tamaño de nuestra cara en relación con la imagen completa capturada por la cámara. Podemos suponer que cuanto más grande sea nuestra cara, más cerca estamos de la pantalla. Podemos configurar una escala de 0 (un brazo separado, la cara ocupa aproximadamente la mitad de la cámara) a 1 (pegado a la pantalla) y detectar el valor actual con una división 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); }
La representación de la cara del usuario por segmentos se coloca dentro del marco de un teléfono inteligente para indicar qué área de la pantalla ocupa la cara.
Dos ejemplos de los rasgos detectados por Facemesh, como la posición e inclinación de ojos, nariz y boca. Podemos usar el área entre los puntos para calcular la proximidad a la cámara del teléfono inteligente. (Vista previa grande)

Ahora que hemos calculado la ratio , es hora de hacer algo de magia, pasando el valor a la hoja de estilo:

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

Con este valor y un poco de cálculo, podríamos aplicar fácilmente pequeños cambios en el peso de la fuente, el tamaño y tal vez también el estilo, pero podemos hacer algo aún mejor. Usando una fuente variable, una fuente que tiene formas y espacios parametrizados de los glifos, podemos ajustar la percepción de cada glifo actualizando su variación de tamaño óptico.

Dado que cada fuente variable usa su propia escala para los valores de tamaño óptico, debemos relacionar nuestro valor de proporción con esa escala. Además, es posible que deseemos movernos solo entre un subconjunto del tamaño óptico disponible, para proporcionar solo pequeñas mejoras.

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

Puedes verlo en vivo aquí. Tenga en cuenta que este ejemplo es solo una demostración de cómo funciona la tecnología. Los cambios tipográficos deben ser casi imperceptibles a los ojos del usuario para brindar realmente una mejor experiencia de lectura. Aquí aprovechamos las formas de los glifos, pero usar colores para aumentar o disminuir los contrastes es solo otra buena solución para probar. Otro experimento fue detectar el ángulo de la cara para calcular la perspectiva de la lectura, modificando ascendentes, descendentes y la altura de las letras:

Vea el Pen [Facemesh y ascendentes/descendentes](https://codepen.io/smashingmag/pen/oNxrYop) de Edoardo Cavazza.

Vea el Pen Facemesh y los ascendentes/descendentes de Edoardo Cavazza.

Caso #2: Ajuste de un diseño cuando cambia el número de personas que miran

En este segundo caso, vamos a cambiar el diseño en función del número de personas que miran la pantalla. Podemos imaginar un ensayo que se muestra en la pizarra interactiva en el contexto de un aula de secundaria. Este escenario es discretamente diferente del detectado por la consulta de medios de proyección obsoletos, ya que queremos ajustar el diseño de la página si el número de estudiantes que miran es menor o mayor que 10. Cuando solo unos pocos estudiantes están en el aula, ellos puede acercarse a la pizarra con seguridad, pero si todo el aula está presente, probablemente el espacio no sea suficiente y necesitemos cambiar el diseño para mostrar menos (y más grandes) cosas.

Solo necesitamos algunos cambios en el script anterior para detectar correctamente la cantidad de rostros que miran en la pizarra. Primero, debemos indicarle a Facemesh que detecte varias caras:

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

Y luego, tenemos que pasar ese número a la hoja de estilo:

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

Nuevamente, podríamos usar ese valor para simplemente aumentar el tamaño de la fuente, pero nuestro objetivo es proporcionar un diseño completamente diferente. Los diseños de cuadrícula CSS pueden ayudarnos en esta misión. Este documento proyectado es un formulario largo con un aparte que contiene imágenes relacionadas:

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

Y este es su diseño predeterminado:

 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; }
Izquierda: cuando hay más de 10 personas mirando, priorizamos el texto principal usando todas las columnas disponibles y moviendo las imágenes después del texto. Derecha: un diseño de página que usa 12 columnas con guías de inspector de cuadrícula de Firefox activas. Las 12 columnas se utilizan para el texto principal, 4 para las imágenes posteriores.
Izquierda : el diseño predeterminado de la página utiliza 8 de las 12 columnas para el texto de formato largo y las 4 restantes para las imágenes. Derecha : cuando hay más de 10 personas viendo, priorizamos el texto principal usando todas las columnas disponibles y moviendo las imágenes después del texto. (Vista previa grande)

Cuando un gran número de personas está viendo, debemos privilegiar el contexto de lectura de formato largo, dando más espacio a la columna principal, aumentando el tamaño de la fuente y eliminando elementos molestos. Para hacer eso, aumentamos el número de columnas distribuidas, moviendo el lado debajo del 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); }
  • Puedes verlo en vivo aquí →

Viceversa, cuando un pequeño grupo de estudiantes está experimentando el texto cerca de la pizarra, podemos dar más detalles, como archivos multimedia y activadores de acciones interactivas.

Más allá del reconocimiento facial

Los casos que enfrentamos () son solo dos ejemplos de cómo podemos usar la tecnología de reconocimiento facial para el diseño o los alcances tipográficos. Tensorflow proporciona otros modelos y bibliotecas que pueden transformar la transmisión de la cámara en variables para nuestras páginas. Además, no debemos olvidar que en nuestros smartphones hay un montón de otros sensores que podríamos explotar usando las Sensor APIs: GPS, acelerómetro, luz ambiental, etc.

Dado que el estado de ánimo influye en la forma en que leemos, estudiamos y buscamos información, con el aprendizaje automático también podemos analizar las expresiones de los usuarios para cambiar de diseños mínimos a detallados según el estado de ánimo del usuario.

Durante muchos años, hemos estado acostumbrados a usar consultas CSS Media para el diseño web receptivo. Sin embargo, el tamaño de la ventana gráfica es solo una de las variables de la experiencia del usuario. Recientemente, un nuevo tipo de consulta de medios diseñado para respetar las preferencias del usuario llegó a los navegadores, como el prefers-color-scheme preferido y prefers-reduced-motion preferido. Esto brinda a los diseñadores y desarrolladores una forma de dar un paso adelante en las prácticas de diseño web, lo que permite que la página web se adapte a todo el entorno en lugar de solo al dispositivo del usuario. En la era de los grandes datos, tenemos la oportunidad de ir más allá del diseño receptivo y adaptativo. Nuestras páginas web finalmente pueden “salir de la pantalla” y convertirse en parte de la experiencia global del usuario. El diseño de interacción va a involucrar todas estas posibilidades, por lo que seguir experimentando con las posibles combinaciones entre tecnología y diseño web será crucial en los próximos años.