Android Crop Center von Bitmap

149

Ich habe Bitmaps, die Quadrate oder Rechtecke sind. Ich nehme die kürzeste Seite und mache so etwas:

int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
    value = bitmap.getHeight();
} else {
    value = bitmap.getWidth();
}

Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);

Dann skaliere ich es auf eine 144 x 144 Bitmap, indem ich Folgendes verwende:

Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);

Das Problem ist, dass die obere linke Ecke der ursprünglichen Bitmap zugeschnitten wird. Hat jemand den Code, um die Mitte der Bitmap zuzuschneiden?

Maurice
quelle

Antworten:

353

Geben Sie hier die Bildbeschreibung ein

Dies kann erreicht werden mit: Bitmap.createBitmap (Quelle, x, y, Breite, Höhe)

if (srcBmp.getWidth() >= srcBmp.getHeight()){

  dstBmp = Bitmap.createBitmap(
     srcBmp, 
     srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
     0,
     srcBmp.getHeight(), 
     srcBmp.getHeight()
     );

}else{

  dstBmp = Bitmap.createBitmap(
     srcBmp,
     0, 
     srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
     srcBmp.getWidth(),
     srcBmp.getWidth() 
     );
}
Lumis
quelle
1
Die Antwort wurde so bearbeitet, dass die tatsächliche Zielbitmap ein Quadrat ist.
Joram van den Boezem
1
@ Lumis: Warum hast du Revision 3 zurückgesetzt? Es schien eine gültige korrekte zu sein. Ihre aktuelle Version erstellt den richtigen Startpunkt, enthält dann aber den Rest der zu langen Seite. Wenn Sie beispielsweise ein 100x1000Bild erhalten, erhalten Sie ein 100x550Bild zurück.
Guvante
Danke, Sie haben Recht, das Format ist Bitmap.createBitmap (Quelle, x, y, Breite, Höhe)
Lumis
11
Siehe meine Antwort zur Verwendung der integrierten ThumbnailUtils.extractThumbnail () -Methode. Warum das Rad neu erfinden ??? stackoverflow.com/a/17733530/1103584
DiscDev
1
Diese Lösung ist kürzer als das Erstellen einer Leinwand und das anschließende Zeichnen einer Zeichenfläche. Außerdem wird weniger verarbeitet als bei der ThumbnailUtils-Lösung (bei der die Stichprobengröße berechnet wird, um herauszufinden, wie skaliert werden soll).
Yincrash
319

Während die meisten der oben genannten Antworten eine Möglichkeit bieten, dies zu tun, gibt es bereits eine integrierte Möglichkeit, dies zu erreichen, und es handelt sich um eine Codezeile ( ThumbnailUtils.extractThumbnail())

int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);

...

//I added this method because people keep asking how 
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
    //use the smallest dimension of the image to crop to
    return Math.min(bitmap.getWidth(), bitmap.getHeight());
}

Wenn Sie möchten, dass das Bitmap-Objekt recycelt wird, können Sie Optionen übergeben, die dies ermöglichen:

bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);

Von: ThumbnailUtils-Dokumentation

public static Bitmap extractThumbnail (Bitmap-Quelle, int width, int height)

In API-Ebene 8 hinzugefügt Erstellt eine zentrierte Bitmap mit der gewünschten Größe.

Parameter Quelle Original-Bitmap-Quellbreite Zielbreite Höhe Zielhöhe

Bei der Verwendung der akzeptierten Antwort kam es manchmal zu Speicherfehlern, und die Verwendung von ThumbnailUtils löste diese Probleme für mich. Außerdem ist dies viel sauberer und wiederverwendbarer.

