2012-10-17 13 views
31

Contesto: Sto scrivendo un'app per fotocamera per un programma di messaggistica. Non riesco a salvare l'immagine catturata sul disco persistente in qualsiasi momento. La fotocamera deve supportare tutti gli orientamenti. La mia implementazione è quella degli esempi familiari di Surfaceview. Uso la classe Display per rilevare l'orientamento e ruotare la fotocamera di conseguenza. Nel callback jpeg di takePicture, costruisco una bitmap dal byte [] per aggirare alcuni problemi di proporzioni che stavo avendo: Camera API: Cross device issueslettura android jpeg Metadati EXIF ​​da callback immagine

Descrizione del problema: Su alcuni dispositivi, la Bitmap costruita eseguita a ROTATION_270 (dispositivo ruotato di 90 gradi in senso orario) è capovolta. Finora, sembra essere Samsung. Posso solo supporre che forse la fotocamera è saldata in un altro modo o qualcosa in tal senso, ma non è né qui né lì. Mentre posso verificare se una bitmap è laterale, non posso controllare logicamente se è capovolta per dimensioni, quindi ho bisogno di accedere ai dati EXIF.

Android fornisce un parser per questo http://developer.android.com/reference/android/media/ExifInterface.html ma sfortunatamente ha un singolo costruttore che accetta un file ... che non ho e non voglio. Intuitivamente Potrei scrivere un costruttore per un array di byte ma che sembra davvero doloroso dato le loro chiamate in codice nativo http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2.1_r1/android/media/ExifInterface.java

La mia domanda ha poi due parti:

  1. Qualcuno sa se il byte [] array contiene full EXIF ​​jpeg header data come è o è il percorso attraverso il BitmapFactory.decode (...)/ BitmapFactory.compress (...) aggiungendo che in qualche modo?

  2. Se questi dati EXIF ​​sono usciti nell'array di byte, come è possibile analizzare le informazioni di orientamento in modo affidabile? risposta

Modifica 10/18/12

pcans' sotto coinvolge la parte 2 della mia domanda. Come ho indicato nei commenti sotto la sua risposta, se vuoi usare quel parser devi incorporare la fonte nel tuo progetto. I cambiamenti di cui quello legato SO postale sono già stati fatti e ripubblicato qui: https://github.com/strangecargo/metadata-extractor

NOTA versioni più recenti di metadata-extractor lavoro direttamente su Android senza modifiche, e sono disponibili tramite Maven.

Tuttavia, per quanto riguarda la parte 1, ottengo 0 tag dal parser quando lo eseguo con l'array di byte che ottengo da takePicture. Mi preoccupo che l'array di byte non abbia i dati di cui ho bisogno. Continuerò a esaminare questo aspetto, ma accolgo con favore qualsiasi ulteriore intuizione.

+0

