it-swarm.com.de

Ändern Sie die Größe einer großen Bitmap-Datei in eine skalierte Ausgabedatei unter Android

Ich habe eine große Bitmap (sagen wir 3888x2592) in einer Datei. Jetzt möchte ich die Größe des Bitmaps auf 800x533 ändern und es in einer anderen Datei speichern. Normalerweise würde ich das Bitmap skalieren, indem ich die Bitmap.createBitmap-Methode aufrufe. Es wird jedoch eine Quellbitmap als erstes Argument benötigt, die ich beim Laden nicht angeben kann Das Originalbild in ein Bitmap-Objekt würde natürlich den Speicher überschreiten (siehe hier zum Beispiel).

Ich kann die Bitmap auch nicht lesen, zum Beispiel mit BitmapFactory.decodeFile(file, options) und einem BitmapFactory.Options.inSampleSize, da ich sie auf eine genaue Breite und Höhe skalieren möchte. Die Verwendung von inSampleSize würde die Bitmap auf 972x648 (bei Verwendung von inSampleSize=4) oder auf 778x518 (bei Verwendung von inSampleSize=5, was nicht einmal eine Potenz von 2 ist) ändern.

Ich möchte auch vermeiden, das Bild mit inSampleSize mit beispielsweise 972x648 in einem ersten Schritt zu lesen und dann in einem zweiten Schritt genau auf 800x533 zu skalieren, da die Qualität im Vergleich zu einer direkten Größenänderung des Originalbilds schlecht wäre.

Um meine Frage zusammenzufassen: Gibt es eine Möglichkeit, eine große Image-Datei mit 10 MP oder mehr zu lesen und in einer neuen Image-Datei zu speichern, deren Größe auf eine bestimmte neue Breite und Höhe geändert wird, ohne eine OutOfMemory-Ausnahme zu erhalten?

Ich habe auch BitmapFactory.decodeFile(file, options) ausprobiert und die Werte für Options.outHeight und Options.outWidth manuell auf 800 und 533 gesetzt, dies funktioniert jedoch nicht.

201
Manuel

Nein. Ich würde mich sehr freuen, wenn mich jemand korrigiert, aber ich habe den Ansatz von load/size akzeptiert, den Sie als Kompromiss versucht haben.

Hier sind die Schritte für alle, die surfen:

  1. Berechnen Sie das maximal mögliche inSampleSize, das immer noch ein Bild ergibt, das größer als Ihr Ziel ist.
  2. Laden Sie das Bild mit BitmapFactory.decodeFile(file, options) und übergeben Sie inSampleSize als Option.
  3. Verändern Sie die Größe mit Bitmap.createScaledBitmap() auf die gewünschten Abmessungen.
140
Justin

Justin Antwort in Code übersetzt (funktioniert perfekt für mich):

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();



    int scale = 1;
    while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", 
       orig-height: " + options.outHeight);

    Bitmap resultBitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        options = new BitmapFactory.Options();
        options.inSampleSize = scale;
        resultBitmap = BitmapFactory.decodeStream(in, null, options);

        // resize to desired dimensions
        int height = resultBitmap.getHeight();
        int width = resultBitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, 
           (int) y, true);
        resultBitmap.recycle();
        resultBitmap = scaledBitmap;

        System.gc();
    } else {
        resultBitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
       resultBitmap.getHeight());
    return resultBitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}
90
Ofir

Dies ist "Mojo Risins und" Ofirs Lösungen "kombiniert". Dadurch erhalten Sie ein proportional angepasstes Bild mit den Grenzen von maximaler Breite und maximaler Höhe.

  1. Es liest nur Metadaten, um die Originalgröße zu erhalten (options.inJustDecodeBounds)
  2. Es verwendet eine rigrierte Größenänderung, um Speicher zu sparen (itmap.createScaledBitmap)
  3. Es verwendet ein genau angepasstes Bild, das auf dem zuvor erstellten groben Bitamp basiert.

