web-dev-qa-db-fra.com

Mise à l'échelle du bitmap maintenant les proportions

Je souhaite redimensionner un Bitmap en une largeur et une hauteur dépendantes de l'exécution, où le rapport de format est conservé et où le Bitmap remplit toute la largeur et centre verticalement l'image, en coupant le surplus ou en le remplissant. dans l'espace avec 0 pixels alpha.

Je suis actuellement en train de redessiner moi-même le bitmap en créant un Bitmap de tous les 0 pixels alpha et en traçant l'image Bitmap au-dessus, en redimensionnant à la largeur exacte spécifiée et en maintenant les proportions, , il finit par perdre/bousiller les données de pixels.

Voici comment je le fais:

Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
float originalWidth = originalImage.getWidth(), originalHeight = originalImage.getHeight();
Canvas canvas = new Canvas(background);
float scale = width/originalWidth;
float xTranslation = 0.0f, yTranslation = (height - originalHeight * scale)/2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
canvas.drawBitmap(originalImage, transformation, null);
return background;

Existe-t-il une bibliothèque ou un code plus performant qui peut le faire mieux? J'aimerais que l'image soit aussi nette que possible, mais je savais que ma fonction ne donnerait pas un bon résultat.

Je sais que je pourrais conserver une image correcte en utilisant la mise à l'échelle entière, au lieu de la mise à l'échelle flottante, mais j'ai besoin que la largeur soit remplie à 100%.

De plus, je connais un ImageView's Gravity.CENTER_CROP, qui utilise également la mise à l'échelle des entiers, coupe donc la largeur de l'image alors qu'elle ne le devrait pas.

48
RileyE

Et ça:

Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);

float originalWidth = originalImage.getWidth(); 
float originalHeight = originalImage.getHeight();

Canvas canvas = new Canvas(background);

float scale = width / originalWidth;

float xTranslation = 0.0f;
float yTranslation = (height - originalHeight * scale) / 2.0f;

Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);

Paint paint = new Paint();
Paint.setFilterBitmap(true);

canvas.drawBitmap(originalImage, transformation, Paint);

return background;

J'ai ajouté un Paint pour filtrer le bitmap mis à l'échelle.

66
Streets Of Boston

Ceci respectera maxWidth et maxHeight, ce qui signifie que le bitmap résultant n'aura jamais des dimensions supérieures à celles:

 private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
    if (maxHeight > 0 && maxWidth > 0) {
        int width = image.getWidth();
        int height = image.getHeight();
        float ratioBitmap = (float) width / (float) height;
        float ratioMax = (float) maxWidth / (float) maxHeight;

        int finalWidth = maxWidth;
        int finalHeight = maxHeight;
        if (ratioMax > ratioBitmap) {
            finalWidth = (int) ((float)maxHeight * ratioBitmap);
        } else {
            finalHeight = (int) ((float)maxWidth / ratioBitmap);
        }
        image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
        return image;
    } else {
        return image;
    }
}
88
joaomgcd

Ici, j'ai une solution testée dans laquelle je crée un bitmap redimensionné à partir d'un fichier bitmap:

    int scaleSize =1024;

    public Bitmap resizeImageForImageView(Bitmap bitmap) {
        Bitmap resizedBitmap = null;
        int originalWidth = bitmap.getWidth();
        int originalHeight = bitmap.getHeight();
        int newWidth = -1;
        int newHeight = -1;
        float multFactor = -1.0F;
        if(originalHeight > originalWidth) {
            newHeight = scaleSize ;
            multFactor = (float) originalWidth/(float) originalHeight;
            newWidth = (int) (newHeight*multFactor);
        } else if(originalWidth > originalHeight) {
            newWidth = scaleSize ;
            multFactor = (float) originalHeight/ (float)originalWidth;
            newHeight = (int) (newWidth*multFactor);
        } else if(originalHeight == originalWidth) {
            newHeight = scaleSize ;
            newWidth = scaleSize ;
        }
        resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
        return resizedBitmap;
    }

