En esta aplicación se construyo un prototipo de un sistema de realidad aumentada para mostrar información sobre componentes químicos en un ambiente de cabina de extracción implementada en un dispositivo Android.
Crear un prototipo de aplicación de realidad aumentada mediante un dispositivo móvil Android, para mostrar información sobre componentes químicos en un ambiente de cabina de extracción.
Un buen tutorial para comenzar a usar D’Fusion Studio es: https://community.t-immersion.com/getting-started. Algunos ejemplos de utilidad para entender el funcionamiento de D’Fusion Studio se pueden conseguir en: https://community.t-immersion.com/samples
El primer programa a instalar es D’Fusion 3.20. Para descargar este software es necesario registrarse de manera gratuita en la página de total immersion (http://www.t-immersion.com/). Después de descargado se debe seguir las instrucciones de instalación proporcionadas por el instalador.
El segundo componente a instalar es el SDK de Android http://developer.android.com/sdk/index.html, que permite desarrollar aplicaciones para Android. Para Windows existe un instalador con extensión .exe que es muy sencillo de instalar. Para que la instalación sea exitosa es necesario tener el kit de desarrollo de java 1.6 (JDK 1.6).
Después de instalar el SDK, es necesario descargar en el SDK Manager el SDK Platform para la versión 2.1. Para esto, se debe abrir el SDK Manager desde el menú inicio, luego se debe seleccionar los siguienes componentes dentro de la carpeta Android 2.1 (API 7):
Después de esto se debe instalar la versión 3.6.2 de Eclipse.
Una vez instalado Eclipse, se debe instalar el ADT Plugin para Eclipse. Las instrucciones detalladas de la instalación se pueden encontrar en: http://developer.android.com/sdk/eclipse-adt.html
Después de crear un nuevo proyecto se debe configurar el proyecto de manera adecuada para que luego pueda correrse en un dispositivo móvil.
Al configurar la cámara de la escena en D’fusion, se debe dar click en “Alternative Platform” y específicar configuraciones adecuadas para las plataformas no PC en las que se desea correr la aplicación. Una manera sencilla de hacer esto es utilizando las plantillas que vienen con D’Fusion y se pueden encontrar en la carpeta de instalación. El resultado se puede observar en la siguiente imagen:
El código del archivo “cameraCalib_android_640x480.xml” se modificó para que sea adecuado para el celular Samsung Galaxy Note, dado que este tiene una cámara HD y la configuración estándar no considera este hecho.
<?xml version="1.0" standalone="yes" ?> <CAMERABASE> <CAMERABASE> <COMMENTS>android_640x480</COMMENTS> <MODELE>OPENCV_0</MODELE> <MAXX>800</MAXX> <MAXY>480</MAXY> <PX>400</PX> <PY>240</PY> <FX>660</FX> <FY>660</FY> <A1>0</A1> <A2>0</A2> <P1>0</P1> <P2>0</P2> <DEDISTORTION>true</DEDISTORTION> </CAMERABASE> </CAMERABASE>
En el componente tipo videocapture también se debe hacer algo similar a lo que se hizo en el paso anterior.
Aquí también se modifico el archivo “videoConfig_android_backcam_640x480_15fps.xml”, el resultado del archivo se muestra a continuación:
<?xml version="1.0" standalone="yes" ?> <TI> <VIDEOCAP> <COMMENTS>android_backcam_640x480_15fps</COMMENTS> <VIDEO_CAPTURE_FILE></VIDEO_CAPTURE_FILE> <VIDEO_CAPTURE_NUM_DRIVER>0</VIDEO_CAPTURE_NUM_DRIVER> <VIDEO_CAPTURE_PIXEL_FORMAT>NV12</VIDEO_CAPTURE_PIXEL_FORMAT> <VIDEO_CAPTURE_WIDTH>800</VIDEO_CAPTURE_WIDTH> <VIDEO_CAPTURE_HEIGHT>480</VIDEO_CAPTURE_HEIGHT> <VIDEO_CAPTURE_DELAY>1</VIDEO_CAPTURE_DELAY> <VIDEO_CAPTURE_RATE>15.000000</VIDEO_CAPTURE_RATE> <VIDEO_CAPTURE_NB_FRAME_BUFFERS>4</VIDEO_CAPTURE_NB_FRAME_BUFFERS> <VIDEO_CAPTURE_INTERLACED>0</VIDEO_CAPTURE_INTERLACED> <VIDEO_CAPTURE_INVERTED>1</VIDEO_CAPTURE_INVERTED> <VIDEO_CAPTURE_PRIORITY>3</VIDEO_CAPTURE_PRIORITY> <VIDEO_CAPTURE_TYPE>0</VIDEO_CAPTURE_TYPE> <VIDEO_CAPTURE_NAME></VIDEO_CAPTURE_NAME> <VIDEO_CAPTURE_CPU>0</VIDEO_CAPTURE_CPU> <VIDEO_CAPTURE_PAUSE>1</VIDEO_CAPTURE_PAUSE> <VIDEO_CAPTURE_DEINTERLACE>1</VIDEO_CAPTURE_DEINTERLACE> <VIDEO_CAPTURE_SHADER>0</VIDEO_CAPTURE_SHADER> <VIDEO_CAPTURE_FRIENDLY_NAME>back</VIDEO_CAPTURE_FRIENDLY_NAME> <VIDEO_CAPTURE_DEBUG>0</VIDEO_CAPTURE_DEBUG> <VIDEO_CAPTURE_RATE_IS_DYNAMIC>true</VIDEO_CAPTURE_RATE_IS_DYNAMIC> <VIDEO_CAPTURE_RESYNCHRO_PERIOD>0</VIDEO_CAPTURE_RESYNCHRO_PERIOD> <VIDEO_CAPTURE_STEADY_STATE_MARGIN>0.330000</VIDEO_CAPTURE_STEADY_STATE_MARGIN> <VIDEO_CAPTURE_TIMECODE_REINIT_PERIOD>0.500000</VIDEO_CAPTURE_TIMECODE_REINIT_PERIOD> </VIDEOCAP> </TI>
Para hacer el seguimiento de los marcadores en la ejecución del programa, se requiere hacer un pre-procesamiento inicial. Para esto se debe abrir la herramienta de Computer Vision de D’Fusion, esto se hace dando clic en Tools y luego en Computer Vision.
Para que el algoritmo de seguimiento funcione en dispositivos móviles, se debe cambiar el modo a Lite, esto se hace dando clic en Mode y luego en Lite.
Para mejores resultados en el reconocimiento, es buena idea utilizar la misma cámara dispositivo para tomar las imágenes que luego se tratarán de identificar, por ello se tomó fotos a los marcadores con la cámara del celular en resolución de 800×480, y se recortó los pedazos que no correspondían a las imágenes. Las imágenes recortadas se pueden descargar del siguiente archivo:
En la pestaña “Scenario Manager”, en la sección “Target” se debe crear los 3 objetos que se pretenden seguir presionando el botón “Create”.
En la otra sección también llamada “Target”, se debe seleccionar el tipo (Type) “Plane” y dar un tamaño que represente la medida del objeto en el mundo real. En este caso el objeto impreso es de tamaño 5 cm x 3cm.
En la sección “Keyframes” se debe importar las imágenes. Esto se hace presionando el botón “Image(s)” y cargando las correspondientes al marcador que se desea seguir. Finalmente se debe presionar el botón “Generate Classifiers”. El proceso se debe repetir por cada marcador que contenga el sistema.
El código para realizar el seguimiento de los objetos y mostrar la información de los componentes se muestra a continuación, este código se debe agregar como script a la escena:
-- GLOBAL VARIABLES -- tracking statuts: if 1 : tracking, if 0 : no tracking gtrackingStatus = 0 -- get the keyframe index from a tracked object with auto-initialization gtrackingKeyFrameIndex = -1 -- scene local scene = getCurrentScene() -- get the virtual camera, will be used to send to the MLT local camera = Camera(scene:getCurrentCamera()) -- get the videocapture, will be used to send to the MLT local videoCapture_live = VideoCapture(scene:getObjectByName("vidCap")) -- Tracking local MLTPlugin = getMLTPluginManager() -- Error status local errorStatus = eOk -- tracking index : the index of the tracker.xml (because we can open more than 1 tracking.xml file). local trackingIndex = -1 -- the fps of the Tracking engine local trackingRate = 0 -- vector to put the tracking position local trackingPosition = Vector3() local trackingPosition1 = Vector3() local trackingPosition2 = Vector3() -- quaternion to put the tracking orientation local trackingOrientation = Quaternion() local trackingOrientation1 = Quaternion() local trackingOrientation2 = Quaternion() -- 3D object receiving tracking pose local trackingObject = Object3D(scene:getObjectByName("ref")) local trackingObject1 = Object3D(scene:getObjectByName("ref1")) local trackingObject2 = Object3D(scene:getObjectByName("ref2")) -- object index from the tracking scenario (0 : first object, 1 : second object...) (this is the index in the "Objects" panel of the CV GUI) local trackingObjectIndex = 0 local trackingObjectIndex1 = 1 local trackingObjectIndex2 = 2 -- textos... local title_hf = Text2D(scene:getObjectByName("title_hf")) local text = Text2D(scene:getObjectByName("text")) local text_visible = false title_hf:setVisible(false) text:setVisible(false) -- this is how to start a tracking. the function needs the path to the tracker.xml file, the videocapture id and the camera object. errorStatus, trackingIndex = MLTPlugin:startTracking("tracker/tracker.xml", videoCapture_live:getVidCapID(), camera) -- if the tracking has correctly started, we can proceed to an infinite loop if errorStatus == eOk then repeat errorStatus, gtrackingStatus = MLTPlugin:getTargetStatus(trackingIndex, trackingObjectIndex) errorStatus, gtrackingKeyFrameIndex = MLTPlugin:getRecognizedKeyFrameIndex(trackingIndex, trackingObjectIndex) -- if our object is detected... if (gtrackingStatus == 1) then --...we can get the position and set it to the "father" object MLTPlugin:getTargetPos(trackingIndex, trackingObjectIndex, trackingPosition, trackingOrientation) --trackingObject:setPosition(trackingPosition, camera) trackingObject:setPosition(trackingPosition:getX(), trackingPosition:getY(), 0) -- we change the visibility of the main "father" object if not trackingObject:getVisible() then trackingObject:setVisible(true) title_hf:setText("ÁCIDO CLORHÍDRICO") text:setText("RIESGOS:\n-Causa quemaduras\n" .. "-Irritante para el sistema respiratorio\n" .. "-Inhalación: irritación, edema y corrosión\n" .. "del sistema respiratorio\n" .. "QUE HACER EN CASO DE EXPOSICIÓN:\n" .. "-En caso de contacto con los ojos, enjuagar\n" .. "inmediatamente y buscar ayuda médica\n".. "-En caso de accidente buscar ayuda\n" .. "médica inmediatamente\n" .. "DESCRIPCIÓN:\n" .. "-Es muy corrosivo y ácido. Se emplea comúnmente\n" .. "como reactivo químico y se trata de un ácido \n" .. "fuerte que se disocia completamente en \n" .. "disolución acuosa.") text_visible = true end -- if the tracking is lost else -- we change the visibility of the main "father" object if trackingObject:getVisible() then trackingObject:setVisible(false) text_visible = false end end errorStatus, gtrackingStatus1 = MLTPlugin:getTargetStatus(trackingIndex, trackingObjectIndex1) errorStatus, gtrackingKeyFrameIndex1 = MLTPlugin:getRecognizedKeyFrameIndex(trackingIndex, trackingObjectIndex1) -- if our object is detected... if (gtrackingStatus1 == 1) then --...we can get the position and set it to the "father" object MLTPlugin:getTargetPos(trackingIndex, trackingObjectIndex1, trackingPosition1, trackingOrientation1) --trackingObject1:setPosition(trackingPosition1, camera) trackingObject1:setPosition(trackingPosition1:getX(), trackingPosition1:getY(), 0) -- we change the visibility of the main "father" object if not trackingObject1:getVisible() then trackingObject1:setVisible(true) title_hf:setText("ÁCIDO FLUORHÍDRICO") text:setText("RIESGOS:\n" .. "-Muy toxico por inhalación, en contacto por la \n" .. "piel o si es ingerido.\n" .. "-Causa quemaduras severas\n" .. "QUE HACER EN CASO DE EXPOSICIÓN:\n" .. "-En caso de contacto con los ojos, enjuagar \n" .. "inmediatamente y buscar ayuda médica\n" .. "-En caso de accidente buscar ayuda médica \n" .. "inmediatamente\n" .. "ELEMENTOS NECESARIOS DE TRABAJO:\n" .. "-Usar traje y guantes de protección\n" .. "DESCRIPCIÓN:\n" .. "Compuesto químico formado por hidrógeno y flúor\n" .. "(HF). No debe ponerse en contacto con elementos\n" .. "de vidrio ya que puede corroerlo, por esto se \n" .. "manipula utilizando material de plástico.") text_visible = true end -- if the tracking is lost else -- we change the visibility of the main "father" object if trackingObject1:getVisible() then trackingObject1:setVisible(false) text_visible = false end end errorStatus, gtrackingStatus2 = MLTPlugin:getTargetStatus(trackingIndex, trackingObjectIndex2) errorStatus, gtrackingKeyFrameIndex2 = MLTPlugin:getRecognizedKeyFrameIndex(trackingIndex, trackingObjectIndex2) -- if our object is detected... if (gtrackingStatus2 == 1) then --...we can get the position and set it to the "father" object MLTPlugin:getTargetPos(trackingIndex, trackingObjectIndex2, trackingPosition2, trackingOrientation2) --trackingObject2:setPosition(trackingPosition2, camera) trackingObject2:setPosition(trackingPosition2:getX(), trackingPosition2:getY(), 0) -- we change the visibility of the main "father" object if not trackingObject2:getVisible() then trackingObject2:setVisible(true) title_hf:setText("AMONÍACO") text:setText("RIESGOS:\n" .. "-Ingestión: Síntomas incluyen náusea y \n" .. "vómitos; daño a los labios, boca y esófago.\n" .. "-Inhalación: Los vapores son extremadamente \n" .. "irritantes y corrosivos.\n" .. "-Piel: Soluciones concentradas pueden producir\n" .. "quemaduras severas y necrosis.\n" .. "-Ojos: Puede causar daños permanentes, incluso \n" .. "en cantidades pequeñas") text_visible = true end -- if the tracking is lost else -- we change the visibility of the main "father" object if trackingObject2:getVisible() then trackingObject2:setVisible(false) text_visible = false end end if text_visible then if not title_hf:getVisible() then title_hf:setVisible(true) text:setVisible(true) end else if title_hf:getVisible() then title_hf:setVisible(false) text:setVisible(false) end end until coroutine.yield() end
A continuación se muestra el video de demonstración del sistema desarrollado:
http://www.youtube.com/watch?v=V_S3ulLdxEo&feature=youtu.be
El proyecto completo se puede descargar de:
Código de la aplicación en D'Fusion: immersion.rar
Código de la aplicación en Eclipse: race.rar