0

I am using Camera2 api to take images. I am using imageReader to capture image from a surfaceView and acquireLatestImage() to collect the image.

Images are getting captured but they are coming as null as shown by the Log.e message "Capture Completed" and "Failed to acquire captured image"

   private fun onCaptureButtonClick(
        captureType: String,
        imageWidth: Int,
        imageHeight: Int,
        controlMode: Int,
        cameraSettings: MutableMap<String, Any?>
    ) {


    if (captureType == "Image") {
        try {
            //CaptureRequest.Builder to configure the capture request
            val captureRequestBuilder =
                cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
            captureRequestBuilder?.addTarget(previewSurface!!)
            captureRequestBuilder?.set(CaptureRequest.CONTROL_MODE, controlMode)

            //ImageReader to capture the image
            val imageReader = ImageReader.newInstance(imageWidth, imageHeight, ImageFormat.JPEG, 1)
            Toast.makeText(applicationContext, "Imgereader Started", Toast.LENGTH_SHORT).show()

            if (imageReader != null && cameraDevice != null) {
                // Create a CameraCaptureSession.CaptureCallback to handle capture results
                val captureCallback = object : CameraCaptureSession.CaptureCallback() {
                    override fun onCaptureCompleted(
                        session: CameraCaptureSession,
                        request: CaptureRequest,
                        result: TotalCaptureResult
                    ) {
                        Log.d(TAG, "Capture completed")
                        super.onCaptureCompleted(session, request, result)

                        //image data from the ImageReader
                        val image = imageReader.acquireLatestImage()

                        if (image != null) {
                            //image data buffer
                            val buffer = image.planes[0].buffer

                            //buffer to a ByteArray
                            val imageData = ByteArray(buffer.remaining())
                            buffer.get(imageData)

                            // Save the image data to a JPEG file
                            val outputFile = File(
                                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                                "my_image.jpg"
                            )
                            try {
                                FileOutputStream(outputFile).use { output ->
                                    output.write(imageData)
                                }
                                Log.d(
                                    TAG,
                                    "Image saved successfully: ${outputFile.absolutePath}"
                                )

                                // Update the gallery so that the image appears in the gallery app
                                MediaScannerConnection.scanFile(
                                    applicationContext,
                                    arrayOf(outputFile.absolutePath),
                                    null,
                                    null
                                )
                            } catch (e: IOException) {
                                e.printStackTrace()
                            } finally {
                                // Close the image after saving
                                image.close()
                            }
                        } else {
                            Log.e(TAG, "Failed to acquire captured image")
                            Toast.makeText(
                                applicationContext,
                                "Failed to acquire captured image",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                        
                        updateCameraPreview()
                    }

                    override fun onCaptureFailed(
                        session: CameraCaptureSession,
                        request: CaptureRequest,
                        failure: CaptureFailure
                    ) {
                        super.onCaptureFailed(session, request, failure)
                        // Capture failed, implement your logic here
                        Log.d(TAG, "Capture failed")
                    }
                }
                //apture session with the configured capture request, imageReader surface, and capture callback
                val surfaces = mutableListOf(previewSurface, imageReader.surface)

                if (cameraCaptureSession == null) {
                    //new capture session if the current capture session is null
                    cameraDevice?.createCaptureSession(surfaces, object : CameraCaptureSession.StateCallback() {
                        override fun onConfigured(session: CameraCaptureSession) {

                            // Capture session configured successfully, get the CameraCaptureSession object
                            cameraCaptureSession = session
                            captureRequestBuilder?.build()?.let {
                                cameraCaptureSession?.capture(
                                    it, captureCallback, null
                                )
                            }
                        }
                            override fun onConfigureFailed(session: CameraCaptureSession) {
                                // Failed to configure capture session
                                Log.e(TAG, "Failed to configure capture session")
                            }
                        },
                            null
                            )
                        } else {
                        // Use the existing capture session if it's already created
                        cameraCaptureSession?.capture(
                            captureRequestBuilder!!.build(),
                            captureCallback,
                            null
                        )
                    }
                } else {
                    Log.e(TAG, "Failed to create ImageReader or CameraDevice is null")
                }
            } catch (e: CameraAccessException) {
                e.printStackTrace()
            }
        } else {
            
        }
    }

I am calling the with a button click

btnCapture.setOnClickListener { onCaptureButtonClick(captureType, imageWidth, (aspectRatio*imageWidth),controlMode, cameraSettings) }

I already tried with different heights and width values. Got the accepted sizes using

try {

    val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)

 
    val maxImageSize = cameraCharacteristics.get(
        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
    )?.getOutputSizes(ImageFormat.JPEG)?.maxByOrNull { it.height * it.width }

    val minImageSize = cameraCharacteristics.get(
        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
    )?.getOutputSizes(ImageFormat.JPEG)?.minByOrNull { it.height * it.width }

   
    if (maxImageSize != null) {
        val maxWidth = maxImageSize.width
        val maxHeight = maxImageSize.height
        Log.d(TAG, "Max Image Size: $maxWidth x $maxHeight")
    }

    if (minImageSize != null) {
        val minWidth = minImageSize.width
        val minHeight = minImageSize.height
        Log.d(TAG, "Min Image Size: $minWidth x $minHeight")
    }

} catch (e: CameraAccessException) {
    e.printStackTrace()
}

Any help is appreciated.

Sam11
  • 63
  • 6

1 Answers1

1

Solved the problem with the use of imageReader.setOnImageAvailableListener and setting the imageReader.surface as the target for imageRequestBuilder

Code for setting up imageReader.setOnImageAvailableListener

       imageReader.setOnImageAvailableListener({ reader ->
            // This callback will be called when an image is available from the ImageReader
            // Access the acquired image
            val image = reader.acquireLatestImage()

            // Process the image data
            if (image != null) {
                // Accessing width and height properties
                val width = image.width
                val height = image.height

                // Access the image data buffer
                val buffer = image.planes[0].buffer

                // Convert the buffer to a ByteArray
                val imageData = ByteArray(buffer.remaining())
                buffer.get(imageData)

                // Save the image data to a JPEG file
                val outputFile = File(
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                    "my_image.jpg"
                )
                try {
                    FileOutputStream(outputFile).use { output ->
                        output.write(imageData)
                    }
                    Log.d(TAG, "Image saved successfully: ${outputFile.absolutePath}")

                    // Update the gallery so that the image appears in the gallery app
                    MediaScannerConnection.scanFile(
                        applicationContext,
                        arrayOf(outputFile.absolutePath),
                        null,
                        null
                    )
                } catch (e: IOException) {
                    e.printStackTrace()
                } finally {
                    // Close the image after saving
                    image.close()
                }
            } else {
                Log.e(TAG, "Failed to acquire captured image")
                Toast.makeText(
                    applicationContext,
                    "Failed to acquire captured image",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }, null)

Setting up imageReader surface as the target of captureRequestBuilder

captureRequestBuilder?.addTarget(imageReader.surface)
Sam11
  • 63
  • 6