27 votes

Android : redimensionnement / mise à l'échelle d'images de haute qualité

Je dois réduire la taille des images provenant d'un flux réseau sans perdre de qualité.

Je suis au courant de cette solution Android : Étrange problème de perte de mémoire mais c'est trop grossier - inSampleSize est un nombre entier et ne permet pas un contrôle plus fin des dimensions résultantes. En d'autres termes, j'ai besoin de mettre à l'échelle des images à des dimensions h/w spécifiques (tout en conservant le rapport d'aspect).

Cela ne me dérange pas d'avoir un algorithme bicubique/lancoz bricolé dans mon code, mais je ne trouve aucun exemple qui fonctionnerait sur Android, car ils reposent tous sur Java2D (JavaSE).

EDIT : J'ai joint une source rapide. L'original est une capture d'écran HD 720x402. Veuillez ignorer les 2 vignettes du haut. La grande image du haut est redimensionnée automatiquement par Android (dans le cadre de la mise en page) à environ 130x72. Elle est belle et nette. L'image du bas a été redimensionnée avec l'API et présente des artefacts importants.

alt text

J'ai également essayé d'utiliser BitmapFactory et, comme je l'ai dit précédemment, cela pose deux problèmes : il n'est pas possible d'obtenir une taille exacte et l'image mise à l'échelle est floue.

Des idées sur la façon de réparer l'artefact ?

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

}

Et la disposition

<?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>

10voto

Anonymous Points 91

Vous pouvez utiliser Options de BitmapFactory con BitmapFactory.decode* fonction(s),
en utilisant inDensity y inTargetDensity' (densité cible)
Exemple : vous avez une image de 1600*1200 et vous voulez la redimensionner en 640*480.
alors 'inDensity' = 5 et 'inTargetDensity' = 2 (1600*2 égal à 640*5).

J'espère que cela vous aidera.

8voto

dronus Points 1925

La grande image du haut est simplement réduite à 450px par la mise en page, sans artefacts.

Les artefacts de la grande image du bas résultent de sa réduction à 130px de large, puis de sa remontée à environ 450px par la mise en page. Les artefacts sont donc dus à votre mise à l'échelle. Essayez

Bitmap resized = getResizedBitmap(original, 450);

dans votre code et tout devrait bien se passer. Cependant, vous devez également l'adapter à la largeur d'écran actuelle du téléphone.

4voto

goRGon Points 291

En bref, un bon algorithme de réduction d'échelle (qui n'est pas du type "nearest neighbor") se compose de deux étapes :

  1. réduire l'échelle en utilisant BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() aussi proche que possible de la résolution dont vous avez besoin, mais pas inférieure à celle-ci
  2. d'obtenir la résolution exacte en réduisant un peu l'échelle en utilisant Canvas::drawBitmap()

Voici une explication détaillée de la manière dont SonyMobile a résolu cette tâche : http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-Android-application/

Voici le code source de SonyMobile scale utils : http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-Android/

-2voto

Rich Points 16818

Bitmap.createScaledBitmap

calculer les dimensions, les arrondir en ints, appeler cette méthode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X