2010-02-02 12 views
12

Ich arbeite an einer Android App, die Fotos anzeigt, die von Flickr heruntergeladen werden. I erhalten, ein Bitmap-Objekt aus einem Byte-Array, das wiederum von der betreffenden Flickr URL gelesen wird, wie folgt:Android: BitmapFactory.decodeByteArray gibt pixelierte Bitmap

BitmapFactory.Options opt = new BitmapFactory.Options(); 
opt.inDither = true; 
opt.inPreferredConfig = Bitmap.Config.ARGB_8888; 
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opt); 

I dann die Bitmap auf eine Leinwand zeichnen im OnDraw Verfahren eines View-Objekt:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 
canvas.drawBitmap(bitmap, 0, 0, paint); 

Das Problem ist, dass das resultierende Bild pixelig ist und ich nicht herausfinden kann warum; Ich habe eine Reihe von Varianten der opt und paint Objekte ohne Glück versucht.

Bad image, see pixelation in top left corner http://homepages.inf.ed.ac.uk/s0677975/bad.jpg

Good picture, this is the expected result http://homepages.inf.ed.ac.uk/s0677975/good.jpg

Schauen Sie zum Beispiel: Der Unterschied zwischen dem in meinem app angezeigten Bild und das Bild an der ursprünglichen URL wird grob durch das folgenden demonstriert in den Wolken in der oberen linken Ecke, um den Unterschied zu sehen.

Beachten Sie, dass JPEG-Bilder, die aus den Projektressourcen geladen und auf ähnliche Weise gezeichnet werden, gut angezeigt werden, d. H. Keine Pixelierung aufweisen.

Kann mir jemand einen Hinweis geben, warum das passiert?

Um ein wenig zu erarbeiten, wird das Byte-Array von Flickr wie folgt erhalten; Dies basiert auf Code aus dem Fotostream App von Romain Guy:

InputStream in = new BufferedInputStream(url.openStream(), IO_BUFFER_SIZE); 
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 
out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); 
copy(in, out); 
out.flush(); 
final byte[] data = dataStream.toByteArray(); 

PS: Ich stellte auch eine Variante dieser Frage auf der android.developer Google-Gruppe.


Vielen Dank für Ihren Vorschlag - jetzt bin ich wirklich verwirrt! Ich tat wie Sie vorgeschlagen und festgestellt, dass das Bild direkt aus dem heruntergeladenen Byte-Array in der Tat pixelig ist. Dies wird jedoch von genau der gleichen URL heruntergeladen, die beim Zugriff auf meinen Computer NICHT pixelig ist. Hier ist die entsprechende Flickr URL:

http://farm3.static.flickr.com/2678/4315351421_54e8cdb8e5.jpg

Noch seltsamer, wenn ich die gleiche Anwendung im Simulator laufen, anstatt auf meinem Handy (ein HTC Hero), gibt es keine pixelation.

Wie ist das möglich?

Unten ist der Code, den ich von einer URL zum Laden eine Bitmap verwenden - es auf dem Fotostream-App von Romain Guy basiert, und es enthält Vorschlag Wills den rohen Byte-Array zu schreiben in Datei:

Bitmap loadPhotoBitmap(URL url) { 
    Bitmap bitmap = null; 
     InputStream in = null; 
     BufferedOutputStream out = null; 

     try { 

      FileOutputStream fos = new FileOutputStream("/sdcard/photo-tmp.jpg"); 
      BufferedOutputStream bfs = new BufferedOutputStream(fos); 

      in = new BufferedInputStream(url.openStream(), 
        IO_BUFFER_SIZE); 

      final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 
      out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); 
      copy(in, out);      
      out.flush(); 
      final byte[] data = dataStream.toByteArray(); 

      bfs.write(data, 0, data.length); 
      bfs.flush(); 

      BitmapFactory.Options opt = new BitmapFactory.Options(); 
      bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opt); 

     } catch (IOException e) { 
      android.util.Log.e(LOG_TAG, "Could not load photo: " + this, e); 
     } finally { 
      closeStream(in); 
      closeStream(out) 
      closeStream(bfs); 
     } 

     return bitmap; 
    } 

