2011-01-08 10 views
8

Necesito escalar una imagen antes de crearla y quiero hacerlo solo si excede 1024 KB (por ejemplo).¿Cómo saber un tamaño de mapa de bits de InputStream antes de crear el mapa de bits?

Al hacer lo siguiente, puedo escalar la imagen, pero solo necesito escalar las que son más grandes que el tamaño dado.

Bitmap bmImg = null; 
InputStream is = url.openStream(); 
BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inSampleSize = 10; 
bmImg = BitmapFactory.decodeStream(is,null,opts); 

¿Cómo puedo obtener el tamaño del mapa de bits? (Estoy feliz de saber la cantidad de bytes, no el tamaño después de descomprimir).

Editar:

Estoy tratando esto:

BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inJustDecodeBounds = true; 
Bitmap bmImg=BitmapFactory.decodeStream(is,null,opts); 
Log.e("optwidth",opts.outWidth+""); 
Bitmap bmImg1 = BitmapFactory.decodeStream(is); 

La primera vez que utilizo el InputStream (es) para decodificar con los "inJustDecodeBounds" funciona bien y puedo conseguir la Dimensiones de mapa de bits El problema es que la segunda vez que lo uso para decodificar realmente la imagen, no se muestra ninguna imagen.

¿Qué estoy haciendo mal?

Respuesta

0

No se puede obtener el tamaño de una secuencia por su propia definición. Si lo desea, puede cargar la secuencia en una matriz de bytes, obtener el recuento de bytes a partir de eso. También puede usar BitmapFactory para decodificar directamente una matriz de bytes, de modo que resulte bastante útil.

InputStream in = ... 
ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
int i; 
while ((i = in.read()) != -1) { 
    bos.write(i); 
} 
byte[] byteArray = bos.toByteArray(); 
if (byteArray.length > SOME_VALUE) { 
    // do things here 
} 
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); 
+0

Esto realmente funciona, pero es mucho más lento. – sergi

-2

No se puede obtener el tamaño de un mapa de bits, simplemente no está integrado. Sin embargo, puede obtener el tamaño del contenido (un archivo), que es lo que probablemente deba hacer. Algo como esto debería funcionar.

int file_size = url.getContentLength(); 
+0

url es de qué tipo? No encuentro este método en java.net.URL – Vikas

+0

