martes, 8 de enero de 2013

Carga de la librería OpenCV en tiempo de ejecución y eventos onResume, onPause y onDestroy (pasos 6 y 7)

Implementar la función de callback de la carga asíncrona de la librería OpenCV

Un cambio importante introducido desde que usaba la versión 2.4.1 a la versión que estamos usando en estos ejemplos, la 2.4.3, ha sido la carga asíncrona de la librería OpenCV. Esto es debido a que ahora las librerías se encuentran concentradas y centralizadas en una aplicación de "Google Play".

La principal ventaja que tiene este nuevo sistema es que las aplicaciones que usan OpenCV ya no son tan pesadas (no contienen todas las librerías OpenCV dentro de ellas para su distribución). Antes tener dos aplicaciones OpenCV instaladas en el móvil requería tener en cada una de ellas las librerías, ocupando el doble de espacio (o el triple si fueran tres aplicaciones las que usan OpenCV).

Por otro lado tenemos la desventaja de que todas aquellas aplicaciones que hayamos desarrollado previamente con OpenCV van a necesitar ciertas modificaciones para que funcionen con las versiones actuales de OpenCV.

En el manual oficial de OpenCV disponemos del proceso de inicialización de la librería y/o instalación de todo lo necesario:

http://docs.opencv.org/android/service/doc/BaseLoaderCallback.html

Crearemos, bajo la definición de "mOpenCvCameraView", una nueva propiedad, que llamaremos "mLoaderCallback", que será la encargada de contener una nueva clase del tipo "BaseLoaderCallback" cuya función es activar la vista "mOpenCvCameraView" cuando la librería OpenCV termine su carga:

 /* Creamos el cargador de OpenCV Manager que usaremos más adelante en OpenCVLoader.initAsync */
 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };

Al teclear el código anterior se resaltará la clase "BaseLoaderCallback" en rojo indicando que ha aparecido un error. Para solucionarlo pulsamos en "Import 'BaseLoaderCallback' (org.opencv.android)":

Localización archivo activity_main.xml

Por último, en el case, habrá un error en "LoaderCallbackInterface" en rojo indicando que no reconoce la clase cuya propiedad "SUCCESS" intentamos obtener. Para solucionar este error pulsamos en "Import 'LoaderCallbackInterface' (org.opencv.android)":

Localización archivo activity_main.xml

Ahora deberemos programar en "onResume" la inicialización asíncrona de la librería OpenCV. Esto conseguirá que se inicialice no sólo durante la carga inicial de la actividad, si no también cuando la volvamos a poner en primer plano o tras apagar la pantalla del móvil:

    /* Cuando cargue la actividad deberemos realizar una inicialización asíncrona de OpenCV */
    @Override
    public void onResume()
    {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
    }

También aprovecharemos para implementar los métodos onPause y onDestroy para que deshabiliten la vista cuando la actividad pierda el foco, apaguemos la pantalla, etc:

    /* Desactivamos la vista cada vez que la actividad se pause o se destruya */
    @Override
    public void onPause()
    {
        if (mOpenCvCameraView != null) {
            mOpenCvCameraView.disableView();
        }
        super.onPause();
    }
    
    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null) {
            mOpenCvCameraView.disableView();
        }
    }

También aprovecharemos para implementar los métodos onPause y onDestroy para que deshabiliten la vista cuando la actividad pierda el foco, apaguemos la pantalla, etc:

sábado, 5 de enero de 2013

Preparando la actividad para recibir fotogramas de la cámara (paso 5)

Configurar nuestra Actividad para que implemente CvCameraViewListener

Para que nuestra actividad reciba eventos asíncronos cada vez que reciba un fotograma de la cámara deberemos modificar dos cosas en nuestra actividad.

La primera es agregar a nuestra actividad un elemento llamado "NativeCameraView" que será el encargado de gestionar la comunicación con la cámara Android.

Para ello abriremos el archivo "activity_main.xml" localizado dentro de la ruta "/res/layout/" de nuestro proyecto haciendo doble click sobre él:

Localización archivo activity_main.xml

Al abrir el archivo nos aseguramos que no estamos en "Graphical Layout", deberemos pulsar en la pestaña inferior donde aparece el nombre de archivo (activity_main.xml) y en la parte superior comprobamos que aparece el código XML del aspecto que tendrá la actividad:

Aspecto de activity_principal.xml

En el código XML dereremos cambiar el "TextView" (un simple campo de texto) que viene de ejemplo:

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/hello_world" />

Por este otro elemento llamado "org.opencv.android.NativeCameraView":

    <org.opencv.android.NativeCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/activity_main_surface_view" />

Hay que notar que he identificado el elemento org.opencv.android.NativeCameraView con el identificador "activity_main_surface_view" para poder referenciarlo más adelante durante la carga de la aplicación.

El resultado una vez modificado el archivo será algo parecido a:

Ahora deberemos abrir el código de nuestra aplicación buscando en el explorador de paquetes (Package Explorer) el archivo "MainActivity.java" dentro de la ruta "/src/com.linaresdigital.blogrobotica.tutorialopencv1":

Aspecto de activity_principal.xml

En la definición de la clase (del tipo "Activity") nos encontraremos con:

Aspecto de activity_principal.xml

Deberemos agregar, tras "public class MainActivity extends Activity", el texto "implements CvCameraViewListener" para indicar que implementaremos los métodos de invocación asíncrona de obtención de fotogramas que generará nuestro "NativeCameraView" quedando la línea completa de la siguiente manera:

public class MainActivity extends Activity implements CvCameraViewListener {

Ahora Eclipse nos subrayará la palabra "CvCameraViewListener" indicando que existe un error que debe ser corregido:

Error en 'CvCameraViewListener'

Pulsaremos en "Import 'CvCameraViewListener' (org.opencv.android.CameraBridgeViewBase)" para solucionarlo (agregará una línea con el texto "import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener;" al comienzo de nuestro código fuente).

Tras hacerlo será ahora la palabra "MainActivity" la que aparecerá resaltada indicándonos un nuevo error que debe ser subsanado:

Error en 'MainActivity'

Esto es debido a que hemos indicado que implementamos la interfaz "CvCameraViewListener" pero no hemos implementado en el código ninguno de sus métodos (en el mensaje nos avisa del método "onCameraViewStarted" en particular).

Lo solucionaremos pulsando en "Add unimplemented methods" (agregar métodos sin implementar) tras lo cual Eclipse creará por nosotros las definiciones de las funciones que aún no hemos implementado listas para insertar código en ellas:

Error en 'MainActivity'

En los puntos 9 y 10 implementaremos todo lo necesario para trabajar con los fotogramas obtenidos.

Ya tenemos casi todo preparado, por ahora agregaremos una propiedad a nuestra clase para almacenar una referencia al control "org.opencv.android.NativeCameraView" que hemos agregado a nuestra actividad:

public class MainActivity extends Activity implements CvCameraViewListener {
        /* Referencia a nuestro control "org.opencv.android.NativeCameraView" */
        private CameraBridgeViewBase mOpenCvCameraView;

De nuevo nos aparecerá otro error resaltando en rojo el tipo de datos "CameraBridgeViewBase" que deberemos solucionar indicando que importe lo necesario pulsando en "Import 'CameraBridgeViewBase' (org.opencv.android)":

Error en 'CameraBridgeViewBase'

Y por último inicializamos nuestra "mOpenCvCameraView" durante el evento onCreate (creación de la actividad):

  /* Al cargar la actividad cargamos el valor de nuestra superficie de trabajo */
  mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_main_surface_view);

El evento onCreate debería quedar tal y como se muestra:

 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  /* Al cargar la actividad cargamos el valor de nuestra superficie de trabajo */
  mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.activity_main_surface_view);
 }

jueves, 3 de enero de 2013

Permisos para acceder a la cámara y enlace con la librería OpenCV (pasos 3 y 4)

Cambiar los permisos del proyecto para acceder a la cámara

Para agregar los permisos necesarios para que nuestra aplicación tenga acceso a la cámara es necesario hacer doble click sobre el archivo "AndroidManifest.xml" de nuestro proyecto.

Creando nuevo proyecto Android

Una vez se haya abierto el editor pulsamos en la pestaña "Permissions" y luego pulsamos en el botón "Add..." para agregar un nuevo permiso:

Creando nuevo proyecto Android

Ahora pulsamos en "Uses permission" y pulsamos en "OK".

En la derecha aparece un cuadro llamado "Attributes For Uses Permission", en el que deberemos teclear (o seleccionar del menú desplegable) el permiso "android.permission.CAMERA".

Creando nuevo proyecto Android

Ahora tenemos preparado nuestro proyecto para hacer uso de la cámara. Si no hacemos este paso OpenCV nos avisará que la cámara no es accesible o está siendo usada por otra aplicación, por lo que no debemos olvidar este paso si no queremos evitar posteriores quebraderos de cabeza.

Configurar el proyecto para hacer uso de la librería OpenCV

Para hacer uso de la librería OpenCV en nuestro proyecto deberemos hacer referencia al proyecto importado anteriormente de la siguiente manera:

Pulsamos Alt+Enter o bien el botón derecho sobre nuestro proyecto (en el cuadro "Package Explorer") y pulsamos en "Properties".

Creando nuevo proyecto Android

Seleccionamos en la lista izquierda el apartado "Android" y en el cuadro "Library" pulsamos en el botón "Add...".

Creando nuevo proyecto Android

Seleccionamos la única librería que tenemos por ahora, OpenCV-2.4.3.2, y pulsamos en "OK".

Creando nuevo proyecto Android

Ahora aparecerá la librería "OpenCV-2.4.3.2" en el listado del cuadro "Library". Pulsamos en "OK" para guardar los cambios en el proyecto y a partir de ahora podremos hacer uso de las librerías de OpenCV en nuestro proyecto.

