Autore Topic: Problema anteprima immagine  (Letto 2665 volte)

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Problema anteprima immagine
« il: 20 Marzo 2012, 00:30:25 CET »
0
Salve, ho setacciato praticamente tutto il web senza arrivare a una conclusione.   :-\
In pratica il mio problema è che prelevando immagini dalla galleria e facendole apparire in una imageview ricevo un errore di memoria:

Codice: [Seleziona]
03-20 00:21:56.535: ERROR/AndroidRuntime(14346): FATAL EXCEPTION: main
03-20 00:21:56.535: ERROR/AndroidRuntime(14346): java.lang.OutOfMemoryError: bitmap size exceeds VM budget

La riga alla quale lo ricevo è la seguente:

Codice (Java): [Seleziona]
((ImageView) findViewById(R.id.immagine_da_galleria)).setImageURI(data.getData());
Ho capito che è un errore di memoria dovuto alle dimensioni delle immagini che vado a prelevare e inserire come anteprima, pertanto ho provato ad utilizzare un metodo alternativo:

Codice (Java): [Seleziona]
            BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 4;
        Bitmap pippo = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(data.getData() + ""), 150, 150, false);
               
                    ((ImageView) findViewById(R.id.immagine_da_galleria)).setImageBitmap(pippo);

In questo caso seleziono le immagini ma non le visualizzo come anteprime.
Ho provato altre soluzioni in rete ma nessuna mi è risultata funzionante.

Nell'esempio che ho inserito ho dovuto mettere gli apici a data.getData() altrimenti ricevevo un errore su eclipse.
Ho anche impostato una dimensione nel file xml per quanto riguarda l'area della imageview ma il tutto vanamente.
Non riesco a trovare un esempio funzionante in merito...   :-(

Offline djdedo

  • Utente normale
  • ***
  • Post: 209
  • Respect: +15
    • Mostra profilo
  • Dispositivo Android:
    Galaxy S2
Re:Problema anteprima immagine
« Risposta #1 il: 20 Marzo 2012, 00:33:45 CET »
0
Ma l'errore te lo da sull'emulatore o su un device fisico?

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Re:Problema anteprima immagine
« Risposta #2 il: 20 Marzo 2012, 00:40:54 CET »
0
Sul dispositivo fisico, ma non appena ho postato mi è venuto un flash ed ho messo:

Codice (Java): [Seleziona]
Bitmap pippo = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(getPath(data.getData())), 150, 150, false);
E finalmente l'anteprima è apparsa anche se adesso l'attenzione si è spostata su un altra questione, volendo 2:

- La scarsa qualità della thumb;
- Il ridimensionamento 150*150 che deforma le immagini.

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Problema anteprima immagine
« Risposta #3 il: 20 Marzo 2012, 12:28:37 CET »
0
- Il ridimensionamento 150*150 che deforma le immagini.
Devi rispettare l'aspect ratio dell'immagine di partenza.
adb logcat | tee /tmp/logcat | grep TAG

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Re:Problema anteprima immagine
« Risposta #4 il: 20 Marzo 2012, 22:52:26 CET »
0
Non vorrei cantare vittoria troppo presto come spesso capita a causa dell'entusiasmo ma credo di aver risolto adattando del codice che ho trovato su stackoverflow, sperando di fare cosa gradita lo riporto di seguito nel caso dovesse servire a qualcuno (ho lasciato anche i commenti originali):

Codice (Java): [Seleziona]
private void scaleImage()
{
    // Get the ImageView and its bitmap
    ImageView view = (ImageView) findViewById(R.id.image_box);
    Drawable drawing = view.getDrawable();
    Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();

    // Get current dimensions AND the desired bounding box
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int bounding = dpToPx(250);
    Log.i("Test", "original width = " + Integer.toString(width));
    Log.i("Test", "original height = " + Integer.toString(height));
    Log.i("Test", "bounding = " + Integer.toString(bounding));

    // Determine how much to scale: the dimension requiring less scaling is
    // closer to the its side. This way the image always stays inside your
    // bounding box AND either x/y axis touches it.
    float xScale = ((float) bounding) / width;
    float yScale = ((float) bounding) / height;
    float scale = (xScale <= yScale) ? xScale : yScale;
    Log.i("Test", "xScale = " + Float.toString(xScale));
    Log.i("Test", "yScale = " + Float.toString(yScale));
    Log.i("Test", "scale = " + Float.toString(scale));

    // Create a matrix for the scaling and add the scaling data
    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);

    // Create a new bitmap and convert it to a format understood by the
