Autore Topic: Manipolazione Bitmap - OutOfMemory  (Letto 2656 volte)

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Manipolazione Bitmap - OutOfMemory
« il: 22 Ottobre 2013, 23:21:08 CEST »
0
Salve ragazzi, sono alle prime armi con lo sviluppo su android quindi perdonatemi se magari dico qualche cosa poco chiara.

Sto scrivendo un semplice programmino che mi permetta di scattare una foto tramite la fotocamera, e una volta salvata di applicare dei filtri di manipolazione (ad esempio l'effetto seppia).
La foto viene scattata e salvata correttamente, riesco anche a visualizzarla su una imageview, il problema è che quando chiamo la funzione che modifica detta foto applicando il filtro seppia, l'applicazione va in crash per OutOfMemory.
Avevo fatto una prova con una thumbnail, e l'effetto veniva applicato correttamente, quindi sono sicuro che si tratti della grandezza della foto.
Ho controllato e una foto fatta con il mio dispositivo ha un peso di circa 3.5MB, come è possibile che manipolare una foto del genere possa saturare i 140MB di ram liberi ?

Questa è la funzione che uso per applicare l'effetto seppia all'immagine (una variabile Bitmap chiamata modifiedPic):
Codice: [Seleziona]
public static void createSepiaToningEffect(int depth, double red, double green, double blue) {
            // image size
            int width = modifiedPic.getWidth();
            int height = modifiedPic.getHeight();
            // create output bitmap
            Bitmap bmOut = Bitmap.createBitmap(width, height, modifiedPic.getConfig());
            // constant grayscale
            final double GS_RED = 0.3;
            final double GS_GREEN = 0.59;
            final double GS_BLUE = 0.11;
            // color information
            int A, R, G, B;
            int pixel;
         
            // scan through all pixels
            for(int x = 0; x < width; ++x) {
                for(int y = 0; y < height; ++y) {
                    // get pixel color
                    pixel = modifiedPic.getPixel(x, y);
                    // get color on each channel
                    A = Color.alpha(pixel);
                    R = Color.red(pixel);
                    G = Color.green(pixel);
                    B = Color.blue(pixel);
                    // apply grayscale sample
                    B = G = R = (int)(GS_RED * R + GS_GREEN * G + GS_BLUE * B);
         
                    // apply intensity level for sepid-toning on each channel
                    R += (depth * red);
                    if(R > 255) { R = 255; }
         
                    G += (depth * green);
                    if(G > 255) { G = 255; }
         
                    B += (depth * blue);
                    if(B > 255) { B = 255; }
         
                    // set new pixel color to output image
                    bmOut.setPixel(x, y, Color.argb(A, R, G, B));
                }
            }
           
            modifiedPic = bmOut;
         
         
        }

Posso fornire altro codice su richiesta.

Confido in un vostro aiuto :)



PS: non posto il logcat giusto perchè l'errore è evidente
« Ultima modifica: 22 Ottobre 2013, 23:55:31 CEST da Luca91 »

Offline eagledeveloper

  • Translate Team
  • Utente senior
  • ****
  • Post: 516
  • Respect: +37
    • Google+
    • 347516210
    • dark_pinz
    • @WandDStudios
    • Mostra profilo
    • W&D Studios
  • Dispositivo Android:
    HTC One X e HTC One
  • Play Store ID:
    W%26D+Studios
  • Sistema operativo:
    Ubuntu / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #1 il: 23 Ottobre 2013, 08:12:22 CEST »
0
Lavorare con le immagini non è così evidente. Da qualche parte sulla documentazione ufficiale c'è una sezione ottimizzazione immagini, se trovo il link te lo posto.
I numeri contano molto di più del seme.

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #2 il: 23 Ottobre 2013, 09:21:04 CEST »
0
In java puoi allocare circa 16-24mb dipende dal device.
Una immagine a 8mpixel sono 32mb.

