web-dev-qa-db-fra.com

Décoder les bitmaps dans Android avec la bonne taille

Je décode les images bitmap de la carte SD en utilisant BitmapFactory.decodeFile. Parfois, les bitmaps sont plus volumineux que ce dont l’application a besoin ou que le tas permet, alors j’utilise BitmapFactory.Options.inSampleSize pour demander un bitmap sous-échantillonné (plus petit).

Le problème est que la plate-forme n'impose pas la valeur exacte de inSampleSize et que je finis parfois avec un bitmap trop petit ou encore trop grand pour la mémoire disponible. 

De http://developer.Android.com/reference/Android/graphics/BitmapFactory.Options.html#inSampleSize :

Remarque: le décodeur essaiera de remplir cette demande, mais le bitmap résultant peut avoir différentes dimensions que précisément ce qui a été demandé . En outre, les puissances de 2 sont souvent plus rapide/plus facile pour le décodeur à honneur.

Comment devrais-je décoder les bitmaps de la carte SD pour obtenir un bitmap de la taille exacte dont j'ai besoin tout en utilisant le moins de mémoire possible pour le décoder? 

Modifier:

Code source actuel:

BitmapFactory.Options bounds = new BitmapFactory.Options();
this.bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, bounds);
if (bounds.outWidth == -1) { // TODO: Error }
int width = bounds.outWidth;
int height = bounds.outHeight;
boolean withinBounds = width <= maxWidth && height <= maxHeight;
if (!withinBounds) {
    int newWidth = calculateNewWidth(int width, int height);
    float sampleSizeF = (float) width / (float) newWidth;
    int sampleSize = Math.round(sampleSizeF);
    BitmapFactory.Options resample = new BitmapFactory.Options();
    resample.inSampleSize = sampleSize;
    bitmap = BitmapFactory.decodeFile(filePath, resample);
}
60
hpique

Vous êtes sur la bonne voie, mais vous essayez de faire deux choses à la fois: lisez le fichier et redimensionnez-le à la taille appropriée. 

La première étape consiste à lire le fichier dans un bitmap légèrement plus volumineux que nécessaire, en utilisant BitmapFactory.Options.inSampleSize pour vous assurer de ne pas utiliser trop de mémoire en lisant un bitmap volumineux lorsque vous souhaitez simplement obtenir une vignette ou une résolution d'écran plus petite.

La deuxième étape consiste à appeler Bitmap.createScaledBitmap () pour créer un nouveau bitmap à la résolution exacte souhaitée.

Assurez-vous de nettoyer après le bitmap temporaire pour récupérer sa mémoire. (Laissez la variable sortir de son domaine et laissez le CPG la gérer, ou appelez-la .recycle () dessus si vous chargez beaucoup d'images et que votre mémoire est saturée.)

44
sirdodger

Vous voudrez peut-être utiliser inJustDecodeBounds . Réglez-le sur TRUE et chargez le fichier tel quel.

L'image ne sera pas chargée en mémoire. Mais les propriétés outheight et outwidth de BitmapFactory.Options contiendront les paramètres de taille réelle de l'image spécifiée. Calculez combien vous voulez sous-échantillonner. c'est-à-dire 1/2 ou 1/4 ou 1/8, etc. et affectez les 2/4/8 etc. en conséquence à inSampleSize.

Définissez maintenant inJustDecodeBounds sur FALSE et appelez BitmapFactory.decodeFile() pour charger l'image de la taille exacte calculée précédemment.

15
TheCodeArtist

Vous devez d’abord échantillonner l’image à la valeur d’échantillonnage la plus proche afin que les images bitmap deviennent efficaces en termes de mémoire, ce que vous avez décrit parfaitement. Une fois que c'est fait, vous pouvez l'adapter à votre écran. 

// This function taking care of sampling
backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height);

// This will scale it to your screen width and height. you need to pass screen width and height.
backImage = Bitmap.createScaledBitmap(backImage, width, height, false); 
7
Ravi Bhojani
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

Vous pouvez directement définir le paramètre THUMBSIZE en fonction de vos besoins. Cependant, je ne suis pas sûr des détails d’implémentation de niveau inférieur, de la qualité de l’image de sortie et de ses performances, mais je le préfère généralement lorsque j’ai besoin d’afficher une miniature.

1
Ankur Gautam

Comme l’API indique déjà que la taille de l’échantillon peut ou non être honorée. Donc, je suppose qu’il n’ya pas de chance en utilisant BitmapFactory

Mais la solution serait d'utiliser votre propre lecteur de bitmap. Mais je suggérerais que vous restiez avec BitmapFactory car il est assez bien testé et normalisé. 

Je voudrais essayer de ne pas trop m'inquiéter d'une faible consommation de mémoire, mais essayer de comprendre pourquoi il ne respecte pas les valeurs. Malheureusement, je n'ai aucune idée à ce sujet :(

1
the100rabh

Pour ceux qui recherchent une image de taille exacte à partir de ressources. 

ReqHight = 0 pour la largeur exacte et reQWidth = 0 pour la hauteur exacte

public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight)
{
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(context.getResources(), resId, options);
    float scale = 1;
    if (reqWidth != 0 && reqHeight == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outWidth / (float) reqWidth);
        scale = (float) options.outWidth / (float) reqWidth;
    }
    else if (reqHeight != 0 && reqWidth == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outHeight / (float) reqHeight);
        scale = (float) options.outHeight / (float) reqHeight;
    }
    else if (reqHeight != 0 && reqWidth != 0)
    {
        float x = (float) options.outWidth / (float) reqWidth;
        float y = (float) options.outHeight / (float) reqHeight;
        scale = x > y ? x : y;
    }
    reqWidth = (int) ((float) options.outWidth / scale);
    reqHeight = (int) ((float) options.outHeight / scale);
    options.inJustDecodeBounds = false;
    Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options);
    Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false);
    tmp.recycle();
    tmp = null;
    return out;
}
1
Amit Padekar