Notez que j'ai besoin de bitmaps mis à l'échelle qui ont une taille maximale de 4096 x 4096 pixels, mais le rapport de format doit être conservé pendant le redimensionnement. Si vous avez besoin d'autres valeurs pour la largeur ou la hauteur, remplacez simplement les valeurs "4096".

Ceci est juste un ajout à la réponse de Coen mais le problème de son code est la ligne où il calcule le ratio. La division de deux entiers donne un entier et si le résultat est <1, il sera arrondi à 0. L'exception "division par zéro" est alors levée.

22
Christopher Reichel

voici une méthode de ma classe Utils, qui fait le travail:

public static Bitmap scaleBitmapAndKeepRation(Bitmap targetBmp,int reqHeightInPixels,int reqWidthInPixels)
    {
        Matrix matrix = new Matrix();
        matrix .setRectToRect(new RectF(0, 0, targetBmp.getWidth(), targetBmp.getHeight()), new RectF(0, 0, reqWidthInPixels, reqHeightInPixels), Matrix.ScaleToFit.CENTER);
        Bitmap scaledBitmap = Bitmap.createBitmap(targetBmp, 0, 0, targetBmp.getWidth(), targetBmp.getHeight(), matrix, true);
        return scaledBitmap;
    }
20
Gal Rom

solution plus simple: notez que nous définissons la largeur à 500 pixels

 public void scaleImageKeepAspectRatio()
    {
        int imageWidth = scaledGalleryBitmap.getWidth();
        int imageHeight = scaledGalleryBitmap.getHeight();
        int newHeight = (imageHeight * 500)/imageWidth;
        scaledGalleryBitmap = Bitmap.createScaledBitmap(scaledGalleryBitmap, 500, newHeight, false);

    }
6
yehyatt

Cela peut également être fait en calculant le ratio vous-même, comme ceci.

private Bitmap scaleBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    Log.v("Pictures", "Width and height are " + width + "--" + height);

    if (width > height) {
        // landscape
        int ratio = width / maxWidth;
        width = maxWidth;
        height = height / ratio;
    } else if (height > width) {
        // portrait
        int ratio = height / maxHeight;
        height = maxHeight;
        width = width / ratio;
    } else {
        // square
        height = maxHeight;
        width = maxWidth;
    }

    Log.v("Pictures", "after scaling Width and height are " + width + "--" + height);

    bm = Bitmap.createScaledBitmap(bm, width, height, true);
    return bm;
}
6
Coen Damen

Aucune des réponses ci-dessus n'a été travaillée pour moi et je viens de créer une méthode qui définit toutes les dimensions dans les dimensions souhaitées en peignant la zone vide en noir. Voici ma méthode:

/**
 * Scale the image preserving the ratio
 * @param imageToScale Image to be scaled
 * @param destinationWidth Destination width after scaling
 * @param destinationHeight Destination height after scaling
 * @return New scaled bitmap preserving the ratio
 */
public static Bitmap scalePreserveRatio(Bitmap imageToScale, int destinationWidth,
        int destinationHeight) {
    if (destinationHeight > 0 && destinationWidth > 0 && imageToScale != null) {
        int width = imageToScale.getWidth();
        int height = imageToScale.getHeight();

        //Calculate the max changing amount and decide which dimension to use
        float widthRatio = (float) destinationWidth / (float) width;
        float heightRatio = (float) destinationHeight / (float) height;

        //Use the ratio that will fit the image into the desired sizes
        int finalWidth = (int)Math.floor(width * widthRatio);
        int finalHeight = (int)Math.floor(height * widthRatio);
        if (finalWidth > destinationWidth || finalHeight > destinationHeight) {
            finalWidth = (int)Math.floor(width * heightRatio);
            finalHeight = (int)Math.floor(height * heightRatio);
        }

        //Scale given bitmap to fit into the desired area
        imageToScale = Bitmap.createScaledBitmap(imageToScale, finalWidth, finalHeight, true);

        //Created a bitmap with desired sizes
        Bitmap scaledImage = Bitmap.createBitmap(destinationWidth, destinationHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(scaledImage);

        //Draw background color
        Paint paint = new Paint();
        Paint.setColor(Color.BLACK);
        Paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), Paint);

        //Calculate the ratios and decide which part will have empty areas (width or height)
        float ratioBitmap = (float)finalWidth / (float)finalHeight;
        float destinationRatio = (float) destinationWidth / (float) destinationHeight;
        float left = ratioBitmap >= destinationRatio ? 0 : (float)(destinationWidth - finalWidth) / 2;
        float top = ratioBitmap < destinationRatio ? 0: (float)(destinationHeight - finalHeight) / 2;
        canvas.drawBitmap(imageToScale, left, top, null);

        return scaledImage;
    } else {
        return imageToScale;
    }
}

