The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +772K…

Follow publication

CAMERA

Machine Learning With CameraX and MLKit

Himanshu Choudhary
The Startup
Published in
8 min readJan 4, 2021
Cover photo

Project Overview

Final App

Let’s start with dependencies

implementation "androidx.camera:camera-camera2:1.0.0-rc01"
implementation "androidx.camera:camera-lifecycle:1.0.0-rc01"
implementation "androidx.camera:camera-view:1.0.0-alpha20"
implementation 'com.google.mlkit:object-detection-custom:16.3.0'
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt' // <-- Add this plugin for data binding
}

android {
... // This represents the existing code compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
aaptOptions {
noCompress "tflite" // To avoid ML model compression
}
dataBinding {
enabled = true // To enable data binding
}
}

dependencies {
.... // This represents the existing dependencies. implementation 'com.google.mlkit:object-detection-custom:16.3.0'

implementation "androidx.camera:camera-camera2:1.0.0-rc01"
implementation "androidx.camera:camera-lifecycle:1.0.0-rc01"
implementation "androidx.camera:camera-view:1.0.0-alpha20"

}

It’s time to integrate ML Model😰

Why am I always specifying model as a custom model ?

Let’s Code 🧑‍💻

<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">

<RelativeLayout
android:id="@+id/layout"
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"/>

</RelativeLayout>

</layout>
private lateinit var objectDetector: ObjectDetector
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
cameraProviderFuture = ProcessCameraProvider.getInstance(this)

cameraProviderFuture.addListener({
// get() is used to get the instance of the future.
val cameraProvider = cameraProviderFuture.get()
// Here, we will bind the preview
}, ContextCompat.getMainExecutor(this))
val localModel = LocalModel.Builder()
.setAssetFilePath("object_labeler.tflite")
.build()
val customObjectDetectorOptions =
CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
.enableClassification()
.setClassificationConfidenceThreshold(0.5f)
.setMaxPerObjectLabelCount(3)
.build()
objectDetector =
ObjectDetection.getClient(customObjectDetectorOptions)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main) // data binding

cameraProviderFuture = ProcessCameraProvider.getInstance(this)

cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// Here, we will bind the preview
}, ContextCompat.getMainExecutor(this))

val localModel = LocalModel.Builder()
.setAssetFilePath("object_labeler.tflite")
.build()

val customObjectDetectorOptions =
CustomObjectDetectorOptions.Builder(localModel)
.setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
.enableClassification()
.setClassificationConfidenceThreshold(0.5f)
.setMaxPerObjectLabelCount(3)
.build()

objectDetector =
ObjectDetection.getClient(customObjectDetectorOptions)
}

Let’s bind the camera preview with the lifecycle

val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.previewView.surfaceProvider)
val cameraSelector : CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val point = Point()
val size = display?.getRealSize(point)
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(point.x, point.y)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), { imageProxy ->
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
val image = imageProxy.image
if(image != null) {
val inputImage = InputImage.fromMediaImage(image, rotationDegrees)
objectDetector
.process(inputImage)
.addOnFailureListener {
imageProxy.close()
}.addOnSuccessListener { objects ->
// Here, we get a list of objects which are detected.
imageProxy.close()
}
}
})
cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
override fun onCreate(savedInstanceState: Bundle?) {
....
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
bindPreview(cameraProvider) // Call the function here
}, ContextCompat.getMainExecutor(this))

....
}
@SuppressLint("UnsafeExperimentalUsageError")
private fun bindPreview(cameraProvider : ProcessCameraProvider) {
val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.previewView.surfaceProvider)

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

val point = Point()
val size = display?.getRealSize(point)
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(point.x, point.y))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()

imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), { imageProxy ->
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
val image = imageProxy.image
if(image != null) {

val inputImage = InputImage.fromMediaImage(image, rotationDegrees)
objectDetector
.process(inputImage)
.addOnFailureListener {
imageProxy.close()
}.addOnSuccessListener { objects ->

imageProxy.close()
}
}
})

cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
}
class Draw(context: Context?, var rect: Rect, var text: String) : View(context) {

lateinit var paint: Paint
lateinit var textPaint: Paint

init {
init()
}

private fun init() {
paint = Paint()
paint.color = Color.RED
paint.strokeWidth = 20f
paint.style = Paint.Style.STROKE

textPaint = Paint()
textPaint.color = Color.RED
textPaint.style = Paint.Style.FILL
textPaint.textSize = 80f
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawText(text, react.centerX().toFloat(), react.centerY().toFloat(), textPaint)
canvas.drawRect(react.left.toFloat(), react.top.toFloat(), react.right.toFloat(), react.bottom.toFloat(), paint)
}
}
objectDetector
.process(inputImage)
.addOnFailureListener {
imageProxy.close()
}.addOnSuccessListener { objects ->
for( it in objects) {
if(binding.layout.childCount > 1) binding.layout.removeViewAt(1)
val element = Draw(this, it.boundingBox, it.labels.firstOrNull()?.text ?: "Undefined")
binding.layout.addView(element,1)

}
imageProxy.close()
}

Everything is done.

Now, it’s

Let’s run the app.

Still, having some doubts?

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

The Startup
The Startup

Responses (5)

Write a response