Iniciando con Wikitude para Android: instalación de la SDK y primeros pasos en el reconocimiento de imágenes
- Andrés Cruz
La realidad aumentada en Wikitude, los Puntos de Interés, el reconocimiento de imágenes y mucho más es un tema ya tratado en anteriores entradas que hablamos sobre Wikitude:
Y la entrada anterior a esta; en esta entrada veremos cómo crear una aplicación que emplee la SDK de Wikitude naiva para Android y emplee el reconocimiento de imágenes para (valga la redundancia) reconocer una imagen y mostrar un enmarque a su alrededor.
En aquellos tiempos cuando existía el famoso Eclipse con el plugin ADT desde ese tiempo han cambiado varias cosas, muchas versiones de Android han pasado, varias de la SDK de Wikitude hasta estar en la versión 8, que es la actual, e inclusive han extendido su SDK hasta crear una nativa para Android.
La SDK nativa y la SDK web/JavaScript
Recordemos que para versiones anteriores solo estaba disponible una especie de SDK híbrida en donde parte de la SDK era nativa (el uso de la cámara y configuración inicial), pero el fuerte del reconocimiento de imágenes, uso de los POIs o Puntos de Interés, interacción con el usuario, conectarse a otras API para traer o enviar datos y más, se realizaba mediante JavaScript; que a mi humilde opinión me parece excelente este esquema ya que facilita mucho el proceso de creación de las app, podemos trabajar en gran parte con librerías y demás funcionalidades webs que desde Android nos harían pasar más trabajo por cómo está elaborado este.
Sin embargo el talón de aquiles de esta funcionalidad es que las interfaces al ser webs no están a la altura de un desarrollo nativo en Android; para ello Wikitude creó una versión nativa para Android paralela a la de JavaScript que podemos usar según nuestras necesidades:
Las SDK de Wikitude para Android y otras más las puedes descargar desde el siguiente enlace oficial:
Claro, desarrollar con la SDK nativa puede ser más complejo que con la SDK para web pero todo depende de las ventajas que veamos entre una y otra al momento de realizar un proyecto.
En esta entrada, nos enfocaremos en dar los primeros pasos con el desarrollo de una sencilla aplicación para el reconocimiento de imágenes, para ello haremos uso de los ejemplos oficiales casi en su totalidad, pero la idea de esta entrada es narrar como yo desarrollé y probé estos ejemplos de una manera más limpia, explicar como funciona y cómo nos podemos ayudar con la documentación.
Instalación de la SDK nativa
Lo primeros que debemos hacer es descargar la SDK natíva de Wikitude para Android; para ello puedes descargarlo desde la sección de descargas:
Probablemente debas registrarte, aceptar unas condiciones etc; una vez descargada la misma, las descomprimes y tendremos en la ruta:
WikitudeSDK_NativeAPI_Android_8-X-X_XXXX-XX-XX_XX-XX-XX\Library
Esta librería o SDK de Wikitude que instalaremos en nuestro Android Studio; el proceso consiste en copiar la librería en la carpeta libs
de Android:
Y luego seguir los pasos que narran en la documentación oficial:
Básicamente consiste en agregar las dependencias en nuestro Android Gradle:
android {
...
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation (name: 'wikitude-native-sdk', ext:'aar')
implementation "com.google.ar:core:1.1.0"
...
}
repositories {
flatDir{
dirs 'libs'
}
}
Y en solicitar los permisos en nuestro Android Manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
Colocar estos atributos en la actividad que emplea la realidad aumentada, que en nuestro ejemplo es nuestro MainActivity:
android:configChanges="orientation|keyboardHidden|screenSize"
Quedando nuestra MainActivity la cual es la que va a mostrar la realidad aumentada mediante la cámara y la pantalla del dispositivo de la siguiente forma:
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize">
Una vez hecho esto, nuestro proyecto debería compilar sin problemas, aunque aún no hemos desarrollado nada.
Iniciando con el Reconocimiento de Imágenes de WIkitude
Como mencionamos antes, vamos a hacer uso de los ejemplos que ofrece Wikitude, y que vienen en el zip que descargamos y descomprimimos antes:
WikitudeSDK_NativeAPI_Android_8-X-X_XXXX-XX-XX_XX-XX-XX\Library\app\src\main\java\com\wikitude\samples\tracking\image\SimpleImageTrackingActivity.java
En la documentación oficial nos podemos guiar por este links que nos van explicando un paso a paso hasta cierto punto:
Nos narra unos pasos que consisten en implementar algunas clases propias de la SDK de Wikitude:
public class MainActivity extends Activity implements ImageTrackerListener, ExternalRendering {...}
Nos indica que en nuestro OnCreate
el cual es el método inicial en nuestro ciclo de vida de una aplicación Android realizamos las configuraciones de la aplicación, creando el objeto WikitudeSDK
con la cual realizamos las configuraciones para indicar la licencia que descargamos desde el punto anterior, la cámara que vamos ha usar, si la delantera o de la parte posterior, la resolución, y luego creamos una instancia de la SDK de Wikitude con los parámetros configurados:
wikitudeSDK.onCreate(getApplicationContext(), this, startupConfiguration);
Creando el TargetCollection
Paso siguiente es crear el TargetCollection que ya hemos hablado de esto antes en una anterior entrada:
Target Collection y el ARchitect World en la nube con Wikitude
Empleando la API nativa de Wikitude, para referenciar el TargetCollection hacemos:
targetCollectionResource = wikitudeSDK.getTrackerManager().createTargetCollectionResource("file:///android_asset/magazine.wtc");
La ruta android_asset
hace referencia al directorio assets
de nuestro proyecto que podemos crear desde Android Studio:
También agregamos o mejor dicho sobreescribimos algunos métodos del propios de la clase que estamos implementando:
@Override
public void onTargetsLoaded(ImageTracker tracker) {
Log.v(TAG, "Image tracker loaded");
}
@Override
public void onErrorLoadingTargets(ImageTracker tracker, WikitudeError error) {
Log.v(TAG, "Unable to load image tracker. Reason: " + error.getMessage());
}
@Override
public void onImageRecognized(ImageTracker tracker, final ImageTarget target) {
Log.v(TAG, "Recognized target " + target.getName());
StrokedRectangle strokedRectangle = new StrokedRectangle(StrokedRectangle.Type.STANDARD);
glRenderer.setRenderablesForKey(target.getName() + target.getUniqueId(), strokedRectangle, NULL);
}
@Override
public void onImageTracked(ImageTracker tracker, final ImageTarget target) {
StrokedRectangle strokedRectangle = (StrokedRectangle)glRenderer.getRenderableForKey(target.getName() + target.getUniqueId());
if (strokedRectangle != NULL) {
strokedRectangle.projectionMatrix = target.getProjectionMatrix();
strokedRectangle.viewMatrix = target.getViewMatrix();
strokedRectangle.setXScale(target.getTargetScale().getX());
strokedRectangle.setYScale(target.getTargetScale().getY());
}
}
@Override
public void onImageLost(ImageTracker tracker, final ImageTarget target) {
Log.v(TAG, "Lost target " + target.getName());
glRenderer.removeRenderablesForKey(target.getName() + target.getUniqueId());
}
Y el siguiente método que es muy importante el permite de hacer de espejo el contenido de la cámara en la pantalla; en otras palabras: renderiza el contenido de la cámara en la pantalla y habilita la Realidad Aumentada:
@Override
public void onRenderExtensionCreated(final RenderExtension renderExtension) {
glRenderer = new GLRenderer(renderExtension);
view = new CustomSurfaceView(getApplicationContext(), glRenderer);
driver = new Driver(view, 30);
setContentView(view);
}
Aquí como vemos la cosa se complica un poco ya que hacemos uso de otras clases y métodos que de repente aparecieron en la documentación; lo más importante aquí es tener presente los métodos autogenerados por la API de Wikitude que prácticamente se auto explican con su nombre pero aun así vamos a definirlos un poco a continuación:
Ya con esta ayuda, lo mejor que podemos hacer es ir viendo método por método y analizar qué es lo que está haciendo.
Método onCreate
En el método onCreate
lo siguiente que hacemos es solicitar permiso para emplear la cámara, ya esto es algo propio de Android desde la versión 6, si la solicitud es rechazada, pues no hay nada que hacer, ya que no podrá funcionar la realidad aumentada de Wikitude y menos reconocer imágenes.
Paso siguiente esta líneas de código que son opcionales:
dropDownAlert = new DropDownAlert(this);
dropDownAlert.setText("Scan Target #1 (surfer):");
dropDownAlert.addImages("surfer.png");
dropDownAlert.setTextWeight(0.5f);
dropDownAlert.show();
Lo que haces es pintar un rectángulo en la parte superior de la pantalla:
En los métodos onResume
, onPause
y onDestroy
lo que hacemos es controlar el ciclo de vida de la aplicación y de algunos componentes que son fundamentales para que trabaje y no nos de algún error inesperado cuando la aplicación pase de un estado a otro y pueda destruirse correctamente.
Tipos de renderizado: Externo e Interno
Tenemos dos tipos de renderizado, el interno y el externo y esto es lo que pinta habilita la cámara en la pantalla (fíjese que ahora no empleamos un layout especial que funcionaba como espejo entre la cámara y la pantalla como se hace en la versión web).
El método onRenderExtensionCreated
es definido con el renderizado externo ya que estamos pasando nuestro parámetro OpenGL a 30 frames por segundo.
Y en este punto es donde ocurre la magia, en donde Wikitude muestra o espeja el contenido de la cámara en la pantalla del dispositivo y habilita toda la SDK de Wikitude a nuestra disposición (Reconocimiento de imágenes, Puntos de interés, y la realidad aumentada en general).
Clase GLRenderer
En general estas clases no las tenemos que analizar mucho, con saber que es la que nos construye la superficie de vista o SurfaceView
nos es suficiente para trabajar con ella; la misma se encarga de construir la vista según el tamaño de la pantalla.
GLRenderer
implementa un par de clases, la primera de ellas es GLSurfaceView.Renderer
que emplea la librería OpenGL que permite realizar gráficos e interfaces en 2D y 3D; ya con esto puedes suponer que estos graficos en Wikitude se refieren al lente (la pantalla) que refleja el contenido enviado por la cámara del dispositivo.
¿Cómo se comunica Wikitude con esta interfaz GLSurfaceView.Renderer
Pues para eso Wikitude cuenta con su propia interfaz llamada RenderExtension
la cual es inicializada en el método GLRenderer
como parámetro generado en el método sobrescrito en la actividad principal llamado onRenderExtensionCreated
; asi de facil, aunque suene un poco enredado; esta interfaz RenderExtension
fue creada específicamente para el renderizado de externo de la SDK de Wikitude.
El método useSeparatedRenderAndLogicUpdates
trabaja en conjunto con el método onDrawFrame
que permite actualizar el renderizado de la aplicación.
Los métodos onSurfaceCreated
, onSurfaceChanged
y onDrawFrame
son métodos provistos por la misma interfaz GLRenderer
que se encargan de realizar los cambios en la pantalla y de dibujar cada frame, de estar realizando cambios cada vez que exista una actualización, etc; con esto ves que es importante realizar las llamadas correspondientes en los onPause
y onDestroy
de la actividad.
La clase GLES20
junto con los métodos que invoca simplemente se encarga de limpiar buffer; ya esto es algo bastante interno de OpenGL que de nuevo no es necesario trabajar con esto para explotar las funcionalidades básicas del core de la SDK de Wikitude.
Lo demás que vemos es simplemente como la SDK de Wikitude se comunica con los métodos provistos en OpenGL y trabajar correctamente; sin nada de esto No será posible que Wikitude renderice los frames de la cámara en la pantalla en otras palabras, nos quedaría una pantalla en blanco, negro o simplemente la app no funciona; la misma define mediante CameraManager.FovChangedListener
el método onFovChanged
que vemos al final del código.
La interfaz CameraManager
, es provista por Wikitude y permite controlar la cámara que es la entrada de la realidad aumentada que hace posible que todo lo demás trabaje correctamente
Aquí tienes algunos enlaces de interés con la documentación oficial:
Clase CustomSurfaceView: construye la vista a embeber en la actividad
La otra clase que empleamos en el método onRenderExtensionCreated
es la de CustomSurfaceView
que le pasamos una instancia de la clase glRenderer
que explicamos en la sección anterior; esta clase CustomSurfaceView
extiende de la clase GLSurfaceView
que provee una superficie para mostrar un reenderizado OpenGl (que es el que generamos en el punto anterior), es decir, en una clase la de glRenderer
genera,ps el renderizado que queremos mostrar mediante OpenGL y WIkitude y en esta clase simplemente generamos la superficie con este contenido y lo embebemos en la actividad como si de una vista se tratase:
customSurfaceView = new CustomSurfaceView(getApplicationContext(), glRenderer);
***
setContentView(customSurfaceView);
Esta clase en resumidas cuentas lo que haces es crear un contexto para renderizar y sobre esta una superficie para dibujar mediante el "EGL rendering context"; ya aquí están dos grandes factores en juego que hacen todo por nosotros OpenGL-Android y Wikitude.
Aquí en estos últimos puntos vemos el talón de aquiles de la API nativa de Wikitude para Android, y es que algunos componentes son más complejos como estos que vimos, cosa con la cual no tenemos que lidiar en la API web o JavaScript de Wikitude para Android.
Clase StrokedRectangle: Pintar enmarque del target reconocido
Esta clase es invocado desde el MainActivity
de nuestra actividad mediante el método onImageTracked
que obtiene las dimensiones y posición del objeto reconocido y en el método onImageRecognize
que dibuja el rectángulo correspondiente sobre la imagen reconocida (además de que este método nos permite obtener el nombre del target reconocido mediante target.getName()
).
Esta clase dibuja el rectángulo en la función onDrawFrame()
de dicha clase; todo esto es empleando la API de OpenGL que no es el punto de interés de esta entrada.
Puedes consultar el código completo en los links de descarga ubicados al inicio y final del este tutorial de realidad aumentada con Wikitude native SDK y Android; recuerda copiar el directorio rendering
dentro del paquete de tu aplicación con el contenido del mismo:
Finalmente, al ejecutar la aplicación y posicionar la cámara sobre la siguiente foto; vemos que la SDK de Wikitude reconoce dicha imagen y coloca un rectángulo alrededor de la misma:
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter