Centrum kadrowania dla Androida bitmap

150

Mam mapy bitowe, które są kwadratami lub prostokątami. Biorę najkrótszą stronę i robię coś takiego:

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

Następnie skaluję ją do mapy bitowej 144 x 144, używając tego:

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

Problem polega na tym, że przycina lewy górny róg oryginalnej mapy bitowej. Czy ktoś ma kod do przycięcia środka mapy bitowej?

Maurice
źródło

Odpowiedzi:

354

wprowadź opis obrazu tutaj

Można to osiągnąć za pomocą: Bitmap.createBitmap (źródło, x, y, szerokość, wysokość)

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
źródło
1
Zmodyfikowano odpowiedź tak, aby rzeczywista docelowa mapa bitowa była kwadratowa.
Joram van den Boezem
1
@Lumis: Dlaczego wycofałeś wersję 3? Wyglądało na to, że jest to poprawne. Twoja aktualna wersja tworzy prawidłowy punkt początkowy, ale następnie zawiera pozostałą część zbyt długiego boku. Na przykład, mając 100x1000obraz, otrzymujesz 100x550obraz.
Guvante
Dzięki, masz rację, format to Bitmap.createBitmap (źródło, x, y, szerokość, wysokość)
Lumis
11
Zobacz moją odpowiedź na temat korzystania z wbudowanej metody ThumbnailUtils.extractThumbnail (). Po co odkrywać koło na nowo ??? stackoverflow.com/a/17733530/1103584
DiscDev
1
to rozwiązanie jest krótsze niż utworzenie płótna, a następnie narysowanie na nim rysunku. wykonuje również mniej przetwarzania niż rozwiązanie ThumbnailUtils (które oblicza rozmiar próbki, aby dowiedzieć się, jak skalować).
yincrash
319

Chociaż większość powyższych odpowiedzi zapewnia sposób na zrobienie tego, istnieje już wbudowany sposób na osiągnięcie tego i jest to 1 wiersz kodu ( 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());
}

Jeśli chcesz, aby obiekt bitmapowy został poddany recyklingowi, możesz podać opcje, które to robią:

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

Od: ThumbnailUtils Documentation

public static Bitmap extractThumbnail (źródło mapy bitowej, szerokość wewnętrzna, wysokość int)

Dodano na poziomie API 8 Tworzy wyśrodkowaną mapę bitową o żądanym rozmiarze.

Parametry źródło oryginalnej bitmapy źródłowej szerokość docelowa szerokość wysokość docelowa wysokość

Czasami wypadały mi błędy pamięci, gdy korzystałem z zaakceptowanej odpowiedzi, a użycie ThumbnailUtils rozwiązało te problemy. Ponadto jest to znacznie czystsze i bardziej wielokrotnego użytku.

DiscDev
źródło
6
+1 Myślę, że musiałbyś ulepszyć ten kod i zamiast używać 400px, podać najkrótszy rozmiar bmp, aby zapewnić alternatywę dla oryginalnego postu powyżej. Ale dziękuję za zwrócenie nam uwagi, wydaje się to bardzo użyteczną funkcją. Szkoda, że ​​wcześniej tego nie widziałem ...
Lumis
Tak, właśnie zakodowałem na stałe 400, aby dać konkretny przykład ... szczegóły zależą od osoby wdrażającej :)
DiscDev
4
@DiscDev - to musiałaby być dosłownie jedna z najbardziej przydatnych odpowiedzi w całej witrynie. Poważnie. To dziwne z pytaniami na Androida - często możesz szukać przez dwie godziny, zanim znajdziesz prostą i oczywistą odpowiedź. Nie wiem, jak ci wystarczająco podziękować. Bounty na trasie!
Fattie
2
Możesz nawet użyć trochę innej funkcji, aby ponownie przetworzyć starą mapę bitową: = ThumbnailUtils.extractThumbnail (bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
programista Androida
1
To najpotężniejsza odpowiedź, jaką do tej pory widziałem na pytanie o Androida. Widziałem 20 różnych rozwiązań, jak skalować / przycinać / zmniejszać / zmieniać rozmiar i tak dalej Bitmap na Androida. Wszystkie odpowiedzi są różne. A ten zajmuje tylko 1 linię i działa dokładnie tak, jak oczekiwano. Dziękuję Ci.
Alex Sorokoletov
12

Czy rozważałeś zrobienie tego od layout.xml? Można ustawić dla ImageViewtej ScaleType do android:scaleType="centerCrop"i ustaw wymiary obrazu w ImageViewwewnątrz layout.xml.

Ovidiu Latcu
źródło
Wypróbowałem ten pomysł z następującym błędem OpenGLRenderer: „Bitmapa jest zbyt duża, aby można ją było przesłać do tekstury (2432x4320, maks. = 4096x4096)” Zgaduję, że wysokość 4320 nie może zostać przetworzona.
GregM,
1
Oczywiście to jest poprawna odpowiedź i odpowiada na pytanie po prostu idealnie! Optymalizacja jakości / rozmiaru obrazu dla dużych obrazów ... Cóż, to inna kwestia!
ichalos
@ichalos Być może znalazłeś to, czego szukałeś, ale to nie odpowiada na pierwotne pytanie. Pierwotne pytanie dotyczyło ręcznego renderowania na płótnie ... właściwie nie jestem pewien, jak ktokolwiek pierwsza próba kadrowania zdjęcia byłaby ręcznym renderowaniem na płótnie, ale hej, dobrze, że znalazłeś tutaj rozwiązanie :)
milosmns
@milosmns Może masz rację. Może właśnie w ten sposób użytkownik próbował podejść do problemu ręcznego przycinania do środka. Wszystko zależy od dokładnych potrzeb pierwotnego użytkownika.
ichalos
9

Możesz użyć następującego kodu, który może rozwiązać twój problem.

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

Powyższa metoda wykonuje postScalling obrazu przed kadrowaniem, dzięki czemu można uzyskać najlepszy wynik z przyciętym obrazem bez błędu OOM.

Aby uzyskać więcej informacji, możesz polecić tego bloga

Hitesh Patel
źródło
1
E / AndroidRuntime (30010): Spowodowane przez: java.lang.IllegalArgumentException: x + width musi wynosić <= bitmap.width (), a to z powodu 4 razy 100px
Stan
9

Tutaj pełniejszy fragment, który wycina środek [bitmapy] o dowolnych wymiarach i skaluje wynik do żądanego [IMAGE_SIZE] . Więc zawsze otrzymasz [croppedBitmap] skalowany kwadrat środka obrazu o stałym rozmiarze. idealny do tworzenia miniatur i tak dalej.

Jest to pełniejsze połączenie pozostałych rozwiązań.

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
źródło
8

Prawdopodobnie najłatwiejsze dotychczas rozwiązanie:

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

import:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
Kirill Kulakov
źródło
3

Aby poprawić rozwiązanie @willsteel:

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
źródło
jest to poprawka do rozwiązania WillSteel. W tym przypadku tempBitmap jest po prostu kopią oryginalnej (niezmienionej) mapy bitowej lub samej siebie.
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
źródło
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
źródło