Ofrezco mis servicios / busco trabajo – Offering my services / job wanted

English version below.

Hace tiempo que compagino mi trabajo remunerado de las mañanas (desarrollador de herramientas de sistemas de información empresarial ERP, ECM, BI) con la investigación y desarrollo de aplicaciónes móviles con tecnología de Realidad Aumentada basada en visión lo cual es lo que me gusta y apasiona y por ello le dedico el resto de mi tiempo.

Y puesto que es lo que me gusta estaría muy bien poderme dedicar a ello a jornada completa así que:

Ofrezco mis servicios y/o busco trabajo como experto en Realidad Aumentada para aplicaciónes móviles.

Aporto experiencia, ganas de trabajar, pasión por esta tecnología y muchas posibles ideas de negocio para aplicarla, tanto es así que también estoy planteandome seriamente el convertirme en emprendedor.

Mis proyectos más importantes (no los únicos) :

  •     «Ganavi», app con demostraciones de Realidad Aumentada (con Unity 3D y Vuforia):

https://play.google.com/store/apps/details?id=com.AlbertoGandulloAvila.Ganavi

  •    Tutorial Interactivo con Realidad Aumentada (Serious Game) consistente en el procedimiento de reparación de un supuesto escape de gas, desarrollado con Unity 3D y Vuforia (Qualcomm). Video de demostración:

  •    ARcode Scanner , la primera app android que combina Realidad Aumentada y códigos QR. ARcode Scanner está desarrollado con SDK + NDK de Android junto con Vuforia.

https://play.google.com/store/apps/details?id=com.albertogandullo.RealidadAumentada.QrAr

Un resumen de mi experiencia como desarrollador de aplicaciones móviles:

Apps Android con SDK y NDK

Apps Realidad aumenada con Vuforia

Apps Móviles con Unity 3D (Game Engine)

Apps Multiplataforma con HTML5 (jQueryMobile, Phonegap) y Parse (Mobile Cloud Backend as a Service)

Apps iOS , xCode, Objetive-C  (nivel medio)

Idioma:

Inglés medio/alto, puedo entender pero tal vez al principio me cueste expresarme con soltura.

Contacto: albertogandullo@gmail.com

English Version

I offer my services and/or seeking employment as an expert in mobile augmented reality applications.

I bring experience and passion for this technology, also I have many business ideas to implement it, in fact I am seriously considering starting a business own.

Main Projects:

  •    «Ganavi», Demonstrations App with Augmented Reality technology ( Unity 3D and Vuforia):

              https://play.google.com/store/apps/details?id=com.AlbertoGandulloAvila.Ganavi

  •     Interactive tutorial (Serious Game) to show the procedure to follow in case of a suspected gas leak in the ducts that run through the basement of a city.( Unity 3D and Vuforia):                     

http://youtu.be/eZzR_yw41BY

  •     ARcode Scanner  is the first Android app that combines Augmented Reality with QR codes (Android SDK + NDK and Vuforia):

             https://play.google.com/store/apps/details?id=com.albertogandullo.RealidadAumentada.QrAr

Experience with Mobile apps Developer : 

Android Apps with SDK and NDK 

Augmented Reality Apps with Vuforia 

Unity 3D for Android and iOS 

Multiplataform Apps with HTML5 (jQueryMobile, Phonegap) and Parse (Mobile Cloud Backend as a Service) 

iOS Apps, xCode, Objetive-C  (medium level for now)

Languages:
I’m Spanish.
My level of English is medium/high. I can understand but perhaps initially cost me express myself fluently.

Contact: albertogandullo@gmail.com

Ganavi mi nueva app con Realidad Aumentada ahora en 3D / Ganavi my new app with Augmented Reality now in 3D

https://play.google.com/store/apps/details?id=com.AlbertoGandulloAvila.Ganavi

Ganavi es una app sencilla con la que pretendo exponer pequeñas muestras de las posibilidades que ofrece la Realidad Aumentada así como practicar con los conocimientos que voy adquiriendo para desarrollar ideas nuevas en este campo a modo de demostración de cara a posibles clientes y/o empleadores.

Con éste fin iré actualizando la aplicación para añadirle nueva funcionalidad conforme vaya teniendo tiempo de desarrollar.