Par exemple;

Supposons que vous avez une image de 100 x 100 mais que la taille souhaitée est de 300x50, cette méthode convertira alors votre image en 50 x 50 et la peindra en une nouvelle image de 300 x 50 (et les champs vides seront en noir). .

Autre exemple: supposons que vous ayez une image au format 600 x 1000 et que les tailles souhaitées soient à nouveau au format 300 x 50. Votre image sera ensuite convertie en 30 x 50 et peinte en une nouvelle image créée au format 300 x 50.

Je pense que c'est ce que ça doit être, Rs.

4
Bahadir Tasdemir

Ajout de RESIZE_CROP à la réponse de Gowrav.

   enum RequestSizeOptions {
    RESIZE_FIT,
    RESIZE_INSIDE,
    RESIZE_EXACT,
    RESIZE_CENTRE_CROP
}
static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
    try {
        if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                options == RequestSizeOptions.RESIZE_INSIDE ||
                options == RequestSizeOptions.RESIZE_EXACT || options == RequestSizeOptions.RESIZE_CENTRE_CROP)) {

            Bitmap resized = null;
            if (options == RequestSizeOptions.RESIZE_EXACT) {
                resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
            } else {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                    resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                }
                if (scale > 1 || options == RequestSizeOptions.RESIZE_CENTRE_CROP) {
                    int smaller_side = (height-width)>0?width:height;
                    int half_smaller_side = smaller_side/2;
                    Rect initialRect = new Rect(0,0,width,height);
                    Rect finalRect = new Rect(initialRect.centerX()-half_smaller_side,initialRect.centerY()-half_smaller_side,
                            initialRect.centerX()+half_smaller_side,initialRect.centerY()+half_smaller_side);
                    bitmap = Bitmap.createBitmap(bitmap,  finalRect.left, finalRect.top, finalRect.width(), finalRect.height(), null, true);
                    //keep in mind we have square as request for cropping, otherwise - it is useless
                    resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
                }

            }
            if (resized != null) {
                if (resized != bitmap) {
                    bitmap.recycle();
                }
                return resized;
            }
        }
    } catch (Exception e) {
        Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
    }
    return bitmap;
}

C’est une superbe bibliothèque d’ArthurHub pour gérer les cultures d’images à la fois par programmation et de manière interactive si vous ne voulez pas réinventer la roue.

Mais si vous préférez une version non gonflée comme moi .., la fonction interne présentée ici est assez sophistiquée pour effectuer le redimensionnement de l’image avec quelques options standard.

/**
 * Resize the given bitmap to the given width/height by the given option.<br>
 */

enum RequestSizeOptions {
    RESIZE_FIT,
    RESIZE_INSIDE,
    RESIZE_EXACT
}

