0

I have implemented the camera using Camera2 API. The pictures taken under the same settings i.e same position of the camera, same location have different level of brightness. The following pictures were taken one after the another and still there is a huge different in the level of brightness.

enter image description here

enter image description here

How can I set the brightness of the pictures captured using Camera2 API?

I tried to manually set the brightness as shown in the code below and still I am not able to get the constant amount of brightness.

My final aim is to be able to compare two images.

import kotlinx.android.synthetic.main.activity_camera.*
import java.io.FileOutputStream
import android.util.SparseIntArray
import android.view.Surface
import android.media.ImageReader
import android.util.Size
import java.io.File
import android.graphics.SurfaceTexture
import android.view.TextureView
import android.content.Context
import android.support.v4.app.ActivityCompat
import android.content.pm.PackageManager
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.graphics.ImageFormat
import android.hardware.camera2.*
import android.widget.Toast
import android.hardware.camera2.TotalCaptureResult
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CameraCaptureSession
import android.media.Image
import android.media.MediaScannerConnection
import android.os.*
import java.io.OutputStream
import java.lang.Exception


class CameraActivity : Activity() {
var createDirectories = CreateDirectories()
private val ORIENTATIONS = SparseIntArray()

private var cameraId: String? = null
private var cameraDevice: CameraDevice? = null
private var cameraCaptureSessions: CameraCaptureSession? = null
private var captureRequestBuilder: CaptureRequest.Builder? = null
private var imageDimension: Size? = null
private var imageReader: ImageReader? = null
private var file: File? = null
private val REQUEST_CAMERA_PERMISSION = 200
private var mBackgroundHandler: Handler? = null
private var mBackgroundThread: HandlerThread? = null
// var manager: CameraManager? =null
var flag = true
var width = 900
var height = 900

private val customHandler = Handler()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)

    ORIENTATIONS.append(Surface.ROTATION_0, 270)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 0)
    ORIENTATIONS.append(Surface.ROTATION_270, 180)

    texture.surfaceTextureListener = textureListener
}

companion object {
    var file1: Boolean = true
}

fun takePicture() {

    try {
        if (null == cameraDevice) {

            return
        }

        val manager = getSystemService(Context.CAMERA_SERVICE) as CameraManager

        val characteristics = manager.getCameraCharacteristics(cameraDevice?.getId());

        val reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1)
        var outputSurfaces = mutableListOf<Surface>()
        outputSurfaces.add(reader.surface)
        outputSurfaces.add(Surface(texture.surfaceTexture))
        val captureBuilder =
            cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
        captureBuilder?.addTarget(reader.surface)
        captureBuilder?.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AE_MODE_ON)
        val brightness = setBrightness(characteristics)
        captureBuilder?.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, brightness)

        val rotation = windowManager.defaultDisplay.rotation
        val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
        captureBuilder?.set(CaptureRequest.JPEG_ORIENTATION, sensorOrientation)
        createDirectories.createFolder("ClipPicNew")


        file = File("${Environment.getExternalStorageDirectory()}/ClipPicNew/pic${System.currentTimeMillis()}.jpg")
        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler)

        cameraDevice?.createCaptureSession(
            listOf(reader.surface),
            object : CameraCaptureSession.StateCallback() {

                override fun onConfigured(session: CameraCaptureSession) {
                    try {
                        session.capture(
                            captureBuilder?.build(),
                            captureListener,
                            mBackgroundHandler
                        )

                    } catch (e: CameraAccessException) {

                        e.printStackTrace()
                    }
                }

                override fun onConfigureFailed(session: CameraCaptureSession) {
                }
            },
            mBackgroundHandler
        )

    } catch (e: CameraAccessException) {

        e.printStackTrace()
    }

}

