Real Time Face detection in Android using CameraX and MLKIT using JAVA
Pre-requisites:
- Firebase ML kit
- CameraX library
- Adding firebase to android app
Approach:
Step 1:
- Create a new android project with compiled SDK version of 28.
- The language used for this project is Java.
Step 2:
- Login on Firebase.
- Go to android studio Tools->Firebase->Firebase ML->click on use firebase ml to recognize image.
- Refer this link to setup (https://firebase.google.com/docs/android/setup)
- Dont forget to import services.json
2.Sync now
Step 4:Designing the UI.
I have used previewview to display camera feed.
<?xml version=”1.0" encoding=”utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=”http://schemas.android.com/apk/res/android"
xmlns:app=”http://schemas.android.com/apk/res-auto"
xmlns:tools=”http://schemas.android.com/tools"
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”#0C0B0B”
tools:context=”.MainActivity”><RelativeLayout
android:id=”@+id/conlay”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent”><androidx.camera.view.PreviewView
android:id=”@+id/pv”
android:layout_width=”match_parent”
android:layout_height=”520dp”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”></androidx.camera.view.PreviewView>
</RelativeLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=”@+id/floatingActionButton2"
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginStart=”177dp”
android:layout_marginEnd=”178dp”
android:layout_marginBottom=”16dp”
android:clickable=”true”
android:foregroundGravity=”center”
android:scaleType=”center”
app:backgroundTint=”#673AB7"
app:barrierMargin=”7dp”
app:fabCustomSize=”60dp”app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:srcCompat=”@drawable/ic_baseline_mic_24" /></androidx.constraintlayout.widget.ConstraintLayout>
Step 5: Render images to previewview using camerax .
private void renderimage() {
ListenableFuture<ProcessCameraProvider> val = ProcessCameraProvider.getInstance(this);
val.addListener(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.P)
@Override
public void run() {try {
ProcessCameraProvider processCameraProvider= val.get();
preview = new Preview.Builder().setTargetResolution(new Size(900,1200)).build();// ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
// imageAnalysis.setAnalyzer(getMainExecutor(),new BaseImage());CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_FRONT).build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
ImageCapture imageCapture = new ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY).build();
ImageAnalysis imageAnalysis1 = new ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetResolution(new Size(900,1200)).build();
imageAnalysis1.setAnalyzer(ContextCompat.getMainExecutor(MainActivity.this)
,new BaseImage(MainActivity.this,textToSpeech,view,findViewById(R.id.textView3)));
processCameraProvider.unbindAll();camera = processCameraProvider.bindToLifecycle(MainActivity.this,cameraSelector,preview,imageCapture,imageAnalysis1);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}}
}, ContextCompat.getMainExecutor(MainActivity.this));}
What this function will do :
- It will pass images frame by frame and pass it to the ImageAnalysis.Analizer class.
- Here cameraselector class is used to access camera whichever you want to use here i have used front camera.
- ImageAnalysis is used to perform image processing on the image frame so you set your own analyer by extending ImageAnalysis.Analizer class .
- Then all you need to do is bind the lifecycle of camera with the process.
This below code is the Analyzer class where you can detect all your Contours , landmarks,classify faces etc.
public class BaseImage implements ImageAnalysis.Analyzer {
FirebaseVisionFaceDetector firebaseVisionFaceDetector;
Context context;
TextToSpeech textToSpeech;
boolean speakornot = true;
boolean eyesopened = true;
boolean right = true;
boolean down = true;
boolean left = true;
boolean smile = true;
RelativeLayout rootview;
TextView textView;public BaseImage(Context context,TextToSpeech textToSpeech,RelativeLayout view,TextView textView){
this.context = context;
this.textToSpeech = textToSpeech;
this.rootview = view;
this.textView = textView;
}@SuppressLint(“UnsafeExperimentalUsageError”)
@Override
public void analyze(@NonNull ImageProxy image) {Image n = image.getImage();
Log.d(“RAn analyzer — — — — — — — — — ->>>>>>>>>>” ,”Ran”);FirebaseVisionFaceDetectorOptions a = new FirebaseVisionFaceDetectorOptions.Builder().setPerformanceMode(FirebaseVisionFaceDetectorOptions.FAST)
.setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
.setMinFaceSize(0.2f)
.enableTracking().build();
int rotation = image.getImageInfo().getRotationDegrees();
FirebaseVisionImage p = FirebaseVisionImage.fromMediaImage(n, finddegree(rotation));
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance().getVisionFaceDetector(a);
detector.detectInImage(p).addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionFace>>() {
@Override
public void onSuccess(List<FirebaseVisionFace> firebaseVisionFaces) {Log.d(“Got values — — — — — — — — — ->>>>>>>>>>” , firebaseVisionFaces.toString());
if(!firebaseVisionFaces.isEmpty()){
textView.setText(“Face detected”);
for(int i=0;i<firebaseVisionFaces.size();i++){
if(rootview.getChildCount()>1){
rootview.removeViewAt(1);
}FirebaseVisionFace firebaseVisionFace = firebaseVisionFaces.get(i);
FirebaseVisionPoint firebaseVisionPoint = firebaseVisionFace.getLandmark(FirebaseVisionFaceLandmark.NOSE_BASE).getPosition();
FirebaseVisionPoint firebaseVisionPoint1 = firebaseVisionFace.getLandmark(FirebaseVisionFaceLandmark.MOUTH_BOTTOM).getPosition();double x = Math.pow(firebaseVisionPoint1.getX() — firebaseVisionPoint.getX() , 2);
double y = Math.pow(firebaseVisionPoint1.getY() — firebaseVisionPoint.getY() , 2);Log.d(“Head down figures — — — ->>>>” ,Math.sqrt(x + y) +” “+ ((0.125) * (firebaseVisionFace.getBoundingBox().width()*1.5)));
if(Math.sqrt(x + y) < ((0.111) * (firebaseVisionFace.getBoundingBox().width()*1.5))){
textToSpeech.speak(“Dont look down see straight”,TextToSpeech.QUEUE_FLUSH,null,null);
textView.setText(“Looking down”);}
if(firebaseVisionFace.getHeadEulerAngleY() < -20){
textView.setText(“Left cheek”);// left = false;
}
if(firebaseVisionFace.getHeadEulerAngleY() >20){
textView.setText(“Right cheek”);// left = false;
}
if(firebaseVisionFace.getSmilingProbability()>0.8){textView.setText(“Happy face”);
// textToSpeech.speak(“dont laugh too much”,TextToSpeech.QUEUE_FLUSH,null,null);// smile = false;
}View view1 = new Drawlayeraroundface(context,firebaseVisionFace.getBoundingBox());
rootview.addView(view1);
}}else{
if(rootview.getChildCount()>1){
rootview.removeViewAt(1);
}textView.setText(“Face Not detected”);
// Toast.makeText(context,”Not Detected”,Toast.LENGTH_LONG).show();
}
image.close();
}
}
).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(context,”Not Detected”,Toast.LENGTH_LONG).show();
image.close();}
});
}