Downsizing Android Bitmap always results in jagged edges

Viewed 497

I'm seeing very ugly artifacting / jagged edges when I downsize an image on an Android device no matter what I try. I've gone through several potential solutions I found on StackOverflow and blogs, and everything seems to give me similar results.

Original Image (4096 x 4096): original image

Scaled Image (214 x 214) (notice the jagged edges): scaled image

What I have tried:

  1. Drawing the image to a Canvas using a Paint with anti-aliasing, and filtering enabled
  2. Multiple variations of BitmapFactory.decode
  3. bitmap.scale()
  4. Compressor - an Android Image Scaling Library

All of the above trials have yielded almost the exact same result. This is such a common problem though, that surely I'm overlooking something, or not doing something properly.

If I use a web-based image-resizer, here is the result: What it should look like: enter image description here

What can I do to get the same results as the above image?

2 Answers

The lack of smoothness in the trial image indicate some anti-aliasing is missing.

A relatively quick search show that Sub-pixel antialiasing rules can get complicated.

The next thing is to figure out which tool the "web-based image resizer" used to generate the trial image.

Nine times out of ten (or more) the website would be using FOSS software, which would lead to ImageMagick

A quick run of your source image through ImageMagick with a command like:

convert SO_source_image.png -resize 5% SO_result.png

Results in this similar but not exact (205x205 pixels) image:

Converted image

At this point you could either dive into the algorithms used by ImageMagick (see the command line options for the variety of methods) or see if you can get a similar result with an existing port like: cherryleafroad/Android-ImageMagick7 or paulasiimwe/Android-ImageMagick

There is a tricky way to (manage to) achieve that only with standard APIs. Just avoid scaling down the bitmap at once.

Bitmap bitmap = BitmapFactory.decodeStream(stream);

:
    
Matrix m = new Matrix();
m.setScale(0.5F, 0.5F);
while (bitmap.getWidth() > 256)
{
    Bitmap bitmap_half = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
    bitmap.recycle();
    bitmap = bitmap_half;
}

imageView.setImageBitmap(bitmap);

Result:

Result

How this works:

Since the filter is short-ranged (primarily designed for scaling up e.g. bilinear or bicubic), it's useless for scaling down (=discrete resampling) in general cases. However it does refer neighboring pixels for calculating new pixel colors. So by avoiding too sparse resampling, it's possible to make it work as a smoothing filter for scaling down. maybe.

Related