val captureListener = object : CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(
        session: CameraCaptureSession,
        request: CaptureRequest,
        result: TotalCaptureResult
    ) {
        super.onCaptureCompleted(session, request, result)
        Toast.makeText(this@CameraActivity, "Saved:$file", Toast.LENGTH_SHORT).show()
        createCameraPreview()
    }
}

val readerListener: ImageReader.OnImageAvailableListener =
    object : ImageReader.OnImageAvailableListener {
        override fun onImageAvailable(reader: ImageReader?) {
            var image: Image? = null
            try {

                image = reader?.acquireLatestImage()
                val buffer = image!!.planes[0].buffer
                val bytes = ByteArray(buffer.capacity())
                buffer.get(bytes)

                save(bytes)
            } catch (e: Exception) {

                e.printStackTrace()
            } finally {
                image?.close()
            }
        }

        fun save(bytes: ByteArray) {

            var output: OutputStream? = null
            try {
                output = FileOutputStream(file)
                output.write(bytes)

            } catch (e: Exception) {

                e.printStackTrace()
            } finally {
                output?.close()
                var filePath = ""
                if (file != null) {
                    filePath = file?.absolutePath.toString()
                }

                MediaScannerConnection.scanFile(
                    this@CameraActivity,
                    arrayOf(filePath),
                    null,
                    null
                )
            }
        }
    }

//texture listener
private var textureListener: TextureView.SurfaceTextureListener =
    object : TextureView.SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(
            surface: SurfaceTexture,
            width1: Int,
            height1: Int
        ) {
            width = width1
            height = height1

            openCamera()
        }

        override fun onSurfaceTextureSizeChanged(
            surface: SurfaceTexture,
            width1: Int,
            height1: Int
        ) {
            width = width1
            height = height1
            // Transform you image captured size according to the surface width and height
        }

        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
            return false
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
    }

//open Camera

private fun openCamera() {
    val manager = getSystemService(Context.CAMERA_SERVICE) as CameraManager

    try {
        cameraId = manager.cameraIdList[0]
        val characteristics = manager.getCameraCharacteristics(cameraId)

        val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
        imageDimension = map.getOutputSizes(SurfaceTexture::class.java)[0]
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.CAMERA
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE),
                REQUEST_CAMERA_PERMISSION
            );
            return
        }
        manager.openCamera(cameraId, stateCallback, null)
    } catch (e: CameraAccessException) {
        e.printStackTrace()
    }
}


private val stateCallback = object : CameraDevice.StateCallback() {

    override fun onOpened(camera: CameraDevice) {
        //This is called when the camera is open

        cameraDevice = camera
        createCameraPreview()
    }

    override fun onDisconnected(camera: CameraDevice) {

    }

    override fun onError(camera: CameraDevice, error: Int) {

    }
}


fun createCameraPreview() {
    try {
        val surfaceTexture = texture.getSurfaceTexture()
        surfaceTexture.setDefaultBufferSize(
            width, height
        );
        val surface = Surface(surfaceTexture)
        captureRequestBuilder =
                cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)


        captureRequestBuilder?.addTarget(surface)


        cameraDevice?.createCaptureSession(listOf(surface), mCameraCaptureSessionCallback, null)
    } catch (e: CameraAccessException) {
        e.printStackTrace()
    }

}

private val mCameraCaptureSessionCallback = object : CameraCaptureSession.StateCallback() {
    override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
        if (null == cameraDevice) {
            return

        }

        cameraCaptureSessions = cameraCaptureSession
        updatePreview()
    }

    override fun onConfigureFailed(session: CameraCaptureSession) {

    }
}


fun updatePreview() {
    if (null == cameraDevice) {
    }


    val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
    cameraId = cameraManager.cameraIdList[0]
    val characteristics = cameraManager.getCameraCharacteristics(cameraId)

    captureRequestBuilder?.set(
        CaptureRequest.CONTROL_AWB_MODE,
        CameraMetadata.CONTROL_AE_MODE_ON
    )
    val brightness = setBrightness(characteristics)
    captureRequestBuilder?.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, brightness)


    try {
        cameraCaptureSessions?.setRepeatingRequest(
            captureRequestBuilder?.build(),
            null,
            mBackgroundHandler
        )
    } catch (e: Exception) {

        e.printStackTrace()
    }

}