DiscDev
quelle
6
+1 Ich denke, Sie müssten diesen Code verbessern und statt 400px die kürzeste bmp-Größe übergeben, um eine Alternative für den ursprünglichen Beitrag oben bereitzustellen. Aber danke, dass Sie uns darauf aufmerksam gemacht haben, es scheint eine sehr nützliche Funktion zu sein. Schade, dass ich es noch nie gesehen habe ...
Lumis
Richtig, ich habe gerade 400 hartcodiert, um ein konkretes Beispiel zu geben ... die Details liegen beim Implementierer :)
DiscDev
4
@DiscDev - dies müsste buchstäblich eine der nützlichsten Antworten auf dieser gesamten Website sein. Ernsthaft. Es ist komisch mit Android-Fragen - Sie können oft zwei Stunden lang suchen, bevor Sie die einfache offensichtliche Antwort finden. Ich weiß nicht genug, wie ich dir danken soll. Kopfgeld unterwegs!
Fattie
2
Sie könnten sogar eine etwas andere Funktion verwenden, um auch die alte Bitmap zu recyceln: = ThumbnailUtils.extractThumbnail (Bitmap, Breite, Höhe, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
Android-Entwickler
1
Dies ist die mächtigste Antwort, die ich bisher für Android-Fragen gesehen habe. Ich habe wie 20 verschiedene Lösungen gesehen, wie man Bitmap auf Android skaliert / beschneidet / verkleinert / in der Größe ändert und so weiter. Alle Antworten sind unterschiedlich. Und dieser nimmt nur 1 Zeile und funktioniert genau wie erwartet. Danke dir.
Alex Sorokoletov
12

Haben Sie darüber nachgedacht, dies von der zu tun layout.xml? Sie könnten für Ihr Set ImageViewdes Scaletype auf android:scaleType="centerCrop"und legen Sie die Abmessungen des Bildes in dem ImageViewInnern der layout.xml.

Ovidiu Latcu
quelle
Ich habe diese Idee mit dem folgenden OpenGLRenderer-Fehler versucht: "Bitmap zu groß, um in eine Textur hochgeladen zu werden (2432x4320, max = 4096x4096)" Ich vermute also, dass die Höhe 4320 nicht verarbeitet werden kann.
GregM
1
Natürlich ist dies eine richtige Antwort und beantwortet die Frage einfach perfekt! Optimierung der Bildqualität / -größe für große Bilder ... Nun, das ist eine andere Frage!
Ichalos
@ichalos Vielleicht haben Sie gefunden, wonach Sie gesucht haben, aber dies beantwortet nicht die ursprüngliche Frage. Die ursprüngliche Frage betraf das manuelle Rendern auf Leinwand. Eigentlich bin ich mir nicht sicher, wie jemandes erster Versuch, ein Foto
zuzuschneiden
@milosmns Vielleicht hast du recht. Vielleicht hat der Benutzer auf diese Weise versucht, das Problem des manuellen Zuschneidens in die Mitte anzugehen. Es hängt alles von den genauen Bedürfnissen des ursprünglichen Benutzers ab.
Ichalos
9

Sie können den folgenden Code verwenden, der Ihr Problem lösen kann.

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);

Führen Sie die oben beschriebene Methode vor dem Zuschneiden nach dem Aufrufen des Bilds aus, damit Sie mit dem zugeschnittenen Bild das beste Ergebnis erzielen, ohne einen OOM-Fehler zu erhalten.

Weitere Informationen finden Sie in diesem Blog

Hitesh Patel
quelle
1
E / AndroidRuntime (30010): Auslöser: java.lang.IllegalArgumentException: x + width muss <= bitmap.width () sein und dies wegen 4 mal 100px
Stan
9

Hier ein vollständigeres Snippet, das die Mitte einer [Bitmap] beliebiger Dimensionen ausschneidet und das Ergebnis auf die gewünschte [IMAGE_SIZE] skaliert . Sie erhalten also immer ein [croppedBitmap] skaliertes Quadrat der Bildmitte mit einer festen Größe. Ideal für Thumbnails und so.

Es ist eine vollständigere Kombination der anderen Lösungen.

final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();

float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);

Bitmap croppedBitmap;
if (landscape){
    int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
    int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
mschmoock
quelle
8

Wahrscheinlich die bisher einfachste Lösung:

public static Bitmap cropCenter(Bitmap bmp) {
    int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
    return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}

Importe:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
Kirill Kulakov
quelle
3

So korrigieren Sie die @ willsteel-Lösung:

if (landscape){
                int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
            } else {
                int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
            }
Yman
quelle
Dies ist eine Lösung für die WillSteel-Lösung. In diesem Fall ist tempBitmap nur eine Kopie der ursprünglichen (unveränderten) Bitmap oder sich selbst.
Yman
1
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    if (w == size && h == size) return bitmap;
    // scale the image so that the shorter side equals to the target;
    // the longer side will be center-cropped.
    float scale = (float) size / Math.min(w,  h);
    Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
    int width = Math.round(scale * bitmap.getWidth());
    int height = Math.round(scale * bitmap.getHeight());
    Canvas canvas = new Canvas(target);
    canvas.translate((size - width) / 2f, (size - height) / 2f);
    canvas.scale(scale, scale);
    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    if (recycle) bitmap.recycle();
    return target;
}

private static Bitmap.Config getConfig(Bitmap bitmap) {
    Bitmap.Config config = bitmap.getConfig();
    if (config == null) {
        config = Bitmap.Config.ARGB_8888;
    }
    return config;
}
Kakopappa
quelle
1
public Bitmap getResizedBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    int narrowSize = Math.min(width, height);
    int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
    width  = (width  == narrowSize) ? 0 : differ;
    height = (width == 0) ? differ : 0;

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
    bm.recycle();
    return resizedBitmap;
}
Vahe Gharibyan
quelle