static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
    try {
        if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                options == RequestSizeOptions.RESIZE_INSIDE ||
                options == RequestSizeOptions.RESIZE_EXACT)) {

            Bitmap resized = null;
            if (options == RequestSizeOptions.RESIZE_EXACT) {
                resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
            } else {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                    resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                }
            }
            if (resized != null) {
                if (resized != bitmap) {
                    bitmap.recycle();
                }
                return resized;
            }
        }
    } catch (Exception e) {
        Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
    }
    return bitmap;
}
2
Gowrav
public static Bitmap scaleBitmap(Bitmap bitmap, int wantedWidth, int wantedHeight) {
    float originalWidth = bitmap.getWidth();
    float originalHeight = bitmap.getHeight();
    Bitmap output = Bitmap.createBitmap(wantedWidth, wantedHeight, Config.ARGB_8888);
    Canvas canvas = new Canvas(output);
    Matrix m = new Matrix();

    float scalex = wantedWidth/originalWidth;
    float scaley = wantedHeight/originalHeight;
    float xTranslation = 0.0f, yTranslation = (wantedHeight - originalHeight * scaley)/2.0f;

    m.postTranslate(xTranslation, yTranslation);
    m.preScale(scalex, scaley);
    // m.setScale((float) wantedWidth / bitmap.getWidth(), (float) wantedHeight / bitmap.getHeight());
    Paint paint = new Paint();
    Paint.setFilterBitmap(true);
    canvas.drawBitmap(bitmap, m, Paint);

    return output;
}
2
Pradeep Sodhi

Ma solution était celle-ci, qui conserve le format de l’image et n’exige qu’une seule taille, par exemple si vous avez une image 1920 * 1080 et 1080 * 1920 et que vous souhaitez la redimensionner à 1280, la première sera 1280 * 720 et la seconde. sera de 720 * 1280

public static Bitmap resizeBitmap(final Bitmap temp, final int size) {
        if (size > 0) {
            int width = temp.getWidth();
            int height = temp.getHeight();
            float ratioBitmap = (float) width / (float) height;
            int finalWidth = size;
            int finalHeight = size;
            if (ratioBitmap < 1) {
                finalWidth = (int) ((float) size * ratioBitmap);
            } else {
                finalHeight = (int) ((float) size / ratioBitmap);
            }
            return Bitmap.createScaledBitmap(temp, finalWidth, finalHeight, true);
        } else {
            return temp;
        }
    }
1
Kiskunk

Pour redimensionner l'image, le calcul suivant est simple: considérez l'extrait suivant et suivez-le, 1. Supposons que vous avez une image Imaan avec 720x1280 et que vous souhaitez être ajusté dans une largeur de 420, obtenez le pourcentage de réduction requis par un calcul donné,

originalWidth = 720;
wP = 720/100;
/*  wP = 7.20 is a percentage value */
  1. Maintenant, soustrayez la largeur requise de la largeur d'origine, puis multipliez le résultat par wP. Vous obtiendrez un pourcentage de largeur réduit.

difference = originalWidth - 420; dP = difference/wP;

Ici, dP sera 41,66, cela signifie que vous réduisez la taille de 41,66%. Il faut donc réduire la hauteur de 41,66 (dP) pour conserver la ration ou l'échelle de cette image. Calculez la hauteur comme indiqué ci-dessous,

hP = originalHeight / 100;
//here height percentage will be 1280/100 = 12.80
height = originalHeight - ( hp * dP);
// here 1280 - (12.80 * 41.66) = 746.75

Voici votre échelle appropriée, vous pouvez redimensionner image/bitmap en 420x747. Il retournera l'image redimensionnée sans perdre le rapport/échelle.

Exemple

public static Bitmap scaleToFit(Bitmap image, int width, int height, bool isWidthReference) {
    if (isWidthReference) {
        int originalWidth = image.getWidth();
        float wP = width / 100;
        float dP = ( originalWidth - width) / wP;
        int originalHeight = image.getHeight();
        float hP = originalHeight / 100;
        int height = originalHeight - (hP * dP);
        image = Bitmap.createScaledBitmap(image, width, height, true);
    } else {
        int originalHeight = image.getHeight();
        float hP = height / 100;
        float dP = ( originalHeight - height) / hP;
        int originalWidth = image.getWidth();
        float wP = originalWidth / 100;
        int width = originalWidth - (wP * dP);
        image = Bitmap.createScaledBitmap(image, width, height, true);
    }
    return image;
}

ici, il vous suffit de redimensionner l’image avec la référence du paramètre height ou width pour l’adapter aux critères requis.

0
Kiran Maniya