Lorsque l'indicateur inScaled est défini (par défaut, il est TRUE), si inDensity et inTargetDensity ne sont pas nuls, le bitmap sera redimensionné pour correspondre à inTargetDensity lorsqu'il sera chargé, plutôt que de compter sur le système graphique le redimensionnant chaque fois qu'il est dessiné sur un canevas. Il suffit donc de définir le paramètre inScaled sur false.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
0
ucMedia

Comme l'a dit le 100rabh, si vous utilisez BitmapFactory, vous n'avez pas vraiment beaucoup de choix. Cependant, le décodage bitmap est très simple et, en fonction de l'algorithme de redimensionnement que vous souhaitez utiliser, il est généralement assez simple de le redimensionner à la volée sans devoir lire de grandes parties du bitmap d'origine en mémoire ne nécessite que le double de la mémoire requise pour 1-2 lignes du bitmap d'origine).

0
Tal Pressman

J'ai pu obtenir approximativement la "bonne taille" en redimensionnant le fichier d'image décodé à 70% de sa taille en bitmap.

Bitmap myBitmap = BitmapFactory.decodeFile(path);
if(myBitmap != null)
{
  //reduce to 70% size; bitmaps produce larger than actual image size
  Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap(
                                                  myBitmap, 
                                                  myBitmap.getWidth()/10*7, 
                                                  myBitmap.getHeight()/10*7, 
                                                  false);

  image.setImageBitmap(rescaledMyBitmap);
}                

J'espère que cela vous aide en quelque sorte! Paix!

0
masarapmabuhay

Depuis que j'utilise inSampleSize, je ne fais plus face à des problèmes de mémoire. Donc, inSampleSize fonctionne assez bien pour moi. Je vous recommanderais de revérifier le problème. La taille peut ne pas être exactement la même que celle que vous avez demandée. Mais il devrait quand même être réduit et il ne devrait plus y avoir de "mémoire insuffisante". Je recommanderais également de poster votre extrait de code ici, il y a peut-être quelque chose qui ne va pas.

0
Fedor