3

I'm trying to save android.hardware.camera2 images to a lossless format.

I've got it working for JPEG (lossy) and DMG (raw, but huge and hard to work with) using scrounged bits of code:

private fun save(image: Image, captureResult: TotalCaptureResult) {
    val fileWithoutExtension = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "myimage_${System.currentTimeMillis()}")
    val file: File = when (image.format) {
        ImageFormat.JPEG -> {
            val buffer = image.planes[0].buffer
            val bytes = ByteArray(buffer.remaining())
            buffer.get(bytes)
            val file = File("$fileWithoutExtension.jpg")
            file.writeBytes(bytes)
            file
        }
        ImageFormat.RAW_SENSOR -> {
            val dngCreator = DngCreator(mode.characteristics, captureResult)
            val file = File("$fileWithoutExtension.dmg")
            FileOutputStream(file).use { os ->
                dngCreator.writeImage(os, image)
            }
            file
        }
        else -> TODO("Unsupported image format: ${image.format}")
    }
    Log.i(TAG, "Wrote image:${file.canonicalPath} ${file.length() / 1024}k")
    image.close() // necessary when taking a few shots
}

but what I'm stuck on is replacing that RAW_SENSOR section with something that saves to a more reasonable PNG. Is it

  1. a bad idea because RAW_SENSOR is so different than a normal image format that I'd have to go through too much pain to convert it?
  2. a bad idea because I should have set the upstream capture to capture something more reasonable like FLEX_RGB_888?
  3. a good idea because there is some silly mistake in the following code? (which dies with Buffer not large enough for pixels at android.graphics.Bitmap.copyPixelsFromBuffer(Bitmap.java:593)

my attempt:

fun writeRawImageToPng(image: Image, pngFile: File) {
    Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888).let { latestBitmap->
        latestBitmap.copyPixelsFromBuffer(image.planes[0].buffer!!)
        ByteArrayOutputStream().use { baos ->
            latestBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos)
            pngFile.writeBytes(baos.toByteArray())
        }
    }
}
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
Benjamin H
  • 5,164
  • 6
  • 34
  • 42

2 Answers2

2

RAW_SENSOR is really difficult. The specs tell us that the parameters of the underlying Bayer mosaic can be retrieved from the CameraDevice, but there is no public API to convert it into Bitmap.

At any rate, there is no advantage requesting this tricky format if your goal is to convert it to 8-bit per channel bitmap.

Furthermore, an ARGB_8888 bitmap for captured image may be huge, and cause out-of-memory if you are not sufficiently careful.

fawaad
  • 341
  • 6
  • 12
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
2

You want to capture data in the YUV_420_888 format; that's what the JPEG compressor starts with in any case.

You'll have to convert that to an RGB Bitmap yourself, however - there's no convenience method for that.

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