Ridimensiona un file bitmap di grandi dimensioni in un file di output ridimensionato su Android


218

Ho una grande bitmap (diciamo 3888x2592) in un file. Ora, voglio ridimensionare quella bitmap a 800x533 e salvarla in un altro file. Normalmente ridimensionerei la bitmap chiamando il Bitmap.createBitmapmetodo ma ha bisogno di una bitmap di origine come primo argomento, che non posso fornire perché il caricamento dell'immagine originale in un oggetto Bitmap supererebbe ovviamente la memoria (vedi qui , per esempio).

Inoltre, non riesco a leggere la bitmap con, ad esempio, BitmapFactory.decodeFile(file, options)fornendo a BitmapFactory.Options.inSampleSize, perché voglio ridimensionarla a una larghezza e altezza esatte. L'uso inSampleSizeridimensionerebbe la bitmap su 972x648 (se uso inSampleSize=4) o su 778x518 (se uso inSampleSize=5, che non è nemmeno una potenza di 2).

Vorrei anche evitare di leggere l'immagine usando inSampleSize con, ad esempio, 972x648 in un primo passaggio e quindi ridimensionandolo a esattamente 800x533 in un secondo passaggio, perché la qualità sarebbe scarsa rispetto a un ridimensionamento diretto dell'immagine originale.

Per riassumere la mia domanda: esiste un modo per leggere un file di immagine di grandi dimensioni con almeno 10 MP e salvarlo in un nuovo file di immagine, ridimensionato a una nuova larghezza e altezza specifiche, senza ottenere un'eccezione OutOfMemory?

Ho anche provato BitmapFactory.decodeFile(file, options)e impostato i valori Options.outHeight e Options.outWidth manualmente su 800 e 533, ma non funziona in questo modo.


No, outHeight e outWidth sono parametri out del metodo di decodifica. Detto questo, ho lo stesso problema di te e non sono mai molto soddisfatto dell'approccio in 2 passaggi.
rd

Spesso, grazie al cielo, è possibile utilizzare una riga di codice .. stackoverflow.com/a/17733530/294884
Fattie

Lettori, si prega di notare questo QA assolutamente critico !!! stackoverflow.com/a/24135522/294884
Fattie

1
Si noti che questa domanda ha ormai 5 anni e la soluzione completa è .. stackoverflow.com/a/24135522/294884 Saluti!
Fattie,

2
Esiste ora una documentazione ufficiale sull'argomento: developer.android.com/training/displaying-bitmaps/…
Vince

Risposte:


146

No. Mi piacerebbe che qualcuno mi correggesse, ma ho accettato l'approccio di caricamento / ridimensionamento che hai provato come compromesso.

Ecco i passaggi per chiunque navighi:

  1. Calcola il massimo possibile inSampleSizeche produce ancora un'immagine più grande del tuo target.
  2. Carica l'immagine usando BitmapFactory.decodeFile(file, options), passando a SampleSize come opzione.
  3. Ridimensionare alle dimensioni desiderate utilizzando Bitmap.createScaledBitmap().

Ho cercato di evitarlo. Quindi non c'è modo di ridimensionare direttamente un'immagine di grandi dimensioni in un solo passaggio?
Manuel,

2
Non per quanto ne sappia, ma non lasciare che ciò ti impedisca di esplorare ulteriormente.
Giustino, il

Bene, lo prenderò per la mia risposta accettata finora. Se scopro altri metodi, te lo farò sapere.
Manuel,

Come PSIXO ha menzionato in una risposta, potresti anche voler usare android: largeHeap se hai ancora problemi dopo aver usato inSampleSize.
user276648

la variabile bitmap si stava svuotando
Prasad il

99

Risposta di Justin tradotta in codice (funziona perfettamente per me):

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

15
Rende difficile leggere quando si utilizzano variabili come "b" ma una buona risposta non meno.
Oliver Dixon,

@Ofir: getImageUri (path); cosa devo passare con questo metodo?
Biginner,

1
Invece di (w h) /Math.pow (scale, 2) è più efficiente usare (w h) >> scale.
david.perez,

2
Non chiamare per System.gc()favore
gw0

Grazie @Ofir ma questa trasformazione non conserva l'orientamento dell'immagine: - /
JoeCoolman

43

Queste sono le soluzioni "combinate" tra Mojo Risin e Ofir. Questo ti darà un'immagine ridimensionata proporzionalmente con i limiti di larghezza massima e altezza massima.

  1. Legge solo i metadati per ottenere la dimensione originale (options.inJustDecodeBounds)
  2. Utilizza un ridimensionamento rought per risparmiare memoria (itmap.createScaledBitmap)
  3. Utilizza un'immagine ridimensionata con precisione basata sul ruvido Bitamp creato in precedenza.

Per me ha funzionato benissimo su immagini da 5 MegaPixel in basso.

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

23

Perché non usare l'API?

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