ImageView
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height,
matrix, true);
    width = scaledBitmap.getWidth(); // re-use
    height = scaledBitmap.getHeight(); // re-use
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);
    Log.i("Test", "scaled width = " + Integer.toString(width));
    Log.i("Test", "scaled height = " + Integer.toString(height));

    // Apply the scaled bitmap
    view.setImageDrawable(result);

    // Now change ImageView's dimensions to match the scaled image
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.
getLayoutParams();
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);

    Log.i("Test", "done");
}

private int dpToPx(int dp)
{
    float density = getApplicationContext().getResources().getDisplayMetrics().
density;
    return Math.round((float)dp * density);
}

Praticamente non mi dà più (speriamo non sia un'illusione) il problema dell'OutOfMemoryError, inoltre l'anteprima mostrata è di ottima qualità e le sue proporzioni di larghezza e altezza sono mantenute.

 O:-) :D

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Re:Problema anteprima immagine
« Risposta #5 il: 21 Marzo 2012, 08:35:22 CET »
0
Diciamo che il problema della memoria posso assodarlo come risolto, ma trovata una soluzione a una cosa spunta inevitabilmente un altro inconveniente che essendo comunque legato per certi versi al problema dell'anteprima lo espongo in questo topic.

In pratica quando richiamo un'immagine dalla galleria e la mostro nell'anteprima della mia imageview è probabile che essa sia capovolta. Immagino per un discorso di orientamento in landscape con il quale l'immagine è stata scattata dalla fotocamera. A livello di visualizzazione dell'immagine stessa direttamente dalla galleria non ci sono problemi e ci deve essere qualcosa che la riconosce come scattata nel verso giusto, ma quando la vado a prendere e visualizzare nella imageview essa è capovolta.

Trovando del codice qua e là e richiamando la classe "Matrix" sono riuscito a capovolgere l'anteprima nella imageview ma dato che l'applicazione che sto tentando di sviluppare deve inviare in ftp l'immagine vera e propria, quando vado ad inviarla mi viene passata in ftp capovolta.

Ho pensato quindi di creare sulla base dell'immagine esistente una sua copia però capovolta, quindi con Bitmap.createBitmap(... basandomi sul file originale e poi salvare il file nella directory della sdcard.
Il codice di seguito è quello che sto usando per provare a creare e salvare il file (di per se non funziona perchè non ho riportato il costrutto try catch ma nella mia applicazione non dà errore e passa proprio dentro il "try" quindi dove dovrebbe eseguire tutte le funzioni per crearmi il file correttamente.)

Codice (Java): [Seleziona]
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
_bitmapScaled.compress(Bitmap.CompressFormat.JPEG, 40, bytes);

//you can create a new file name "test.jpg" in sdcard folder.
File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.jpg")
f.createNewFile();
//write the bytes in file
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());

Il problema è che nonostante esegua le funzioni per creare il file io questo file non lo trovo ma non mi viene nemmeno generato errore.