Für mich hat es eine gute Leistung bei 5 MegaPixel-Bildern und darunter ergeben.

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

    // decode image size (decode metadata only, not the whole image)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();
    in = null;

    // save width and height
    inWidth = options.outWidth;
    inHeight = options.outHeight;

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // resize bitmap
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}
41
blubl

Warum nicht die API verwenden?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);
22
Bostone

Die andere hervorragende Antwort, die ich bisher gesehen habe, ist der beste Code, den ich bisher dafür gesehen habe, in der Dokumentation des Tools zum Fotografieren.

Siehe den Abschnitt "Decodieren eines skalierten Bildes".

http://developer.Android.com/training/camera/photobasics.html

Die Lösung, die es vorschlägt, ist eine Größenanpassung und dann eine Skalierungslösung wie die anderen hier, aber es ist ziemlich ordentlich.

Ich habe den folgenden Code aus praktischen Gründen als Ready-to-Go-Funktion kopiert.

private void setPic(String imagePath, ImageView destination) {
    int targetW = destination.getWidth();
    int targetH = destination.getHeight();
    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    destination.setImageBitmap(bitmap);
}
19
Alex

Nach dem Lesen dieser Antworten und Android-Dokumentation hier der Code zum Ändern der Bitmap-Größe, ohne sie in den Speicher zu laden:

public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    //inJustDecodeBounds = true <-- will not load the bitmap into memory
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    return(bitmap);
}
18
penduDev

Wenn ich große Bitmaps habe und sie in ihrer Größe decodieren möchte, verwende ich Folgendes

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
5
Mojo Risin

Dies kann für andere Personen nützlich sein, die sich diese Frage ansehen. Ich habe den Code von Justin neu geschrieben, damit die Methode auch das gewünschte Zielgrößenobjekt erhalten kann. Dies funktioniert sehr gut bei der Verwendung von Canvas. Alles Gute sollte an Justin für seinen großartigen anfänglichen Code gehen.

    private Bitmap getBitmap(int path, Canvas canvas) {

        Resources resource = null;
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            resource = getResources();

            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(resource, path, options);

            int scale = 1;
            while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                  IMAGE_MAX_SIZE) {
               scale++;
            }
            Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

            Bitmap pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeResource(resource, path, options);

                // resize to desired dimensions
                int height = canvas.getHeight();
                int width = canvas.getWidth();
                Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

                double y = Math.sqrt(IMAGE_MAX_SIZE
                        / (((double) width) / height));
                double x = (y / height) * width;

                Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;

                System.gc();
            } else {
                pic = BitmapFactory.decodeResource(resource, path);
            }

            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    }

Der Code von Justin ist sehr effektiv, um den Aufwand bei der Arbeit mit großen Bitmaps zu reduzieren. 

5
Music Monkey

Ich weiß nicht, ob meine Lösung die beste Methode ist, aber ich habe das Laden einer Bitmap mit der gewünschten Skalierung mithilfe der Optionen inDensity und inTargetDensity erreicht. inDensity ist anfangs 0, wenn keine zeichnungsfähige Ressource geladen wird. Dieser Ansatz dient dem Laden von Nicht-Ressourcen-Images.

Die Variablen imageUri, maxImageSideLength und context sind Parameter meiner Methode. Ich habe nur die Methodenimplementierung ohne die Umhüllung AsyncTask zur Verdeutlichung bereitgestellt.

            ContentResolver resolver = context.getContentResolver();
            InputStream is;
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Options opts = new Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, opts);

            // scale the image
            float maxSideLength = maxImageSideLength;
            float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
            // do not upscale!
            if (scaleFactor < 1) {
                opts.inDensity = 10000;
                opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
            }
            opts.inJustDecodeBounds = false;

            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }

            return bitmap;
4
cybergen

