1

I am trying to get multiple pictures with different exposure time to perform my HDR algorithm. However, I cannot figure out how to use captureBurst() in Android camera2 API and ImageReader to save files. My code will create duplicated files. Can anyone gives me a hand?

private ImageReader mImageReader;
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener =
            new ImageReader.OnImageAvailableListener() {

                @Override
                public void onImageAvailable(ImageReader reader) {
                    mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage()));
                }
            };

private static class ImageSaver implements Runnable {
        private final Image mImage;

        private ImageSaver(Image image) {
            mImage = image;

        }

        private File createNewImageFile() throws IOException {
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String imageFileName = "IMG_" + timeStamp;
            File storageDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera");
            File image = File.createTempFile(imageFileName, ".jpg", storageDirectory);
            return image;
        }

        @Override
        public void run() {
            ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[byteBuffer.remaining()];
            byteBuffer.get(bytes);

            FileOutputStream fileOutputStream = null;

            try {
                File newFile = null;
                try {
                    newFile = createNewImageFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                fileOutputStream = new FileOutputStream(newFile);
                fileOutputStream.write(bytes);

                mImageFileNameList.add(newFile.getName());

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

private void captureStillImage() {
        try {
            CaptureRequest.Builder captureStillBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureStillBuilder.addTarget(mImageReader.getSurface());

            CaptureRequest.Builder captureStillBuilder2 = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureStillBuilder2.addTarget(mImageReader.getSurface());

            int rotation = getWindowManager().getDefaultDisplay().getRotation();

            captureStillBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                    ORIENTATIONS.get(rotation));
            captureStillBuilder2.set(CaptureRequest.JPEG_ORIENTATION,
                    ORIENTATIONS.get(rotation));

            CameraCaptureSession.CaptureCallback captureCallback =
                    new CameraCaptureSession.CaptureCallback() {

                        @Override
                        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                            super.onCaptureCompleted(session, request, result);
                            unlockFocus();
                        }
                    };

            List<CaptureRequest> list = new ArrayList<>();

            captureStillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
            captureStillBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
            captureStillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, ONE_SECOND / 100);

            captureStillBuilder2.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
            captureStillBuilder2.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
            captureStillBuilder2.set(CaptureRequest.SENSOR_EXPOSURE_TIME, ONE_SECOND / 20);

            list.add(captureStillBuilder.build());
            list.add(captureStillBuilder2.build());

            mCameraCaptureSession.captureBurst(list, captureCallback, null);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59
Roderick
  • 9
  • 3

2 Answers2

1

If everything seems to work otherwise, and your issue is just the file names getting duplicated, that may just be because your phone can capture two JPEGs faster than 1 second apart.

Your format string for the image file name is: "yyyyMMdd_HHmmss" This doesn't include any fractional seconds, so pictures taken at, say 12:35:15.100 and 12:35:15.700 (600 ms apart) will map to the same filename, IMG_..._123515.jpg.

You can just add "_SSS" to your string to include milliseconds as well, per the SimpleDateFormat documentation, which should disambiguate your filenames unless they're being captured really fast.

Alternatively, you could keep some sort of counter for files with the same name, and append _1, _2, etc when there's a conflict.

Eddy Talvala
  • 17,243
  • 2
  • 42
  • 47
0

Here is my code from a similar scenario, where I am saving my files in a folder named C2 under the external Pictures folder:

@Override
public void onImageAvailable(ImageReader reader) {
    String currentDateTime = generateTimestamp();

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()
            + "/C2/" + mCount++ + "_" + currentDateTime + ".jpg");
    if (mCount == 3)  mCount = 1; // Reset the counter
    mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), file));
}

and mCount is set as

private int mCount = 1;

And generateTimestamp is from Google's sample code:

/**
 * Generate a string containing a formatted timestamp with the current date and time.
 *
 * @return a {@link String} representing a time.
 */
private static String generateTimestamp() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US);
    return sdf.format(new Date());
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
zeeshan
  • 4,913
  • 1
  • 49
  • 58