21
Perché non risolverebbe il mio problema. Che è: "... ha bisogno di una bitmap di origine come primo argomento, che non posso fornire perché il caricamento dell'immagine originale in un oggetto Bitmap supererebbe ovviamente la memoria." Quindi, non riesco nemmeno a passare una Bitmap al metodo .createScaledBitmap, perché avrei comunque bisogno di caricare prima un'immagine di grandi dimensioni in un oggetto Bitmap.
Manuel,

2
Destra. Rileggo la tua domanda e sostanzialmente (se ho capito bene) si riduce a "posso ridimensionare l'immagine a dimensioni esatte senza caricare il file originale in memoria?" In tal caso - non so abbastanza della complessità dell'elaborazione delle immagini per rispondere, ma qualcosa mi dice che 1. non è disponibile dall'API, 2. non sarà 1-liner. Lo segnerò come preferito - sarebbe interessante vedere se tu (o qualcun altro) lo risolverai.
Bostone,

ha funzionato per me perché sto ottenendo l'uri e la conversione in bitmap, quindi ridimensionarli è facile per me 1+ per quello più semplice.
Hamza,

22

Riconoscendo l'altra eccellente risposta finora, il miglior codice che abbia mai visto per questo è nella documentazione per lo strumento per scattare foto.

Vedi la sezione intitolata "Decodifica un'immagine in scala".

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

La soluzione che propone è una soluzione di ridimensionamento e ridimensionamento come le altre qui, ma è abbastanza pulita.

Ho copiato il codice di seguito come una funzione pronta all'uso per comodità.

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

1
Per prima cosa stai dividendo numeri interi che determineranno il risultato. In secondo luogo il codice si arresta in modo anomalo con targetW o targetH essendo 0 (anche se questo non ha molto senso lo so). Terzo in SampleSize dovrebbe essere un potere di 2.
cybergen

Non fraintendetemi. Questo caricherà definitivamente un'immagine ma se si pavimenta l'ints, questo non sembra. E questa non è sicuramente la risposta giusta perché l'immagine non verrà ridimensionata come previsto. Non farà nulla fino a quando la vista dell'immagine non sarà la metà della dimensione dell'immagine o più piccola. Quindi non accade nulla fino a quando la vista dell'immagine non è 1/4 della dimensione dell'immagine. E così via con potenze di due!
cybergen,

18

Dopo aver letto queste risposte e la documentazione Android ecco il codice per ridimensionare la bitmap senza caricarla in memoria:

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

Si noti che bmOptions.inPurgeable = true; è deprecato.
Ravit,

6

Quando ho bitmap di grandi dimensioni e voglio decodificarle ridimensionate, utilizzo quanto segue

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

1
Poiché inSampleSize è un numero intero, raramente otterrai la larghezza e l'altezza esatte dei pixel che desideri ottenere. A volte potresti avvicinarti, ma potresti anche essere molto lontano da esso, a seconda dei decimali.
Manuel,

Buongiorno, ho provato il tuo codice (post sopra in questo thread), ma sembra non funzionare, dove ho sbagliato? Qualsiasi suggerimento è il benvenuto :-)
RRTW

5

Questo può essere utile per qualcun altro che sta guardando questa domanda. Ho riscritto il codice di Justin per consentire al metodo di ricevere anche l'oggetto della dimensione target richiesto. Funziona molto bene quando si utilizza Canvas. Tutto il merito dovrebbe andare a JUSTIN per il suo ottimo codice iniziale.

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

Il codice di Justin è MOLTO efficace nel ridurre il sovraccarico di lavorare con bitmap di grandi dimensioni.


4

Non so se la mia soluzione sia la migliore pratica, ma ho ottenuto il caricamento di una bitmap con il ridimensionamento desiderato utilizzando le opzioni inDensitye inTargetDensity. inDensityè 0inizialmente quando non si carica una risorsa disegnabile, quindi questo approccio è per il caricamento di immagini non di risorsa.

Le variabili imageUri, maxImageSideLengthe contextsono parametri del mio metodo. Ho pubblicato solo l'implementazione del metodo senza il wrapping AsyncTask per chiarezza.

            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;

2
Molto bella! L'uso di inDensity invece di Bitmap.createScaledBitmap mi ha fatto risparmiare un sacco di memoria. Ancora meglio combinato con inSamplesize.
Ostkontentitan,

2

Tenendo conto del fatto che vuoi ridimensionare alle dimensioni esatte e vuoi mantenere la qualità necessaria, penso che dovresti provare questo.

  1. Scopri la dimensione dell'immagine ridimensionata con la chiamata di BitmapFactory.decodeFile e fornendo checkSizeOptions.inJustDecodeBounds
  2. Calcola il massimo possibile in SampleSize che puoi utilizzare sul tuo dispositivo per non superare la memoria. bitmapSizeInBytes = 2 * larghezza * altezza; Generalmente per la tua foto inSampleSize = 2 andrebbe bene dato che avrai bisogno solo di 2 * 1944x1296) = 4.8Mbб che dovrebbe avere i piedi in memoria
  3. Utilizzare BitmapFactory.decodeFile con inSampleSize per caricare Bitmap
  4. Ridimensiona la bitmap alla dimensione esatta.