Unter Berücksichtigung der Tatsache, dass Sie die Größe exakt anpassen möchten und so viel Qualität wie nötig beibehalten möchten, sollten Sie dies versuchen.

  1. Ermitteln Sie die Größe des skalierten Bildes mit dem Aufruf von BitmapFactory.decodeFile und der Angabe der checkSizeOptions.inJustDecodeBounds
  2. Berechnen Sie das maximum inSampleSize, das Sie auf Ihrem Gerät verwenden können, um den Speicher nicht zu überschreiten. bitmapSizeInBytes = 2 * width * height; In der Regel wäre inSampleSize = 2 in Ordnung, da Sie nur 2 * 1944x1296) = 4,8 Mbps benötigen, was sich im Speicher befinden sollte
  3. Verwenden Sie BitmapFactory.decodeFile mit inSampleSize, um die Bitmap zu laden
  4. Skalieren Sie die Bitmap auf die exakte Größe. 

Motivation: Durch die Skalierung mit mehreren Schritten können Sie eine bessere Bildqualität erzielen. Es gibt jedoch keine Garantie dafür, dass es besser funktioniert als mit der Verwendung von high inSampleSize. Eigentlich denke ich, dass Sie auch inSampleSize wie 5 (nicht 2) verwenden können, um eine direkte Skalierung in einem Vorgang durchzuführen. Oder verwenden Sie einfach 4 und dann können Sie dieses Bild in der Benutzeroberfläche verwenden. Wenn Sie es an den Server senden, können Sie es auf die serverseitige Größe genau skalieren, um erweiterte Skalierungstechniken verwenden zu können.

Hinweise: Wenn die in Schritt 3 geladene Bitmap mindestens viermal so groß ist (also 4 * targetWidth <width), können Sie wahrscheinlich mehrere Größenanpassungen verwenden, um eine bessere Qualität zu erzielen. zumindest funktioniert das in generischem Java. In Android haben Sie nicht die Möglichkeit, die für die Skalierung verwendete Interpolation anzugeben http://today.Java.net/pub/a/today/2007/ 04/03/bedrohlich-von-image-getscaledinstance.html

2
Andrey Chorniy

Der obige Code wurde etwas sauberer. InputStreams haben endlich eine enge Umhüllung, um sicherzustellen, dass sie auch geschlossen werden:

*Hinweis
Eingabe: InputStream ist, int w, int h
Ausgabe: Bitmap

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }
2
user1327738

Ich habe Code wie folgt verwendet:

  String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg";
  BitmapFactory.Options options=new BitmapFactory.Options();
  InputStream is=new FileInputStream(filePath);
  BitmapFactory.decodeStream(is, null, options);
  is.close();
  is=new FileInputStream(filePath);
  // here w and h are the desired width and height
  options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired...
  // bmp is the resized bitmap
  Bitmap bmp=BitmapFactory.decodeStream(is, null, options);
  is.close();
  Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

Ich habe versucht, das Originalbild ist 1230 x 1230, und die Bitmap sagt, es sei 330 x 330.
Wenn ich 2590 x 3849 ausprobiert habe, bekomme ich OutOfMemoryError.

Ich habe es verfolgt, es wirft immer noch OutOfMemoryError in der Zeile "BitmapFactory.decodeStream (is, null, options);".

2
RRTW

Hier ist ein Artikel, der einen anderen Ansatz zur Größenänderung verwendet. Es wird versuchen, die größtmögliche Bitmap auf der Grundlage des verfügbaren Arbeitsspeichers in den Speicher zu laden, und dann die Transformationen ausführen.

http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-Android/

1
Theo

Um das Bild auf die "richtige" Weise zu skalieren, ohne Pixel zu überspringen, müssen Sie den Bilddecoder einhaken, um die Downsampling-Reihe zeilenweise durchzuführen. Android (und die dazugehörige Skia-Bibliothek) bietet keine solchen Haken, so dass Sie Ihre eigenen Rollen haben müssen. Angenommen, Sie sprechen von JPEG-Bildern, wäre es am besten, wenn Sie libjpeg direkt in C verwenden.

