Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: high quality image resizing / scaling

I need to scale down images coming from a Network stream without losing quality.

I am aware of this solution Strange out of memory issue while loading an image to a Bitmap object but it is too coarse - inSampleSize is an integer and does not allow finer control over the resulting dimensions. That is, I need to scale images to specific h/w dimensions (and keeping aspect ratio).

I dont mind having a DIY bicubic/lancoz algorithm in my code but I cant find any examples that would work on Android as they all rely on Java2D (JavaSE).

EDIT: Ive attached a quick source. The original is 720x402 HD screen capture. Please ignore the top 2 thumbnails. The top large image is resized automatically by android (as part of layout) to about 130x72. It is nice and crisp. The bottom image is resized with API and has severe artifacting

alt text

I've also tried using the BitmapFactory and, as I said earlier, it has two problems - no way to scale to exact size and the scaled image is blurry.

Any ideas on how to fix the artifcating?

Thanks, S.O.!

    package qp.test;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;

public class imgview extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
        Bitmap resized = getResizedBitmap(original, 130);
        //Bitmap resized = getResizedBitmap2(original, 0.3f);
        System.err.println(resized.getWidth() + "x" + resized.getHeight());

        ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
        image.setImageBitmap(resized);

    }

    private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {

        int width = bm.getWidth();

        int height = bm.getHeight();

    float aspect = (float)width / height;

    float scaleWidth = newWidth;

    float scaleHeight = scaleWidth / aspect;        // yeah!

    // create a matrix for the manipulation

    Matrix matrix = new Matrix();

    // resize the bit map

    matrix.postScale(scaleWidth / width, scaleHeight / height);

    // recreate the new Bitmap

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        bm.recycle();

        return resizedBitmap;
    }

    private Bitmap getResizedBitmap2(Bitmap bm, float scale) {

    /*    float aspect = bm.getWidth() / bm.getHeight();

        int scaleWidth = (int) (bm.getWidth() * scale);
        int scaleHeight = (int) (bm.getHeight() * scale);
*/
        // original image is 720x402 and SampleSize=4 produces 180x102, which is
        // still too large

        BitmapFactory.Options bfo = new BitmapFactory.Options();
        bfo.inSampleSize = 4;

        return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
    }

}

And the layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
<!-- <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="hullo" android:background="#00ff00"
    />
     -->
    <ImageView android:id="@+id/ImageViewThumbAuto"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  />

    <ImageView android:id="@+id/ImageViewThumbManual"
            android:layout_width="130dip" android:layout_height="72dip"
            android:src="@drawable/a000001570402"  
            android:layout_toRightOf="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullAuto" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewThumbAuto"
            />

<ImageView android:id="@+id/ImageViewFullManual" android:layout_width="300dip"
            android:layout_height="169dip"
            android:scaleType="fitXY"
            android:src="@drawable/a000001570402"
            android:layout_below="@id/ImageViewFullAuto"
            />

</RelativeLayout>
like image 859
Saideira Avatar asked Nov 17 '10 18:11

Saideira


People also ask

Does scaling reduce image quality?

The most common side effect of scaling an image larger than its original dimensions is that the image may appear to be very fuzzy or pixelated. Scaling images smaller than the original dimensions does not affect quality as much, but can have other side effects.

What happens to image quality when bitmaps are scaled?

As you may have guessed, the more pixels in the image and the higher the resolution is, the higher quality the image will be. For example, if we scale a raster image to enlarge it, without changing resolution, it will lose quality and look blurry or pixilated.

How can I reduce the size of an image without losing quality?

If you want to resize an image without losing quality, you need to make sure that the "Resample" checkbox is unchecked. This checkbox tells Paint to change the number of pixels in the image. When you uncheck this box, Paint will not change the number of pixels, and the quality of the image will not be reduced.

What is scaleX and scaleY in Android?

Have you tried?: android:scaleX="1.5" // 1 is 100% size android:scaleY="1.5" Follow this answer to receive notifications.


3 Answers

You can use BitmapFactory.Options with BitmapFactory.decode function(s),
using inDensity and inTargetDensity

Example: you have 1600x1200 size of image and want to resize to 640x480
then 'inDensity'=5 and 'inTargetDensity'=2 (1600x2 equal to 640x5).

Hoping this help.

like image 185
Anonymous Avatar answered Sep 29 '22 04:09

Anonymous


The top large image is simply scaled down to 450px by the layout so no artifacts.

The artifacts of the bottom large image result from scaling it down to 130px wide and then up again to about 450px by the layout. So the artifacts are made by your scaling. Try

Bitmap resized = getResizedBitmap(original, 450);

in your code and it should be fine. However, you need to adapt that to the actuall screen width of the phone either.

like image 38
dronus Avatar answered Sep 29 '22 06:09

dronus


Briefly, good downscaling algorithm (not nearest neighbor like) consists of 2 steps:

  1. downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
  2. get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()

Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/

Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

like image 32
goRGon Avatar answered Sep 29 '22 04:09

goRGon