Bienvenidos. En este informe explico algo sobre el video que hice acerca de cómo crear una aplicación de dibujo básica con TensorFlow.js, MediaPipe modelo de reconocimiento de manos y mi librería de WebComponents para simplificar el reconocimiento de objetos y acciones en consecuencia.
El video es el siguiente.
Primero creamos un proyecto web, simple, un documento HTML. Incorporamos en un Script la librería https://gorosito.red/componentes/
Insertamos el webComponent de etiqueta reconocer-manos. Agregamos dos atributos: DER="" e IZQ="", y cada uno apunta al nombre de una función que crearemos en un Script aparte.
Cada función se dedica a reconocer puntos clave de la mano izquierda y de la derecha de la primera persona reconocida. Bueno, simplifiqué a la herramienta para que fuera una sola persona.
Como función de tipo "callback" que son, se les pasa como parámetro un objeto, el mismo generado por TFjs, con tres factores: handedness (tipo de mano, string, si es "Left" o "Right"), keypoints (listado de puntos clave, en 2d) y keypoints3d (listado de puntos clave, en 3D).
En el ejemplo hice algo para reconocer 3 dedos (fingers), la punta (the tip) de cada uno: pulgar (thumb), índice (index) y el dedo medio (middle).
Si no ubicamos el thumb_finger_tip, index_finger_tip y middle_finger_tip entre los 21 ítems de cada lista, podemos filtrar la lista. Tomamos la lista, ejecutamos el método .filter() pasándole una función adecuada, y de los resultados nos quedamos con sólo el primero (ítem [0] de la lista).
La función de filtrado recibe un primer parámetro $item (por ejemplo), que representa a cada keypoint. Si hacen console.log($item) observarán que se tiene disponible una clave ["name"]/.name, indicando el nombre de la articulación o punto de la mano. Filtramos con una expresión regular /thumb/i y /tip/i, mediada por un operador lógico && (and) y obtendremos lo necesitado.
Una vez reconocidos los dedos, tendremos disponibles su ubicación a través de las claves .x y .y.
Si observamos las lecturas, a medida que un objeto baja, la .y crece. Por lo tanto, podemos comparar las alturas .y del índice y del dedo medio; si la de éste es un número más grande, estará más abajo. Con una toma de decisiones podemos "disparar" un evento y ejecutar una función.
La otra posibilidad, con un poco de geometría, podemos reconocer si dos dedos están pegados o muy cercanos, si trigonométricamente su hipotenusa es menor a una proporción respecto de la imagen. Aunque para el ejemplo, un número menor a 20 ó 30 nos basta.
Hacemos un delta horizontal y un delta vertical. No hace falta que hallemos el valor absoluto, ya que por teorema de Pitágoras los elevaremos al cuadrado a cada uno. Al hallar la raíz cuadrada de sus sumas, obtendremos la distancia solicitada.
¡Perfecto! Con un par de tomas de decisiones podemos reconocer la ubicación de los dedos y entonces dibujar en pantalla.
Ahora viene lo complicado, ¿Cómo dibujar? ¿En dónde dibujar? Se puede realizar esto mediante SVG; el webComponent desde el comienzo ya lo tenía agregado. Pero dibujar desde este objeto HTML, sin una librería, reconocí que es algo engorroso. Y yo conocía la API de Canvas HTML. Así que me dediqué a mejorar el webComponent y agregar un Canvas.
Para poder controlarlo, debía esperar un incierto tiempo, aquel por el cual la librería se descarga desde Internet y el navegador requiere generarlo. Así que modifiqué en el momento al WebComponent y le agregué un atributo LISTO="". Este atributo debe apuntar a una función que se ejecutará cuando el WebComponent se renderice.
Ahí se puede exponer el objeto Canvas que se ubicará en la misma posición que el objeto Video de la WebCam. Para ello creamos una variable global, $dibujo. En la función de LISTO deberemos igualarlo al .canvas del objeto de reconocer. Por ello, antes identifiqué al objeto mediante el atributo ID="", como idReconocer (id="idReconocer"). El navegador nos crea una variable con el mismo identificador para controlar al objeto. Por lo tanto, $dibujo = idReconocer.canvas.
Pero para la posibilidad de dibujar en él, tenemos que extraer el contexto 2D. Así que directamente $dibujo = idReconocer.canvas.getContext("2d");
Con $dibujo.beginPath(); podemos comenzar a dibujar. Si vamos a dibujar líneas contínuas (equivalente al objeto PATH de SVG), debemos jugar con $dibujar.moveTo() y $dibujar.lineTo(). Todo esto en la función fnDibujar(), a la cual se le pasa las coordenadas $x e $y de algún punto de la mano (por ejemplo, la punta del dedo índice o el pulgar). Cuando debamos efectuar el dibujo, se aplica $dibujar.stroke();
Finalmente, deberíamos hacer algo para cambiar de colores. Así que diseñé en el SVG algo para generar bloques de colores superpuestos sobre el Canvas. Ancho y alto 50 pixeles, en el medio de la imagen. Me confundí al principio porque el método que hice para que la webCam trabajara en espejo requiere dar vuelta el dibujo resultante (regla CSS de transform:translateX()).
Ahora, con la otra mano (función fnDer), hice algo para que al ubicar los dedos clave en el recuadro del color, el stroke se pintara de dicho color. No obstante, el dibujo se arruina debido a que como no reinicié el path (no hice $dibujo.beginPath()) todo lo anterior dibujado también cambiaba al nuevo color. Así que lo último del video es realizar ese cambio.
Muy buen comienzo de ilustraciones con Inteligencia Artificial.
Comentarios
Publicar un comentario