Este usuario se está refiriendo a URLConnection [ver aquí] (http://developer.android.com/reference/java/net/URLConnection.html). Pero este método realmente no se recomienda ... se basa en que el servidor proporcione un encabezado Content-Length válido (y este encabezado puede ser engañoso si los datos se comprimen, por ejemplo). – kwazi

2

Si conoce el formato del archivo, es posible que pueda leer el encabezado del archivo. Habitualmente, el encabezado contiene el tamaño y el formato de la imagen. Y afortunadamente para ti, estos son siempre los primeros bytes del archivo.

Es posible que no sea posible para ciertos tipos.

PNG, JPEG, BMP, TGA

acaba de leer acerca de la cabecera y algunos archivos puede que tenga que adivinar el ancho de primera. Por ejemplo, PNG no parece incluir widht/height, pero debería poder leer el ancho y adivinar una altura con el tamaño/relación de píxeles.

Editar: Lo malo, pensé que quería escalar usando la dimensión ... Sí ContentLenght debería ayudar, pero a veces devolverá 0 si el servidor no establece la dimensión correcta en el encabezado http. En ese caso, necesita saber el tamaño devuelto. Pero de todos modos, puedes guardar un bytearray y una vez que todo esté cargado, simplemente comprueba la longitud de la matriz. si es más grande que 1024kb, cambie el tamaño de la imagen. Y guárdalo o haz lo que quieras con él.

0

La mayoría de las transmisiones se consumen cuando lee de ellas. En su segundo bloque de código, consume la secuencia la primera vez como un mapa de bits. Cuando invocas decodeString por segunda vez, la transmisión está 'vacía' (solo si todos los datos ya han sido leídos).

La mejor respuesta probablemente sea la de monkjack, pero quizás con solo un pequeño código adicional para asegurarse de que no está leyendo un archivo gigantesco en la memoria.Recomiendo solo una verificación ya que la está transfiriendo a un búfer para el tamaño (es decir, detén en 10MB); No estoy seguro de qué tan bien manejará Android una excepción de falta de memoria.

+0

El problema es que la solución de Monkjack es mucho más lenta. Android simplemente se bloquea cuando trato de asignar una imagen demasiado grande. – sergi

2

Puede envolver is con new BufferedInputStream(is); y usar los métodos mark(int readlimit) y reset() para volver a leer la secuencia. Tendrá que decidir qué configurar readlimit para permitir que lea el encabezado completo, los enlaces provistos por Loïc deberían ayudar aquí.

BufferedInputStream bis = new BufferedInputStream(is); 
bis.mark(1024 * 1024); 
BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inJustDecodeBounds = true; 
Bitmap bmImg=BitmapFactory.decodeStream(bis,null,opts); 
Log.e("optwidth",opts.outWidth+""); 
bis.reset(); 
Bitmap bmImg1 = BitmapFactory.decodeStream(bis); 

He dejado de lado el manejo para el método reset() de error apropiado, se generará un IOException si han leído más de readlimit bytes de la transmisión.

+0

Estoy intentando esto y estoy obteniendo algunas imágenes aleatorias: java.io.IOException: Mark ha sido invalidado Lo obtengo en imágenes de 75KB, 120KB y no en imágenes de 45KB o 700KB .. – sergi

+0

@ sergi el parámetro readLimit debe ser igual a bis.available(), de modo que esa marca no se invalide mientras se estaba leyendo la secuencia para la primera decodificación. –

5

Soy novato, así que no puedo comentar la respuesta de monkjack directamente. La razón por la que su respuesta es tan lenta es porque está copiando un byte a la vez. Usar un buffer de incluso 1K mejorará significativamente el rendimiento.

InputStream in = getContentResolver().openInputStream(docUri); 

ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
int i; 
byte[] buffer = new byte[1024]; 
while ((i = in.read(buffer)) != -1) { 
    bos.write(buffer); 
} 
byte[] docbuffer = bos.toByteArray(); 
+0

@sergi: debe aceptar esta respuesta –

+1

Esto no funcionará si el propósito de la reducción a escala es evitar OutOfMemoryException. – Ken

0

Si la reducción de escala para evitar OutOfMemoryException como yo, esta solución puede funcionar mejor para usted (basándose en las respuestas proporcionadas aquí):

// <initialize stream> 

// figure out number of bytes in stream 
byte[] bytes = new byte[1024]; 
int i; 
int byteCount = 0; 
while ((i = stream.read(bytes)) != -1) { 
    byteCount += i; 
} 

// <reset stream> 

Bitmap bmImg; 

// downsample if necessary 
if (byteCount > MAX_SIZE) { 
    // scale down photo if too large 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    // scale to nearest power of 2 - faster 
    options.inSampleSize = (int)Math.pow(2, (int)(Math.log10(Math.sqrt((double)byteCount/MAX_SIZE))/Math.log10(2) + 1)); // 2^(log2(lengthRatio)+1) 
    options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    bmImg = BitmapFactory.decodeStream(stream, null, options); 
} 
else { 
    bmImg = BitmapFactory.decodeStream(stream); 
} 

que evitar el uso de mark(int) y reset() porque en mi caso no hay un límite superior en el tamaño de la secuencia. Entonces, cómo restablecer la transmisión puede depender de su implementación. Mi flujo se especifica por un Uri, por lo que sólo utiliza:

stream.close(); 
stream = this.getContentResolver().openInputStream(uri); // where this is an Activity 

para restablecer la corriente.

2

Si tu estás tratando de evitar que OutOfMemory o alguna otra cosa ...

puede crear un tampón con sus datos de imagen,

BitmapFactory.Options buffer = new BitmapFactory.Options(); 
buffer.inJustDecodeBounds = true; 
BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer); 

entonces se puede obtener el tamaño utilizando buffer.outHeight, buffer.outWidth ..

Si desea escalar, solo usa buffer.isSampleSize pero noté que está estableciendo un valor "10" ... que creo que está mal ... sampleSize debería obtener un 2^valor .. como 2,4,8,16 etc.

Finalmente, después de configurar el tamaño de muestra, puede crear el mapa de bits como siempre.

Bitmap bmp = BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer); 

Esperamos ayudarte!

PS: SQS mi inglés = (!!!

0

Comprobar Logcat, si usted tiene un error que dice "Bitmap too large to be uploaded into a texture" entonces usted tiene que hacer lo siguiente Mi respuesta es similar a A. @Sergio pero se puede utilizar. BitmapFactory.decodeStream directamente en lugar de decodeByteArray.

final BitmapFactory.options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 

// Pass in a Rect if you want to know the padding. 
BitmapFactory.decodeStream(inputStream,null,options); 

Esto hará que el tamaño de la imagen (no toda la imagen en sí misma). se puede aislar el ancho y la altura usando el siguiente código.

final int height = options.outHeight; 
final int width = options.outWidth; 

Puede utilizar estos valores (junto con su anchura y la altura deseada - muy probablemente basado en la imageView o la pantalla) para calcular options.inSampleSize. No voy a entrar en cómo hacer eso, pero Google me ha proporcionado un tutorial here.

Espero que ayude :)

Cuestiones relacionadas