Para usar esta app es necesario dirigir la cámara a una imagen rastreable. Como imagen se puede usar:

– un billete de 1 Dólar (cara de la pirámide),
– un billete de 5 o 10 Euros (cara del arco)
– o bien puede imprimir un marcador especifico que además ofrece mejor rendimiento. La imagen a imprimir es la siguiente:

Leonardo

Si hay poca luz de ambiente conviene usar el flash (luz trasera, si el dispositivo lo tiene) para que la detección de la imagen sea posible, también se puede activar o desactivar el modo de autoenfoque, todo ello desde el botón de menú.

Las funcionalidades actuales son:

– Starship :  Esta opción por ahora renderiza una nave espacial sobre la imagen rastreable a la que dirijamos la cámara. Espero hacerla volar en una actualización próxima.

– Demolition: Esta opción crea un muro de ladrillos (cubos) que se ve afectado por la gravedad y por explosiones que se producen al tocar la pantalla. Es una pequeña demostración de lo que se puede hacer aplicando física a los elementos 3D.

https://play.google.com/store/apps/details?id=com.AlbertoGandulloAvila.Ganavi

 

In English

Ganavi is a simple app developed to expose samples of the potential of Augmented Reality. In this way I can practice and show my knowledge in this field to potential clients and / or employers.

To this end I will update the application to add new functionality.

To use this app you need to direct the camera to an image trackable. As a trackable image you can use:
 

– A $ 1 bill (side of the pyramid),
– 5 and 10 Euros bill (side of the arch)
– Or you can print a specific marker that also offers better performance. The image to be printed is:
 
Leonardo

To improve the detection of the image you can turn on the flashlight of your device or activate the autofocus mode, you just have to press the menu button.
 

The current features are:

– Starship: This option now renders a spacecraft on the image that we direct traceable to the camera. I hope to fly it in a upcoming update.
 

– Demolition: This option creates a wall of bricks (cubes) that is affected by gravity and explosions that occur when you touch the screen. It is a small demonstration of what can be done using physical 3D elements.

https://play.google.com/store/apps/details?id=com.AlbertoGandulloAvila.Ganavi

Algunas capturas / Some screenshots:

captura2

captura3

captura4

captura5

ARcode Scanner mi primera app Android

ARcode Scanner primera aplicación android que combina Realidad Aumentada y códigos QR.

https://play.google.com/store/apps/details?id=com.albertogandullo.RealidadAumentada.QrAr

¿Conoces la Realidad Aumentada?

La Realidad Aumentada simplemente consiste en añadir a la realidad información virtual que solo puede ser visualizada mediante la pantalla de un dispositivo con cámara. El ejemplo más reconocible para la gente de mi generación es le dispositivo de rastreo que llevaban los guerreros del espacio en la serie Bola de Dragón. Otro ejemplo más actual y menos friki es la aplicación de movil layar, la cual nos proporciona información según nuestra situación y orientación geográfica.

¿Conoces los códigos QR?

Son códigos bidimensionales que almacenan información que se puede decodificar mendiante por ejemplo un smartphone con cámara.

De la combinación de la realidad aumentada y los códigos QR nace  la aplicación ARcode Scanner, aquí dejo un vídeo con una pequeña prueba de sus posibilidades:

Un “ARcode” (Augmented Reality Code) es un código imprimible parecido a un QR o código de barras pero éste almacena información la cual puede ser visualizada mediante realidad aumentada.

La aplicación  “ARcode Scanner” nos permite visualizar, crear y compartir ARcodes con los datos que le indiquemos.

Através de la cámara se verá la información superpuesta al ARcode con iconos virtuales táctiles para cada función disponible.

Las funciones disponibles en esta versión son:
– Imagen (Permite ver una imagen almacenada en el ARcode mediante la dirección url de ésta)
– Web (Abre el navegador web hacia la dirección almacenada en el ARcode)
– Email (Permite redactar un email hacia la dirección almacenada en el ARcode)
– Telefono (Realiza una llamada al número almacenado en el ARcode)
– Información (Muestra un mensaje de información con el contenido de la descripción almacenada en el ARcode)
– Facebook (Abre el navegador web hacia la dirección almacenada en el ARcode)

CONCLUSION:

La gran ventaja de esta app de realidad aumentada con respecto a otras es que tu eliges que información que se va a mostrar en realidad aumentada pudiendo generar códigos de realidad aumentada personalizados (ARcodes) que puedes compartir con todo el mundo.

Enlace de descarga:

https://play.google.com/store/apps/details?id=com.albertogandullo.RealidadAumentada.QrAr

A continuación unos ejemplos de ARcodes para probar la aplicación desde vuestros moviles apuntando a la pantalla (usar la opción de menú “Reescanear” para cambiar de un código a otro) .  Y animate a crear el tuyo propio desde la misma aplicación.

PFC SIMOCAP 10 Matrícula de Honor

Pues ya presente el proyecto Simocap como Proyecto fin de carrera de Ingeniería Superior en Informática en mi Escuela, (Escuela Técnica Superior de Ingeniería Informática de la Universidad de Sevilla), y he recibido la clasificación de Sobresaliente 10 Matrícula de Honor,  además esto se une a la Mención Especial recibida en el Premio local de Sevilla del Concurso Universitario de Software Libre.

Dejo aquí los vídeos de una demostración de uso del software.

Y con esto voy a denener el desarrollo del proyecto hasta nuevo aviso.

La dirección de descarga del software y de la documentación es la siguiente:

https://forja.rediris.es/projects/cusl4-simocap/

Tutorial Instalación de Artoolkit (y ejecución) en Ubuntu

Si te interesa la realidad aumentada echale un vistazo a mi último post:
ARcode Scanner la primera app Android que combina Realidad Aumentada con códigos QR

y prueba mi app «ARcode Scanner» para Android con la cual podrás personalizar un código para que muestre la información que tu elijas con realidad aumentada.


Bueno hace unos días tuve que instalar de nuevo artoolkit en otra máquina con Ubuntu 10.04 y hacía tanto tiempo de la primera vez, que no me acordaba muy bien, así que para que no me pase más he escrito un pequeño manual de instrucciones / tutorial con los pasos para la instalación y la ejecución de las aplicaciones de ejemplo en Ubuntu (probado en Ubuntu 10.04).

Y como la mayoría de visitas que recibe este blog es por este motivo, pues aquí lo dejo:

Instalación Artoolkit

Paso1. Instalar los siguientes paquetes o la versión actual más parecida desde Synaptic aceptando también la instalación de todas las dependencias que nos proponga

freeglut3-dev
libgstreamer0.10-dev
libgstreamer-plugins-base0.10-dev  (quizas este no es necesario)
libxi-dev
libxmu-headers
libxmu-dev
libjpeg62-dev
libglib2.0-dev
libgtk2.0-dev

Paso2.  Descargar artoolkit, la última versión libre es esta:

ARToolKit-2.72.1.tgz

para otros sistemas operativos:

http://sourceforge.net/projects/artoolkit/files/

Paso3. Descomprimir desde un terminal en la ubicación deseada con el comando:

tar zxvf ARToolKit-2.72.1.tgz

Paso4. Ir dentro de la carpeta de artoolkit y configurar desde un terminal con el comando

./Configure

elegir opción 5 Gstreamer Media Framework y responder sí (yes) a las siguientes preguntas (no he probado con las otras opciones la verdad).

Paso5. En la misma carpeta ejecutar desde el terminal el comando make

make

(cruzar los dedos para que todo se compile bien, y ya está terminada la instalación, los ejecutables de los ejemplos estarán en la carpeta bin)

Ejecución Artoolkit

Para la opción de instalación seleccionada supongo que lo que la mayoría desconoce es que hay que darle valor a la variable de entorno ARTOOLKIT_CONFIG

Ejecución de Ejemplos con webcam

Para ejecutar ejemplos de artoolkit con video proveniente de una webcam ejecutar antes el siguiente comando en un terminal para darle valor a la variable de entorno:

export ARTOOLKIT_CONFIG="v4l2src device=/dev/video0 use-fixed-fps=false ! ffmpegcolorspace ! capsfilter caps=video/x-raw-rgb,bpp=24 ! identity name=artoolkit ! fakesink"

donde pone video0 puede que sea otro número video1 por ejemplo, depende de donde instale la webcam o cuando.

Ejecución de ejemplos con archivos de vídeo en disco

Hay que ejecutar primero el siguiente comando desde un terminal cambiando la ruta de location=/…   por nuestra.

