2015-12-27 18 views
8

Voglio ritagliare l'immagine senza ottenere l'eccezione OutOfMemory.
significa che ho x, y, larghezza e altezza dell'immagine ritagliata e voglio ritagliare l'immagine originale senza portarla in memoria.
Sì, so che BitmapRegionDecoder è una buona idea, ma forse l'immagine ritagliata sarebbe troppo grande per portarla in memoria. Ritaglia l'immagine senza OutOfMemory - Android

In effetti non voglio bitmap ritagliata, voglio solo scrivere l'immagine ritagliata dal file sorgente al file di destinazione.


EDIT: voglio Salva immagine ritagliata non solo mostrando in un ImageView voglio salvarlo in un nuovo file senza quote perdere

Questo è l'esempio
enter image description here

in questa situazione la risoluzione dell'immagine ritagliata è 20000x20000 e il codice di sotto non funziona causa di OOM:

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); 
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inPreferredConfig = Bitmap.Config.RGB_565; 
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width/2 - 100, height/2 - 100, width/2 + 100, height/2 + 100), options); 
mImageView.setImageBitmap(bitmap); 

utilizzando inSampleSize per diminuire la dimensione dell'immagine originale è buona ma il risultato che ho salvato non è più 20000x20000.

Come posso ritagliare il 25000x25000 e salvare la parte di immagine 20000x20000 in un file?

+0

why down vote? condividi le tue conoscenze –

+1

Stack Overflow è per domande di programmazione. Qual è la tua domanda? Se la tua domanda è "come faccio a fare questo?", Spiega cosa hai provato e quali problemi specifici hai riscontrato. – CommonsWare

+0

sì! la mia domanda è come faccio a farlo in Android? Ho provato tutte le risposte relative alle domande correlate in StackOverflow e ogni singola risposta dipendeva dall'oggetto 'Bitmap' che è un rischio di OOM. Non so quale parte della mia domanda sia vaga. @CommonsWare –

risposta

1

In poche parole, richiede un sacco di programmazione a basso livello e ottimizzazioni.

come potete vedere, molte risposte in questa regione stanno puntando a concetti generici di compressione bitmap, ecc. Che sono effettivamente applicabili nella maggior parte dei problemi, ma non in quelli specifici.

Anche BitmapRegionDecoder come suggerito nelle risposte non funzionerà correttamente. Di sicuro impedisce il caricamento dell'intera bitmap nella RAM, ma per quanto riguarda l'immagine ritagliata? dopo aver ritagliato un'immagine ti dà una gigantesca bitmap che non importa cosa, ti dà una OOM.

Poiché il problema, come hai descritto, richiede Bitmap per essere scritto o letto dal disco così come viene scritto o letto dalla memoria; qualcosa chiamato BufferedBitmap (o così) che gestisce in modo efficiente la memoria richiesta salvando piccoli pezzi di una bitmap su disco e usandoli successivamente, evitando così OOM.

Qualsiasi altra soluzione che voglia affrontare il problema con il ridimensionamento fa solo metà del lavoro. perché? perché l'immagine ritagliata può essere troppo grande per la memoria (come hai detto tu).

Tuttavia, risolvere il problema con il ridimensionamento non è poi così male, se non ti interessa la qualità dell'immagine ritagliata rispetto a quella che l'utente di qualità aveva visto quando la stava ritagliando. questo è ciò che fa Google Photos, riduce semplicemente la qualità dell'immagine ritagliata, molto semplice!

Non ho visto alcuna classe BufferedBitmap in giro (ma se ci sono, sarebbe fantastico). Sono sicuramente utili per risolvere problemi simili.

È possibile controllare l'app di messaggistica Telegram fornita con un'implementazione open source di servizi di ritaglio di immagini; indovinate, gestisce tutte le opere simili cattive con la buona vecchia C ... Quindi, possiamo concludere che una buona soluzione globale (o meglio, UNA DELLE PIÙ SOLUZIONI APPLICABILI) sembra essere una programmazione di basso livello per gestire il disco e memoria te stesso.

