Como primer enfoque para el desarrollo del proyecto, intentamos vincular la tecnología de FaceMesh de ML5 con modelos 3D en formato .obj, buscando una solución que permitiera superponer esa estructura tridimensional sobre los rostros detectados. Significó una serie de dificultades debido a que los archivos .obj/.stl que probamos eran demasiado pesados, lo que impactaba negativamente en el rendimiento, y en muchos casos el sistema no era capaz de reconocerlos correctamente como rostros.
Se implementó una funcionalidad que permitió hacer un seguimiento de las coordenadas de la nariz y los ojos en tiempo real, usando el reconocimiento facial. Este seguimiento permitió que una imagen siga dichos puntos clave, aunque el rendimiento no fue del todo preciso y aún necesita ajustes. En este enfoque, sigue la fase de desarrollo y prueba.
Al no obtener los resultados deseados con faceMesh, exploramos otras ideas, encontrando el modelo de “sticker” (Daniel Shiffman). Esta alternativa permitió la implementación de un objeto gráfico (sticker) sobre el rostro, logrando cierta interacción con las poses faciales. Aparte del primer acercamiento a los comandos LET EMOJI y POSENET.
En la fase de prueba, la imagen superpuesta sobre el rostro seguía las coordenadas de los puntos faciales, pero no de manera completamente estable. En ciertos momentos, la imagen se movía excesivamente o incluso desaparecía cuando el reconocimiento de los puntos esenciales del rostro fallaba.
https://github.com/user-attachments/assets/7a3a4ba6-8155-4c69-b124-cc9c22427367
Se añadió la funcionalidad CANVASTEXT para proporcionar al usuario instrucciones claras durante la experiencia. Estas incluían mensajes como “Colocar una imagen o URL” para guiar en la interacción con la aplicación.
Debido a los problemas de estabilidad con PoseNet, optamos por cambiar a BodyPose de ML5, lo que permitió obtener mejores resultados en el reconocimiento facial y una mayor estabilidad en la superposición de imágenes.
https://github.com/user-attachments/assets/82e7cc87-72d7-479a-8cf9-25e28ae94150
Durante el proceso de desarrollo, realizamos varias modificaciones y ajustes manuales en el código para optimizar el rendimiento general del sistema. Entre estas modificaciones, se incluyó la eliminación de funciones que no cumplían un rol importante o que afectaban negativamente el rendimiento del programa. También se corrigieron detalles menores, como redundancias en el código, lo que nos permitió depurar y simplificar el flujo de trabajo, mejorando tanto la velocidad de procesamiento como la estabilidad del programa en su conjunto.
Como parte de la optimización del código establecido, se añadió una nueva funcionalidad que permite proyectar dos imágenes de manera simultánea en el canvas. Para lograr esto, se implementó una división visual en la mitad de la pantalla, dividiendo el área de trabajo en dos secciones claramente diferenciadas. Esta hendidura actúa como una línea divisoria que facilita la proyección de una imagen en cada lado del lienzo, manteniendo un balance visual y estructural dentro de la experiencia. Esta división ayuda a experimentar con comparaciones en tiempo real, lo que resulta útil para probar diferentes parámetros de reconocimiento facial y ajustes gráficos sin la necesidad de recargar el entorno.
Para mejorar la visualización de las dos imágenes proyectadas en la pantalla dividida, se implementó el uso del comando tint, que permite ajustar la transparencia de las imágenes de manera dinámica. Este ajuste de opacidad resulta esencial para ofrecer una experiencia visual más clara y evitar que las imágenes superpuestas se confundan o se mezclen en exceso. Al reducir la opacidad de una o ambas imágenes mediante el uso de tint, logramos una distinción más nítida entre las dos secciones del lienzo, lo que refuerza la función de la hendidura visual central y mejora la percepción de cada imagen de manera individual.
function drawImage(img, imgSize, imgX, imgY) {
for (let i = 0; i < poses.length; i++) {
let pose = poses[i];
let nosePoint = pose.keypoints[0];
let leftEyePoint = pose.keypoints[1];
let rightEyePoint = pose.keypoints[2];
let noseX, noseY, leftX, leftY, rightX, rightY;
if (nosePoint.confidence > 0.2) {
noseX = width - nosePoint.x;
noseY = nosePoint.y;
}
if (leftEyePoint.confidence > 0.2) {
leftX = width - leftEyePoint.x;
leftY = leftEyePoint.y;
}
if (rightEyePoint.confidence > 0.2) {
rightX = width - rightEyePoint.x;
rightY = rightEyePoint.y;
}
if (rightX && rightY && leftX && leftY && noseX && noseY) {
dis = dist(rightX, rightY, leftX, leftY);
//Reescalado de imagen
let calculatedSize = imgSize1;
image(img1, noseX - imgSize1 / 2 + imgX1, noseY - imgSize1 / 2 - imgY1, imgSize1, imgSize1);
}
}
}
La función Connection fue descartada al no ser necesaria para el funcionamiento del sistema.
Para hacer mas comoda la experiencia se optó por utilizar el código de Dropeo de imagenes, las cuales se actualizan automaticamente al momento de Arrastrar y dejar una nueva imagen.
let canvasText = 'ARRASTRA TU IMAGEN\n(PNG)\nO PEGA UNA URL\n(JPG)';
// Permitir el "drop" de imagen en el canvas
let dropArea = select('canvas');
dropArea.drop(gotFile);
function gotFile(file) {
if (file.type === 'image') {
img1 = createImg(file.data, '').hide();
canvasText = '';
} else {
canvasText = 'ERROR:\nARCHIVO NO VÁLIDO\nVUELVA A INTENTAR';
}
}
Además del Drop de imágenes de decidió añadir una barra en el que se puedan escribir enlaces URL de iamgenes en caso de querer utilizar el código en un dispositivo movil, ofreciendo más flexibilidad. El usuario puede pegar la dirección de una imagen y cargarla directamente en el lienzo. Además, se implementó un botón de carga y una barra de progreso que se resetea automáticamente tras su carga.
// Crear cuadro de texto para URL
let urlInput1 = createInput();
urlInput1.position(10, height + 10);
urlInput1.size(150);
urlInput1.attribute('placeholder', 'Ingresar URL');
//Crea botón de carga
let button1 = createButton('CARGAR');
button1.position(urlInput1.x + urlInput1.width + 10, urlInput1.y);
button1.mousePressed(() => {
let url1 = urlInput1.value();
loadImage(url1, (loadedImage) => {
img1 = loadedImage;
//Limpia el texto del canvas
canvasText = '';
//Vacía la barra de URL
urlInput1.value('');
}, (error) => {
urlInput1.value('');
});
});
Al ver el resultado se optó por añadir Sliders para personalizar las medidas de tamaño y posicionamiento de la imagen en el rostro, con sus respectivas etiquetas de identificación:
// Crear sliders para drop 1
sizeSlider1 = createSlider(50, 350, imgSize1);
sizeSlider1.position(25, height + 50);
xSlider1 = createSlider(-100, 100, imgX1);
xSlider1.position(25, height + 90);
ySlider1 = createSlider(-100, 100, imgY1);
ySlider1.position(25, height + 140);
// Crear etiquetas para los sliders
createP('Tamaño Imagen').position(sizeSlider1.x + 15, sizeSlider1.y - 30);
createP('Mover Eje X').position(xSlider1.x + 25, xSlider1.y - 30);
createP('Mover Eje Y').position(ySlider1.x + 25, ySlider1.y - 30);
createP('+').position(sizeSlider1.x + sizeSlider1.width + 10, sizeSlider1.y - 15);
createP('+').position(xSlider1.x + xSlider1.width + 10, xSlider1.y - 15);
createP('+').position(ySlider1.x + ySlider1.width + 10, ySlider1.y - 15);
createP('-').position(sizeSlider1.x - 10, sizeSlider1.y - 15);
createP('-').position(xSlider1.x - 10, xSlider1.y - 15);
createP('-').position(ySlider1.x - 10, ySlider1.y - 15);
}
El código reconoce el rostro de más de una persona capaz de asisgnarle la misma imagen.
Se intentó crear un código que permitiera la superposición de dos imagenes, asignando una “imagen A” en el lado izquierdo de la pantalla y otra “imagen B” en el lado derecho, finalmente esto no fue posible
https://editor.p5js.org/Dielox-X9/sketches/7ePb1opfI
Se añadió un conjunto de imágenes PNG que se pueden usar para probar el funcionamiento del código
https://github.com/Dielox-X9/Pruebas
Links imagen con URL: https://eltallerdehector.com/wp-content/uploads/2022/10/spiderman-png-free.png
## Fallos
mi equipo de trabajo es <https://github.com/cottito> y <<https://github.com/Dielox-X9>>, entregamos en el repositorio en este enlace <https://github.com/disenoUChile/audiv027-2024-1/estudiantes/NOMBRE/clase-06>.