8

I am trying to get Exif data from YUV_420_888 image but it is not working. I tried several solutions like saving image to disk as jpeg, converting it to a input stream but nothing seems to work.

I capture YUV_420_888 image using android camera2 api. Then in the OnImageAvailableListener I get the image and try to read its EXIF data using ExifInterface APIs. But it is always empty. I tried all the approaches from this link to get the correct byte array.

Here is my code:

@Override
public void onImageAvailable(ImageReader imageReader) {
    if (!isRecording) {
        return;
    }
    Image image = imageReader.acquireNextImage();
    File file = Util.getImagePath(context);

    OutputStream outputStream = null;
    try {
        outputStream = new FileOutputStream(file);
        outputStream.write(data); 
//// This byte array I am making using all the approaches given in this link 
https://stackoverflow.com/questions/44022062/converting-yuv-420-888-to-jpeg-and-saving-file-results-distorted-image

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    try {
        ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath()); /// This is always empty
        int currentIso = (int)exifInterface.getAttributeDouble(ExifInterface.TAG_ISO_SPEED_RATINGS, 0); /// Always 0
    } catch (Exception e) {
        e.printStackTrace();
    }
    image.close();
}

EDIT: Code for capturing image:

captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureRequest.addTarget(preview);
captureRequest.addTarget(imageReader.getSurface());
captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
captureRequest.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
captureRequest.set(CaptureRequest.SENSOR_SENSITIVITY, <MANUAL_ISO>);
captureRequest.set(CaptureRequest.SENSOR_EXPOSURE_TIME, <MANUAL_EXPOSURE>);
mSession.capture(captureRequest.build(), new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            int capturedISO = result.get(CaptureResult.SENSOR_SENSITIVITY);
            long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
/// Save somewhere to be used later
            super.onCaptureCompleted(session, request, result);
        }
    }, backgroundHandler);
Shivam Pokhriyal
  • 1,044
  • 11
  • 26
Shivam Pokhriyal
  • 479
  • 4
  • 14
  • 1
    I don't think `YUV_420_888` format supports **Exif** data. In some cases there is some metadata in the first row of the frame, but this is different from camera to camera. What is the model of the camera you are using? – Rotem Jul 11 '19 at 15:02
  • @Rotem Thanks for answering. I too suspected the same. I am actually using Android's Camera2 API to capture photos in the android devices. I can get the exif data when I capture a jpeg but not with YUV. I even tried saving YUV as jpeg with the hope of getting the exif data but it didn't help. If you can add any input it will be of great help – Shivam Pokhriyal Jul 11 '19 at 17:30
  • 1
    I don't have any experience with Camera2 API and not with android programming. I do know that exif data is supported by images in the file system (like JPEG, DNG, Tiff) and not by RAW frames in the RAM like `YUV_420_888`. When capturing frames to the memory and not to disk there is probably other API, for receiving the data. According to documentation it looks like you can use `TotalCaptureResult` class. – Rotem Jul 11 '19 at 17:54
  • Yes I can and I did use it, but my use case isn't fulfilled by TotalCaptueResult. I habe to get exif data from image somehow. – Shivam Pokhriyal Jul 12 '19 at 17:08
  • @Shivam what you are trying to do is, next to impossible in newer generation mobile devices. Camera2 API was developed by keeping in mind that nowadays mobile devices use multiple cameras to capture a single picture. In this case, no metadata will be provided as the image is not directly from a physical camera but from a logical plane. If you capture this image you can set metadata manually in onImageAvailable after saving by ExifInterface exif = new ExifInterface(mFile.getAbsolutePath()); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, "10"); exif.saveAttributes(); – Vanshaj Daga Jul 13 '19 at 06:57
  • @VanshajDaga I want to get metadata not set. It doesn't seem possible. – Shivam Pokhriyal Jul 13 '19 at 08:16
  • @ManojPerumarath ISO and ExposureTime. – Shivam Pokhriyal Jul 15 '19 at 10:01
  • @ManojPerumarath Edited the question, Please take a look – Shivam Pokhriyal Jul 15 '19 at 10:34
  • @ManojPerumarath Ohh I'm sorry, Please check again. – Shivam Pokhriyal Jul 15 '19 at 10:40
  • @ShivamPokhriyal You are already setting these values, so why can't use it? `captureRequest.set(CaptureRequest.SENSOR_SENSITIVITY, ); captureRequest.set(CaptureRequest.SENSOR_EXPOSURE_TIME, );` – Manoj Perumarath Jul 15 '19 at 10:49
  • @ManojPerumarath I am capturing many images, some with auto exposure and some with manual, so sometimes the images gets mixed up. In other words, the values I set in the captureParameters are different than the image's metadata. I confirmed it by using JPEG images rather than YUV. In case of YUV, I cannot get the metadata values of image so cannot filter them. – Shivam Pokhriyal Jul 15 '19 at 10:59
  • @ShivamPokhriyal YUV format doesn't have EXIF Meta Data, Why do you want to capture in YUV – Manoj Perumarath Jul 15 '19 at 11:12
  • @ManojPerumarath I want to get raw image data for further processing. JPEG's are compressed images, although I can specify the JPEG quality to 100 but I'm afraid still a lot of data will be compressed. – Shivam Pokhriyal Jul 15 '19 at 11:19
  • https://github.com/googlesamples/android-Camera2Raw/blob/master/Application/src/main/java/com/example/android/camera2raw/Camera2RawFragment.java – Manoj Perumarath Jul 15 '19 at 11:25

1 Answers1

1

Exif information are placed in CaptureResult, which is available in CaptureCallback, in onCaptureCompleted method - you should "remember" result in one of your classes. When your image is available in onImageAvailableListener, you should save image, and "convert" result to suit exif format. Similary you can look at DngCreator class, which saves image with exif data from CaptureResult. If you are taking burst, then it will be more complicated.

404pio
  • 1,080
  • 1
  • 12
  • 32
  • Yes I am doing exactly the same, you can see my code in the question itself, but the thing is when I am capturing a lot of images then how do I compare the callbacks in onImageAvailableListener and onCaptureCompleted. FYI I used timestamp but it didn't work out, gave wrong result. – Shivam Pokhriyal Aug 05 '19 at 14:39
  • if you post 10 requests to capture, then you will get 10 images in the same order. If you post burst of requests, then you will get image for each image. This can be done using queue. – 404pio Aug 11 '19 at 18:21