Step 0:- add dependencies this
implementation "androidx.camera:camera-core:1.6.1"
implementation "androidx.camera:camera-camera2:1.6.1"
implementation "androidx.camera:camera-lifecycle:1.6.1"
implementation "androidx.camera:camera-view:1.6.1"
implementation 'com.google.mlkit:face-detection:16.1.7'
Step 1 :-
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
class FaceOverlayView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
// FACE VALID OR NOT
var isFaceValid = false
set(value) {
field = value
invalidate()
}
private val borderPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 8f
isAntiAlias = true
}
private val overlayPaint = Paint().apply {
color = Color.parseColor("#88000000")
}
val faceRect = RectF()
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val overlay =
Path().apply {
addRect(
0f,
0f,
width.toFloat(),
height.toFloat(),
Path.Direction.CW
)
}
val ovalWidth =
width * 0.65f
val ovalHeight =
height * 0.45f
val left =
(width - ovalWidth) / 2
val top =
(height - ovalHeight) / 2
val right =
left + ovalWidth
val bottom =
top + ovalHeight
faceRect.set(
left,
top,
right,
bottom
)
overlay.addOval(
faceRect,
Path.Direction.CCW
)
// DARK BACKGROUND
canvas.drawPath(
overlay,
overlayPaint
)
// BORDER COLOR
borderPaint.color =
if (isFaceValid) {
Color.GREEN
} else {
Color.WHITE
}
// DRAW OVAL
canvas.drawOval(
faceRect,
borderPaint
)
}
}
Step 2:- xml will be like this
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"/>
<com.lifealchemy.yuvaap.utils.FaceOverlayView
android:id="@+id/faceOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tvFace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Face"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@android:color/white"
android:layout_margin="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:orientation="horizontal"
android:padding="20dp">
<Button
android:id="@+id/btnSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Switch"/>
<Button
android:id="@+id/btnCapture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Capture"
android:layout_marginStart="20dp"/>
</LinearLayout>
</FrameLayout>
Step 3:- camera activity
package com.lifealchemy.yuvaap.ui.test
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.lifealchemy.yuvaap.R
import com.lifealchemy.yuvaap.databinding.ActivityCameraFeatureBinding
import java.io.File
class CameraFeatureActivity : AppCompatActivity() {
private lateinit var binding:
ActivityCameraFeatureBinding
private lateinit var imageCapture:
ImageCapture
private var cameraSelector =
CameraSelector.DEFAULT_FRONT_CAMERA
// FACE DETECTOR
private val detector by lazy {
val options =
FaceDetectorOptions.Builder()
.setPerformanceMode(
FaceDetectorOptions.PERFORMANCE_MODE_FAST
)
.enableTracking()
.build()
FaceDetection.getClient(options)
}
// PERMISSION
private val cameraPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
startCamera()
} else {
Toast.makeText(
this,
"Camera Permission Denied",
Toast.LENGTH_SHORT
).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding =
ActivityCameraFeatureBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(
findViewById(R.id.main)
) { v, insets ->
val systemBars =
insets.getInsets(
WindowInsetsCompat.Type.systemBars()
)
v.setPadding(
systemBars.left,
systemBars.top,
systemBars.right,
systemBars.bottom
)
insets
}
requestPermission()
initListeners()
}
// START CAMERA
private fun startCamera() {
val cameraProviderFuture =
ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider =
cameraProviderFuture.get()
// PREVIEW
val preview =
Preview.Builder().build()
preview.surfaceProvider =
binding.previewView.surfaceProvider
// IMAGE CAPTURE
imageCapture =
ImageCapture.Builder().build()
// ANALYSIS
val imageAnalysis =
ImageAnalysis.Builder()
.setBackpressureStrategy(
ImageAnalysis
.STRATEGY_KEEP_ONLY_LATEST
)
.build()
imageAnalysis.setAnalyzer(
ContextCompat.getMainExecutor(this)
) { imageProxy ->
detectFace(imageProxy)
}
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview,
imageCapture,
imageAnalysis
)
} catch (e: Exception) {
e.printStackTrace()
}
}, ContextCompat.getMainExecutor(this))
}
// FACE DETECTION
@OptIn(ExperimentalGetImage::class)
private fun detectFace(
imageProxy: ImageProxy
) {
val mediaImage =
imageProxy.image
if (mediaImage == null) {
imageProxy.close()
return
}
val image =
InputImage.fromMediaImage(
mediaImage,
imageProxy.imageInfo.rotationDegrees
)
detector.process(image)
.addOnSuccessListener { faces ->
if (faces.isNotEmpty()) {
val face =
faces[0]
val box =
face.boundingBox
val centerX =
box.centerX()
val centerY =
box.centerY()
val faceWidth =
box.width()
val imageWidth =
image.width
val imageHeight =
image.height
// CENTER CHECK
val isCenter =
centerX > imageWidth * 0.20 &&
centerX < imageWidth * 0.80 &&
centerY > imageHeight * 0.15 &&
centerY < imageHeight * 0.85
// SIZE CHECK
val isBigEnough =
faceWidth > imageWidth * 0.15
val isFaceValid =
isCenter && isBigEnough
binding.faceOverlay.isFaceValid =
isFaceValid
if (
isFaceValid
) {
binding.tvFace.text =
"Face Inside Frame 😄"
} else {
binding.tvFace.text =
"Align Face Properly"
}
} else {
binding.tvFace.text =
"No Face"
}
}
.addOnFailureListener {
it.printStackTrace()
}
.addOnCompleteListener {
// IMPORTANT
imageProxy.close()
}
}
// CAMERA PERMISSION
private fun requestPermission() {
if (
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
) {
startCamera()
} else {
cameraPermissionLauncher.launch(
Manifest.permission.CAMERA
)
}
}
// CAPTURE PHOTO
private fun capturePhoto() {
val photoFile = File(
externalMediaDirs.first(),
"${System.currentTimeMillis()}.jpg"
)
val outputOptions =
ImageCapture.OutputFileOptions.Builder(photoFile)
.build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(
outputFileResults:
ImageCapture.OutputFileResults
) {
Toast.makeText(
this@CameraFeatureActivity,
"Photo Saved",
Toast.LENGTH_SHORT
).show()
}
override fun onError(
exception: ImageCaptureException
) {
exception.printStackTrace()
}
}
)
}
// SWITCH CAMERA
private fun switchCamera() {
cameraSelector =
if (
cameraSelector ==
CameraSelector.DEFAULT_BACK_CAMERA
) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
startCamera()
}
private fun initListeners() {
binding.btnCapture.setOnClickListener {
capturePhoto()
}
binding.btnSwitch.setOnClickListener {
switchCamera()
}
}
override fun onDestroy() {
super.onDestroy()
detector.close()
}
}

0 Comments