Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android

Identificación de personas con el SDK Snapdragon de Qualcomm

by
Difficulty:AdvancedLength:LongLanguages:

Spanish (Español) translation by CYC (you can also view the original English article)

No fue hace tanto tiempo que tomar fotos era bastante caro. Las cámaras requieren película con capacidad limitada y ver los resultados también requiere tiempo adicional y más dinero. Estas restricciones inherentes aseguraron que fuéramos selectivos con las fotos que tomamos.

Avanzamos rápidamente hasta el día de hoy y estas limitaciones se han visto mermadas gracias a la tecnología, pero ahora nos enfrentamos a un nuevo problema, filtrando, organizando y descubriendo fotos importantes de las muchas que tomamos.

Este nuevo problema es lo que inspiró este tutorial. En él, demostraré cómo podemos usar nuevas herramientas para ayudar a facilitar la vida del usuario mediante la introducción de nuevas formas de filtrar y organizar nuestro contenido.

1. Concepto

Para este proyecto, vamos a ver una forma diferente de filtrar a través de tu colección de fotos. En el camino, aprenderás a integrar y usar el SDK Snapdragon de Qualcomm para el procesamiento y reconocimiento facial.

Permitiremos al usuario filtrar una colección de fotos por identidad/identidades. La colección se filtrará por las identidades de una foto que el usuario toque, como se muestra a continuación.

Wireframes of the application showing a master detail pattern

2. Resumen

El objetivo principal de esta publicación es la introducción del procesamiento facial y el reconocimiento mediante el SDK Snapdragon de Qualcomm, mientras que, con un poco de suerte, fomenta indirectamente nuevas formas de pensar y el uso de metadatos derivados del contenido.

Para evitar quedar obsesionado con la fontanería, he creado una plantilla que proporciona el servicio básico para escanear a través de la colección de fotos del usuario y una cuadrícula para mostrar las fotos. Nuestro objetivo es mejorar esto con el concepto propuesto anteriormente.

En la siguiente sección, revisaremos brevemente estos componentes antes de pasar a la introducción del SDK de Snapdragon de Qualcomm.

3. Esqueleto

Como se mencionó anteriormente, nuestro objetivo es centrarnos en el SDK de Snapdragon, así que he creado un esqueleto que tiene implementadas todas las tuberías. A continuación se muestra un diagrama y una descripción del proyecto, que está disponible para su descarga desde GitHub.

Component model for the application broken down into Presentation Service and Data layers

Nuestro paquete de datos contiene una implementación de SQLiteOpenHelper (IdentityGalleryDatabase) responsable de crear y administrar nuestra base de datos. La base de datos constará de tres tablas, una para actuar como un puntero al registro multimedia (photo), otra para las identidades detectadas (identity) y finalmente la tabla de relaciones que conecta las identidades con sus fotos (identity_photo).

Highlevel database schema - 3 tables of photo identity and a relationship table identity_photo

Utilizaremos la tabla de identidad para almacenar los atributos proporcionados por el SDK de Snapdragon, detallados en una sección posterior de este tutorial.

También se incluyen en el paquete de datos una clase Provider (IdentityGalleryProvider) y Contract (IdentityGalleryContract), que no es más que un proveedor estándar que actúa como un contenedor de la clase SQLiteOpenHelper.

Para darte una idea de cómo interactuar con la clase Provider, se toma el siguiente código de la clase TestProvider. Como su nombre lo sugiere, se usa para probar la clase de Provider.

El paquete service es responsable de iterar, catalogar y, finalmente, procesar las imágenes disponibles a través de MediaStore. El servicio en sí extiende IntentService como una forma fácil de realizar el procesamiento en su propio hilo. El trabajo real se delega en GalleryScanner, que es la clase que ampliaremos para el procesamiento facial y el reconocimiento.

Este GalleryScannerIntentService se crea una instancia cada vez que se crea MainActivity con la siguiente llamada:

Cuando se inicia, GalleryScannerIntentService obtiene la última fecha de escaneo y la pasa al constructor de GalleryScanner. Luego llama al método scan para comenzar a iterar a través del contenido del proveedor de contenido de MediaItem, para los artículos después de la última fecha de escaneo.