private fun setBrightness(characteristics: CameraCharacteristics): Int {

    val controlAECompensationStep =
        characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)
    if (controlAECompensationStep != null) {
        var compensationStep = controlAECompensationStep
    }

    var minCompensationRange = 0
    var maxCompensationRange = 0
    val controlAECompensationRange =
        characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE)
    if (controlAECompensationRange != null) {
        minCompensationRange = controlAECompensationRange.getLower();
        maxCompensationRange = controlAECompensationRange.getUpper();
    }

    return (minCompensationRange + (maxCompensationRange - minCompensationRange) * (90 / 100))

}

private fun startBackgroundThread() {
    mBackgroundThread = HandlerThread("Camera Background")
    mBackgroundThread?.start()
    mBackgroundHandler = Handler(mBackgroundThread?.looper)
}

private fun stopBackgroundThread() {
    mBackgroundThread?.quitSafely()
    try {
        mBackgroundThread?.join()
        mBackgroundThread = null
        mBackgroundHandler = null
    } catch (e: InterruptedException) {
        e.printStackTrace()
    }

}

private fun closeCamera() {
    if (null != cameraDevice) {
        cameraDevice?.close()
        cameraDevice = null
    }
    if (null != imageReader) {
        imageReader?.close()
        imageReader = null
    }
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String>,
    grantResults: IntArray
) {
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
            // close the app
            Toast.makeText(
                this,
                "Sorry!!!, you can't use this app without granting permission",
                Toast.LENGTH_LONG
            ).show()
            finish()
        }
    }
}

override fun onResume() {
    super.onResume()

    startBackgroundThread()
    if (texture.isAvailable) {
        openCamera()
    } else {
        texture.surfaceTextureListener = textureListener
    }
    if (flag) {
        flag = false
        customHandler.postDelayed(updateTimerThread, 500)

    }

}

override fun onDestroy() {
    super.onDestroy()
    customHandler.removeCallbacks(updateTimerThread)
}

override fun onPause() {
    closeCamera()
    stopBackgroundThread()
    super.onPause()
}

private var updateTimerThread: Runnable = object : Runnable {

    override fun run() {
        takePictureAndSendResult()
    }
}

fun takePictureAndSendResult() {

    takePicture()
    val resultIntent = Intent()
    if (file != null) {

        resultIntent.putExtra("File Path", file!!.absolutePath)
    }

    setResult(Activity.RESULT_OK, resultIntent)
    finish()
}

}
  • Possibly related: [https://stackoverflow.com/questions/31925769/pictures-with-camera2-api-are-really-dark](https://stackoverflow.com/questions/31925769/pictures-with-camera2-api-are-really-dark) – greeble31 Jan 30 '19 at 18:35

1 Answers1

1

How long are you allowing preview to run before you take a picture? It can take several frames for the autoexposure routine to stabilize, so if you immediately take a picture after starting the camera, you can have problems like this.

Otherwise, I'll note that exposure compensation is not about setting an absolute exposure value, it's about setting an offset to the default solution auto-exposure would select (so it's a control for "I want this brighter than AE would make it"). If you want true manual exposure, "I want to expose for 0.1 seconds", you need to use different controls (SENSOR_EXPOSURE_TIME, AE_MODE_OFF, etc).

If things were consistenly dark, I'd suggest checking the target FPS range (CONTROL_AE_TARGET_FPS_RANGE), since if it's set to something like [30,30], the AE routine can't extend exposure time past 1/30s, which can be too little in darker places. You'd want to switch to something like [15,30], which would allow exposures up to 1/15s.

Do the captured images match the preview you see?

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