Ho messo a disposizione una libreria di metadati Java per Android che può estrarre e inserire metadati (Exif, XMP, IPTC, ICC_Profile, Photoshop IRB ecc.) Per immagini come JPEG, TIFF, PNG, GIF ecc. Controllalo [qui] (https://github.com/dragon66/pixymeta-android) – dragon66

+0

Articolo su [lettura e scrittura EXIF] (http://onetouchcode.com/2016/08/23/write-exif-data-image-android/) su un file immagine in Android – Shailendra

+0

Questo link metadata-extractor ha bisogno di un .com: https://drewnoakes.com/code/exif/ – user4851

risposta

18

La cattiva notizia:

Android Api purtroppo non vi permetterà di leggere i dati EXIF ​​da un Stream, solo da un File.
ExifInterface non ha un costruttore con un InputStream. Quindi devi analizzare il contenuto jpeg da solo.

La buona notizia:

API esiste in puro Java per questo.È possibile utilizzare questo: https://drewnoakes.com/code/exif/
È Open Source, pubblicato con Licenza Apache 2 e disponibile come Maven package.

C'è un costruttore con una InputStream: public ExifReader(java.io.InputStream is)

si può costruire un InputStream sostenuta dal byte[] utilizzando un ByteArrayInputStream come questo:

InputStream is = new ByteArrayInputStream(decodedBytes); 
+0

Impressionante. Non sono riuscito a trovarne uno con Apache. Ora spero solo che l'array di byte contenga le informazioni che voglio! Lo farò uno scatto ora –

+0

Domanda stupida forse, ma ci sarebbe alcuna ragione per cui questo non funzionerebbe su Android? Non sono stato in grado di rilevare la libreria anche se ho eseguito il solito processo di inserimento del jar nella cartella libs e l'aggiunta per creare il percorso ... –

+0

Mi dispiace, era una domanda stupida - dovrei hai cercato in modo più approfondito http://stackoverflow.com/questions/2536194/android-image-exif-reader-3rd-party-api –

4

Quindi, utilizzando la mia modifica e pcans suggerimento, ho avuto i dati dell'immagine ma non era quello che mi aspettavo. Nello specifico, non tutti i dispositivi daranno un orientamento. Se si segue questa nota percorso che

  • La biblioteca "Android fisso" ExifReader Indico è in realtà il a cura 2.3.1 che è a pochi ESONERATE vecchia. I nuovi esempi sul sito Web e nella fonte appartengono al più recente 2.6.x in cui l' modifica l'API in modo significativo. Utilizzando l'interfaccia 2.3.1, è possibile scaricare tutti i dati EXIF ​​da un byte [] nel modo seguente:

     Metadata header;  
         try { 
          ByteArrayInputStream bais= new ByteArrayInputStream(data); 
          ExifReader reader = new ExifReader(bais); 
          header = reader.extract(); 
          Iterator<Directory> iter = header.getDirectoryIterator(); 
          while(iter.hasNext()){ 
           Directory d = iter.next(); 
           Iterator<Tag> iterTag = d.getTagIterator(); 
           while(iterTag.hasNext()){ 
            Tag t = iterTag.next(); 
            Log.e("DEBUG", "TAG: " + t.getTagName() + " : " + t.getDescription()); 
           } 
          } 
         } catch (JpegProcessingException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
         } catch (MetadataException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
         } 
    

se si desidera che i valori delle variabili numeriche, è sufficiente sostituire

t.getDescription() 

con

d.getInt(t.getTagType()) 
  • Anche se ExifReader ha un co nstructor using byte [], devo avere frainteso ciò che si aspetta perché se provo ad usarlo direttamente con l'array di dati , ottengo Tag nella directory restituita.

Non ho aggiunto molto per quanto riguarda la risposta, quindi accetto la risposta dei pcan.

32

Per leggere i metadati/EXIF ​​dall'immagine byte[] (utile per Camera.takePicture()) utilizzando versione 2.9.1 del metadata extraction library in Java da Drew Noakes:

try 
{ 
    // Extract metadata. 
    Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(new ByteArrayInputStream(imageData)), imageData.length); 

    // Log each directory. 
    for(Directory directory : metadata.getDirectories()) 
    { 
     Log.d("LOG", "Directory: " + directory.getName()); 

     // Log all errors. 
     for(String error : directory.getErrors()) 
     { 
      Log.d("LOG", "> error: " + error); 
     } 

     // Log all tags. 
     for(Tag tag : directory.getTags()) 
     { 
      Log.d("LOG", "> tag: " + tag.getTagName() + " = " + tag.getDescription()); 
     } 
    } 
} 
catch(Exception e) 
{ 
    // TODO: handle exception 
} 

Per leggere EXIF ​​orientamento dell'immagine (non l'orientamento della miniatura):

try 
{ 
    // Get the EXIF orientation. 
    final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); 
    if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) 
    { 
     final int exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); 

     /* Work on exifOrientation */ 
    } 
    else 
    { 
     /* Not found */ 
    } 
} 
catch(Exception e) 
{ 
    // TODO: handle exception 
} 

L'orientamento è compreso tra 1 e 8. Vedere here, here, here o here.


per trasformare una bitmap in base alla sua all'orientamento EXIF:

try 
{ 
    final Matrix bitmapMatrix = new Matrix(); 
    switch(exifOrientation) 
    { 
     case 1:                      break; // top left 
     case 2:             bitmapMatrix.postScale(-1, 1);  break; // top right 
     case 3:   bitmapMatrix.postRotate(180);            break; // bottom right 
     case 4:   bitmapMatrix.postRotate(180);   bitmapMatrix.postScale(-1, 1);  break; // bottom left 
     case 5:   bitmapMatrix.postRotate(90);   bitmapMatrix.postScale(-1, 1);  break; // left top 
     case 6:   bitmapMatrix.postRotate(90);            break; // right top 
     case 7:   bitmapMatrix.postRotate(270);   bitmapMatrix.postScale(-1, 1);  break; // right bottom 
     case 8:   bitmapMatrix.postRotate(270);            break; // left bottom 
     default:                     break; // Unknown 
    } 

    // Create new bitmap. 
    final Bitmap transformedBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.getWidth(), imageBitmap.getHeight(), bitmapMatrix, false); 
} 
catch(Exception e) 
{ 
    // TODO: handle exception 
} 
+1