miércoles, 2 de enero de 2013

Creación del proyecto (pasos 1 y 2)

Importar librería OpenCV al espacio de trabajo Eclipse

Pulsamos en el menú "File > Import", seleccionamos "Existing Android Code Into Workspace" y pulsamos en siguiente:

Importando proyecto existente

Ahora pulsamos en "Browse..." y buscamos el directorio donde descomprimimos el SDK de OpenCV. Deberemos buscar dentro de él el subdirectorio "java" que hay dentro del directorio "sdk".

Localizando ruta al SDK

Nos aparecerá un proyecto llamado "java". Podemos cambiar el nombre de proyecto pulsando en él y posteriormente pulsando la tecla F2. En nuestro caso vamos a ponerle el nombre "OpenCV-2.4.3.2".

Si aparece un mensaje de error en la consola (en la ventana "Console") que muestre el texto Unable to resolve target 'android-xx' lo ignoramos. Eclipse elegirá automáticamente la versión del SDK de Android más alta que tengamos instalada para corregirlo.

Crear proyecto nuevo

Es hora de crear nuestro primer proyecto OpenCV para Android pulsando en "File > New > Android Application Project":

Creando nuevo proyecto Android

Rellenaremos la siguiente información:

  • Minimum Required SDK: Es la versión mínima en la que sabemos que funcionará la aplicación. OpenCV está soportado únicamente por el Android 2.2 (API 8, Froyo) y superiores.
  • Target SDK: La máxima versión de Android sobre la que estamos trabajando o sabemos a ciencia cierta que funciona. Yo uso Android ICS 4.0.4, por lo que marco el API 15 aún sabiendo que es muy probable que también funcione en Jelly Bean (Android 4.1).
  • Compile With: Versión de SDK sobre la que se compilará el proyecto. Yo suelo seleccionar la misma sobre la que estoy desarrollando, aunque haya una versión superior instalada en mi equipo.
  • Theme: Como será una aplicación a pantalla completa prefiero no usar ningún tema.

Pulsamos en "Next >" para configurar qué vamos a tener en el proyecto inicialmente.

Creando nuevo proyecto Android<

Quien quiera puede marcar la opción para crear un icono personalizado, agregando un paso más al proceso de creación del proyecto.

En este caso particular usaré los ajustes por defecto del icono (desmarco la casilla) pero dejaré marcada la casilla para crear una nueva actividad.

Pulsamos en "Next >" y configuramos cómo queremos que sea la actividad principal.

Creando una actividad vacía

Seleccionamos una actividad en blanco. Más adelante configuraremos la actividad para que abra a pantalla completa.

Hay que tener en cuenta que FullscreenActivity no creará una actividad a pantalla completa si no hemos configurado la versión de Android 4.1 (Jelly Bean).

Podemos finalizar el proceso de creación de proyecto en este punto pulsando "Finish", pero tenemos la posibilidad de cambiar el nombre de la actividad y de la disposición de la actividad pulsando en "Next >".

El aspecto final del gestor de proyectos será algo parecido a:

Aspecto del gestor de proyectos de Eclipse

Crear una aplicación que use la cámara en un dispositivo Android usando OpenCV (desde cero) en sólo 10 pasos

En esta guía pretendo crear un guión paso a paso sobre cómo empezar a usar OpenCV en un proyecto Android.
Los requisitos previos son:
  • Tener instalado Eclipse+ADT (yo uso el conjunto ofrecido por Google).
  • Haber descargado y descomprimido el SDK de OpenCV (en este caso estoy usando OpenCV-2.4.3.2-android-sdk).
El resumen de los pasos son:
  1. Importar librería OpenCV al espacio de trabajo Eclipse.
  2. Crear proyecto nuevo.
  3. Cambiar los permisos del proyecto para acceder a la cámara.
  4. Configurar el proyecto para hacer uso de la librería OpenCV.
  5. Configurar nuestra Actividad para que implemente CvCameraViewListener.
  6. Implementar la función de callback de la carga asíncrona de la librería OpenCV.
  7. Implementar el comportamiento de la aplicación en los eventos onResume, onPause y onDestroy.
  8. Configurar en onCreate nuestro CameraBridgeViewBase definiendo qué clase implementa CvCameraViewListener.
  9. Implementar en onCameraViewStarted y onCameraViewStopped la creación de las superficies necesarias para trabajar con cada fotograma.
  10. Implementar en onCameraFrame qué hacemos con cada fotograma obtenido.
(Edición: 26 de enero de 2015) La guía finalmente la he dividido en los siguientes artículos del blog:
Experimentalmente me he atrevido con la grabación del proceso en vídeo y la he subido a mi canal de YouTube. Hay que tener que son pruebas y que no está completo, pero puede ayudar en algunos aspectos tener parte del tutorial en vídeo: