1

I'm creating an app that takes pictures in .dng format in order to process them. I'm using the camera2 API. I was able to take pictures and save them into my phone, but in .jpg format. But when I change my code in order to save them with .dng extension, it compiles, show me the preview on my phone, but when the picture is taken, I get an error. The part of my code that takes and saves the picture is as follows.

val reader = ImageReader.newInstance(1280, 720, ImageFormat.RAW_SENSOR, 1)

val outputSurfaces = ArrayList<Surface>(2)
outputSurfaces.add(reader.surface)
outputSurfaces.add(Surface(previewTextureView.surfaceTexture))

val captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
captureBuilder.addTarget(reader.surface)
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO)

val file = File("myPath/myImageName.dng")
var captureResult: CaptureResult? = null

And my listeners :

val readerListener = object : ImageReader.OnImageAvailableListener {
    override fun onImageAvailable(reader: ImageReader) {
        var image: Image? = null
        var output: OutputStream? = null
        val dngCreator = DngCreator(cameraManager.getCameraCharacteristics("0"), captureResult)

        try {
             image = reader.acquireLatestImage()
             output = FileOutputStream(file)
             dngCreator.writeImage(output, image)
         } catch (e: FileNotFoundException) {
             e.printStackTrace()
         } catch (e: IOException) {
             e.printStackTrace()
         } finally {
             output?.close()
             image?.close()
         }
    }
}

reader.setOnImageAvailableListener(readerListener, backgroundHandler)

val captureListener = object : CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
        captureResult = result
        super.onCaptureCompleted(session, request, result)
    }
}

And finally I capture the session with:

cameraDevice.createCaptureSession(outputSurfaces, object : CameraCaptureSession.StateCallback() {
    override fun onConfigured(session: CameraCaptureSession) {
        try {
             session.capture(captureBuilder.build(), captureListener, backgroundHandler)
         } catch (e: CameraAccessException) {
              e.printStackTrace()
         }
     }
     override fun onConfigureFailed(session: CameraCaptureSession) {}
}, backgroundHandler)

I'm having one warning and one error that I didn't have before, when I was saving the image as jpeg:

W/CameraDevice-JV-0: Stream configuration failed due to: createSurfaceFromGbp:1106: Camera 0: No supported stream configurations with format 0x20 defined, failed to create output stream

E/CameraCaptureSession: Session 1: Failed to create capture session; configuration failed

Things that I changed in order to save a dng file are :

  • I replaced ImageFormat.JPEG with ImageFormat.RAW_SENSOR
  • I changed the file extension from .jpg to .dng
  • Instead of using dngCreator.writeImage(output, image), I used :
val buffer = image!!.planes[0].buffer
val bytes = ByteArray(buffer.capacity())
buffer.get(bytes)
output.write()

Since there is not a lot of information about this subject, I'm not sure if my implementation is correct.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
NicoC
  • 151
  • 1
  • 11
  • Not all devices can capture ImageFormat.RAW_SENSOR – Alex Cohn Jul 20 '19 at 20:10
  • My device can. The built-in camera app has an option to take RAW pictures and saved them as an DNG file. – NicoC Jul 20 '19 at 23:16
  • It is not clear what you mean by *Instead of using dngCreator.writeImage(output, image), I used : … output.write()*. Is that what you tried to do to check if `dngCreator` was causing the problem, but the error persisted even after this attempt? – Alex Cohn Jul 21 '19 at 07:54
  • It's not healthy to use fixed resolution for capture. No one can guarantee that a device can produce RAW_SENSOR images with 1280x720. – Alex Cohn Jul 21 '19 at 08:42
  • I know it's not healthy but I'm targeting exactly one device, and it's the one that I'm using. But how you'll go to use dynamic resolution. – NicoC Jul 21 '19 at 09:54
  • What I mean, is that the first thing that I tried, was to take pictures and saved them using jpeg. And once I checked that it was working, I decided to go towards my main objective, saving the images using dng. And what I changed, between my successful attempt of saving jpeg and my failure attempt of saving using dng, are the 3 bullets. Hence my deduction that I'm implementing the dng in a wrong way – NicoC Jul 21 '19 at 09:58
  • Even though the native app captures RAW pictures, you can't be sure if capturing RAW pictures are supported. I asked a similar question here https://stackoverflow.com/questions/56817053/cameracharacteristics-sensor-info-exposure-time-range-gives-wrong-values Even my device RedmiNote 4 native camera app allows manual control over ISO and exposure, but when queried over camera2 API, it doesn't have support for ManualControl. – Shivam Pokhriyal Jul 21 '19 at 15:04
  • 1
    You can use this api to check whether the image format is supported or not. https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap#getInputFormats() That too I doubt will be correct or not :-( because I've found most of the result these API gives aren't correct. – Shivam Pokhriyal Jul 21 '19 at 15:09
  • I would strongly recommend to begin with an official sample, https://github.com/googlesamples/android-Camera2Raw. If it does not work on your device, try to open an issue on GitHub. – Alex Cohn Jul 21 '19 at 15:16

2 Answers2

1

This a bit of an old post but for raw images you cannot set the resolution to an arbitrary value. Assuming your device can do raw_sensor reads it has to be set to the sensor size. You need to do something like this.

val largestRaw = Collections.max(Arrays.asList(*map.getOutputSizes(ImageFormat.RAW_SENSOR)), CompareSizesByArea())

rawImageReader = ImageReader.newInstance(largestRaw.width, largestRaw.height, ImageFormat.RAW_SENSOR, /*maxImages*/ 5).apply { setOnImageAvailableListener(onRawImageAvailableListener, backgroundHandler) }

Unfortunately in kotlin i am now encountering:

java.lang.IllegalArgumentException: Missing metadata fields for tag AsShotNeutral (c628)

The outdated java Camera2Raw sample listed above does work though.

draekko
  • 151
  • 2
  • 5
0

After some research, I found a implementation in order to save an image that was taken with the Camera2API, in a .dng file :

if (mImage.format == ImageFormat.RAW_SENSOR) {
    val dngCreator = DngCreator(mCharacteristics, mCaptureResult)
    var output: FileOutputStream? = null
    try {
        output = FileOutputStream(mFile)
        dngCreator.writeImage(output, mImage)
    } catch (e: IOException) {
         e.printStackTrace()
    } finally {
         mImage.close()
         closeOutput(output)
     }
}

Where :

  • mCharacteristics are CameraCharacteristics, ie the properties describing the CameraDevice
  • mCaptureResult is produced by the CameraDevice after processing the CaptureRequest
  • mImage is the image retrieved in the function dequeuAndSaveImage : image = reader.get()!!.acquireNextImage()
  • mFile is the File where the image will be saved, for example :
mFile = Environment
     .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
     "RAW_" + generateTimestamp()+ ".dng"

Maybe it will help somebody, but as @Alex Cohn said, it's recommended to begin with the official sample github.com/googlesamples/android-Camera2Raw. It's written in Java and not in Kotlin, but it's not that hard to transform it, if needed.

NicoC
  • 151
  • 1
  • 11