performance aplicaciones - ¿La forma más eficiente de cambiar el tamaño de bitmaps en Android?




pdf manual (3)

Estoy construyendo una aplicación social con gran cantidad de imágenes donde las imágenes se envían desde el servidor al dispositivo. Cuando el dispositivo tiene resoluciones de pantalla más pequeñas, necesito cambiar el tamaño de los mapas de bits, en el dispositivo, para que coincidan con los tamaños de visualización previstos.

El problema es que usar createScaledBitmap hace que me encuentre con una gran cantidad de errores de memoria después de cambiar el tamaño de una horda de imágenes en miniatura.

¿Cuál es la forma más eficiente de cambiar el tamaño de mapas de bits en Android?


Answers

Tan agradable (y preciso) como es esta respuesta, también es muy complicado. En lugar de reinventar la rueda, considere bibliotecas como Glide , Picasso , UIL , Ion o cualquier otra que implemente esta lógica compleja y propensa a errores.

El propio Colt incluso recomienda echar un vistazo a Glide y Picasso en el video Pre-scaling Bitmaps Performance Patterns .

Al utilizar las bibliotecas, puede obtener toda la eficiencia mencionada en la respuesta de Colt, pero con API mucho más simples que funcionan consistentemente en todas las versiones de Android.


Esta respuesta se resume a partir de la carga eficiente de mapas de bits grandes, que explica cómo usar inSampleSize para cargar una versión de mapa de bits a escala reducida.

En particular , los mapas de bits previos a la escala explican los detalles de varios métodos, cómo combinarlos y cuáles son los más eficientes en cuanto a la memoria.

Hay tres formas dominantes de cambiar el tamaño de un mapa de bits en Android que tienen diferentes propiedades de memoria:

createScaledBitmap

Esta API incluirá un mapa de bits existente y creará un NUEVO mapa de bits con las dimensiones exactas que haya seleccionado.

En el lado positivo, puedes obtener exactamente el tamaño de imagen que estás buscando (independientemente de cómo se vea). Pero la desventaja es que esta API requiere un mapa de bits existente para funcionar . Lo que significa que la imagen debería cargarse, decodificarse y crearse un mapa de bits antes de poder crear una nueva versión más pequeña. Esto es ideal en términos de obtener sus dimensiones exactas, pero horrible en términos de sobrecarga de memoria adicional. Como tal, este es un factor decisivo para la mayoría de los desarrolladores de aplicaciones que tienden a ser conscientes de la memoria

Indicador inSampleSize

BitmapFactory.Options tiene una propiedad que se indica como inSampleSize que cambiará el tamaño de su imagen al decodificarla, para evitar la necesidad de descodificar a un mapa de bits temporal. Este valor entero utilizado aquí cargará una imagen en un tamaño reducido 1 / x. Por ejemplo, establecer inSampleSize en 2 devuelve una imagen que tiene la mitad del tamaño, y al establecerla en 4 se obtiene una imagen de 1/4 del tamaño. Básicamente, el tamaño de las imágenes siempre será una potencia-de-dos más pequeña que el tamaño de su fuente.

Desde la perspectiva de la memoria, usar inSampleSize es una operación realmente rápida. Efectivamente, solo decodificará cada X pixel de su imagen en su mapa de bits resultante. Sin embargo, hay dos problemas principales con inSampleSize :

  • No te da resoluciones exactas . Solo disminuye el tamaño de tu mapa de bits con una potencia de 2.

  • No produce el mejor tamaño de calidad . La mayoría de los filtros de cambio de tamaño producen imágenes atractivas al leer bloques de píxeles y luego ponderarlos para producir el píxel redimensionado en cuestión. inSampleSize evita todo esto simplemente leyendo cada pocos píxeles. El resultado es bastante eficiente y baja memoria, pero la calidad sufre.

Si solo está tratando de reducir su imagen en algún tamaño de pow2, y el filtrado no es un problema, entonces no puede encontrar un método más eficiente en cuanto a la memoria (o rendimiento eficiente) que en inSampleSize .

Banderas inScaled, inDensity, inTargetDensity

Si necesita escalar una imagen a una dimensión que no es igual a una potencia de dos, necesitará los inScaled , inDensity y inTargetDensity de BitmapOptions . Cuando se ha establecido el indicador inScaled , el sistema derivará el valor de escala para aplicarlo a su mapa de bits dividiendo la inTargetDensity entre los valores de inDensity .

mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
      mImageIDs, mBitmapOptions);

El uso de este método cambiará el tamaño de su imagen y también le aplicará un "filtro de cambio de tamaño", es decir, el resultado final se verá mejor porque se han tenido en cuenta algunas operaciones matemáticas adicionales durante el paso de cambio de tamaño. Pero tenga cuidado: ese paso adicional de filtro, requiere un tiempo de procesamiento adicional y puede sumarse rápidamente para imágenes grandes, lo que da como resultado un cambio de tamaño lento y asignaciones adicionales de memoria para el filtro en sí.

En general, no es una buena idea aplicar esta técnica a una imagen que es significativamente más grande que su tamaño deseado, debido a la sobrecarga de filtrado adicional.

Combinación mágica

Desde la perspectiva de la memoria y el rendimiento, puede combinar estas opciones para obtener los mejores resultados. (configuración de los inSampleSize , inScaled , inDensity y inTargetDensity )

inSampleSize aplicará primero a la imagen, y se inSampleSize a la siguiente potencia de dos MÁS GRANDE que su tamaño objetivo. Luego, inDensity y inTargetDensity se usan para escalar el resultado a las dimensiones exactas que desee, aplicando una operación de filtro para limpiar la imagen.

La combinación de estos dos es una operación mucho más rápida, ya que el paso inSampleSize reducirá el número de píxeles que necesitará el paso resultante basado en densidad para aplicar su filtro de cambio de tamaño.

mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth * mBitmapOptions.inSampleSize;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);

Si necesita ajustar una imagen a dimensiones específicas y un mejor filtrado, esta técnica es el mejor puente para obtener el tamaño correcto, pero se realiza en una operación de huella rápida y con poca memoria.

Obtener dimensiones de imagen

Cómo obtener el tamaño de la imagen sin decodificar toda la imagen Para cambiar el tamaño de su mapa de bits, necesitará conocer las dimensiones entrantes. Puede usar el indicador inJustDecodeBounds para ayudarlo a obtener las dimensiones de la imagen, sin necesidad de decodificar realmente los datos de píxeles.

// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;


//now go resize the image to the size you want

Puede usar este indicador para decodificar primero el tamaño y luego calcular los valores adecuados para escalar a su resolución objetivo.


En el Android se le da una función de intercalación llamada LOCALIZADA.

Cuando especifique su columna, haga lo siguiente:

CREAR TABLA canción (..., título COLOCACIÓN DE TEXTO ASC, LOCALIZADO, ...)

Funciona muy bien Si tiene el código fuente de Android, simplemente haga una búsqueda de "COLLATE" para ver todo tipo de ejemplos.





android performance bitmap out-of-memory