export ARTOOLKIT_CONFIG="filesrc location=/home/alberto/Escritorio/proyectos/prueba.MOV ! decodebin ! ffmpegcolorspace ! capsfilter caps=video/x-raw-rgb,bpp=24 ! identity name=artoolkit ! fakesink"

Nos fijamos que el valor que se le da a location es la ruta del vídeo que queremos usar como entrada de vídeo, el resto mejor dejarlo como está.

Ejemplo con otra ruta:

export ARTOOLKIT_CONFIG="filesrc location=/home/nosotros/Escritorio/prueba.MOV ! decodebin ! ffmpegcolorspace ! capsfilter caps=video/x-raw-rgb,bpp=24 ! identity name=artoolkit ! fakesink"

Y listo, ahora podemos ir a la carpeta bin, o donde tengamos el ejecutable y llamarlo desde línea de comandos, siempre con ./ delante.

ejemplo:

./simpleTest

Si te interesa la realidad aumentada echale un vistazo a mi último post:
ARcode Scanner la primera app Android que combina Realidad Aumentada con códigos QR

y prueba mi app «ARcode Scanner» para Android con la cual podrás personalizar un código para que muestre la información que tu elijas con realidad aumentada.

Descripción del Formato BVH

En un post anterior ya hablé de los formatos de archivo para la captura de movimientos corporales.

En este quiero explicar uno en contrato el formato BVH (Biovision Hierarchical Data) de paso añado algo de documentación sobre él en la red ya que se usa mucho pero parece que a nadie le ha dado por documentarlo, supongo también que esto se debe a que existen multitud de editores de movimientos que dan como resultados este tipo de archivos pero realmente no tienes porqué enfrentare al código.

Bueno lo dicho, aquí expongo a modo de documentación lo que he descubierto sobre el formato BVH.



Esto es lo que contiene un archivo BVH:

HIERARCHY
ROOT hip
{
	OFFSET	0.00  0.00  0.00
	CHANNELS 6 Xposition Yposition Zposition Xrotation Zrotation Yrotation
	JOINT abdomen
	{
		OFFSET	0.000000 3.422050 0.000000
		CHANNELS 3 Xrotation Zrotation Yrotation
		JOINT chest
		{
			OFFSET	0.000000 8.486693 -0.684411
			CHANNELS 3 Xrotation Zrotation Yrotation
			JOINT neck
			{
				OFFSET	0.000000 10.266162 -0.273764
				CHANNELS 3 Xrotation Zrotation Yrotation
				JOINT head
				{
					OFFSET	0.000000 3.148285 0.000000
					CHANNELS 3 Xrotation Zrotation Yrotation
					End Site
					{
						OFFSET 0.000000 3.148289 0.000000
					}
				}
			}
			JOINT lCollar
			{
				OFFSET	3.422053 6.707223 -0.821293
				CHANNELS 3 Yrotation Zrotation Xrotation
				JOINT lShldr
				{
					OFFSET	3.285171 0.000000 0.000000
					CHANNELS 3 Zrotation Yrotation Xrotation
					JOINT lForeArm
					{
						OFFSET	10.129278 0.000000 0.000000
						CHANNELS 3 Yrotation Zrotation Xrotation
						JOINT lHand
						{
							OFFSET	8.486692 0.000000 0.000000
							CHANNELS 3 Zrotation Yrotation Xrotation
							End Site
							{
								OFFSET 4.106464 0.000000 0.000000
							}
						}
					}
				}
			}
			JOINT rCollar
			{
				OFFSET	-3.558935 6.707223 -0.821293
				CHANNELS 3 Yrotation Zrotation Xrotation
				JOINT rShldr
				{
					OFFSET	-3.148289 0.000000 0.000000
					CHANNELS 3 Zrotation Yrotation Xrotation
					JOINT rForeArm
					{
						OFFSET	-10.266159 0.000000 0.000000
						CHANNELS 3 Yrotation Zrotation Xrotation
						JOINT rHand
						{
							OFFSET	-8.349810 0.000000 0.000000
							CHANNELS 3 Zrotation Yrotation Xrotation
							End Site
							{
								OFFSET -4.106464 0.000000 0.000000
							}
						}
					}
				}
			}
		}
	}
	JOINT lThigh
	{
		OFFSET	5.338403 -1.642589 1.368821
		CHANNELS 3 Xrotation Zrotation Yrotation
		JOINT lShin
		{
			OFFSET	-2.053232 -20.121670 0.000000
			CHANNELS 3 Xrotation Zrotation Yrotation
			JOINT lFoot
			{
				OFFSET	0.000000 -19.300380 -1.231939
				CHANNELS 3 Xrotation Yrotation Zrotation
				End Site
				{
					OFFSET 0.000000 -2.463878 4.653993
				}
			}
		}
	}
	JOINT rThigh
	{
		OFFSET	-5.338403 -1.642589 1.368821
		CHANNELS 3 Xrotation Zrotation Yrotation
		JOINT rShin
		{
			OFFSET	2.053232 -20.121670 0.000000
			CHANNELS 3 Xrotation Zrotation Yrotation
			JOINT rFoot
			{
				OFFSET	0.000000 -19.300380 -1.231939
				CHANNELS 3 Xrotation Yrotation Zrotation
				End Site
				{
					OFFSET 0.000000 -2.463878 4.653993
				}
			}
		}
	}
}
MOTION
Frames:     1
Frame Time: 0.033333
0.000000 43.528519 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