È possibile trovare versioni più recenti nel repository git : https: // github.com/drewfarris/metadata-extractor – dwbrito

+1

@Pang questo è per la fotocamera principale, per quanto riguarda la rotazione per la fotocamera frontale? –

+4

@dwbrito, in realtà l'autore del progetto originale (me) da allora ha spostato il progetto in GitHub. Quello a cui ci si collega ha diversi anni ora e manca molte correzioni/miglioramenti. https://github.com/drewnoakes/metadata-extractor/ –

0

Per qualcuno che potrebbe essere interessato, ecco come ottenere il tag di orientamento con il 2.3.1 interfaccia da https://github.com/strangecargo/metadata-extractor

Metadata header; 
try { 
    ByteArrayInputStream bais= new ByteArrayInputStream(data); 
    ExifReader reader = new ExifReader(bais); 
    header = reader.extract(); 
    Directory dir = header.getDirectory(ExifDirectory.class); 
    if (dir.containsTag(ExifDirectory.TAG_ORIENTATION)) { 
     Log.v(TAG, "tag_orientation exists: " + dir.getInt(ExifDirectory.TAG_ORIENTATION)); 
    } 
    else { 
     Log.v(TAG, "tag_orietation doesn't exist"); 
    } 


} catch (JpegProcessingException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
} catch (MetadataException e) { 
    e.printStackTrace(); 
} 
3

Se si utilizza libreria Glide è possibile ottenere l'orientamento Exif da un InputStream:

InputStream is=getActivity().getContentResolver().openInputStream(originalUri); 
int orientation=new ImageHeaderParser(is).getOrientation(); 
+1

Questo metodo non ha funzionato per me per l'array di byte restituito in Camera.PictureCallback.onPictureTaken quando l'immagine viene catturata tramite mCamera .takePicture (null, null, mPictureCallback). In questo caso, l'orientamento era -1 che indica un errore. – Andrew

0

Se si dispone di un tipo content:// di Uri, Android fornisce le API attraverso ContentResolver e c'è non è necessario utilizzare librerie esterne:

public static int getExifAngle(Context context, Uri uri) { 
    int angle = 0; 
    Cursor c = context.getContentResolver().query(uri, 
      new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, 
      null, 
      null, 
      null); 

    if (c != null && c.moveToFirst()) { 
     int col = c.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION); 
     angle = c.getInt(col); 
     c.close(); 
    } 
    return angle; 
} 

È anche possibile leggere qualsiasi altro valore che trovi in ​​MediaStore.Images.ImageColumns, come latitudine e longitudine.

Attualmente non funziona con file:/// Uris ma può essere facilmente modificato.

+0

Non funziona almeno su alcuni dispositivi. Restituisce 'col = -1' per me. –

+0

@AleksN funziona solo se l'immagine è stata aggiunta con i tag corretti al MediaStore. Inoltre, deve essere un uri contenuto e non un file uri. Comunque, da allora ho cambiato idea, mi consiglia di utilizzare un lettore Exif e di lasciare solo le query del risolutore di contenuti. Funziona sia con i file che con i contenuti. – natario

+0

Vedere http://stackoverflow.com/q/34696787/4288782 – natario

1

Se si desidera un modo per leggere i dati EXIF ​​che non dipendono da dove proveniva l'URI, è possibile utilizzare exif support library e leggerlo da uno streaming. Ad esempio questo è il modo in cui ottengo l'orientamento dell'immagine.

build.gradle codice

dependencies { 
...  
compile "com.android.support:exifinterface:25.0.1" 
... 
} 

Esempio:

import android.support.media.ExifInterface; 
... 
try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { 
     ExifInterface exif = new ExifInterface(inputStream); 
     int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

La ragione per cui ho avuto a che fare in questo modo una volta che ha iniziato il targeting api 25 (forse un problema su 24+ anche), ma ancora supportando indietro a API 19, su Android 7 la nostra app si arresterebbe in modo anomalo se passassi in un URI che faceva semplicemente riferimento a un file. Quindi ho dovuto creare un URI per passare all'intento della videocamera in questo modo.

FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", tempFile); 

Il problema è che il file sua non è possibile trasformare l'URI in un percorso di file vero e proprio (diversi aggrappata al percorso del file temp).

Problemi correlati