Android Crop Center de Bitmap


150

Tengo mapas de bits que son cuadrados o rectángulos. Tomo el lado más corto y hago algo como esto:

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

Luego lo escalo a un mapa de bits de 144 x 144 usando esto:

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

El problema es que recorta la esquina superior izquierda del mapa de bits original. ¿Alguien tiene el código para recortar el centro del mapa de bits?

Respuestas:


354

ingrese la descripción de la imagen aquí

Esto se puede lograr con: Bitmap.createBitmap (fuente, x, y, ancho, alto)

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

1
Editó la respuesta para que el mapa de bits de destino real sea un cuadrado.
Joram van den Boezem el

1
@Lumis: ¿Por qué revierte la revisión 3? Parecía ser una correcta válida. Su versión actual crea el punto de partida correcto pero luego incluye el resto del lado demasiado largo. Por ejemplo, dada una 100x1000imagen, obtienes una 100x550imagen.
Guvante

Gracias, tiene razón, el formato es Bitmap.createBitmap (fuente, x, y, ancho, alto)
Lumis

11
Vea mi respuesta sobre el uso del método incorporado ThumbnailUtils.extractThumbnail (). ¿¿¿Por qué reinventar la rueda??? stackoverflow.com/a/17733530/1103584
DiscDev

1
esta solución es más corta que crear un lienzo y luego dibujar un dibujo en él. también procesa menos que la solución ThumbnailUtils (que calcula el tamaño de la muestra para descubrir cómo escalar).
yincrash

319

Si bien la mayoría de las respuestas anteriores proporcionan una forma de hacer esto, ya hay una forma integrada de lograr esto y es 1 línea de código ( 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());
}

Si desea que el objeto de mapa de bits se recicle, puede pasar opciones que lo hagan así:

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

De: ThumbnailUtils Documentation

Extracto público de mapa de bits estático (fuente de mapa de bits, ancho int, alto int)

Agregado en API nivel 8 Crea un mapa de bits centrado del tamaño deseado.

Parámetros fuente original mapa de bits ancho fuente ancho objetivo altura altura objetivo

Algunas veces me estaba quedando sin memoria cuando usaba la respuesta aceptada, y usar ThumbnailUtils resolvió esos problemas por mí. Además, esto es mucho más limpio y más reutilizable.


66
+1 Creo que tendrías que mejorar este código y, en lugar de usar 400px, pasa el tamaño más pequeño de bmp para proporcionar una alternativa para la publicación original anterior. Pero gracias por informarnos de esto, parece una función muy útil. Lástima que no lo haya visto antes ...
Lumis

Correcto, acabo de codificar 400 para dar un ejemplo concreto ... los detalles
dependen

44
@DiscDev: esta debería ser literalmente una de las respuestas más útiles en todo este sitio. Seriamente. Es extraño con las preguntas de Android: a menudo puede buscar durante dos horas antes de encontrar la respuesta obvia simple. No sé cómo agradecerte lo suficiente. ¡Recompensa en el camino!
Fattie

2
Incluso podría usar una función un poco diferente, para reciclar también el mapa de bits anterior: = ThumbnailUtils.extractThumbnail (mapa de bits, ancho, altura, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
desarrollador de Android

1
Esta es la respuesta más poderosa que he visto hasta ahora para la pregunta de Android. He visto como 20 soluciones diferentes cómo escalar / recortar / reducir / cambiar el tamaño y así sucesivamente Bitmap en Android. Todas las respuestas son diferentes. Y este solo toma 1 línea y funciona exactamente como se esperaba. Gracias.
Alex Sorokoletov

12

¿Has considerado hacer esto desde el layout.xml? Se podría establecer para su ImageViewla ScaleType a android:scaleType="centerCrop"y establecer las dimensiones de la imagen en el ImageViewinterior de la layout.xml.


Probé esta idea con el siguiente error de OpenGLRenderer: "Mapa de bits demasiado grande para cargarlo en una textura (2432x4320, máx = 4096x4096)" Entonces, supongo que la altura 4320 no se puede procesar.
GregM

1
Por supuesto, esta es una respuesta correcta y la pregunta es perfecta. Optimizando la calidad / tamaño de la imagen para imágenes grandes ... Bueno, ¡es una pregunta diferente!
ichalos

@ichalos Quizás haya encontrado lo que estaba buscando, pero esto no responde a la pregunta original. La pregunta original era sobre el renderizado manual en lienzo ... en realidad, no estoy seguro de cómo el primer intento de alguien para recortar una foto sería renderizar manualmente en un lienzo, pero bueno, que haya encontrado una solución aquí :)
milosmns

@milosmns Quizás tengas razón. Tal vez así es como el usuario intentó abordar el problema del recorte manual al centro. Todo depende de las necesidades exactas del usuario original.
ichalos

9

Puede usar el siguiente código que puede resolver su problema.

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

El método anterior realiza el escalado posterior de la imagen antes de recortar, para que pueda obtener el mejor resultado con la imagen recortada sin obtener el error OOM.

Para más detalles puedes consultar este blog


1
E / AndroidRuntime (30010): Causado por: java.lang.IllegalArgumentException: x + width debe ser <= bitmap.width () y esto debido a 4 veces 100px
Stan

9

Aquí hay un fragmento más completo que recorta el centro de un [mapa de bits] de dimensiones arbitrarias y escala el resultado a su [IMAGE_SIZE] deseado . Entonces siempre obtendrás un [croppedBitmap] cuadrado escalado del centro de la imagen con un tamaño fijo. ideal para miniaturas y tal.

Es una combinación más completa de las otras soluciones.

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

8

Probablemente la solución más fácil hasta ahora:

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

importaciones:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;

3

Para corregir la solución @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);
            }

Esta es una solución a la solución WillSteel. En este caso, tempBitmap es solo una copia del mapa de bits original (sin modificaciones), o de sí mismo.
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;
}

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;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.