private static void copy(InputStream in, OutputStream out) throws IOException { 
    byte[] b = new byte[IO_BUFFER_SIZE]; 
    int read; 
    while ((read = in.read(b)) != -1) { 
     out.write(b, 0, read); 
    } 
} 

private static void closeStream(Closeable stream) { 
    if (stream != null) { 
     try { 
      stream.close(); 
     } catch (IOException e) { 
      android.util.Log.e(LOG_TAG, "Could not close stream", e); 
     } 
    } 
} 

Werde ich hier verrückt? Beste, Michael.

+0

Ich habe mir die Freiheit genommen, die Bilder zu inlinern, da das im Allgemeinen die Frage schöner und einfacher zu lesen macht. – unwind

+1

Haben Sie jemals einen Workaround gefunden? Ich habe die Vorschläge ausprobiert, aber kein Glück. Es ist super nervig. – blork

+0

als Fußnote, vergessen Sie nicht, dass JPEG in Android-Umgebung nicht verlustfrei ist. obwohl es unterstützt wird, ist es zugunsten von PNG entmutigt. – user836725

Antwort

3

Schreiben Sie die rohen Bytes, die von der URL abgerufen wurden, auf /sdcard/tmp.jpg und zeigen Sie sie auf Ihrem PC an.

JPEG-Bilder werden in 8x8 (oder 16x16) Kacheln komprimiert. Die "Pixelierung", wie Sie sie beschreiben, befindet sich tatsächlich in diesen Kacheln, was darauf hindeutet, dass das "schlechte" Bild ein JPEG ist, das aggressiver komprimiert ist als das andere.

So würde ich erwarten, dass das eigentliche Problem ist, dass das Bild heruntergeladen wird, ist eine sehr niedrige Qualität, z. eine für Thumbnailing/Vorschau-Anwendungsfälle.

+0

Ich vermutete das auch, aber die tatsächliche Dateigröße des "schlechten" Bildes ist etwa 18% * größer * als die "gute". Seltsamkeit. – unwind

+0

Das beweist nichts :) – Will

4

Ok, also habe ich es endlich verstanden: Es scheint, dass mein mobiles Netzwerk Bildkompression durchführt, um Bandbreite zu sparen.

Daher ist ein Bild von meinem Telefon heruntergeladen von geringerer Qualität als das gleiche Bild von meinem Computer heruntergeladen.

Das ist ein Nachteil für meine App, aber ich glaube nicht, dass ich irgendetwas dagegen tun kann. Seufzer.

Nochmals vielen Dank für Ihre Eingabe!

Beste, Michael.

+0

könnte es auch durch http Header ausgelöst werden - facebook selbst könnte minderwertige Bilder für Mobiltelefone bieten. Bearbeiten Sie die Header vor dem Senden - im Grunde, entfernen Sie sie alle! – Will

+1

Sie könnten versuchen, die HTTP-Header auf Accept-Encoding gzip einzustellen. Nicht sicher, aber es ist möglich, dass es sich um die Netzwerk-Bildkomprimierung handelt. Siehe http://stackoverflow.com/questions/1573391/android-http-communication-should-use-accept-encoding-gzip –

+0

Haben Sie versucht, dies durch Herunterladen des Bildes im Browser zu bestätigen? Ich habe das gleiche Problem, aber wenn ich das Bild im Webbrowser öffne, bekomme ich ein unpixeliertes Bild. Also muss es einen Weg geben, das richtige Bild zu bekommen. – Janusz

1

Einige Android-Versionen haben einen Fehler in der Bitmap-Klasse und konvertieren die Bitmap bei einigen Operationen in RGB_565. Dies würde sich in ähnlichen Artefakten wie auf Ihrem Bild zeigen. Dies würde auch die Streifenbildung des blauen Himmels erklären. Denken Sie auch daran, dass Android versucht, das Bild zu "optimieren", indem es beim Laden und sogar Kompilieren in Ressourcendateien nach rgb_565 konvertiert wird. Sehen Sie sich an: http://android.nakatome.net/2010/04/bitmap-basics.html