reading android jpeg EXIF metadata from picture callback

前端 未结 8 1817
天命终不由人
天命终不由人 2020-11-30 20:48

Background: I am writing a camera app for a messenger program. I cannot save the captured image to persistent disk at any time. The camera must support all orientations.

相关标签:
8条回答
  • 2020-11-30 21:23

    If you are using Glide library you can get the Exif orientation from an InputStream:

    InputStream is=getActivity().getContentResolver().openInputStream(originalUri);
    int orientation=new ImageHeaderParser(is).getOrientation();
    
    0 讨论(0)
  • 2020-11-30 21:26

    To read metadata/EXIF from image byte[] (useful for Camera.takePicture()) using version 2.9.1 of the metadata extraction library in Java by 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
    }
    

    To read the EXIF orientation of the image (not the orientation of the thumbnail):

    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
    }
    

    The orientation is from 1 to 8. See here, here, here or here.


    To transform a bitmap based on its EXIF orientation:

    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
    }
    
    0 讨论(0)
  • 2020-11-30 21:29

    AndroidX ExifInterface supports reading EXIF information from an inputstream:

    implementation "androidx.exifinterface:exifinterface:1.1.0"
    

    You can then just pass the inputstream into the constructor like that:

    val exif = ExifInterface(inputStream)
    val orientation =
            exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
    
    0 讨论(0)
  • 2020-11-30 21:29

    For anybody who might be interested, here is how to get the Orientation tag using the 2.3.1 interface from 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();
    }
    
    0 讨论(0)
  • 2020-11-30 21:31

    The bad news:

    Android Api sadly won't allow you to read exif data from a Stream, only from a File.
    ExifInterface don't have a constructor with an InputStream. So you must parse jpeg content by yourself.

    The good news:

    API exists in pure Java for this. You can use this one: https://drewnoakes.com/code/exif/
    It's Open Source, published under Apache Licence 2 and available as a Maven package.

    There is a constructor with an InputStream: public ExifReader(java.io.InputStream is)

    You can build an InputStream backed by your byte[] using a ByteArrayInputStream like this:

    InputStream is = new ByteArrayInputStream(decodedBytes);
    
    0 讨论(0)
  • 2020-11-30 21:34

    So using my edit and pcans suggestion, I got the image data but it was not what I expected. Specifically, not all devices will give an orientation at all. If you follow this path note that

    • The "Android fixed" ExifReader library I point to is actually the edited 2.3.1 which is a few releasees old. The new examples on the website and in the source pertain to the newest 2.6.x where he changes the API significantly. Using the 2.3.1 interface, you can dump all EXIF data from a byte[] by doing the following:

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

    if you want numerical tag values, simply replace

    t.getDescription()
    

    with

    d.getInt(t.getTagType())
    
    • Although ExifReader has a constructor using byte[], I must have misunderstood what it expects because if I try to use it with the data array directly, I get to Tags in the returned directory.

    I really didn't add much as far as the answer is concerned so I'm accepting pcans' answer.

    0 讨论(0)
提交回复
热议问题