Android: BitmapFactory.decodeByteArray gives pixelated bitmap
Asked Answered
A

3

12

I am working on an Android app that displays photos which are downloaded from Flickr. I obtain a bitmap object from a byte array, which in turn is read from the relevant Flickr URL, as follows:

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 then draw the bitmap onto a canvas in the onDraw method of a View object:

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

The problem is that the resulting picture is pixelated and I can't figure out why; I have tried a number of variations of the opt and paint objects with no luck. The difference between the picture displayed in my app and the picture at the original URL is roughly demonstrated by the following:

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

Look e.g. at the clouds in the top-left corner to see the difference.

Note that JPEG pictures which are loaded from the project resources and drawn in a similar way display just fine, i.e. have no pixelation.

Can anybody give me a hint as to why this is happening?

To elaborate a little, the byte array is obtained from Flickr as follows; this is based on code from the Photostream app by 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: I also posted a variant of this question on the android.developer Google group.


Thanks a lot for your suggestion -- now I am really puzzled! I did as you suggested and found that the image resulting directly from the downloaded byte array is indeed pixelated. However, this is downloaded from exactly the same URL which, when accessed on my computer, is NOT pixelated. Here is the corresponding Flickr URL:

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

Even stranger, when I run the same app in the simulator rather than on my phone (a HTC Hero), there is no pixelation.

How on earth is this possible?

Below is the code I use for loading a bitmap from a URL -- it is based on the Photostream app by Romain Guy, and it incorporates Will's suggestion to write the raw byte array to file:

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);
        }
    }
}

Am I going crazy here? Best, Michael.

Aesthetically answered 2/2, 2010 at 12:4 Comment(3)
I took the liberty of inlining the images, since that generally makes the question nicer and easier to read.Alysiaalyson
Did you ever find a workaround for this? I've tried the suggestions, but no luck. It's super annoying.Keeton
as a footnote, do not forget that JPEG is not lossless in android environment. though it is supported, it is discouraged in favor of PNG.Massimo
G
3

Write the raw bytes fetched from the URL to /sdcard/tmp.jpg, and view on your PC.

JPEG images are compressed in 8x8 (or 16x16) tiles. The 'pixelation' as you describe it is actually in these tiles, suggesting that the 'bad' image is a JPEG that is more aggressively compressed than the other.

So I'd anticipate that the actual issue is that the image being downloaded is a very low-quality version, e.g. one intended for thumbnailing/preview use-cases.

Gantlet answered 2/2, 2010 at 12:14 Comment(1)
I suspected this too, but the actual file size of the "bad" image is about 18% larger than the "good" one. Weirdness.Alysiaalyson
A
4

Ok, so I finally get it: it appears that my mobile network does image compression to save bandwidth.

Hence a picture downloaded from my phone is of lower quality than the same picture downloaded from my computer.

That's a bummer for my app, but I don't suppose there is anything I can do about it. Sigh.

Thanks again for your input though!

Best, Michael.

Aesthetically answered 2/2, 2010 at 16:28 Comment(3)
it could also be prompted by http headers - facebook itself could be providing low-quality images for mobile phones. Edit the headers before sending - basically, remove them all!Gantlet
You could try setting the http headers to Accept-Encoding gzip. Not sure, but it's possible it'll get around the network image compression. See #1573891Aristides
Have you tried to confirm this through downloading the image in the browser? I have the same problem but if I open the image in the web browser I get a unpixelated image. So there must be a way to get the correct image.Amarette
G
3

Write the raw bytes fetched from the URL to /sdcard/tmp.jpg, and view on your PC.

JPEG images are compressed in 8x8 (or 16x16) tiles. The 'pixelation' as you describe it is actually in these tiles, suggesting that the 'bad' image is a JPEG that is more aggressively compressed than the other.

So I'd anticipate that the actual issue is that the image being downloaded is a very low-quality version, e.g. one intended for thumbnailing/preview use-cases.

Gantlet answered 2/2, 2010 at 12:14 Comment(1)
I suspected this too, but the actual file size of the "bad" image is about 18% larger than the "good" one. Weirdness.Alysiaalyson
H
1

Some version of Android have a bug in Bitmap class and convert the Bitmap to RGB_565 upon some operations. This would manifest itself in artifacts similar to those on your picture. This would also explain the banding of the blue sky. Also, have in mind that android attempts to "optimize" image by converting them to rgb_565 upon loading and even compiling in resource files. Take a look at: http://android.nakatome.net/2010/04/bitmap-basics.html

Hyposthenia answered 29/1, 2012 at 22:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.