Tengo un mapa de bits grande (por ejemplo, 3888x2592) en un archivo. Ahora, quiero cambiar el tamaño de ese mapa de bits a 800x533 y guardarlo en otro archivo. Normalmente escalaría el mapa de bits llamando al Bitmap.createBitmap
método, pero necesita un mapa de bits de origen como primer argumento, que no puedo proporcionar porque la carga de la imagen original en un objeto de mapa de bits, por supuesto, excedería la memoria (ver aquí , por ejemplo).
Tampoco puedo leer el mapa de bits con, por ejemplo, BitmapFactory.decodeFile(file, options)
proporcionar un BitmapFactory.Options.inSampleSize
, porque quiero cambiar su tamaño a un ancho y alto exactos. El uso inSampleSize
cambiaría el tamaño del mapa de bits a 972x648 (si lo uso inSampleSize=4
) o a 778x518 (si lo uso inSampleSize=5
, lo que ni siquiera es una potencia de 2).
También me gustaría evitar leer la imagen usando inSampleSize con, por ejemplo, 972x648 en un primer paso y luego cambiar su tamaño exactamente a 800x533 en un segundo paso, porque la calidad sería pobre en comparación con un cambio de tamaño directo de la imagen original.
Para resumir mi pregunta: ¿Hay alguna forma de leer un archivo de imagen grande con 10MP o más y guardarlo en un nuevo archivo de imagen, redimensionado a un nuevo ancho y alto específico, sin obtener una excepción OutOfMemory?
También intenté BitmapFactory.decodeFile(file, options)
y configuré manualmente los valores Options.outHeight y Options.outWidth a 800 y 533, pero no funciona de esa manera.
Respuestas:
No. Me encantaría que alguien me corrigiera, pero acepté el enfoque de carga / cambio de tamaño que intentaste como un compromiso.
Estos son los pasos para cualquiera que esté navegando:
inSampleSize
que aún produce una imagen más grande que su objetivo.BitmapFactory.decodeFile(file, options)
, pasando inSampleSize como una opción.Bitmap.createScaledBitmap()
.fuente
Respuesta de Justin traducida al código (funciona perfecto para mí):
fuente
System.gc()
por favorEstas son las soluciones 'combinadas' de 'Mojo Risin y' Ofir. Esto le dará una imagen redimensionada proporcionalmente con los límites de ancho máximo y alto máximo.
Para mí, ha funcionado bien en 5 imágenes de MegaPixel como se muestra a continuación.
fuente
¿Por qué no usar la API?
fuente
Reconociendo la otra excelente respuesta hasta ahora, el mejor código que he visto hasta ahora está en la documentación de la herramienta para tomar fotos.
Consulte la sección titulada "Decodificar una imagen a escala".
http://developer.android.com/training/camera/photobasics.html
La solución que propone es una solución de cambio de tamaño y luego escala como las otras aquí, pero es bastante clara.
Copié el código a continuación como una función lista para usar para mayor comodidad.
fuente
Después de leer estas respuestas y la documentación de Android, aquí está el código para cambiar el tamaño del mapa de bits sin cargarlo en la memoria:
fuente
Cuando tengo mapas de bits grandes y quiero decodificarlos, utilizo lo siguiente
fuente
Esto puede ser útil para alguien más mirando esta pregunta. Reescribí el código de Justin para permitir que el método reciba el objeto de tamaño objetivo requerido también. Esto funciona muy bien cuando se usa Canvas. Todo el crédito debe ir a JUSTIN por su gran código inicial.
El código de Justin es MUY efectivo para reducir la sobrecarga de trabajar con mapas de bits grandes.
fuente
No sé si mi solución es la mejor práctica, pero logré cargar un mapa de bits con mi escala deseada utilizando las opciones
inDensity
yinTargetDensity
.inDensity
es0
inicialmente cuando no se carga un recurso dibujable, por lo que este enfoque es para cargar imágenes sin recursos.Las variables
imageUri
,maxImageSideLength
ycontext
son parámetros de mi método. Publiqué solo la implementación del método sin el AsyncTask de ajuste para mayor claridad.fuente
Teniendo en cuenta que desea cambiar el tamaño al tamaño exacto y desea mantener tanta calidad como sea necesario, creo que debería intentar esto.
Motivación: el escalado de múltiples pasos podría proporcionarle una imagen de mayor calidad, sin embargo, no hay garantía de que funcione mejor que usar inSampleSize alto. En realidad, creo que también puede usar inSampleSize como 5 (no Pow de 2) para tener una escala directa en una operación. O simplemente use 4 y luego puede usar esa imagen en la interfaz de usuario. si lo envía al servidor, puede escalar al tamaño exacto en el lado del servidor, lo que le permite utilizar técnicas de escalado avanzadas.
Notas: si el mapa de bits cargado en el paso 3 es al menos 4 veces más grande (por lo que 4 * targetWidth <ancho), probablemente pueda usar varios cambios de tamaño para lograr una mejor calidad. al menos eso funciona en java genérico, en android no tienes la opción de especificar la interpolación utilizada para escalar http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getscaledinstance.html
fuente
Usé un código como este:
Intenté que la imagen original sea 1230 x 1230, y obtuve un mapa de bits que dice que es 330 x 330.
Y si probé 2590 x 3849, obtendré OutOfMemoryError.
Lo rastreé, todavía arroja OutOfMemoryError en la línea "BitmapFactory.decodeStream (es, nulo, opciones);", si el mapa de bits original es demasiado grande ...
fuente
El código anterior hizo un poco más limpio. InputStreams finalmente tiene una envoltura cerrada para garantizar que también se cierren:
* Nota de
entrada: InputStream es, int w, int h
Salida: mapa de bits
fuente
Para escalar la imagen de la manera "correcta", sin omitir ningún píxel, tendría que conectar el decodificador de imágenes para realizar el muestreo descendente fila por fila. Android (y la biblioteca de Skia que lo subyace) no proporciona tales ganchos, por lo que tendrías que rodar el tuyo. Suponiendo que está hablando de imágenes jpeg, su mejor opción sería usar libjpeg directamente, en C.
Dadas las complejidades involucradas, usar la submuestra de dos pasos y luego reescalar es probablemente el mejor para las aplicaciones de tipo de vista previa de imagen.
fuente
Aquí hay un artículo que toma un enfoque diferente para cambiar el tamaño. Intentará cargar el mapa de bits más grande posible en la memoria en función de la memoria disponible en el proceso y luego realizará las transformaciones.
http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/
fuente
Si realmente desea cambiar el tamaño de un paso, probablemente pueda cargar un mapa de bits completo si android: largeHeap = true, pero como puede ver, esto no es realmente recomendable.
De documentos: android: largeHeap Si los procesos de su aplicación deben crearse con un gran montón de Dalvik. Esto se aplica a todos los procesos creados para la aplicación. Solo se aplica a la primera aplicación cargada en un proceso; Si está utilizando una ID de usuario compartida para permitir que varias aplicaciones utilicen un proceso, todas deben usar esta opción de manera consistente o tendrán resultados impredecibles. La mayoría de las aplicaciones no deberían necesitar esto, sino que deberían centrarse en reducir el uso general de la memoria para mejorar el rendimiento. Habilitar esto tampoco garantiza un aumento fijo en la memoria disponible, porque algunos dispositivos están limitados por su memoria total disponible.
fuente
Hay un gran artículo sobre este problema exacto en el sitio web para desarrolladores de Android: Carga eficiente de mapas de bits grandes
fuente
Esto funcionó para mí. La función obtiene una ruta a un archivo en la tarjeta SD y devuelve un mapa de bits en el tamaño máximo visualizable. El código es de Ofir con algunos cambios como el archivo de imagen en sd en lugar de un Ressource y el ingenio y la altura se obtienen del objeto de visualización.
fuente
Aquí está el código que uso que no tiene ningún problema para decodificar imágenes grandes en la memoria de Android. He podido decodificar imágenes de más de 20 MB siempre que mis parámetros de entrada sean de 1024x1024. Puede guardar el mapa de bits devuelto en otro archivo. Debajo de este método hay otro método que también uso para escalar imágenes a un nuevo mapa de bits. Siéntase libre de usar este código como lo desee.
NOTA: Los métodos no tienen nada que ver entre sí, excepto createScaledBitmap invoca el método de decodificación anterior. El ancho y la altura de la nota pueden cambiar de la imagen original.
fuente
o:
fuente
yo suelo
Integer.numberOfLeadingZeros
para calcular el mejor tamaño de muestra, mejor rendimiento.Código completo en kotlin:
fuente
Cambiar el tamaño del mapa de bits con el siguiente código
Lo mismo también se explica en el siguiente consejo / truco
http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory
fuente