Si inspeccionas el método scan de la clase GalleryScanner, notarás que es bastante detallado: nada complicado está sucediendo aquí. El método necesita consultar los archivos multimedia almacenados internamente (MediaStore.Images.Media.INTERNAL_CONTENT_URI) y externamente (MediaStore.Images.Media.EXTERNAL_CONTENT_URI). Cada elemento se pasa a un método de gancho, que es donde colocaremos nuestro código para el procesamiento facial y el reconocimiento.

Se encuentran disponibles otros dos métodos de enlace en la clase GalleryScanner (como sugieren los nombres de los métodos) para inicializar y des-inicializar la instancia FacialProcessing.

El paquete final es el paquete de presentación. Como su nombre indica, aloja la clase Activity responsable de renderizar nuestra galería. La galería es un GridView conectado a un CursorAdapter. Como se explicó anteriormente, al tocar un elemento consultará en la base de datos las fotos que contengan una de las identidades de la foto seleccionada. Por ejemplo, si tocas una foto de tu amiga Lisa y su novio Justin, la consulta filtrará todas las fotos que contengan una o ambas, Lisa y Justin.

4. SDK Snapdragon de Qualcomm

Para ayudar a los desarrolladores a hacer que su hardware se vea bien y haga justicia, Qualcomm ha lanzado un increíble conjunto de SDK, uno de ellos es el SDK de Snapdragon. Snapdragon SDK expone un conjunto optimizado de funciones para el procesamiento facial.

El SDK se divide ampliamente en dos partes: procesamiento facial y reconocimiento facial. Dado que no todos los dispositivos admiten ambas o ninguna de estas características, que probablemente sea la razón por la que se separan estas características, el SDK proporciona una manera fácil de verificar qué características admite el dispositivo. Cubriremos esto con más detalle más adelante.

El procesamiento facial proporciona una forma de extraer las características de una foto (de una cara), que incluye:

  • Detección de parpadeo: Mide qué tan abierto está cada ojo.
  • Seguimiento de la mirada: Evalúa dónde mira el sujeto.
  • Valor de sonrisa: Estima el grado de la sonrisa.
  • Orientación de la cara: Sigue la guiñada, el cabeceo y el balanceo de la cabeza.

El reconocimiento facial, como su nombre lo indica, proporciona la capacidad de identificar personas en una foto. Vale la pena señalar que todo el procesamiento se realiza localmente, a diferencia de la nube.

Estas características se pueden usar en tiempo real (video/cámara) o fuera de línea (galería). En nuestro ejercicio, utilizaremos estas funciones sin conexión, pero existen diferencias mínimas entre los dos enfoques.

Consulta la documentación en línea de los dispositivos compatibles para obtener más información sobre el procesamiento facial y el reconocimiento facial.

5. Agregar procesamiento facial y reconocimiento

En esta sección, completaremos esos métodos de anzuelo (con pocas líneas de código) para brindar a nuestra aplicación la capacidad de extraer propiedades faciales e identificar personas. Para trabajar, descarga la fuente de GitHub y abre el proyecto en Android Studio. Alternativamente, puedes descargar el proyecto completo.

Paso 1: Instalación del SDK de Snapdragon

Lo primero que debemos hacer es tomar el SDK del sitio web de Qualcomm. Ten en cuenta que deberás registrarte/iniciar sesión y aceptar los términos y condiciones de Qualcomm.

Una vez descargado, desarchiva los contenidos y navega a /Snapdragon_sdk_2.3.1/java/libs/libs_facial_processing/. Copia el archivo sd-sdk-facial-processing.jar en la carpeta /app/libs/ de tu proyecto como se muestra a continuación.

Android Studio Libs folder via the Project File panel

Después de copiar el SDK de Snapdragon, haz clic con el botón derecho en sd-sdk-facial-processing.jar y selecciona Agregar como biblioteca... en la lista de opciones.

Android Studio File menu - Add As Library option

Esto agregará la biblioteca como una dependencia en tu archivo build.gradle como se muestra a continuación.

El último paso es agregar la biblioteca nativa. Para ello, crea una carpeta llamada jniLibs en tu carpeta /app/src/main/  y copia la carpeta armeabi (de la descarga del SDK) y su contenido en ella.

Android Studios Project folder view showing the structure where to put the native binaries

Ahora estamos listos para implementar la lógica para identificar a las personas que usan la funcionalidad de la API. Los siguientes fragmentos de código pertenecen a la clase GalleryScanner.

Paso 2: Inicialización

Primero abordemos el método de enganche de inicialización.

Primero debemos verificar que el dispositivo sea compatible con el procesamiento facial y el reconocimiento facial. Si no es así, lanzamos una excepción UnsupportedOperationException.

Después de eso, asignamos nuestra referencia local de la clase FacialProcessing, mFacialProcessing, a una nueva instancia utilizando el método de fábrica getInstance. Esto devolverá null si una instancia ya está en uso, en cuyo caso se requiere que el consumidor llame a release en esa referencia.

Si hemos obtenido con éxito una instancia de un objeto FacialProcessing, lo configuramos primero configurando la confianza. Hacemos esto usando una variable local, que es 57 en este caso desde un rango de 0 a 100. La confianza es un umbral cuando se trata de resolver identidades. Cualquier coincidencia por debajo de este umbral se considerará como identidades separadas.

En términos de determinar el valor, hasta donde puedo decir, este es un proceso de prueba y error. Obviamente, cuanto más alto es el umbral, más preciso es el reconocimiento, con la compensación de aumentar el número de falsos positivos.

Luego configuramos el modo FacialProcessing en FP_MODE_STILL. Tus opciones aquí son FP_MODE_STILL o FP_MODE_VIDEO. Como sugieren los nombres, uno está optimizado para imágenes fijas mientras que el otro para cuadros continuos, ambos con casos de uso obvios.

P_MODE_STILL, como podrías sospechar, proporciona resultados más precisos. Pero como verás más adelante, FP_MODE_STILL está implícito en el método que usamos para procesar la imagen, por lo que esta línea se puede omitir. Solo lo agregué para completarlo.

Luego llamamos a loadAlbum (método de la clase GalleryScanner), que es lo que veremos a continuación.

La única línea interesante aquí es:

Su método contrario es:

Una sola instancia FacialProcessing puede considerarse como una sesión. Las personas agregadas (explicadas a continuación) se almacenan localmente (denominado "álbum de reconocimiento") dentro de esa instancia. Para permitir que tu álbum persista en varias sesiones, es decir, cada vez que obtienes una nueva instancia, necesitas una forma de persistir y cargarlas.

El método serializeRecogntionAlbum convierte el álbum en una matriz de bytes y, a la inversa, deserializeRecognitionAlbum cargará y analizará un álbum previamente almacenado como una matriz de bytes.

Paso 3: Des-inicialización

Ahora sabemos cómo inicializar la clase FacialProcessing para el procesamiento facial y el reconocimiento. Ahora volvamos nuestro enfoque a la des-inicialización implementando el método deinitFacialProcessing.

Como se mencionó anteriormente, solo puede haber una instancia de la clase FacialProcessing a la vez, por lo que debemos asegurarnos de liberarla antes de finalizar nuestra tarea. Hacemos esto a través de un método de lanzamiento llamado release. Pero primero hacemos que el álbum de reconocimiento persista para que podamos usar los resultados en varias sesiones. En este caso, cuando el usuario toma o recibe fotos nuevas, queremos asegurarnos de que usamos las identidades previamente reconocidas para las mismas personas.

Paso 4: Procesando la imagen

Finalmente estamos listos para desarrollar el método del gancho final y usar la clase FacialProcessing. Los siguientes bloques de código pertenecen al método processImage. Los he separado por claridad.

El método toma como referencia una instancia de la clase ContentValues, que contiene los metadatos de esta imagen, junto con el URI que apunta a la imagen. Usamos esto para cargar la imagen en la memoria.

El siguiente fragmento de código reemplaza el comentario anterior // continúa debajo (1).

Como se mencionó anteriormente, primero pasamos la imagen estática a la instancia FacialProcessing a través del método setBitmap. El uso de este método utiliza implícitamente el modo FP_MODE_STILL. Este método devuelve True si la imagen se procesó correctamente y False si el proceso falló.

El método alternativo para procesar imágenes de transmisión (típicamente para frames de vista previa de la cámara) es:

La mayoría de los parámetros son obvios. Debe pasar si el frame está volteado (esto suele ser necesario para la cámara frontal) y si se ha aplicado alguna rotación (generalmente se configura mediante el método setDisplayOrientation de una instancia de cámara llamada Camera).

