2011-01-30 7 views
12

Tengo un código de procesamiento de imágenes Java en Android que actúa sobre dos matrices int grandes. La mayoría de las veces, Java es lo suficientemente rápido, pero necesito usar C a través de JNI y NDK para acelerar algunas operaciones.Envío de int [] s entre Java y C

La única forma en que sé que puedo pasar los datos de las matrices en int a C es usar ByteBuffer.allocateDirect para crear un nuevo búfer, copiar los datos a eso y luego hacer que el código C actúe sobre el búfer.

Sin embargo, no veo ninguna manera de manipular los datos en este búfer en Java como si el búfer fuera un int [] o un byte []. Por ejemplo, una llamada a ByteBuffer.array() fallará en el búfer recién creado. ¿Hay alguna manera de hacer funcionar esto?

Tengo memoria limitada y quiero reducir la cantidad de matrices/almacenamientos intermedios que necesito. Por ejemplo, sería bueno si pudiera usar IntBuffer.wrap (new int [...]) para crear el búfer y luego manipular la matriz que respalda el búfer directamente en Java, pero no puedo hacer esto porque lo único que parece trabajar aquí para JNI es ByteBuffer.allocateDirect.

¿Hay alguna otra forma de enviar y recibir datos entre C y Java? ¿Puedo de alguna manera asignar memoria en el lado C y hacer que Java envíe datos directamente a allí?

Editar: Un punto de referencia que compara el uso de tampón a int [] uso:

int size = 1000; 
IntBuffer allocateDirect = java.nio.ByteBuffer.allocateDirect(4 * size).asIntBuffer(); 
for (int i = 0; i < 100; ++i) 
{ 
    for (int x = 0; x < size; ++x) 
    { 
    int v = allocateDirect.get(x); 
    allocateDirect.put(x, v + 1); 
    } 
} 

int[] intArray = new int[size]; 
for (int i = 0; i < 100; ++i) 
{ 
    for (int x = 0; x < size; ++x) 
    { 
    int v = intArray[x]; 
    intArray[x] = v + 1; 
    } 
} 

En un teléfono Droid, la versión tampón toma ~ 10 segundos para terminar y la versión de matriz toma ~ 0.01 segundos.

+1

Espera, ¿qué pasa con [ 'ByteBuffer.asIntBuffer'] (http://developer.android.com/reference/java/nio/ByteBuffer.html#asIntBuffer%28%29)? ¿De verdad necesitas tener un 'int []'? – ephemient

+0

... y, además, puede llamar .array() en el IntBuffer si así lo desea. – mpontillo

+0

.array() arroja una excepción no admitida para ByteBuffer e IntBuffer para las matrices directamente asignadas en Java. Realmente quiero acceder a un int [] para poder hacer algunas de las tareas de procesamiento de imágenes menos intensivas en Java, p. Ej. convierta una imagen a blanco y negro verificando qué píxeles están por encima/debajo de un umbral de brillo. Usar IntBuffer get/put para cada píxel sería demasiado lento en Android, además de torpe. – rbcc

Respuesta

17

De http://java.sun.com/docs/books/jni/html/objtypes.html, el uso de JNI Get/Release<TYPE>ArrayElements(...)

En este ejemplo, voy a pasar una matriz (por el argumento, es int array = new int[10] y luego llenarlo con 0-9

JNIEXPORT jint JNICALL 
Java_IntArray_doStuffArray(JNIEnv *env, jobject obj, jintArray arr) 
{ 

    // initializations, declarations, etc 
    jint *c_array; 
    jint i = 0; 

    // get a pointer to the array 
    c_array = (*env)->GetIntArrayElements(env, arr, NULL); 

    // do some exception checking 
    if (c_array == NULL) { 
     return -1; /* exception occurred */ 
    } 

    // do stuff to the array 
    for (i=0; i<10; i++) { 
     c_array[i] = i; 
    } 

    // release the memory so java can have it again 
    (*env)->ReleaseIntArrayElements(env, arr, c_array, 0); 

    // return something, or not.. it's up to you 
    return 0; 
} 

estudio de la sección 3.3, y en concreto 3.3.2 - esto le permitirá obtener un puntero a la matriz en la memoria de Java, modificarlo, y lo liberan, en efecto, que le permite modificar la matriz en código nativo .

solo lo he usado en mi propio proyecto (con matrices cortas) y funciona muy bien :)

+0

gracias este código me ayudó a conectarme al método de biblioteca siglib para los filtros FIR – JPM

+2

para obtener el recuento int arrayLength = (* env) -> GetArrayLength (env, arr); –

+0

Cuando intento esto, el Java doStuffArray (arr) regresa antes de que el Java_IntArray_doStuffArray nativo se complete. ¿Qué debo hacer para bloquear el lado de Java hasta que se complete el lado nativo? –

3

Puede usar la devolución de llamada para enviar los datos de la capa nativa a Java.

En la capa de Java: en mi clase nativa tengo siguientes métodos:

//Native method 
public native String getStrData(int size); 

//Callback method 
public void addData(char[] native_data, int size) { 

    ... 

} 

en la capa nativa: en mi aplicación nativa:

JNIEXPORT jstring JNICALL Java_com_pkg_NativeClass_getStrData 
    (JNIEnv *env, jobject obj, jint size) { 
    ... 

    jclass native_class;   /* Callback: native class */ 
    jmethodID native_method_id; /* Callback: native method id */ 
    jcharArray row;    /* Callback: native data */ 

    ... 

    /* Start Callback: Native to Java */ 
    native_class = (*env)->GetObjectClass(env, obj); 
    native_method_id = (*env)->GetMethodID(env, native_class, "addData", "([CI)V"); 
    if (native_method_id == 0) { 
     return (jstring)ERR_NATIVE_METHODID; 
    } 
    row = (jcharArray)(*env)->NewCharArray(env, size); 
    /* jc has the data to be sent to Java */ 
    (*env)->SetCharArrayRegion(env, (jcharArray)row, (jsize)0, size, (jchar *)jc); 

    (*env)->CallVoidMethod(env, obj, native_method_id, row, size); 
    /* End Callback */ 

    ... 
} 
+0

¿Podría por favor mencione, ¿qué tipo es 'jc'? ¿Es 'char * jc'? –

+0

@dma_k: En mi caso, 'jc' era una matriz de tamaño conocido. – TheCottonSilk

+0

¿Podría darnos una pista, cómo funciona con 'jc' (por ejemplo, establecer los datos del carácter)? Miro aquí y allá ejemplos de cómo usar 'SetCharArrayRegion()', pero no puedo encontrar un ejemplo completo de cómo crear/inicializar una matriz de 'jchar's (por ejemplo, de ASCII cadena de 1 byte,' char * '). Gracias –

5

Si está utilizando buffers asignados directos, se puede acceder a la matriz de soporte directamente desde C, utilizando el GetDirectBufferAddress función. Esto evita la posibilidad de copiar regiones del área.

Puede operar en la dirección devuelta directamente como lo haría con una matriz C normal, y modificará directamente el búfer asignado directamente de Java.

Luego, como estados ephemient, puede usar ByteBuffer.asIntBuffer() y su familia para acceder al búfer de forma que emule las matrices de las distintas primitivas de Java.

http://download.oracle.com/javase/1.4.2/docs/guide/jni/jni-14.html