it-swarm.com.de

Decodierung von Bitmaps in Android mit der richtigen Größe

Ich decodiere Bitmaps von der SD-Karte mit BitmapFactory.decodeFile. Manchmal sind die Bitmaps größer als das, was die Anwendung benötigt oder was der Heap erlaubt, so benutze ich BitmapFactory.Options.inSampleSize, um eine unterabgetastete (kleinere) Bitmap anzufordern.

Das Problem ist, dass die Plattform den genauen Wert von inSampleSize nicht erzwingt, und ich bekomme manchmal eine Bitmap, die entweder zu klein oder für den verfügbaren Speicher noch zu groß ist. 

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

Hinweis: Der Decoder versucht, .__ zu erfüllen. diese Anforderung, aber die resultierende Bitmap kann andere Abmessungen haben als genau das, was angefordert wurde . Potenzen von 2 sind häufig schneller/einfacher für den Decoder um Ehre.

Wie kann ich Bitmaps von der SD-Karte decodieren, um eine Bitmap mit der genauen Größe zu erhalten, die ich benötige, und dabei so wenig Speicher wie möglich für die Decodierung verbraucht? 

Bearbeiten:

Aktueller Quellcode:

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

Sie sind auf dem richtigen Weg, Sie versuchen jedoch, zwei Dinge gleichzeitig auszuführen: Lesen Sie die Datei ein und skalieren Sie sie auf die entsprechende Größe. 

Der erste Schritt besteht darin, die Datei mit BitmapFactory.Options.inSampleSize in eine etwas größere Bitmap einzulesen, als Sie benötigen, um sicherzustellen, dass Sie nicht zu viel Speicher benötigen, wenn Sie eine große Bitmap lesen, wenn Sie nur ein kleineres Miniaturbild oder eine Bildschirmauflösung benötigen.

Der zweite Schritt ist das Aufrufen von Bitmap.createScaledBitmap (), um eine neue Bitmap mit der genauen Auflösung zu erstellen, die Sie benötigen.

Stellen Sie sicher, dass Sie nach der temporären Bitmap bereinigen, um den Speicherplatz wiederherzustellen. (Entweder lassen Sie die Variable den Gültigkeitsbereich verlassen und lassen Sie den GC damit umgehen, oder rufen Sie .recycle () darauf auf, wenn Sie viele Bilder laden und nicht genügend Speicherplatz haben.)

44
sirdodger

Möglicherweise möchten Sie inJustDecodeBounds verwenden. Setzen Sie es auf TRUE und laden Sie die Datei so wie sie ist.

Das Bild wird nicht in den Speicher geladen. Die Eigenschaften outheight und outwidth von BitmapFactory.Options enthalten jedoch die tatsächlichen Größenparameter des angegebenen Bildes. Berechnen Sie, wie viel Sie subsampling möchten. d. h. 1/2 oder 1/4 oder 1/8 usw. und weisen 2/4/8 usw. entsprechend der inSampleSize zu.

Setzen Sie nun inJustDecodeBounds auf FALSE und rufen Sie BitmapFactory.decodeFile() auf, um das Bild mit der genau berechneten Größe zu laden.

15
TheCodeArtist

Zuerst müssen Sie das Bild auf den nächsten Abtastwert abtasten, damit die Bitmap speichereffizient wird, was Sie perfekt beschrieben haben. Sobald dies erledigt ist, können Sie es an Ihren Bildschirm anpassen. 

// 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))

Sie können den THUMBSIZE-Wert je nach Anforderung direkt einstellen. Ich bin mir jedoch nicht sicher, ob Details zur Implementierung auf niedrigerer Ebene vorliegen, die Bildqualität der Ausgabe und deren Leistung, aber ich bevorzuge es, wenn ich ein Miniaturbild anzeigen muss.

1
Ankur Gautam

Da die API bereits feststellt, dass die Stichprobengröße möglicherweise nicht berücksichtigt wird. Ich schätze also, dass Sie mit BitmapFactory kein Glück haben. 

Der Ausweg wäre jedoch die Verwendung eines eigenen Bitmap-Lesers. Ich würde jedoch vorschlagen, dass Sie bei BitmapFactory bleiben, da es ziemlich gut getestet und standardisiert ist. 

Ich würde versuchen und nicht zu viel über wenig zusätzlichen Speicherverbrauch sorgen, aber versuchen Sie herauszufinden, warum es nicht die Werte einhält. Leider habe ich keine Ahnung davon :(

1
the100rabh

Für diejenigen, die ein genaues Bild aus Ressourcen suchen. 

Für die exakte Breite pass reqHight = 0 und für die exakte Höhe pass reqWidth = 0

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

Wenn das inScaled-Flag gesetzt ist (standardmäßig TRUE), wenn inDensity und inTargetDensity nicht 0 sind, wird die Bitmap beim Laden so skaliert, dass sie mit InTargetDensity übereinstimmt, und muss sich nicht jedes Mal darauf verlassen, dass das Grafiksystem in einen Canvas gezeichnet wird. Setzen Sie also einfach inScaled auf false.

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

Wie The100rabh sagte, wenn Sie BitmapFactory verwenden, haben Sie nicht wirklich eine große Auswahl. Die Bitmap-Dekodierung ist jedoch sehr einfach. Abhängig vom Skalierungsalgorithmus, den Sie verwenden möchten, ist es normalerweise relativ einfach, sie schnell zu skalieren, ohne große Teile der ursprünglichen Bitmap in den Speicher lesen zu müssen (im Allgemeinen werden Sie das tun benötigen nur den doppelten Speicher, der für 1-2 Zeilen der ursprünglichen Bitmap erforderlich ist).

0
Tal Pressman

Ich konnte die "richtige Größe" ungefähr ermitteln, indem ich die decodierte Bilddatei auf 70% der Bitmapgröße skaliere.

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

Ich hoffe das hilft dir irgendwie raus! Frieden!

0
masarapmabuhay

Seit ich inSampleSize verwende, habe ich nie mehr Probleme mit dem Arbeitsspeicher. InSampleSize funktioniert für mich also recht stabil. Ich würde Ihnen empfehlen, das Problem noch einmal zu überprüfen. Die Größe stimmt möglicherweise nicht exakt mit der von Ihnen gewünschten Größe überein. Es sollte aber trotzdem reduziert werden und es sollte kein "Out of memory" mehr geben. Ich würde auch empfehlen, Ihr Code-Snippet hier zu posten, vielleicht stimmt etwas nicht.

0
Fedor