A continuación, consultamos el número de caras detectadas y solo continuamos si se encuentra al menos una. El método getFaceData devuelve los detalles de cada cara detectada como una matriz de objetos FaceData, donde cada objeto FaceData encapsula las características faciales que incluyen:

  • Límite de cara (FACE_RECT)
  • Ubicaciones de cara, boca y ojo (FACE_COORDINATES)
  • Contorno de la cara (FACE_CONTOUR)
  • Grado de sonrisa (FACE_SMILE)
  • Dirección de los ojos (FACE_GAZE)
  • Indicador que muestra si alguno de los ojos (o ambos ojos) parpadea (FACE_BLINK)
  • Guiñada, inclinación y balanceo de la cara (FACE_ORIENTATION)
  • Identificación generada o derivada (FACE_IDENTIFICATION)

Hay una sobrecarga en este método que toma un conjunto de enumeraciones (como se describió anteriormente) para incluir puntos de características, eliminando/minimizando los cálculos redundantes.

Ahora pasamos a inspeccionar el objeto FaceData para extraer la identidad y las características. Primero veamos cómo se hace el reconocimiento facial.

El siguiente fragmento de código reemplaza el comentario anterior //continúa debajo (2).

Primero solicitamos la identificación de la persona asignada mediante el método getPersonId. Esto devolverá -111 (FP_PERSON_NOT_REGISTERED) si no existe identidad en el álbum cargado actualmente, de lo contrario devolverá el id de una persona correspondiente del álbum cargado.

Si no existe identidad, la agregamos a través del método addPerson del objeto FacialProcessing, pasándole el índice del elemento FaceData que estamos inspeccionando actualmente. El método devuelve la identificación de la persona asignada si tiene éxito, de lo contrario devolverá un error. Esto ocurre cuando intentas agregar una identidad que ya existe.

Alternativamente, cuando la persona fue emparejada con una identidad almacenada en nuestro álbum cargado, llamamos al método updatePerson del objeto FacialProcessing, pasándole el id existente y el índice del ítem FaceData. Agregar a una persona varias veces aumenta el rendimiento del reconocimiento. Puedes agregar hasta diez caras para una sola persona.

La línea final simplemente devuelve el ID de identidad asociado de nuestra base de datos, insertándolo si el ID de persona aún no existe.

No se muestra arriba, pero la instancia de FaceData expone el método getRecognitionConfidence para devolver la confianza de reconocimiento (0 a 100). Dependiendo de tus necesidades, puedes usar esto para influir en el flujo.

El fragmento final muestra cómo consultar cada una de las otras funciones desde la instancia de FaceData. En esta demostración, no hacemos uso de ellos, pero con un poco de imaginación estoy seguro de que puedes pensar en formas de hacer un buen uso de ellos.

El siguiente fragmento de código reemplaza el comentario anterior //continúa debajo (3).

Eso completa el código de procesamiento. Si regresas a la galería y tocas una imagen, deberías verla filtrando las fotos que no contienen personas identificadas en la foto seleccionada.

Conclusión

Comenzamos este tutorial sobre cómo la tecnología se puede utilizar para ayudar a organizar el contenido del usuario. En el contexto de la computación consciente, cuyo objetivo es utilizar el contexto como una clave implícita para enriquecer la interacción empobrecida de los seres humanos a las computadoras, lo que facilita la interacción con las computadoras, esto se conoce como auto-etiquetado. Al marcar el contenido con datos más significativos y útiles, tanto para la computadora como para nosotros, permitimos un filtrado y procesamiento más inteligente.

Hemos visto que esto se usa con frecuencia con contenido de texto, el ejemplo más obvio son los filtros de correo no deseado y, más recientemente, los lectores de noticias, pero no tanto con el contenido multimedia enriquecido, como fotos, música y video. Herramientas como el SDK de Snapdragon nos brindan la oportunidad de extraer características significativas de los medios enriquecidos, exponiendo sus propiedades al usuario y la computadora.

No es difícil imaginar cómo podría ampliar nuestra aplicación para permitir el filtrado basado en el sentimiento mediante el uso de una sonrisa como la principal característica o actividad social contando el número de caras. Se puede ver una implementación de este tipo en esta característica de Smart Gallery.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.