In Anbetracht der Komplexität ist die Verwendung der zweistufigen Subsample-Then-Rescale-Methode wahrscheinlich am besten für Apps mit Bildvorschau-Typ.

1
D0SBoots

Wenn Sie unbedingt die Größe eines Schritts ändern möchten, können Sie wahrscheinlich die gesamte Bitmap laden, wennAndroid:largeHeap = true, aber wie Sie sehen, ist dies nicht wirklich ratsam.

Von docs: Android: largeHeap Gibt an, ob die Prozesse Ihrer Anwendung mit einem großen Dalvik-Heap erstellt werden sollen. Dies gilt für alle Prozesse, die für die Anwendung erstellt wurden. Es gilt nur für die erste Anwendung, die in einen Prozess geladen wird. Wenn Sie eine gemeinsam genutzte Benutzer-ID verwenden, um mehreren Anwendungen die Verwendung eines Prozesses zu gestatten, müssen sie alle diese Option konsequent verwenden, da sie sonst zu unvorhersehbaren Ergebnissen führen. Die meisten Apps sollten dies nicht benötigen und sollten sich stattdessen auf die Reduzierung des Gesamtspeichers konzentrieren Verwendung für verbesserte Leistung. Die Aktivierung dieser Option garantiert auch keine feste Erhöhung des verfügbaren Arbeitsspeichers, da einige Geräte durch ihren insgesamt verfügbaren Arbeitsspeicher eingeschränkt werden.

1
PSIXO

Das hat bei mir funktioniert. Die Funktion ruft einen Pfad zu einer Datei auf der SD-Karte ab und gibt eine Bitmap in der maximal darstellbaren Größe zurück. Der Code stammt von Ofir mit einigen Änderungen, z. B. einer Image-Datei auf SD, statt einer Ressource Objekt anzeigen.

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}
0
Penta

Ich verwende Integer.numberOfLeadingZeros, um die beste Probengröße und bessere Leistung zu berechnen.

Vollständiger Code in Kotlin:

@Throws(IOException::class)
fun File.decodeBitmap(options: BitmapFactory.Options): Bitmap? {
    return inputStream().use {
        BitmapFactory.decodeStream(it, null, options)
    }
}

@Throws(IOException::class)
fun File.decodeBitmapAtLeast(
        @androidx.annotation.IntRange(from = 1) width: Int,
        @androidx.annotation.IntRange(from = 1) height: Int
): Bitmap? {
    val options = BitmapFactory.Options()

    options.inJustDecodeBounds = true
    decodeBitmap(options)

    val ow = options.outWidth
    val oh = options.outHeight

    if (ow == -1 || oh == -1) return null

    val w = ow / width
    val h = oh / height

    if (w > 1 && h > 1) {
        val p = 31 - maxOf(Integer.numberOfLeadingZeros(w), Integer.numberOfLeadingZeros(h))
        options.inSampleSize = 1 shl maxOf(0, p)
    }
    options.inJustDecodeBounds = false
    return decodeBitmap(options)
}
0
lymoge

Hier ist der Code, den ich verwende, der keine Probleme mit der Dekodierung großer Bilder im Speicher von Android hat. Ich konnte Bilder größer als 20 MB decodieren, solange meine Eingabeparameter bei 1024 x 1024 liegen. Sie können die zurückgegebene Bitmap in einer anderen Datei speichern. Unter dieser Methode befindet sich eine weitere Methode, mit der ich auch Bilder auf eine neue Bitmap skalieren kann. Fühlen Sie sich frei, diesen Code nach Belieben zu verwenden.

/*****************************************************************************
 * public decode - decode the image into a Bitmap
 * 
 * @param xyDimension
 *            - The max XY Dimension before the image is scaled down - XY =
 *            1080x1080 and Image = 2000x2000 image will be scaled down to a
 *            value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image - a value of "null" if there is an issue decoding
 *         image dimension
 * 
 * @throws FileNotFoundException
 *             - If the image has been removed while this operation is
 *             taking place
 */