La questione è che non so se sto facendo un volo pindarico della ragione e magari ci sono soluzioni più semplici ed efficaci per ovviare al mio problema delle immagini rovesciate o se la strada è quella giusta ma sbaglio qualcosa nell'approccio.  :-X
« Ultima modifica: 21 Marzo 2012, 08:38:12 CET da Androi-dé! »

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Re:Problema anteprima immagine
« Risposta #6 il: 22 Marzo 2012, 08:32:38 CET »
0
Dopo un po' di prove e incacchiature diciamo che sono riuscito ad arrivare ad una soluzione funzionante anche se sinceramente non proprio così ottimale da poter considerare la questione, e quindi anche il topic, come risolta.
In pratica il problema è che non mi leggeva la cartella "Camera" della fotocamera ma per evitare qualsiasi problema mi sono creato una cartella apposta dove inserire l'immagine di appoggio per poi richiamarla.
L'inserimento più ottimale mi pare lo faccia il metodo MediaStore.Images.Media.insertImage(... tuttavia rimango un po' insoddisfatto dalla questione della rotazione dell'immagine in quanto per farla sono costretto a scalare l'immagine per evitare la comparsa dell'errore di memoria, scalando però l'immagine si perde qualità e se la rotazione avviene più volte le conseguenze sono intuibili, su foto di grandi dimensioni un paio di rotazioni sono ammortizzate ma su foto relativamente piccole si perde molto.

Rimango pertanto con questa soluzione diciamo... a metà, e passo ad altro prima di fondermici i nervi, magari ci tornerò su in un secondo momento, per ora chi utilizzerà quella funzione si preoccupi di scattare le foto dritte prima di upparle  :-P

Offline iceweasel

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 878
  • Respect: +147
    • Mostra profilo
  • Dispositivo Android:
    LGE P990 - Google Nexus 5
  • Sistema operativo:
    Linux Debian Sid
Re:Problema anteprima immagine
« Risposta #7 il: 22 Marzo 2012, 15:14:04 CET »
+1
Il JPEG è un algoritmo a perdita e ad ogni ricodifica, come hai potuto notare, perdi informazioni e diminuisce la qualità dell'immagine. Android gestisce i dati EXIF, ti devi basare su quei dati per visualizzarla se necessario ruotata senza toccare l'immagine originaria:

http://developer.android.com/reference/android/media/ExifInterface.html

se serve salvare delle immagini intermedie è meglio utilizzare il formato PNG, comprime ma utilizza un algoritmo senza perdita.

Android ha una quota massima per le bitmap, per gestire tante bitmap serve un sistema di cache, dove vengono mantenute in memoria solo le ultime immagini, le più vecchie vengono marcate per essere liberate dal sistema se necessario. Di solito per implementare un sistema del genere si utilizzano i weak reference di Java ma bisogna saperli usare nel modo corretto.


adb logcat | tee /tmp/logcat | grep TAG

Offline Androi-dé!

  • Utente junior
  • **
  • Post: 62
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S4
  • Sistema operativo:
    Windows 7
Re:Problema anteprima immagine
« Risposta #8 il: 23 Marzo 2012, 00:40:18 CET »
0
Ti ho dato un meritato "thanks" in quanto l'informazione su ExifInterface era proprio quella che mi ci voleva, daltronde come Android si accorge del verso con cui un'immagine è stata scattata ci doveva essere la maniera di sfruttare quella funzione. Adesso riesco a visualizzare senza problemi le immagini nel loro orientamento corretto.

Credo che dovrò concentrarmi anche sulla questione della chace perchè al momento per fare in modo di salvare l'immagine per l'anteprima e quella per l'upload in ftp eseguo il codice due volte con valore di resize diverso:

Codice (Java): [Seleziona]
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 4;
               
                BitmapFactory.Options options_big = new BitmapFactory.Options();
                options.inSampleSize = 2;

Per poi fare una compressione sia per l'una che per l'altra:

Codice (Java): [Seleziona]
bitmapImage_big.compress(Bitmap.CompressFormat.PNG, 100, fOut);
E salvare la mia immagine "big" nella sdcard per poi farci l'upload.

Ma non credo sia il modo più pulito, anche perchè l'immagine uppata subisce comunque una riduzione della metà della sua dimensione e per immagini grosse il problema è tamponato ma per immagini più piccole si perde di funzionalità.

Se cambio quei valori (cioè se provo a salvarla con options.inSampleSize = 1) riottengo l'errore OOM.

Avevo infatti pensato a trovare qualcosa che mi svuotasse la memoria (la cache) ogni volta che scelgo una nuova immagine o almeno quando l'anteprima è stata caricata in modo da diminuire lo spazio occupato da essa. Proverò a studiare questi weak reference sperando che non sia troppo ardua l'impresa.