So che la mia risposta non è riuscita a fornire alcuna soluzione copy-paste-ish al problema, ma almeno spero che vi abbia dato alcune idee amico mio.

+0

Sì, hai ragione. Ho provato un paio di strumenti di modifica delle immagini e indovina cosa? niente mi ha dato il risultato che volevo. –

1

Hai controllato BitmapRegionDecoder? Estrae un rettangolo dall'immagine originale.

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); 
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inPreferredConfig = Bitmap.Config.RGB_565; 
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width/2 - 100, height/2 - 100, width/2 + 100, height/2 + 100), options); 
mImageView.setImageBitmap(bitmap); 

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

+0

il problema è che forse il rettangolo sarebbe troppo grande e questa linea potrebbe generare OOM 'Bitmap bitmap = bitmapRegionDecoder.decodeRegion (...' –

1

si può risolvere questo usando BitmapFactory. Per determinare la dimensione del bitmap originale senza metterlo in memoria, fare il vuoto sanitario:

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeResource(..., options); 

int originalImageWith = options.outWidth; 
int originalImageHeight = options.outHeight; 

Ora è possibile utilizzare options.inSampleSize

Se impostato su un valore> 1, chiede al decoder per sottocampione il immagine originale, restituendo un'immagine più piccola per salvare la memoria . La dimensione del campione è il numero di pixel in entrambe le dimensioni corrispondenti a un singolo pixel nella bitmap decodificata. Ad esempio, inSampleSize == 4 restituisce un'immagine pari a 1/4 della larghezza/altezza dell'originale e 1/16 del numero di pixel. Qualsiasi valore < = 1 viene trattato come 1. Nota: il decodificatore utilizza un valore finale basata sulle potenze di 2, qualsiasi altro valore verrà arrotondato alla più vicina potenza di 2.

Ora è non è una soluzione perfetta, ma puoi fare matematica per trovare il fattore 2 più vicino che puoi utilizzare su options.inSampleSize per risparmiare memoria.

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inSampleSize = sampleSize; 
Bitmap bitmap = BitmapFactory.decodeResource(..., options); 
+0

no amico, il mio punto era completamente qualcos'altro. Aggiornerò la mia domanda –

+1

@SepehrBehroozi don aggiorna la domanda ma ne chiedi una nuova Ho già risposto alla domanda attuale così com'è: –

+0

rispettosamente non l'hai :) Voglio salvare l'immagine ritagliata in un file non solo mostrandola. l'uso di 'inSampleSize' è utile per visualizzare l'immagine ma per salvarla causa dimensioni perdenti –

0

BitmapRegionDecoder è il buon modo per tagliare grandi o immagini di grandi dimensioni, ma è disponibile da API 10 e sopra.

Esiste una classe denominata BitmapRegionDecoder che potrebbe essere di aiuto, ma è disponibile dall'API 10 e versioni successive.

Se non è possibile utilizzarlo:

molti formati di immagine sono compressi e quindi richiedono una sorta di caricamento in memoria.

Avrete bisogno di leggere sul miglior formato di immagine che si adatta alle vostre esigenze, e poi leggerlo da soli, usando solo la memoria di cui avete bisogno.

un compito un po 'più semplice sarebbe quello di fare tutto in JNI, così anche se userai molta memoria, almeno la tua app non entrerà in OOM così presto poiché non sarà vincolata al dimensione massima dell'heap imposto alle normali app.

Ovviamente, poiché Android è open source, puoi provare a utilizzare BitmapRegionDecoder e utilizzarlo per qualsiasi dispositivo.

Riferimento: Crop image without loading into memory

Oppure si può trovare un altro modo di seguito che potrebbe essere utile a voi:

Bitmap/Canvas use and the NDK