Camera

CameraX: HDR, Night and Portrait mode

Simplest way to click and analyse photos in Android

Himanshu Choudhary

--

I know, you too are also sick of the various complicated methods of handling camera in Android. But before jumping direct to it. I want you to know some of the features of CameraX so that you can choose them according to your requirement.

Features :
1. Lifecycle-aware.
2. Consistency across devices.
3. Includes effects like Portrait, HDR, Night, and Beauty.
4. You can access frames for use in your algorithms, such as to pass into MLKit.

Right now, CameraX is in beta and provides only 3 use cases i.e preview, Image analysis and Image capture.

Some important notes :

  • Instead of an application placing specific start and stop method calls in onResume() and onPause(), the application specifies a lifecycle to associate the camera with, using cameraProvider.bindToLifecycle(). That lifecycle then informs CameraX when to configure the camera capture session and ensures camera state changes appropriately to match lifecycle transitions.
  • CameraX observes a lifecycle to determine when to open the camera, when to create a capture session, and when to stop and shut down. APIs provide method calls and callbacks to monitor progress.
  • Following are some of the use case combinations.
Use case combinations
  • As explained in Combine use cases, you can bind some mixes of use cases to a single lifecycle. When your app needs to support use cases that cannot be combined, you can do one of the following:
  1. Group compatible use cases together into more than one fragment and then switch between fragments.
  2. Create a custom lifecycle component and use it to manually control the camera lifecycle.

To know more about creating custom lifecycle Click here

  • When extensions are enabled, only ImageCapture and Preview are guaranteed. Depending on the OEM implementation, it may not be possible to also use ImageAnalysis.

Don’t like reading much? Check out the video.

video

Requirements :

CameraX has the following minimum version requirements:

  • Android API level 21
  • Android Architecture Components 1.1.1

Permissions :

Your app will need the CAMERA permission. To save images to files, it will also require the WRITE_EXTERNAL_STORAGEpermission, except on devices running Android 10 or later.

Declare dependencies :

  • build.gradle (Project level)
allprojects {
repositories {
google()
jcenter()
}
}
  • build.gradle (App level)
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...

// CameraX core library using the camera2 implementation
def camerax_version = "1.0.0-beta02"
implementation "androidx.camera:camera-core:${cameraX_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to use the CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha09"
// If you want to use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:1.0.0-alpha09"
// If you want to use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
}

Use cases

1. Preview :

  • Implement the CameraXConfig.Provider interface in your Application class,

App.kt

class App: Application(), CameraXConfig.Provider {
override fun getCameraXConfig(): CameraXConfig {
return Camera2Config.defaultConfig()
}
}
  • Add this “App” as your application name in Manifest file.
<application
android:name=".App"
...
</application>
  • Also add permissions to it.
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

activity_main.xml

<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

MainActivity.kt

  • Request a camera provider.
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>private lateinit var previewView:PreviewViewprivate lateinit var preview:Preview
private lateinit var cameraSelector:CameraSelector
private lateinit var cameraProvider: ProcessCameraProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
previewView = findViewById(R.id.previewView)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
}
  • After requesting a CameraProvider, verify that its initialization succeeded when the view is created.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...

cameraProviderFuture.addListener(Runnable {
cameraProvider = cameraProviderFuture.get()
bindPreview()
}, ContextCompat.getMainExecutor(this))
setOnClickListener()
}
  • Select a camera and bind the lifecycle and use case.
fun bindPreview(cameraProvider : ProcessCameraProvider) {
preview = Preview.Builder()
.build()

cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()

var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)

preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.cameraInfo))
}

NOTE : Please check the permission in the settings before running the application.

Preview Screen

Now, take a break and absorb what you have read till now.

2. Image Capture

activity_main.xml

  • Add image in XML layout which will be used as a button for clicking images.
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
...

<ImageView
android:id="@+id/captureBtn"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="bottom|center"
android:src="@drawable/ic_camera_black_24dp"
tools:ignore="ContentDescription" />
</FrameLayout>

MainActivity.kt

  • Add ImageCapture variable in your activity.
private lateinit var imageCapture: ImageCapture
private fun captureImage() {    val builder = ImageCapture.Builder()

imageCapture = builder
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetRotation(binding.root.display.rotation)
.build()

cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture)
}
  • Call captureImage from your bindPreview().
private fun bindPreview() {
...
captureImage()
}
  • Add onClickListener for clicking pictures.
private lateinit var captureBtn: ImageViewoverride fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

...
setOnClickListener()
}
private fun setOnClickListener() {
binding.captureBtn.setOnClickListener {
takePhoto()
}
}
private fun takePhoto() {
val dialog = AlertDialog.Builder(this)
.setMessage("Saving picture...")
.setCancelable(false)
.show()
val file = File(getExternalFilesDir(null)?.absolutePath, System.currentTimeMillis().toString() + ".jpg")
file.createNewFile()
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
imageCapture.takePicture(outputFileOptions,
Executors.newSingleThreadExecutor(),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exception: ImageCaptureException) {
runOnUiThread {
dialog.dismiss()
Toast.makeText(this@MainActivity, "Error - ${exception.message}",Toast.LENGTH_LONG).show()
}
}
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
runOnUiThread {
dialog.dismiss()
Toast.makeText(this@MainActivity, "Saved successfully !!",Toast.LENGTH_LONG).show()
}
}
})
}

Thats It. I know it will take you time to understand these new APIs but trust me they are worth it.

They will save you from all the huge code that you will have to write while dealing with n number of android devices.

Image capture screen

Add Ons :

  1. Bokeh Effect a.k.a Portrait mode
  • To add this effect add 3 lines in your captureImage().
private fun captureImage() {
val builder = ImageCapture.Builder()
val bokehImageCapture = BokehImageCaptureExtender.create(builder) // Query if extension is available (optional).
if (bokehImageCapture.isExtensionAvailable(cameraSelector)) {
// Enable the extension if available.
bokehImageCapture.enableExtension(cameraSelector)
}

...
}

It will check whether the portrait mode is available on the device and if yes, it will enable it.

2. HDR effect

private fun captureImage() {
val builder = ImageCapture.Builder()

val hdrImageCapture = HdrImageCaptureExtender.create(builder)
if (hdrImageCapture.isExtensionAvailable(cameraSelector)) {
// Enable the extension if available.
hdrImageCapture.enableExtension(cameraSelector)
}
...
}

Same availability check is for HDR effect.

Right now, there is no function for disabling these effect.

You can also add the other effects in the same way.

Play with the code and find your own cool way to handle the camera :)

If ( YOU_LIKED_THIS_BLOG) {Hit the clap button.} else {Comment the area which you don't like. (Will try to improve that )}

Thank you :)

--

--