DesarrolloavanzadoenAndroid MiguelÁngelLozano,BoyánBonev,PabloSuau,JuanManuelSáez {malozano,boyan,pablo,jmsaez}@dccia.ua.es MobileVisionResearchLab Dep.CienciadelaComputacióneInteligenciaArtificial InstitutoUniversitariodeInvestigaciónenInformática UniversidaddeAlicante
Esquema MobileVisionResearchLab Orígenes:robóticamóvilyvisión Trabajoactual DesarrolloavanzadoenAndroid NDK NEON Necessitas
MobileVisionResearchLab Creaciónen2011 Origen:RobotVisionGroup Miembros FranciscoEscolano MiguelÁngelLozano JuanManuelSáez PabloSuau AntonioPeñalver BoyánBonev
Actividadesdeinvestigación Visiónartificial(monocularyestéreo) Robóticamóvil Reconocimientodepatrones(teoríadela información) Visiónendispositivosmóviles
Visiónmóvilenrobótica
Visiónmóvilenrobótica
Visiónmóvilenrobótica
Reconocimientodepatrones
Reconocimientodepatrones
Visiónmóvilenelteléfono
VisiónenAndroid Métodosestándardemanejodeimágenes ClaseBitmap setpixels,getpixels Arraydeenteros FormatoARGB_8888 Operacionesaniveldebit Alternativa:getPixel,setPixel Necesidaddecopiarimágenes
VisiónenAndroid SetPixelsygetPixelsmuylentos Ejemplo:pasaragris,SamsungGalaxyS,imagen de320x240,10segundos(conenteros) Alternativas NDK NEON Necessitas
AndroidNDK http://developer.android.com/sdk/ndk/index.html Herramientainstaladaporseparado CódigonativoenCparalaspartescríticasde laaplicación Requiere Android1.5osuperior Android2.2osuperiorparatratarconimágenes
JavaNativeInterface Plataformaparaejecutarcódigoescritoen otroslenguajesdesdejava http://java.sun.com/developer/onlinetraining/pr ogramming/jdcbook/jni.html http://java.sun.com/developer/onlinetraining/pr ogramming/jdcbook/jniexamp.html
EjemploNDK Carpetajni Android.mk archivo.c ContenidodeAndroid.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := imagenes LOCAL_SRC_FILES := nativo.c LOCAL_LDLIBS := -llog -ljnigraphics -lm include $(BUILD_SHARED_LIBRARY)
EjemploNDK Contenidodenativo.c(1) #include #include #include #include #include #include #include <jni.h> <string.h> <math.h> <time.h> <stdlib.h> <android/bitmap.h> <android/log.h> // La macro LOGE permite mostrar en el log de android (ejecutando en un terminal adb -logcat) // diferentes mensajes de error. El formato de los parámetros es exactamente igual a los del // printf de c #define LOG_TAG "libimages" #define LOGE(...) android_log_print(android_log_error,log_tag, VA_ARGS ) // FROM RGB888 TO RGB565 // r,g and b values are 8-bit values (from 0 to 255) inline int rgb(int r, int g, int b) { return((r>>3<<11) + (g>>2<<5) + (b>>3)); } // FROM RGB565 TO RGB888 inline int red(uint16_t color) { return (color&0xf800)>>11<<3;} inline int green(uint16_t color) { return (color&0x07e0)>>5<<2;} inline int blue(uint16_t color) { return (color&0x001f)<<3;}
EjemploNDK Contenidodenativo.c(2) // Convertir a escala de grises void colortogray(androidbitmapinfo info, uint16_t *pixels16) { int i,r,g,b,gray; } for (i=0;i<info.width*info.height;i++) { r = red(pixels16[i]); g = green(pixels16[i]); b = blue(pixels16[i]); gray = (299*r + 587*g + 114*b)/1000; // Las operaciones se hacen con //enteros para que el procesamiento sea más rápido pixels16[i] = rgb(gray, gray, gray); }
EjemploNDK Contenidodenativo.c(3) // Método que se llamará desde java. El formato del nombre debe ser como se muestra. En primer lugar la palabra // Java, seguida por el nombre del paquete, el nombre de la clase, y el nombre del método (en este caso processimage) JNIEXPORT jfloat JNICALL Java_rvg_ua_es_Gris_procesarImage(JNIEnv * env, jobject bitmap) { AndroidBitmapInfo info; void *pixels; int ret; jfloat timep; clock_t start, finish; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("Fallo en AndroidBitmap_getInfo()! error=%d", ret); return; } if (info.format!= ANDROID_BITMAP_FORMAT_RGB_565) { LOGE("El formato del bitmap no es RGB_565!"); return; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("Fallo en AndroidBitmap_lockPixels()! error=%d", ret); return; } // EMPEZANDO PROCESAMIENTO uint16_t *pixels16 = (uint16_t *)pixels; start = clock(); // Transformar a escala de grises colortogray(info, pixels16); // TERMINANDO PROCESAMIENTO AndroidBitmap_unlockPixels(env, bitmap); finish = clock(); timep = ((double)finish - (double)start)/clocks_per_sec; return timep; // El método devuelve el tiempo de ejecución }
EjemploNDK Compilación Directoriojni ndk build Segeneraelficherolibimagenes.so Importante:sinosecambióelcódigoJava, haceruncleardelproyecto
EjemploNDK UtilizarcódigonativodesdeJava private native float processimage(bitmap bitmap); static { System.loadLibrary("images"); } Tiempo:0.2s
NEON SetdeinstruccionesARMv7 ArquitecturaSIMD(SingleInstructionMultiple Data) Registrosvectorialesalosqueselesaplicauna mismaoperación 32registrosde64bits(ó16registrosde128bits) Procesamientoimágenes,video,gráficos http://www.arm.com/products/technologies/neo n.php
UsandoNEONenAndroid ConjuntamenteconNDK AñadiraAndroid.mk ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) LOCAL_CFLAGS := -DHAVE_NEON=1 LOCAL_SRC_FILES += native-neon.c.neon endif LOCAL_C_INCLUDES := $(NDK_ROOT)/sources/cpufeatures LOCAL_STATIC_LIBRARIES := cpufeatures include $(NDK_ROOT)/sources/cpufeatures/Android.mk Extensión neon.caarchivosconcódigoneon
UsandoNEONenAndroid Libreríacpu features.hparalacomprobación delaexistenciadeinstruccionesneon // Comprobando la existencia del set de instrucciones NEON if (android_getcpufamily() == ANDROID_CPU_FAMILY_ARM && (android_getcpufeatures() & ANDROID_CPU_ARM_FEATURE_NEON)!=0 && (android_getcpufeatures() & ANDROID_CPU_ARM_FEATURE_ARMv7)!=0) { LOGE( NEON disponible ); } else { LOGE("El procesador no es ARMv7 o no es compatible con NEON"); return(-1); } Códigoalternativoencasodeincompatibilidad conneon
SetdeinstruccionesNEON http://gcc.gnu.org/onlinedocs/gcc/arm NEON Intrinsics Ejemplos Suma: Comparación: uint32x2_tvadd_u32(uint32x2_t,uint32x2_t) uint32x2_tvceq_u32(uint32x2_t,uint32x2_t)
EjemploNEON(1) // Transforms image from rgb to grayscale void rgbtogray(androidbitmapinfo info, uint8_t *pixels) { int i; // Tamaño total de la imagen en pixels int n = info.width*info.height; // Puntero auxiliar para acceder en cada iteracion del bucle // a una posicion absoluta de la imagen, en lugar de ir incrementando // el puntero pixels uint8_t *auxp = pixels; int pos = 0; // Tres variables vectoriales compuestas por 8 valores de 8 bits. En // concreto, estos son los coeficientes por los que se van a multiplicar // los valores RGB de cada pixel para obtener el tono de gris uint8x8_t rfac = vdup_n_u8(77); uint8x8_t gfac = vdup_n_u8(151); uint8x8_t bfac = vdup_n_u8(28); // Dividimos el tamaño entre 8 porque los pixeles se van a procesar de // 8 en 8 (por lo que hay 8 veces menos iteraciones) n /= 8; // 8 pixels are processed simultaneously for (i=0;i<n;i++) { uint16x8_t temp; uint8x8_t result; // La variable rgb se comportará como un registro, con un unico // campo, un vector llamado val, de 4 componentes. Cada componente // de ese vector es un registro vectorial // Asi que aqui lo que estamos haciendo es almacenar las cuatro // componentes RGBA de los ocho pixeles actuales (apuntados por // auxp) en cuatro registros vectoriales uint8x8x4_t rgb = vld4_u8(auxp); // Lo siguiente son instrucciones de multiplicacion y // multiplicacion con acumulacion temp = vmull_u8(rgb.val[0], rfac); temp = vmlal_u8(temp, rgb.val[1], gfac); temp = vmlal_u8(temp, rgb.val[2], bfac);
EjemploNEON(2) // Se realiza un desplazamiento para dividir y volver a tener // valores entre 0 y 255 result = vshrn_n_u16(temp,8); // Se asigna // del pixel rgb.val[0] = rgb.val[1] = rgb.val[2] = el mismo valor de intensidad a todas las componentes para tener una imagen en tonos de gris result; result; result; // Se vuelven a almacenar los 4 conjuntos de 8 bytes // (correspondientes a los 8 pixeles procesados esta iteracion) // en su posicion correspondiente de memoria vst4_u8(auxp,rgb); // Se apuna al siguiente conjunto de pixeles (aumentamos 8*4 bytes) pos += 32; // En lugar de ir incrementando el valor de pixels, vamos calculando // auxp como una suma absoluta. // Si no se hacía de esta forma el resultado no era correcto. Quiza // es debido a que el procesamiento con NEON se realiza de forma // asincrona auxp = pixels + pos; } return; } Tiempo:0.04s(50vecesmásrápidoquesin NEON,250vecesmásrápidoquesinNDK)
Necessitas PortdeQTaAndroid http://sourceforge.net/p/necessitas/home/necessitas/ Ventajas Códigonativo CompilaciónbasadaenelsetdeNEON Inconvenientes Estadomuyprematuro Nosemanejancorrectamentealgunoseventosdeinterfaz Necesidaddeinstalargrancantidaddelibrerías(Ministro)
Necessitas Reconstrucción2Dentiemporealbasada únicamenteenvisión
Necessitas
Necessitas
DesarrolloavanzadoenAndroid MiguelÁngelLozano,BoyánBonev,PabloSuau,JuanManuelSáez {malozano,boyan,pablo,jmsaez}@dccia.ua.es MobileVisionResearchLab Dep.CienciadelaComputacióneInteligenciaArtificial InstitutoUniversitariodeInvestigaciónenInformática UniversidaddeAlicante