studio redimensionar android image resize bitmap

redimensionar - Cambiar el tamaño de un archivo de mapa de bits grande a un archivo de salida escalado en Android



resize image android studio (20)

Tengo un gran mapa de bits (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 Bitmap.createBitmap el mapa de bits llamando Bitmap.createBitmap método Bitmap.createBitmap pero necesita un mapa de bits de origen como primer argumento, que no puedo proporcionar porque cargar la imagen original en un objeto de mapa de bits, por supuesto, excederá la memoria (vea here , por ejemplo, ).

Tampoco puedo leer el mapa de bits con, por ejemplo, BitmapFactory.decodeFile(file, options) , proporcionando un BitmapFactory.Options.inSampleSize , porque quiero cambiar el tamaño a un ancho y alto exactos. Usar inSampleSize cambiaría el tamaño del mapa de bits a 972x648 (si uso inSampleSize=4 ) o a 778x518 (si uso inSampleSize=5 , que no es ni siquiera 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 a exactamente 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 una manera 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 de OutOfMemory?

También probé BitmapFactory.decodeFile(file, options) y configuré los valores de Options.outHeight y Options.outWidth manualmente a 800 y 533, pero no funciona de esa manera.


¿Por qué no usar la API?

int h = 48; // height in pixels int w = 48; // width in pixels Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);


Aquí está el código que utilizo, 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 y cuando mis parámetros de entrada sean de aproximadamente 1024x1024. Puede guardar el mapa de bits devuelto a otro archivo. Debajo de este método hay otro método que también utilizo para escalar imágenes a un nuevo mapa de bits. Siéntase libre de usar este código como desee.

/***************************************************************************** * public decode - decode the image into a Bitmap * * @param xyDimension * - The max XY Dimension before the image is scaled down - XY = * 1080x1080 and Image = 2000x2000 image will be scaled down to a * value equal or less then set value. * @param bitmapConfig * - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444, * Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 ) * * @return Bitmap - Image - a value of "null" if there is an issue decoding * image dimension * * @throws FileNotFoundException * - If the image has been removed while this operation is * taking place */ public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException { // The Bitmap to return given a Uri to a file Bitmap bitmap = null; File file = null; FileInputStream fis = null; InputStream in = null; // Try to decode the Uri try { // Initialize scale to no real scaling factor double scale = 1; // Get FileInputStream to get a FileDescriptor file = new File( this.imageUri.getPath() ); fis = new FileInputStream( file ); FileDescriptor fd = fis.getFD(); // Get a BitmapFactory Options object BitmapFactory.Options o = new BitmapFactory.Options(); // Decode only the image size o.inJustDecodeBounds = true; o.inPreferredConfig = bitmapConfig; // Decode to get Width & Height of image only BitmapFactory.decodeFileDescriptor( fd, null, o ); BitmapFactory.decodeStream( null ); if( o.outHeight > xyDimension || o.outWidth > xyDimension ) { // Change the scale if the image is larger then desired image // max size scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) ); } // Decode with inSampleSize scale will either be 1 or calculated value o.inJustDecodeBounds = false; o.inSampleSize = (int) scale; // Decode the Uri for real with the inSampleSize in = new BufferedInputStream( fis ); bitmap = BitmapFactory.decodeStream( in, null, o ); } catch( OutOfMemoryError e ) { Log.e( DEBUG_TAG, "decode : OutOfMemoryError" ); e.printStackTrace(); } catch( NullPointerException e ) { Log.e( DEBUG_TAG, "decode : NullPointerException" ); e.printStackTrace(); } catch( RuntimeException e ) { Log.e( DEBUG_TAG, "decode : RuntimeException" ); e.printStackTrace(); } catch( FileNotFoundException e ) { Log.e( DEBUG_TAG, "decode : FileNotFoundException" ); e.printStackTrace(); } catch( IOException e ) { Log.e( DEBUG_TAG, "decode : IOException" ); e.printStackTrace(); } // Save memory file = null; fis = null; in = null; return bitmap; } // decode

NOTA: Los métodos no tienen nada que ver entre sí, excepto el método de decodificación de llamadas createScaledBitmap anterior. La anchura y la altura de la nota pueden cambiar de la imagen original.

/***************************************************************************** * public createScaledBitmap - Creates a new bitmap, scaled from an existing * bitmap. * * @param dstWidth * - Scale the width to this dimension * @param dstHeight * - Scale the height to this dimension * @param xyDimension * - The max XY Dimension before the original image is scaled * down - XY = 1080x1080 and Image = 2000x2000 image will be * scaled down to a value equal or less then set value. * @param bitmapConfig * - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444, * Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 ) * * @return Bitmap - Image scaled - a value of "null" if there is an issue * */ public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig ) { Bitmap scaledBitmap = null; try { Bitmap bitmap = this.decode( xyDimension, bitmapConfig ); // Create an empty Bitmap which will contain the new scaled bitmap // This scaled bitmap should be the size we want to scale the // original bitmap too scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig ); float ratioX = dstWidth / (float) bitmap.getWidth(); float ratioY = dstHeight / (float) bitmap.getHeight(); float middleX = dstWidth / 2.0f; float middleY = dstHeight / 2.0f; // Used to for scaling the image Matrix scaleMatrix = new Matrix(); scaleMatrix.setScale( ratioX, ratioY, middleX, middleY ); // Used to do the work of scaling Canvas canvas = new Canvas( scaledBitmap ); canvas.setMatrix( scaleMatrix ); canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) ); } catch( IllegalArgumentException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" ); e.printStackTrace(); } catch( NullPointerException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" ); e.printStackTrace(); } catch( FileNotFoundException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" ); e.printStackTrace(); } return scaledBitmap; } // End createScaledBitmap



Cambia el tamaño del mapa de bits usando el siguiente código

public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){ // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getPath(), options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(file.getPath(), options); } private static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; }

Lo mismo se explica también en el siguiente consejo / truco.

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory


Cuando tengo grandes mapas de bits y quiero decodificarlos, los redimensiono, uso lo siguiente

BitmapFactory.Options options = new BitmapFactory.Options(); InputStream is = null; is = new FileInputStream(path_to_file); BitmapFactory.decodeStream(is,null,options); is.close(); is = new FileInputStream(path_to_file); // here w and h are the desired width and height options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h); // bitmap is the resized bitmap Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);


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:

public Bitmap getResizedBitmap(int targetW, int targetH, String imagePath) { // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); //inJustDecodeBounds = true <-- will not load the bitmap into memory bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); return(bitmap); }


El código anterior hizo un poco más limpio. InputStreams finalmente ha cerrado la envoltura para asegurar que también se cierren:

*Nota
Entrada: InputStream es, int w, int h
Salida: Bitmap

try { final int inWidth; final int inHeight; final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp"); { final FileOutputStream tempOut = new FileOutputStream(tempFile); StreamUtil.copyTo(is, tempOut); tempOut.close(); } { final InputStream in = new FileInputStream(tempFile); final BitmapFactory.Options options = new BitmapFactory.Options(); try { // decode image size (decode metadata only, not the whole image) options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); } finally { in.close(); } // save width and height inWidth = options.outWidth; inHeight = options.outHeight; } final Bitmap roughBitmap; { // decode full image pre-resized final InputStream in = new FileInputStream(tempFile); try { final BitmapFactory.Options options = new BitmapFactory.Options(); // calc rought re-size (this is no exact resize) options.inSampleSize = Math.max(inWidth/w, inHeight/h); // decode full image roughBitmap = BitmapFactory.decodeStream(in, null, options); } finally { in.close(); } tempFile.delete(); } float[] values = new float[9]; { // calc exact destination size Matrix m = new Matrix(); RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); RectF outRect = new RectF(0, 0, w, h); m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); m.getValues(values); } // resize bitmap final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); return resizedBitmap; } catch (IOException e) { logger.error("Error:" , e); throw new ResourceException("could not create bitmap"); }


Estas son las soluciones de ''Mojo Risin y'' Ofir "combinadas". Esto le dará una imagen de tamaño proporcional con los límites de ancho máximo y altura máxima.

  1. Solo lee metadatos para obtener el tamaño original (options.inJustDecodeBounds)
  2. Utiliza un tamaño de rought para ahorrar memoria (itmap.createScaledBitmap)
  3. Utiliza una imagen de tamaño exacto basada en el bitamp en bruto creado anteriormente.

Para mí, ha estado funcionando bien en 5 imágenes de MegaPixel más abajo.

try { int inWidth = 0; int inHeight = 0; InputStream in = new FileInputStream(pathOfInputImage); // decode image size (decode metadata only, not the whole image) BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); in = null; // save width and height inWidth = options.outWidth; inHeight = options.outHeight; // decode full image pre-resized in = new FileInputStream(pathOfInputImage); options = new BitmapFactory.Options(); // calc rought re-size (this is no exact resize) options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight); // decode full image Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options); // calc exact destination size Matrix m = new Matrix(); RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); RectF outRect = new RectF(0, 0, dstWidth, dstHeight); m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); float[] values = new float[9]; m.getValues(values); // resize bitmap Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); // save image try { FileOutputStream out = new FileOutputStream(pathOfOutputImage); resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out); } catch (Exception e) { Log.e("Image", e.getMessage(), e); } } catch (IOException e) { Log.e("Image", e.getMessage(), e); }


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 visible. El código es de Ofir con algunos cambios, como el archivo de imagen en sd, en lugar de un Recurso y el ancho y la altura se obtienen del Objeto Visualizado.

private Bitmap makeBitmap(String path) { try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP //resource = getResources(); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap pic = null; if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; pic = BitmapFactory.decodeFile(path, options); // resize to desired dimensions Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.y; int height = size.x; //int height = imageView.getHeight(); //int width = imageView.getWidth(); Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true); pic.recycle(); pic = scaledBitmap; System.gc(); } else { pic = BitmapFactory.decodeFile(path); } Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight()); return pic; } catch (Exception e) { Log.e("TAG", e.getMessage(),e); return null; } }


Esto puede ser útil para alguien que está mirando esta pregunta. Reescribí el código de Justin para permitir que el método también reciba el objeto de tamaño de destino requerido. Esto funciona muy bien cuando se usa Canvas. Todo el crédito debe ir a JUSTIN por su gran código inicial.

private Bitmap getBitmap(int path, Canvas canvas) { Resources resource = null; try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP resource = getResources(); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resource, path, options); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap pic = null; if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; pic = BitmapFactory.decodeResource(resource, path, options); // resize to desired dimensions int height = canvas.getHeight(); int width = canvas.getWidth(); Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true); pic.recycle(); pic = scaledBitmap; System.gc(); } else { pic = BitmapFactory.decodeResource(resource, path); } Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight()); return pic; } catch (Exception e) { Log.e("TAG", e.getMessage(),e); return null; } }

El código de Justin es MUY efectivo para reducir la sobrecarga de trabajar con mapas de bits grandes.



La respuesta de Justin traducida al código (funciona perfecto para mí):

private Bitmap getBitmap(String path) { Uri uri = getImageUri(path); InputStream in = null; try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP in = mContentResolver.openInputStream(uri); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap resultBitmap = null; in = mContentResolver.openInputStream(uri); if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; resultBitmap = BitmapFactory.decodeStream(in, null, options); // resize to desired dimensions int height = resultBitmap.getHeight(); int width = resultBitmap.getWidth(); Log.d(TAG, "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, (int) y, true); resultBitmap.recycle(); resultBitmap = scaledBitmap; System.gc(); } else { resultBitmap = BitmapFactory.decodeStream(in); } in.close(); Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + resultBitmap.getHeight()); return resultBitmap; } catch (IOException e) { Log.e(TAG, e.getMessage(),e); return null; }


No sé si mi solución es la mejor práctica, pero logré cargar un mapa de bits con la escala deseada utilizando las opciones inDensity e inTargetDensity . inDensity es 0 inicialmente cuando no se está cargando un recurso dibujable, por lo que este enfoque es para cargar imágenes sin recursos.

Las variables imageUri , maxImageSideLength y context son parámetros de mi método. Publiqué solo la implementación del método sin envolver AsyncTask para mayor claridad.

ContentResolver resolver = context.getContentResolver(); InputStream is; try { is = resolver.openInputStream(imageUri); } catch (FileNotFoundException e) { Log.e(TAG, "Image not found.", e); return null; } Options opts = new Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, opts); // scale the image float maxSideLength = maxImageSideLength; float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight); // do not upscale! if (scaleFactor < 1) { opts.inDensity = 10000; opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor); } opts.inJustDecodeBounds = false; try { is.close(); } catch (IOException e) { // ignore } try { is = resolver.openInputStream(imageUri); } catch (FileNotFoundException e) { Log.e(TAG, "Image not found.", e); return null; } Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts); try { is.close(); } catch (IOException e) { // ignore } return bitmap;


Para escalar la imagen de la manera "correcta", sin omitir ningún píxel, tendría que conectar el decodificador de imagen 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 hacer tuyos. Asumiendo que estás hablando de imágenes jpeg, lo mejor sería usar libjpeg directamente, en C.

Dadas las complejidades involucradas, el uso de dos pasos de subsample-then-reescale es probablemente el mejor para aplicaciones de tipo de vista previa de imagen.


Reconociendo la otra excelente respuesta hasta el momento, 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 "Descodificar una imagen escalada".

http://developer.android.com/training/camera/photobasics.html

La solución que propone es una solución para cambiar el tamaño y luego la escala como las otras aquí, pero es bastante clara.

He copiado el código a continuación como una función lista para usar para su comodidad.

private void setPic(String imagePath, ImageView destination) { int targetW = destination.getWidth(); int targetH = destination.getHeight(); // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); destination.setImageBitmap(bitmap); }


Si absolutamente quieres hacer un cambio de tamaño en un solo paso, probablemente puedas cargar el mapa de bits completo si android: largeHeap = true, pero como puedes ver, esto no es realmente recomendable.

De docs: 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 y deberían centrarse en reducir su uso general de 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.


Teniendo en cuenta que desea cambiar el tamaño al tamaño exacto y desea mantener la calidad necesaria, creo que debería intentarlo.

  1. Averigüe el tamaño de la imagen redimensionada con la llamada de BitmapFactory.decodeFile y proporcione el checkSizeOptions.inJustDecodeBounds
  2. Calcule el máximo tamaño de inSampleSize que puede usar en su dispositivo para no exceder la memoria. bitmapSizeInBytes = 2 * width * height; En general, para su imagen, inSampleSize = 2 estaría bien, ya que solo necesitará 2 * 1944x1296) = 4.8Mbб, que deben tener los pies en la memoria
  3. Utilice BitmapFactory.decodeFile con inSampleSize para cargar el Bitmap
  4. Escala el mapa de bits al tamaño exacto.

Motivación: la escala de pasos múltiples podría darle una imagen de mayor calidad, sin embargo, no hay garantía de que funcione mejor que el uso de un alto tamaño de muestra. En realidad, creo que también puede utilizar inSampleSize como 5 (no pow de 2) para tener una escala directa en una operación. O simplemente usa 4 y luego puedes usar esa imagen en la interfaz de usuario. si lo envía al servidor, entonces puede escalar hasta el tamaño exacto en el lado del servidor, lo que le permite utilizar técnicas avanzadas de escalado.

Notas: si el mapa de bits cargado en el paso 3 es al menos 4 veces más grande (por lo tanto, 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 tiene 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


Utilicé código como este:

String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg"; BitmapFactory.Options options=new BitmapFactory.Options(); InputStream is=new FileInputStream(filePath); BitmapFactory.decodeStream(is, null, options); is.close(); is=new FileInputStream(filePath); // here w and h are the desired width and height options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired... // bmp is the resized bitmap Bitmap bmp=BitmapFactory.decodeStream(is, null, options); is.close(); Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

Intenté que la imagen original es de 1230 x 1230, y el mapa de bits que se dice que es de 330 x 330
Y si lo intenté con 2590 x 3849, obtendré OutOfMemoryError.

Lo rastreé, aún lanza OutOfMemoryError en la línea "BitmapFactory.decodeStream (es, nulo, opciones);", si el mapa de bits original es demasiado grande ...


No. Me encantaría que alguien me corrigiera, pero acepté el enfoque de carga / cambio de tamaño que probó como un compromiso.

Aquí están los pasos para cualquiera que esté navegando:

  1. Calcule el máximo inSampleSize posible que aún produzca una imagen más grande que su objetivo.
  2. Cargue la imagen utilizando BitmapFactory.decodeFile(file, options) , pasando inSampleSize como una opción.
  3. Cambie el tamaño a las dimensiones deseadas utilizando Bitmap.createScaledBitmap() .

Bitmap yourBitmap; Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

o:

resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);