Tengo una aplicación que toma una galería de imágenes (todas en Jpeg) y da puntajes de similitud entre cada posible pareja. En todo momento, solo se puede seleccionar un par y se muestra su puntuación de similitud.¿Una forma barata/rápida de hash Bitmaps?
El algoritmo que compara las dos imágenes tiene un cierto costo de rendimiento, por lo que se tarda unos segundos en comparar un par.
Cuando dos fotos son seleccionadas:
- Si la pareja nunca ha sido comparado, el resultado muestra "No anotó todavía.". El usuario puede hacer clic en el botón "Puntuación" y el par se enviará a un hilo que pondrá en cola los puntajes que se computarán. Ejemplo: http://db.tt/gb1Yk6yx
- Si el par está actualmente en la cola para ser computado, el campo de puntaje muestra "Computando ...". Ejemplo: http://db.tt/OvS1qGP3
- Si se ha comparado el par, se muestra la puntuación adjunta al par. Ejemplo: http://db.tt/m2OQGybW
ejemplo (al hacer un lote): http://db.tt/iD67SdCp
Si la puntuación nunca ha sido calculada, y un usuario haga clic en "Cuenta", el campo cambiará a "Informática ...", entonces mostrará el puntaje cuando el cálculo se complete.
Antes de mostrar algo en el campo del puntaje, cuando se seleccionan dos pares, sus mapas de bits adjuntos se envían a un HashMap que verifica si esos dos mapas de bits ya tienen un puntaje adjunto, en cuyo caso simplemente lo devuelve. Si no hay puntaje, el trabajo se envía en la cola.
Para saber si la puntuación existe en la memoria caché, necesito encontrar una manera de hash la pareja para que pueda usar la clave resultante para buscar la memoria caché. Ahí es donde está mi problema. Para tener sentido, el hash de los dos mapas de bits debe ser rápido. De lo contrario, solo estoy agregando otra capa de computación. Pero, la forma en que hago hasta ahora para criticar los dos mapas de bits es enviarlos en una matriz de bytes y obtener su suma de comprobación MD5. De esta manera:
private Long getHashKey(Bitmap first, Bitmap second){
// TODO this IS costly, it render useless the cache optimization.
// also, it doesn't detect that comp(A,B) is the same as comp(B,A).
// much work to do here.
if(D) Profiling.start(TAG, "getHashKey");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
first.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] firstArray = stream.toByteArray();
second.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] secondArray = stream.toByteArray();
byte[] bitmapBuffer = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, bitmapBuffer, 0, firstArray.length);
System.arraycopy(secondArray, 0, bitmapBuffer,
firstArray.length, secondArray.length);
Adler32 md5Hash = new Adler32();
md5Hash.update(bitmapBuffer);
long hashKey = md5Hash.getValue();
if(D) Profiling.stop();
return hashKey;
}
Sin embargo, este método, de acuerdo con el perfilado lo hice, costará alrededor de 53 ms para funcionar, lo que provoca un retraso en la interfaz de usuario que es bastante desagradable. En un perfil más detallado, descubrí que aproximadamente el 95% del tiempo de computación se realiza en los métodos compress
. Sin embargo, no he encontrado otra forma de obtener los bytes que respaldan los mapas de bits.
05-26 17:56:13.220: D/Profiling(9458): Profile for ImageCompareActivity.getHashKey:
05-26 17:56:13.220: D/Profiling(9458): > Count : 1996 calls
05-26 17:56:13.220: D/Profiling(9458): > Total runtime : 105765140 us
05-26 17:56:13.220: D/Profiling(9458): > Avg runtime : 52988 us
Sé que mi camino a hash the Bitmap es bastante bruto. Pero no sé mucho acerca de las funciones hash, y qué partes de un mapa de bits podría utilizar para identificar de manera única los archivos. No quiero usar el nombre del archivo o algo así, ya que quiero enviar esos mapas de bits en una base de datos con el tiempo.
[Actualización 1] No sabía acerca de Object.hashCode(). Ahora, he modificado el método de esta manera:
private Integer getHashKey(Bitmap first, Bitmap second){
if(D) Profiling.start(TAG, "getHashKey");
Integer hashKey = new Integer(
1013 * (first.hashCode())^1009 * (second.hashCode()));
if(D) Profiling.stop();
return hashKey;
}
Lo que se ejecuta en promedio por unos 18 us.
¿Podría usar Bitmap.getPixels? Devuelve una matriz de enteros (bueno, de hecho rellena una matriz de entradas que pasas, pero ¿qué ocurre entre amigos?). – Iain
¿Por qué no utiliza el nombre del archivo mientras usa los archivos para almacenar los mapas de bits y la clave principal de la fila (o un indicador en la base de datos) una vez que usa la base de datos para almacenar los mapas de bits? –
Mire en el método 'copyPixelsToBuffer' que acepta un' ByteBuffer'. Además, JB es perfecto; ¿Alguna razón por la que no quieres usar nombres de archivos? –