Motivazione: il ridimensionamento in più passaggi potrebbe offrire immagini di qualità superiore, tuttavia non vi è alcuna garanzia che funzionerà meglio dell'uso di inSampleSize di alta qualità. In realtà, penso che puoi anche usare inSampleSize come 5 (non pow di 2) per avere il ridimensionamento diretto in un'unica operazione. O semplicemente usa 4 e quindi puoi semplicemente usare quell'immagine nell'interfaccia utente. se lo si invia al server, è possibile eseguire il ridimensionamento alla dimensione esatta sul lato server che consente di utilizzare tecniche di ridimensionamento avanzate.

Note: se la bitmap caricata nel passaggio 3 è almeno 4 volte più grande (quindi 4 * targetWidth <larghezza) probabilmente è possibile utilizzare diversi ridimensionamenti per ottenere una migliore qualità. almeno funziona con java generico, in android non hai la possibilità di specificare l'interpolazione usata per il ridimensionamento http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getscaledinstance.html


2

Ho usato il codice in questo modo:

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

Ho provato che l'immagine originale è 1230 x 1230, e la bitmap dice che è 330 x 330.
E se provata 2590 x 3849, otterrò OutOfMemoryError.

L'ho tracciato, lancia ancora OutOfMemoryError on line "BitmapFactory.decodeStream (is, null, options);", se la bitmap originale è troppo grande ...


2

Il codice sopra ha reso un po 'più pulito. Gli InputStreams hanno finalmente un wrapping ravvicinato per assicurarsi che vengano chiusi anche:

* Nota
Input: InputStream è, int w, int h
Output: 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");

    }

1

Per ridimensionare l'immagine nel modo "corretto", senza saltare alcun pixel, dovresti agganciarti al decodificatore dell'immagine per eseguire il campionamento verso il basso riga per riga. Android (e la libreria Skia che sta alla base) non fornisce tali hook, quindi dovresti farlo tu stesso. Supponendo che tu stia parlando di immagini jpeg, la soluzione migliore sarebbe usare libjpeg direttamente, in C.

Date le complessità coinvolte, l'utilizzo del sottocampione in due passaggi quindi riscalare è probabilmente il migliore per le app di tipo anteprima immagine.



1

Se vuoi assolutamente fare un ridimensionamento di un passaggio, potresti probabilmente caricare l'intera bitmap se android: largeHeap = true ma come puoi vedere questo non è davvero consigliabile.

Dai documenti: android: largeHeap Indica se i processi dell'applicazione devono essere creati con un grande heap Dalvik. Questo vale per tutti i processi creati per l'applicazione. Si applica solo alla prima applicazione caricata in un processo; se si utilizza un ID utente condiviso per consentire a più applicazioni di utilizzare un processo, tutti devono utilizzare questa opzione in modo coerente o avranno risultati imprevedibili. La maggior parte delle app non dovrebbe averne bisogno e dovrebbe invece concentrarsi sulla riduzione dell'utilizzo complessivo della memoria per migliorare le prestazioni. L'abilitazione di questo non garantisce inoltre un aumento fisso della memoria disponibile, poiché alcuni dispositivi sono limitati dalla memoria totale disponibile.



0

Questo ha funzionato per me. La funzione ottiene un percorso a un file sulla scheda SD e restituisce una bitmap nella dimensione massima visualizzabile. Il codice proviene da Ofir con alcune modifiche come il file immagine su sd invece un Ressource e il witdth e l'altezza sono ricavati dall'oggetto Display.

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

Ecco il codice che uso che non ha problemi a decodificare immagini di grandi dimensioni in memoria su Android. Sono stato in grado di decodificare immagini più grandi di 20 MB purché i miei parametri di input siano circa 1024x1024. È possibile salvare la bitmap restituita in un altro file. Sotto questo metodo c'è un altro metodo che uso anche per ridimensionare le immagini su una nuova bitmap. Sentiti libero di usare questo codice come desideri.

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

NOTA: i metodi non hanno nulla a che fare l'uno con l'altro tranne che per il metodo di decodifica delle chiamate createScaledBitmap sopra. La larghezza e l'altezza della nota possono cambiare dall'immagine originale.

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

il calcolo della potenza per la bilancia è semplicemente sbagliato qui; basta usare il calcolo nella pagina doco di Android.
Fattie,

0
 Bitmap yourBitmap;
 Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

o:

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

0

Uso Integer.numberOfLeadingZerosper calcolare la migliore dimensione del campione, prestazioni migliori.

Codice completo 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)
}

-2

Ridimensiona la bitmap usando il seguente codice

    public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;        
    BitmapFactory.decodeFile(file.getPath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(file.getPath(), options);
   }

    private static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }

     return inSampleSize;
   }    

Lo stesso è spiegato anche nel seguente suggerimento / trucco

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.