Se pueden distinguir dos partes bien diferenciadas, HIERARCHY y MOTION.

HIERARCHY
En la primera se representa la estructura del esqueleto describiendo los huesos o partes del cuerpo en una jerarquía, de este modo por ejemplo en este ejemplo podemos ver como la raíz de todas las partes del cuerpo es la cadera, y tiene como uniones hijas al abdomen y las 2 piernas, a su vez el abdomen tiene como hijo al pecho, y así sucesivamente hasta completar las partes del cuerpo que se quieran simular.

Cada una de estos huesos o partes del cuerpo vienen caracterizadas por unos parámetros.

OFFSET indica las coordenadas x, z e y donde comienza.

CHANNELS indica el numero y nombre de las variables que pueden cambiar la posición y rotación del hueso. (Solo se indica posición en la raíz, en los demás solo se indicará la rotación.

JOINT indica los huesos hijos a los que está unido y cuyo OFFSET será su límite Nota: el valor de las variables de rotación de un hueso siempre se consideran con respecto al hueso padre.


MOTION
En la segunda parte se representa el movimiento del esqueleto.
Para ello primero se indica el numero de frames (numero de movimientos a realizar), después indicamos la velocidad de esos movimientos(Frames Time).

A partir de ahí cada línea (hasta un retorno de carro explicito «/n» no de los que ocurren con el auto-formateado para que no se salga de la pantalla) representa uno de estos frames con una serie de cifras que se corresponden con las variables declaradas en la estructura del esqueleto en cada canal en el orden en el que se han declarado. De esta manera cuanto más complicado es nuestro esqueleto más cifras habrá en cada línea de movimiento.

Ejemplo de frame (aunque aparezca formateado en varias líneas en realidad solo tiene un retorno de carro al final):

0.000000 43.528519 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 60.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

Este frame solo tiene valores distintos de 0 para un par de variables,

El valor 43.528519 se corresponde con Yposicion del elemento hip, esto se traduce en que el esqueleto tiene una posición absoluta elevada, (suponiendo que en 0 está el suelo se habrá elevado la cadera que es el origen de todas las demás partes para que al estar de pie el modelo virtual los pies estén en posición 0).

Y el valor 60.00000 se corresponde con Xrotation del elemento neck (cuello), lo cual significa que el modelo está mirando hacia arriba.

Movimiento de la cabeza

SE MUEVE!!! SE MUEVE!!!

Casi grito eso cuando conseguí capturar el movimiento de la cabeza en un archivo bvh y después reproducirlo en un modelo virtual.

Después del  vídeo explico un poco el funcionamiento.

El de la izquierda soy yo, otra vez haciendo un poco el gamba con ese bonito sombrero que me he fabricado con una caja de zapatos al cual le he colocado diversas marcas de seguimiento.

Y el de la izquierda es un modelo virtual (tristemente para mí más guapo que yo)  que nos proporciona q-avimator para visualizar archivos con formato «bvh» en este caso el archivo «bvh» que reproduce es el que simocap ha generado automáticamente a partir del video de la izquierda.

Va un poco lento pero es porque al hacer la composición de los dos vídeos mi portátil no daba para más. Los vídeos por separado se ven de forma fluida, también están en youtube por si alguien tiene curiosidad.

Lo que hago es medir los ángulos que hay entre las marcas de mi sombrero y la marca del pecho que uso como marca base.

El motivo de que en la cabeza tenga varias marcas es que al girar es posible que se deje de ver la marca frontal, de este modo las marcas laterales son las que dan la posición.  Así cuando solo se visualiza una marca uso esta pero si se visualizan dos hago la media.

Una vez tengo el angulo de la cabeza lo voy trasladando al archivo de formato «bvh» para guardarlo.

Además una vez este proceso ha terminado para todo el vídeo de captura,  recorro el archivo modificando los ángulos para suavizar los movimientos ya que debido a pequeños fallos en la captura se producen movimientos bruscos no reales.

Este archivo con formato «bvh» se puede visualizar en múltiples programas como blender, pose , o incluso en Second Life, yo he usado QAVIMATOR por ser código de fuentes abiertas, y consumir pocos recursos .

Mi siguiente paso es capturar el movimiento de los brazos. Me las tengo que ingeniar para colocarme las marcas sin que se me caigan y tal 😛 .

Saludos.

Obteniendo los ángulos de Euler

Solución para salvar el problema del Gimbal Lock al obtener los ángulos de Euler a partir de la matriz que se usa en ArToolkit.

Para obtener los ángulos de euler a partir de la matriz de rotación he programado el siguiente código:


int getAngulos(double rot[3][3],  double *rfXAng, double *rfYAng, double *rfZAng) {

// Matriz de Rotacion

// +-           -+   +-                                      -+

// | r00 r01 r02 |   |  cy*cz  cz*sx*sy-cx*sz  cx*cz*sy+sx*sz |

// | r10 r11 r12 | = |  cy*sz  cx*cz+sx*sy*sz -cz*sx+cx*sy*sz |

// | r20 r21 r22 |   | -sy     cy*sx           cx*cy          |

// +-           -+   +-                                      -+

// gimbalLock == 0 , no se produce gimbal Lock

// gimbalLock == 1 , se ha producido gimbal Lock por y angulo = pi/2

// gimbalLock == 2 , se ha producido gimbal Lock por y angulo = -pi/2

int gimbalLock = 0;

if (rot[2][0] < 0.995) {

if (rot[2][0] > -0.995) {

// y_angulo = asin(-r20)

// z_angulo = atan2(r10,r00)

// x_angulo = atan2(r21,r22)

*rfYAng = asin(-rot[2][0]);

*rfZAng = atan2(rot[1][0],rot[0][0]);

*rfXAng = atan2(rot[2][1],rot[2][2]);

}

else {

// y_angulo = +pi/2

// x_angulo + z_angulo = atan2(r01,r02)

// NOTA.  La solución no es unica.  Tomamos x_angulo = 0.

*rfYAng = PI/2;

*rfZAng = -atan2(rot[0][1],rot[0][2]);

*rfXAng = 0.0;

gimbalLock = 1;

}

}

else {

// y_angulo = -pi/2

// x_angulo + z_angulo = atan2(-r01,-r02)

// NOTA.  La solución no es unica.  Tomamos x_angulo = 0.

*rfYAng = -PI/2;

*rfZAng = atan2(-rot[0][1],-rot[0][2]);

*rfXAng = 0.0;

gimbalLock = 2;

}

printf(«Radianes: %3.1f %3.1f %3.1f \n»,*rfXAng,*rfYAng,*rfZAng);

printf(«Centigrados: %3.1f %3.1f %3.1f \n»,(*rfXAng*180)/PI, (*rfYAng*180)/PI, (*rfZAng*180)/PI);

return gimbalLock;

}
Esta función nos indica los ángulos de Euler y además devuelve un valor 1 u 2 cuando se produce Gimbal Lock por angulo con respecto al eje «Y» de 90 o -90 grados respectivamente, de este modo estaré advertido de la situación en un futuro.

En breve voy a empezar a construir mis propios archivos bvh, espero no tener demasiados problemas.

Interpretación de la matriz de coordenadas

Ahora que tengo la matriz de coordenadas de una marca con respecto a otra lo que necesito es obtener los ángulos de rotación con respecto a cada uno de los ejes. El problema es que en la escasa documentación que ofrece Artoolkit lo que dice es que la última columna de la matriz indica la distancia en los distintos ejes X, Y, y Z. Y efectivamente al acercar o alejar las marcas se puede apreciar.

Pero no dice nada acerca de las otras 3 columnas que después descubrí que indican la rotación que yo buscaba.

Matriz de rotación

r [0,0] r [0,1] r [0,2]

r [1,0] r [1,1] r [1,2]

r [2,0] r [2,1] r [2,2]

Así que empiezo a hacer pruebas primero coloco una marca base y debajo de ella coloco otra. Como se puede apreciar están sobre la mesa sin giro alguno

Sin figura 3D

Con figura 3D

La tetera roja está colocada en la marca base.

Y la matriz resultado es la siguiente:

[R] =
0,9988 0,0060 -0,0496 0,1213
-0,0064 0,9999 -0,0091 -130,7769
0,0495 0,0094 0,9987 5,0256
Cuya matriz de rotación se parece bastante a:

1 0 0
0 1 0
0 0 1

Efectivamente la última columna cumple su cometido de indicar la distancia, ya que para dx y dz su valor es cercano a 0 pero para dy nos da un valor d -130 es decir la marca secundaria está por debajo de la marca base. Y el resto de la matriz (la matriz de rotación) se parece a la matriz identidad.

Después hice pruebas con distintas marcas colocándolas en ángulos reconocibles de 90 grados con respecto a una marca base (la de la tetera rosa) que será respecto a la que calcule las matrices de coordenadas.

Como se puede observar cada una de las marcas 1, 2, y 3 están giradas 90 grados en un sentido u otro con respecto a uno delos ejes de la marca 0.

La marca 1 está girada con respecto al eje X.

La marca 2 está girada con respecto al eje Y.

La marca 3 está girada con respecto al eje Z.

Y las matrices de coordenadas respecto a la marca base 0 (tetera rosa) obtenidas para cada un una de las otras marcas son las siguientes:

Matriz de la marca 1.

[R] =
0,9984 0,0036 0,0557 -3,8267
0,0556 -0,1288 -0,990 53,1803
0,0036 0,9917 -0,1288 67,2298
Cuya matriz de rotación se parece bastante a:

1 0 0
0 0 -1
0 1 0

Matriz de la marca 2.

[R] =
0,0168 -0,0193 -0,9997 74,2620
-0,0366 0,9991 -0,0199 10,7033
0,9992 0,0370 0,0161 57,7545
Cuya matriz de rotación se parece bastante a:

0 0 -1
0 1 0
1 0 0

Matriz de la marca 3.

[R] =
-0,0356 0,9993 -0,0067 8,1143
-0,9970 -0,0351 0,0685 -115,7258
0,0682 0,0092 0,9976 -11,3858
Cuya matriz de rotación se parece bastante a:

0 1 0
-1 0 0
0 0 1

Estos valores tan poco aleatorios tienen su explicación, la cual encontré en los apuntes de la asignatura SIO, Síntesis de imágenes por Ordenador (José Cortés Parejo).

Seno de 90º es 1 y de -90º es -1 y el coseno 90 es 0. Así que lo que nos dicen las matrices es que la marca 1 está girada 90 grados alrededor del eje X, y las marcas 2 y 3 -90 grados alrededor de los ejes Y y Z respectivamente.

Así que la conclusión es que la matriz de rotación general es la multiplicación de las matrices de rotación con respecto a cada eje. Si consideramos α, β y γ los ángulos de giro alrededor de los ejes X, Y y Z respectivamente tenemos que multiplicando las matrices de rotación obtenemos lo siguiente.

[R] = Rotación en eje Z

cos γ -sen β 0
sen γ cos γ 0
0 0 1
* ( Rotación en eje Y

cos β 0 senβ
0 1 0
-sen β 0 cos β
* Rotación en eje X

1 0 0
0 cos α -senα
0 senα cos α
)

[R] =

cos β * cos γ sen α * sen β * cos γ – cos α * sen γ cos α * sen β * cos γ + sen α * sen γ
cos β * sen γ sen α * sen β * sen γ + cos α * cos γ cos α * sen β * sen γ – sen α * cos γ
-sen β sen α * cos β cos α * cos β

Se corresponde con:

Matriz de rotación

r [0,0] r [0,1] r [0,2]

r [1,0] r [1,1] r [1,2]

r [2,0] r [2,1] r [2,2]

Esta sería entonces la matriz de rotación compacta para los 3 ejes que obtenemos como resultado de la función arGetTransMat junto con el último vector que ya comentamos que era el de la distancia.

Para obtener entonces los ángulos α , β y γ simplemente tenemos que despejar operando sobre los componentes apropiados de la matriz.

(sen α * cos β)/(cos α * cos β) = sen α /cos α = tan α → α = atan (tan α)

Sustituyendo por los componentes de la matriz : α = atan ( r [2,1] / r [2,2])

β = asen -(-sen β) = asen -(r [2,0])

(cos β * sen γ)/(cos β * cos γ) = sen γ /cos γ = tan γ → γ = atan (tan γ)

Sustituyendo por los componentes de la matriz : γ = atan ( r [1,0] / r [0,0])

Estos ángulos estarán expresados en radianes. Para expresarlos en grados sistema más reconocible por nosotros solo tenemos que multiplicar por 180 y dividir por Pi.

El código podría ser algo así:

#define PI 3.14159265358979323846

Ángulo de giro en X → α = (atan (matriz[2][1] / matriz[2][2]) * 180) / PI;

Ángulo de giro en Y → β = (asin (-matriz[2][0]) * 180) / PI;

Ángulo de giro en Z → γ = (atan (matriz[1][0] / matriz[0][0]) * 180) / PI;


Que bonito sería el mundo si esto fuera tan fácil, pero no lo es, y este código después de probarlo un poco se aprecia un error en la estimación de los ángulos con respecto a X y Z esto es debido a que produce Gimball lock. Este error se produce cuando hay giros de 90 con respecto al eje Y (cos 90º = 0) segundo en la multiplicación de matrices y es un error intrínseco a este tipo de uso de los ángulos de euler.

Estoy estudiando distintas opciones para solucionarlo, parece que una buena opción es el uso de quaternion para la representación de la rotación, aunque después para el paso a archivos de movimiento de esqueleto formato bvh necesitaría los ángulos de euler, lo que supondría una nueva conversión.

En principio voy a seguir estudiando opciones, y empezando a tocar archivos bvh, para su mejor compresión de este modo espero encontrar la solución más adecuada al desarrollo de la siguiente fase, la escritura automática de un archivo bvh a partir de las coordenadas obtenidas por las marcas.

Saludos.

0,9999

Coordenadas de una marca respecto a otra

En cualquier caso para mi proyecto principalmente necesito la posición de las marcas con respecto a otras marcas, las coordenadas con respecto a la cámara solo me servirá para la marca base, que determinará el movimiento de traslación del esqueleto 3d. El resto de marcas me servirán para medir sus ángulos relativos y determinar los movimientos de rotación de las articulaciones.

Para determinar las coordenadas de unas marcas con respecto a otras necesitamos la matriz de transformación entre ellas. Para ello una vez tenemos las matrices de coordenadas con respecto al sistema de referencia de la cámara de las dos marcas implicadas realizamos las siguientes operaciones:

Pasándole a la función arUtilMatInv la matriz de coordenadas de la marca base con respecto a la cámara obtendremos la matriz de coordenadas de la cámara con respecto a la marca.

Esta función lo que ha hecho es invertir los sistemas de referencias. (Todo esto tiene una base teórica que explicaré en la memoria del proyecto).

Una vez tengo esta matriz la multiplicamos por la matriz de la marca de la cual queríamos obtener sus coordenadas respecto a la primera. Podemos usar la función arUtilMatMul.

El código podría ser algo así:

arUtilMatInv(m1C, m1Inv);

arUtilMatMul(m1Inv, m2C, m2M);

m1C y m2C son las matrices de coordenadas de las marcas con respecto a la cámara

– m1Inv es la matriz de coordenadas de la cámara respecto a la marca 1

– m2M es la matriz de coordenadas de la marca 2 con respecto a la marca 1.

El siguiente post será un mega post donde explico como interpretar la matriz de coordenadas.