Quindi con java non si può fare devi usare c o c++.

 :-(

Ps il peso di 3.5 mb si riferisce al file? Quello é compresso non "conta"  :-(
« Ultima modifica: 23 Ottobre 2013, 09:22:57 CEST da undead »

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #3 il: 23 Ottobre 2013, 09:26:42 CEST »
0
Guarda fosse per me (che non sopporto il java) scriverei tutto in C.. il problema è ke non so dove mettere mani nell'ndk e le eccessive frasi di avvertimento di Google che ne sconsiglia l'uso mi hanno spaventato.. mi sa ke stasera mi metto d'impegno e vedo come usare l'ndk.. con l'ndk posso allocare liberamente memoria ?

Inviato dal mio GT-I9300 utilizzando Tapatalk


Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #4 il: 23 Ottobre 2013, 10:36:19 CEST »
0
Si, in Android puoi allocare tanta memoria quanta è la ram libera (a volte anche di più perché android può decidere di reclamare memoria extra e chiudere servizi) in due casi:
- NDK
- API OpenGL (anche se le chiami in Java e per lo stesso problema che hai tu, cioè che se esistesse il limite dopo 2-3 texture ti finirebbe la memoria)

Purtroppo le soluzioni che vengono proposte spesso vanno bene per altri tipi di problemi. Per esempio ti consigliano di vedere la risoluzione dell'immagine prima di aprirla e poi aprirla scalandola a una risoluzione più bassa. Questo ha senso per una immagine in una listview ma se fai una foto a 8mpixel non puoi prendere caricarla a 2mpixel e poi farci l'effetto...

L'unica cosa che posso suggerirti sono gli effetti già presenti nel sistema ma funzionano se tu fai una foto, non se carichi una immagine.

Camera.Parameters | Android Developers()

 :-(

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #5 il: 23 Ottobre 2013, 11:24:44 CEST »
0
Probabilmente, senza pretendere di caricare l'intera bitmap in memoria, si riesce a fare anche con un'app SDK.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #6 il: 23 Ottobre 2013, 13:20:12 CEST »
0
Grazie ad entrambi per le risposte,  tra poco vedo un po come funziona l'ndk.. spero di essere in grado di portare il codice che fino ad ora ho scritto

Inviato dal mio GT-I9300 utilizzando Tapatalk


Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #7 il: 23 Ottobre 2013, 14:25:12 CEST »
0
Probabilmente, senza pretendere di caricare l'intera bitmap in memoria, si riesce a fare anche con un'app SDK.
Beh il problema è che una bitmap a 8mpixel è circa 3500x2300 e se il risultato voluto è un'altra immagine da 3500x2300 salvata sul device con l'effetto non la vedo semplicissima. Il tutto è reso peggiore dal fatto che stiamo parlando di immagini compresse. Per dire se tu avessi una immagine non compressa potresti pensare di lavorare direttamente sul file (anche se credo sarebbe lento) ma con una JPG non è così semplice. Tra l'altro se l'utente sceglie una PNG che succede?

Non è semplice secondo me.  :-(

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #8 il: 23 Ottobre 2013, 14:55:15 CEST »
0
Beh il problema è che una bitmap a 8mpixel è circa 3500x2300 e se il risultato voluto è un'altra immagine da 3500x2300 salvata sul device con l'effetto non la vedo semplicissima. Il tutto è reso peggiore dal fatto che stiamo parlando di immagini compresse. Per dire se tu avessi una immagine non compressa potresti pensare di lavorare direttamente sul file (anche se credo sarebbe lento) ma con una JPG non è così semplice. Tra l'altro se l'utente sceglie una PNG che succede?

Non è semplice secondo me.  :-(

Mmm non ho capito se intendi che anche usando l'ndk, quello che cerco di fare è molto difficile ?

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #9 il: 23 Ottobre 2013, 15:18:48 CEST »
0
Per Luca91 : si riferiva alla mia affermazioni, secondo la quale non caricando tutta l'immagine in memoria prima di elaborarla, potrebbe essere gestibile anche con applicazione Java che gira nella dalvik.

Non è semplice secondo me.  :-(

Sono d'accordo, non è semplice e non so nemmeno se fattibile. Sarebbe quasi una cosa da studiare per "curiosità".
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #10 il: 23 Ottobre 2013, 16:13:11 CEST »
0
Mi riferivo a quanto detto da bradipao ma purtroppo alcuni problemi ci sono anche utilizzando l'NDK.

Uno dei problemi è che non puoi caricarla da NDK con un comando solo, per ogni formato che supporti devi aprire il file e decodificartelo a mano. Ovviamente visto che la devi risalvare devi anche ricomprimertela da solo.

Post unito: 23 Ottobre 2013, 16:35:46 CEST
Faccio un doublepost tanto poi il sistema me lo unisce..
---------------------------------------------------------------------------------------------------
Allora.. ho dovuto fare una cosa simile in una app e benchè i problemi fossero simili la situazione era abbastanza diversa e avevo meno grattacapi... lavoravo solo ed esclusivamente con JPG.
La mia "soluzione" è stata prendere l'immagine come file compresso, passarla al codice C, decodificarla, filtrarla e salvarla di nuovo ricomprimendola.

Nel tuo caso hai il problema fondamentale dei formati, cioè non è detto che l'immagine sia una JPG potrebbe essere una gif, una bmp, una png, etc.

Rispondendo a quello che dice bradipao sulla "curiosità" il sistema esiste ma è macchinoso e a parte i formati diversi (che in questo caso però potrebbero fare la differenza) non ha grossi vantaggi, anzi.

Non mi ricordo bene le regole, ho qualche progetto vecchio in giro ma comunque il trucco sono i bytebuffer, cerca per java.nio.ByteBuffer. C'è una funzione che permette di allocare memoria a basso livello, mi sembra sia allocateDirect o roba simile.

In pratica tu puoi allocare memoria a basso livello (credo che questo esuli dall'heap space anche se li crei in java, altrimenti li crei in C e li passi via JNI) oltre l'heap space.

Leggi il file senza decodificarlo e ottieni la risoluzione voluta, diciamo 3500x2300.
Allochi un bytebuffer nativo di 3500x2300.
Dividi idealmente la bitmap in regioni di una dimensione accettabile (1000x1000=4mb) e utilizzi la funzione apposita per decodificare area per area.
Per ogni area hai una bitmap che puoi andare a disegnare sopra al tuo bytebuffer.
Alla fine ti ritrovi con il tuo bytebuffer di 3500x2300 sul quale puoi applicare il filtro.

Per la ricompressione c'è un attimo da vedere perchè potresti essere costretto a ripassare da NDK ma l'encoder credo che lo possa decidere tu senza che l'utente abbia di che lamentarsi.

Se scegli questa strada stai attento quando manipoli il bytebuffer perchè se lo tratti come int e applichi filtri che agiscono su colori singoli potrebbe accadere che i risultati dell'emulatore e quelli del tuo s3 siano diversi. Il fatto è che siccome stai trattando dati a basso livello se li gestisci come int il tuo pc e il tuo device non hanno la stessa endianess.

In generale, ma non credo sia questo il tuo caso, ricordati sempre che l'environment di jni non è thread safe, giusto per aggiungere altro diverimento.  :D
« Ultima modifica: 23 Ottobre 2013, 16:35:46 CEST da undead, Reason: Merged DoublePost »

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #11 il: 23 Ottobre 2013, 16:44:05 CEST »
0
Accidenti è molto piu' complesso del previsto  :-\
Avevo in mente di usare SOLO il formato JPG comunque.
Per caso ti ritrovi parte del source code con il quale leggi l'immagine ?

Mi sa pero' che se le cose sono cosi complesse non continuero' questo progetto, purtroppo devo dare priorità alla tesi di laurea :(

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #12 il: 23 Ottobre 2013, 16:56:41 CEST »
0
Per il codice non-NDK non so dove sia ma faceva delle cose diverse. In pratica allocavo e manipolavo i byte array come se fossero dei buffer video... non ci caricavo direttamente le bitmap.  :-(

Comunque a parte imparare ad usare NDK che può sempre servirti, se prendi delle scorciatoie (tipo supportare solo JPG) semplifichi abbastanza.

Per leggere l'immagine utilizzi org.apache.commons.io.FileUtils.readFileToByteArray passandogli un file e ottenendo un byte[] (siccome le immagini sono circa 3.5mb sei tranquillo) che passerai ad NDK.

Poichè a nessuno piace reinventare la ruota basta che prendi una qualsiasi libreria C per codifica/decodifica JPG, la ricompili per android e con una semplice chiamata a funzione ti ritroverai con la tua immagine decompressa che puoi manipolare in C e ricomprimere (sempre con una semplice funzione).

 ;-)

Offline Luca91

  • Nuovo arrivato
  • *
  • Post: 16
  • Respect: 0
    • Mostra profilo
  • Sistema operativo:
    Ubuntu 12.04 / Windows 7
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #13 il: 23 Ottobre 2013, 17:03:14 CEST »
0
Per il codice non-NDK non so dove sia ma faceva delle cose diverse. In pratica allocavo e manipolavo i byte array come se fossero dei buffer video... non ci caricavo direttamente le bitmap.  :-(

Comunque a parte imparare ad usare NDK che può sempre servirti, se prendi delle scorciatoie (tipo supportare solo JPG) semplifichi abbastanza.

Per leggere l'immagine utilizzi org.apache.commons.io.FileUtils.readFileToByteArray passandogli un file e ottenendo un byte[] (siccome le immagini sono circa 3.5mb sei tranquillo) che passerai ad NDK.

Poichè a nessuno piace reinventare la ruota basta che prendi una qualsiasi libreria C per codifica/decodifica JPG, la ricompili per android e con una semplice chiamata a funzione ti ritroverai con la tua immagine decompressa che puoi manipolare in C e ricomprimere (sempre con una semplice funzione).

 ;-)

Eh si, di imparare l'ndk è sicuro!
Se ho capito bene dovrebbe essere qualcosa tipo:
1) Usare org.apache.commons.io.FileUtils.readFileToByteArray per mettere l'immagine in un array di bytes
2) Passare questo array ad una funzione che decodifica l'immagine
3) Modificare questo array per applicare il filtro
4) Ricodificare questo array passando ad una funzione per la codifica dei JPG
5) Salvare questo array su file (risultando nella foto con il filtro finale)

Correggimi se qualche punto è errato. Mi è capitato in passato di dover leggere e scrivere da devices a basso livello, quindi non sono del tutto a digiuno, ma d'altro canto voglio essere sicuro che sto seguendo la giusta strada.

Potresti dirmi che libreria supporta la codifica e decodifica delle JPG ? Ti ringrazio...

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Manipolazione Bitmap - OutOfMemory
« Risposta #14 il: 23 Ottobre 2013, 18:24:34 CEST »
0
Il giro è corretto ma volendo potresti aprire e salvare il file già da NDK, alla fine si tratta di usare funzioni standard del C.  ;-)

Citazione
Potresti dirmi che libreria supporta la codifica e decodifica delle JPG ? Ti ringrazio...
Ce ne sono diverse...

Independent JPEG Group
OpenJPEG library : an open source JPEG 2000 codec
libjpeg
libjpeg-turbo | Free  software downloads at SourceForge.net

La libjpg turbo supporta anche le funzioni NEON per velocizzare.  :-)