public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException
{
    // The Bitmap to return given a Uri to a file
    Bitmap bitmap = null;
    File file = null;
    FileInputStream fis = null;
    InputStream in = null;

    // Try to decode the Uri
    try
    {
        // Initialize scale to no real scaling factor
        double scale = 1;

        // Get FileInputStream to get a FileDescriptor
        file = new File( this.imageUri.getPath() );

        fis = new FileInputStream( file );
        FileDescriptor fd = fis.getFD();

        // Get a BitmapFactory Options object
        BitmapFactory.Options o = new BitmapFactory.Options();

        // Decode only the image size
        o.inJustDecodeBounds = true;
        o.inPreferredConfig = bitmapConfig;

        // Decode to get Width & Height of image only
        BitmapFactory.decodeFileDescriptor( fd, null, o );
        BitmapFactory.decodeStream( null );

        if( o.outHeight > xyDimension || o.outWidth > xyDimension )
        {
            // Change the scale if the image is larger then desired image
            // max size
            scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) );
        }

        // Decode with inSampleSize scale will either be 1 or calculated value
        o.inJustDecodeBounds = false;
        o.inSampleSize = (int) scale;

        // Decode the Uri for real with the inSampleSize
        in = new BufferedInputStream( fis );
        bitmap = BitmapFactory.decodeStream( in, null, o );
    }
    catch( OutOfMemoryError e )
    {
        Log.e( DEBUG_TAG, "decode : OutOfMemoryError" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "decode : NullPointerException" );
        e.printStackTrace();
    }
    catch( RuntimeException e )
    {
        Log.e( DEBUG_TAG, "decode : RuntimeException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "decode : FileNotFoundException" );
        e.printStackTrace();
    }
    catch( IOException e )
    {
        Log.e( DEBUG_TAG, "decode : IOException" );
        e.printStackTrace();
    }

    // Save memory
    file = null;
    fis = null;
    in = null;

    return bitmap;

} // decode

ANMERKUNG: Methoden haben nichts miteinander zu tun, es sei denn, die createScaledBitmap-Methode ruft die decodierte Methode auf. Die Breite und Höhe der Notiz kann vom Originalbild abweichen.

/*****************************************************************************
 * public createScaledBitmap - Creates a new bitmap, scaled from an existing
 * bitmap.
 * 
 * @param dstWidth
 *            - Scale the width to this dimension
 * @param dstHeight
 *            - Scale the height to this dimension
 * @param xyDimension
 *            - The max XY Dimension before the original image is scaled
 *            down - XY = 1080x1080 and Image = 2000x2000 image will be
 *            scaled down to a value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image scaled - a value of "null" if there is an issue
 * 
 */
public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig )
{
    Bitmap scaledBitmap = null;

    try
    {
        Bitmap bitmap = this.decode( xyDimension, bitmapConfig );

        // Create an empty Bitmap which will contain the new scaled bitmap
        // This scaled bitmap should be the size we want to scale the
        // original bitmap too
        scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig );

        float ratioX = dstWidth / (float) bitmap.getWidth();
        float ratioY = dstHeight / (float) bitmap.getHeight();
        float middleX = dstWidth / 2.0f;
        float middleY = dstHeight / 2.0f;

        // Used to for scaling the image
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale( ratioX, ratioY, middleX, middleY );

        // Used to do the work of scaling
        Canvas canvas = new Canvas( scaledBitmap );
        canvas.setMatrix( scaleMatrix );
        canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) );
    }
    catch( IllegalArgumentException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" );
        e.printStackTrace();
    }

    return scaledBitmap;
} // End createScaledBitmap
0
user560663
 Bitmap yourBitmap;
 Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

oder:

 resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);
0

Es gibt einen großartigen Artikel zu diesem Problem auf der Android-Entwickler-Website: Große Bitmaps effizient laden

0
Muzikant