Skalowana bitmapa z zachowaniem proporcji

Chciałbym przeskalować Bitmap do szerokości i wysokości zależnej od środowiska wykonawczego, gdzie zachowany jest współczynnik proporcji, a Bitmap wypełnia całą szerokość i Wyśrodkowuje obraz w pionie, przycinając nadmiar lub wypełniając lukę 0 pikselami Alfa.

Obecnie sam przerysowuję bitmapę, tworząc Bitmap WSZYSTKICH 0 pikseli alfa i rysując na niej obraz Bitmap, skalując do dokładnie określonej szerokości i zachowując proporcje, jednak kończy się to utrata / zepsucie danych pikseli.

Oto Jak to robię:

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;

Czy jest jakaś Biblioteka, czy jakiś lepszy kod, który może to zrobić lepiej? Chciałbym, aby obraz wyglądał jak najbardziej rześko, ale wiedziałem, że moja funkcja nie zapewni dobrego rezultatu.

Wiem, że obraz mógłby pozostać w porządku, używając skalowania liczb całkowitych, zamiast skalowania float, ale szerokość musi być w 100% wypełniona.

Również, Wiem o ImageView's Gravity.CENTER_CROP zdolność, jednak, że również wykorzystuje skalowanie całkowite, więc odcina szerokość obrazu, gdy nie powinno.

Author: RileyE, 2013-03-15

11 answers

A co z tym:

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;

Dodałem paint do filtrowania skalowanej bitmapy.

 56
Author: Streets Of Boston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-26 15:03:03

Będzie to respektować maxWidth i maxHeight, co oznacza, że wynikowa bitmapa nigdy nie będzie miała większych wymiarów niż te:

 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;
    }
}
 62
Author: joaomgcd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-22 13:00:00

Tutaj mam Przetestowane rozwiązanie, w którym tworzę skalowaną bitmapę z pliku bitmapowego:

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

Zauważ, że potrzebuję skalowanych Bitmap, które mają maksymalny rozmiar 4096x4096 pikseli, ale współczynnik proporcji musi być zachowany podczas zmiany rozmiaru. Jeśli potrzebujesz innych wartości dla szerokości lub wysokości, po prostu zamień wartości "4096".

To tylko dodatek do odpowiedzi Coena, ale problemem w jego kodzie jest linia, w której oblicza stosunek. Dzielenie dwóch liczb całkowitych daje liczbę całkowitą i jeśli wynik jest

 22
Author: Christopher Reichel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-28 12:54:36

Oto metoda z mojej klasy Utils, która wykonuje zadanie:

public static Bitmap scaleBitmapAndKeepRation(Bitmap TargetBmp,int reqHeightInPixels,int reqWidthInPixels)
    {
        Matrix m = new Matrix();
        m.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(), m, true);
        return scaledBitmap;
    }
 6
Author: Gal Rom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 06:52:58

Prostsze rozwiązanie: Uwaga ustawiamy szerokość na 500 pikseli

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

    }
 4
Author: yehyatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-10 11:04:27

Można to również zrobić, obliczając współczynnik samodzielnie, w ten sposób.

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;
}
 3
Author: Coen Damen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-18 08:44:39
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
Author: Pradeep Sodhi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-09 12:38:23

Żadna z powyższych odpowiedzi nie zadziałała dla mnie i właśnie stworzyłem metodę, która ustawia wszystkie wymiary na pożądane z malowaniem pustego obszaru na czarno. Oto moja metoda:

/**
 * 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;
    }
}

Na przykład;

Załóżmy, że masz obraz 100 x 100, ale pożądany rozmiar to 300x50, wtedy ta metoda przekonwertuje Twój obraz do 50 x 50 i pomaluje go na nowy obraz, który ma wymiary 300 x 50 (a puste pliki będą czarne).

Inny przykład: powiedzmy, że ty mieć obraz jako 600 x 1000 i pożądane rozmiary są 300 x 50 ponownie, a następnie obraz zostanie przekonwertowany na 30 x 50 i malowane na nowo utworzony obraz, który ma rozmiary jak 300 x 50.

Myślę, że tak to musi być, Rs.

 2
Author: Bahadir Tasdemir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-27 16:31:07

Dodano RESIZE_CROP do odpowiedzi Gowrava.

   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;
}
 2
Author: Vodyanikov Andrew Anatolevich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-02 07:09:20

Moje rozwiązanie było takie, które zachowuje proporcje i wymaga tylko jednego rozmiaru, na przykład jeśli masz obraz 1920*1080 i 1080*1920 i chcesz zmienić jego rozmiar na 1280, pierwszy będzie 1280 * 720, a drugi będzie 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
Author: Kiskunk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-06 13:36:56

Jest to niesamowita biblioteka od ArthurHub do obsługi zbiorów obrazów zarówno programowo, jak i interaktywnie, jeśli nie chcesz odkrywać koła na nowo.

Ale jeśli wolisz wersję bez nadęty jak ja.. funkcja wewnętrzna pokazana tutaj jest dość wyrafinowana do skalowania obrazu z kilkoma standardowymi opcjami
/**
 * 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;
}
 1
Author: Gowrav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 05:37:34