Start using packagecloud in minutes
Join thousands of developers using packagecloud to distribute software securely, consistently, and affordably.
README
last updated: Fri 09/13/24 at 06:32:55 PM bylucas-yupistudios
BioPass ID Face Liveness SDK Android
Latest Version
September 13, 2024 - [v1.0.5]
Table of Contents
Quick Start Guide
First, you will need a license key to use the SDK. To get your license key contact us through our website BioPass ID.
Check out our official documentation for more in depth information on BioPass ID.
1. Prerequisites:
- Java 17 or higher
- Kotlin 1.9.0 or highr
- Gradle 8.6 or higher
- Android Gradle Plugin 8.4.0 or higher
- A device with a camera
- License key
- Internet connection is required to verify the license
Before proceeding, you should add the following dependencies in your app/build.gradle
file:
dependencies
implementation 'com.google.mediapipe:tasks-vision:0.10.13'
implementation 'com.google.mlkit:face-detection:16.1.6'
implementation 'androidx.camera:camera-core:1.3.4'
implementation 'androidx.camera:camera-camera2:1.3.4'
implementation 'androidx.camera:camera-lifecycle:1.3.4'
implementation 'androidx.camera:camera-view:1.3.4'
Change the minimum Android sdk version to 24 (or higher) in your app/build.gradle
file.
minSdkVersion 24
2. Installation
With Gradle
The simplest and easiest way to install the plugin to your project, is to just add the following dependencies to your build.gradle:
dependencies
implementation 'com.biopassid:facelivenesssdk:1.0.5' // latest version
Then on your settings.gradle file:
repositories
maven
url "https://packagecloud.io/biopassid/FaceLivenessSDKAndroid/maven2"
With Local File
Another alternative to use Face Liveness SDK is to download and install the AAR file locally. Here you can find the latest releases and after downloading place the .aar file in any folder of your choice.
We will use Android Studio for the following steps:
- First, with your project open, go to File --> Project Structure --> Dependencies.
- Then in the Dependencies tab, select your app in the modules tab and click on the plus symbol to show the option to add a JAR/AAR dependency.
- On step 1 input the AAR file path, and select the implementation option on step 2.
- Just rebuild your project and should be ready to use.
3. How to use
Basic Example
By now you should have all the tools available to start using the plugin in your own project. Here is a code example showing how to launch Intent and use Face Liveness SDK. Through FaceLivenessConfig, you can configure custom settings (such as colors and features).
activity_main
In your xml layout of your main activity:
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btnCapture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Capture Face"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
In your main activity:
package com.example.facelivenessdemo
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import br.com.biopassid.facelivenesssdk.FaceLiveness
import br.com.biopassid.facelivenesssdk.FaceLivenessCallback
import br.com.biopassid.facelivenesssdk.LivenessFaceAttributes
import br.com.biopassid.facelivenesssdk.config.FaceLivenessConfig
class MainActivity : AppCompatActivity()
private lateinit var btnCapture: Button
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Button in your xml layout responsible for calling the Face Liveness SDK
btnCapture = findViewById(R.id.btnCapture)
// Instantiate FaceLivenessConfig with your preferred settings
val config = FaceLivenessConfig(licenseKey = "your-license-key")
// Define a FaceLivenessCallback to receive the image bitmap and detection result
val callback = object: FaceLivenessCallback
override fun onFaceCapture(
image: Bitmap,
faceAttributes: LivenessFaceAttributes?
)
Log.d(TAG, "onFaceCapture: $image")
Log.d(TAG, "onFaceCapture: $faceAttributes")
override fun onFaceDetected(faceAttributes: LivenessFaceAttributes)
Log.d(TAG, "onFaceDetected: $faceAttributes")
// Start Face Liveness capture
btnCapture.setOnClickListener
FaceLiveness.takeFace(this, config, callback, false)
companion object
private const val TAG = "FaceLivenessDemo"
Example using Retrofit to call the BioPass ID API
For this example we used the Liveness from the Multibiometrics plan.
First, add the Retrofit package. To install the Retrofit
package, add it to the dependencies section of the app/build.gradle
file.
dependencies
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
Additionally, in your AndroidManifest.xml file, add the Internet permission.
<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />
LivenessRequest
Create the LivenessRequest data class:
package com.example.facelivenessdemo
import com.google.gson.annotations.SerializedName
data class LivenessRequest(
@SerializedName("Spoof") val spoof: ImageData
)
data class ImageData(
@SerializedName("Image") val image: String
)
LivenessResponse
Create the LivenessResponse data class:
package com.example.facelivenessdemo
import com.google.gson.annotations.SerializedName
data class LivenessResponse(
@SerializedName("Success") val success: Boolean?,
@SerializedName("result") val result: String?,
@SerializedName("spoof") val spoof: Boolean?
)
BioPassIDApi
Here, you will need an API key to be able to make requests to the BioPass ID API. To get your API key contact us through our website BioPass ID.
Create the BioPassIDApi interface to make requests to the BioPass ID API:
package com.example.facelivenessdemo
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
interface BioPassIDApi
@Headers("Content-Type: application/json", "Ocp-Apim-Subscription-Key: your-api-key")
@POST("multibiometrics/v2/liveness")
fun enrollPerson(@Body livenessRequest: LivenessRequest) : Call<LivenessResponse>
Network
Create the Network class to make requests to the BioPass ID API:
package com.example.facelivenessdemo
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class Network
companion object
/** Returns a Client Retrofit Instance for Requests
*/
fun getRetrofitInstance() : BioPassIDApi
return Retrofit.Builder()
.baseUrl("https://api.biopassid.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(BioPassIDApi::class.java)
activity_main
In your xml layout of your main activity:
<?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"
tools:context=".MainActivity">
<Button
android:id="@+id/btnCapture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Capture Face"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
In your main activity:
package com.example.facelivenessdemo
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Base64
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import br.com.biopassid.facelivenesssdk.FaceLiveness
import br.com.biopassid.facelivenesssdk.FaceLivenessCallback
import br.com.biopassid.facelivenesssdk.LivenessFaceAttributes
import br.com.biopassid.facelivenesssdk.config.FaceLivenessConfig
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.ByteArrayOutputStream
class MainActivity : AppCompatActivity()
private lateinit var btnCapture: Button
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Button in your xml layout responsible for calling the Face Liveness SDK
btnCapture = findViewById(R.id.btnCapture)
// Instantiate FaceLivenessConfig with your preferred settings
val config = FaceLivenessConfig(licenseKey = "your-license-key")
// Define a FaceLivenessCallback to receive the image bitmap and detection result
val callback = object: FaceLivenessCallback
override fun onFaceCapture(
image: Bitmap,
faceAttributes: LivenessFaceAttributes?
)
// Encode Bitmap to base64 string
val imageData = bitmapToBas64(image)
// Instantiate Liveness request
val livenessRequest = LivenessRequest(ImageData(imageData))
// Get retrofit
val retrofit = Network.getRetrofitInstance()
// Execute request to the BioPass ID API
val callback = retrofit.enrollPerson(livenessRequest)
// Handle API response
callback.enqueue(object : Callback<LivenessResponse>
override fun onFailure(call: Call<LivenessResponse>, t: Throwable)
Log.e(TAG, "Error trying to call liveness. $t.message")
override fun onResponse(
call: Call<LivenessResponse>,
response: Response<LivenessResponse>
)
Log.d(TAG, "LivenessResponse: $response.body()")
)
override fun onFaceDetected(faceAttributes: LivenessFaceAttributes)
Log.d(TAG, "onFaceDetected: $faceAttributes")
// Start Face Liveness capture
btnCapture.setOnClickListener
FaceLiveness.takeFace(this, config, callback, false)
// Method to assist converting Bitmap to Base64 string
private fun bitmapToBas64(bitmap: Bitmap): String
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray = stream.toByteArray()
stream.close()
return Base64.encodeToString(byteArray, Base64.NO_WRAP)
companion object
private const val TAG = "FaceLivenessDemo"
4. LicenseKey
First, you will need a license key to use the SDK. To get your license key contact us through our website BioPass ID.
To use Face Liveness SDK you need a license key. To set the license key needed is simple as setting another attribute. Simply doing:
val config = FaceLivenessConfig()
config.licenseKey = "your-license-key"
5. FaceLivenessCallback
You can set a custom callback to receive the captured image and the face detection result. You can write you own callback following this example:
val callback = object : FaceLivenessCallback
override fun onFaceCapture(image: Bitmap, faceAttributes: LivenessFaceAttributes?)
Log.d(TAG, "onFaceCapture: $image")
Log.d(TAG, "onFaceCapture: $faceAttributes")
override fun onFaceDetected(faceAttributes: LivenessFaceAttributes)
Log.d(TAG, "onFaceDetected: $faceAttributes")
LivenessFaceAttributes
| Name | Type | Description |
| ----------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| faceProp | Float | Proportion of the area occupied by the face in the image, in percentage |
| faceWidth | Int | Face width, in pixels |
| faceHeight | Int | Face height, in pixels |
| ied | Int | Distance between left eye and right eye, in pixels |
| bbox | Rect | Face bounding box |
| rollAngle | Float | The Euler angle X of the head. Indicates the rotation of the face about the axis pointing out of the image. Positive z euler angle is a counter-clockwise rotation within the image plane |
| pitchAngle | Float | The Euler angle X of the head. Indicates the rotation of the face about the horizontal axis of the image. Positive x euler angle is when the face is turned upward in the image that is being processed |
| yawAngle | Float | The Euler angle Y of the head. Indicates the rotation of the face about the vertical axis of the image. Positive y euler angle is when the face is turned towards the right side of the image that is being processed |
| leftEyeOpenProbability | Float | Probability that the face’s left eye is open, in percentage |
| rightEyeOpenProbability | Float | Probability that the face’s right eye is open, in percentage |
| smilingProbability | Float | Probability that the face is smiling, in percentage |
| averageLightIntensity | Float | The average intensity of the pixels in the image |
FaceLivenessConfig
You can also use pre-build configurations on your application, so you can automatically start using multiples features that better suit your application. You can instantiate each one and use it's default properties, or if you prefer you can change every config available. Here are the types that are supported right now:
FaceLivenessConfig
| Name | Type | Default value |
| ---------------- | ------------------------------- | ------------------------------------- |
| licenseKey | String | "" |
| resolutionPreset | FaceLivenessResolutionPreset | FaceLivenessResolutionPreset.VERYHIGH |
| fontFamily | Int | R.font.facelivenesssdk_opensans_bold |
| faceDetection | FaceLivenessDetectionOptions | |
| mask | FaceLivenessMaskOptions | |
| titleText | FaceLivenessTextOptions | |
| loadingText | FaceLivenessTextOptions | |
| helpText | FaceLivenessTextOptions | |
| feedbackText | FaceLivenessFeedbackTextOptions | |
| backButton | FaceLivenessButtonOptions | |
FaceLivenessDetectionOptions
| Name | Type | Default value | Description |
| ------------------------ | ----- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| timeToCapture | Long | 3000 | Time it takes to perform an automatic capture, in miliseconds |
| maxFaceDetectionTime | Long | 60000 | Maximum facial detection attempt time, in miliseconds |
| minFaceProp | Float | 0.1f | Minimum limit of the proportion of the area occupied by the face in the image, in percentage |
| maxFaceProp | Float | 0.4f | Maximum limit on the proportion of the area occupied by the face in the image, in percentage |
| minFaceWidth | Int | 150 | Minimum face width, in pixels |
| minFaceHeight | Int | 150 | Minimum face height, in pixels |
| ied | Int | 90 | Minimum distance between left eye and right eye, in pixels |
| bboxPad | Int | 20 | Padding the face's bounding box to the edges of the image, in pixels |
| faceDetectionThresh | Float | 0.5f | Minimum trust score for a detection to be considered valid. Must be a number between 0 and 1, which 0.1 would be a lower face detection trust level and 0.9 would be a higher trust level |
| rollThresh | Float | 4.0f | The Euler angle X of the head. Indicates the rotation of the face about the axis pointing out of the image. Positive z euler angle is a counter-clockwise rotation within the image plane |
| pitchThresh | Float | 4.0f | The Euler angle X of the head. Indicates the rotation of the face about the horizontal axis of the image. Positive x euler angle is when the face is turned upward in the image that is being processed |
| yawThresh | Float | 4.0f | The Euler angle Y of the head. Indicates the rotation of the face about the vertical axis of the image. Positive y euler angle is when the face is turned towards the right side of the image that is being processed |
| closedEyesThresh | Float | 0.7f | Minimum probability threshold that the left eye and right eye of the face are closed, in percentage. A value less than 0.7 indicates that the eyes are likely closed |
| smilingThresh | Float | 0.7f | Minimum threshold for the probability that the face is smiling, in percentage. A value of 0.7 or more indicates that a person is likely to be smiling |
| tooDarkThresh | Int | 50 | Minimum threshold for the average intensity of the pixels in the image |
| tooLightThresh | Int | 170 | Maximum threshold for the average intensity of the pixels in the image |
| faceCentralizationThresh | Float | 0.05f | Threshold to consider the face centered, in percentage |
FaceLivenessMaskOptions
| Name | Type | Default value |
| ----------------- | ------- | ----------------------------- |
| enabled | Boolean | true |
| backgroundColor | Int | Color.parseColor("#CC000000") |
| frameColor | Int | Color.WHITE |
| frameEnabledColor | Int | Color.parseColor("#16AC81") |
| frameErrorColor | Int | Color.parseColor("#E25353") |
FaceLivenessFeedbackTextOptions
| Name | Type | Default value |
| --------- | -------------------------------- | ---------------------------------- |
| enabled | Boolean | true |
| messages | FaceLivenessFeedbackTextMessages | FaceLivenessFeedbackTextMessages() |
| textColor | Int | Color.WHITE |
| textSize | Int | 14 |
FaceLivenessFeedbackTextMessages
| Name | Type | Default value |
| ------------------- | ------ | ----------------------------- |
| noDetection | String | "No faces detected" |
| multipleFaces | String | "Multiple faces detected" |
| faceCentered | String | "Face centered. Do not move" |
| tooClose | String | "Turn your face away" |
| tooFar | String | "Bring your face closer" |
| tooLeft | String | "Move your face to the right" |
| tooRight | String | "Move your face to the left" |
| tooUp | String | "Move your face down" |
| tooDown | String | "Move your face up" |
| invalidIED | String | "Invalid inter-eye distance" |
| faceAngleMisaligned | String | "Misaligned face angle" |
| closedEyes | String | "Open your eyes" |
| smiling | String | "Do not smile" |
| tooDark | String | "Too dark" |
| tooLight | String | "Too light" |
FaceLivenessButtonOptions
| Name | Type | Default value |
| --------------- | ----------------------- | ------------- |
| enabled | Boolean | true |
| backgroundColor | Int | Color.WHITE |
| buttonPadding | Int | 0 |
| buttonSize | Size | Size(56, 56) |
| iconOptions | FaceLivenessIconOptions | |
| labelOptions | FaceLivenessTextOptions | |
FaceLivenessIconOptions
| Name | Type | Default value |
| --------- | ------- | ----------------------------------- |
| enabled | Boolean | true |
| iconFile | Int | R.drawable.facelivenesssdk_ic_close |
| iconColor | Int | Color.parseColor("#323232") |
| iconSize | Size | Size(32, 32) |
FaceLivenessTextOptions
| Name | Type | Default value |
| --------- | ------- | --------------------------- |
| enabled | Boolean | true |
| content | String | "" |
| textColor | Int | Color.parseColor("#323232") |
| textSize | Int | 14 |
FaceLivenessResolutionPreset (enum)
| Name | Resolution |
| ------------------------------------- | ----------------- |
| FaceLivenessResolutionPreset.HIGH | 720p (1280x720) |
| FaceLivenessResolutionPreset.VERYHIGH | 1080p (1920x1080) |
How to change font family
You can use the default font family or set one of your own. To set a font family, create a font folder under res directory. Download the font which ever you want and paste it inside font folder. All font names must be only: lowercase a-z, 0-9, or underscore. The structure should be some thing like below.
Then, just set the font family passing the reference of the font family file.
val config = FaceLivenessConfig()
config.licenseKey = "your-license-key"
config.fontFamily = R.font.roboto_mono_bold_italic
How to change icon
You can use the default icons or define one of your own. To set a icon, download the icon which ever you want and paste it inside drawable folder. The structure should be some thing like below.
Then, just set the icon passing the reference of the icon file.
val config = FaceLivenessConfig()
config.licenseKey = "your-license-key"
// changing back button icon
config.backButton.iconOptions.iconFile = R.drawable.ic_baseline_photo_camera
Changelog
v1.0.5
- Documentation update;
- Connection timeout for license activation removed.
v1.0.4
- Documentation update;
- Fixed a bug where the maximum detection time was reset even after the SDK was closed.
v1.0.3
- Documentation update;
- Fixed validation of facial angles.
v1.0.2
- Documentation update;
- Fix in the drawing of the face's bounding box, which was inverted on the X axis.
v1.0.1
- Documentation update;
- Upgrade from Camera2 to CameraX, see prerequisites section;
- FaceLivenessFaceCaptureResult renamed to LivenessFaceAttributes;
- New feedback message specific to invalid IED;
- New faceCentralizationThresh in FaceLivenessDetectionOptions;
- New debug parameter:
- If debug=true, the facial detection attributes that result in an invalid face will be printed in the UI;
- If debug=true, the face rectangle will be drawn in the UI.
- Bug fixes for face centering;
- Fixed a bug that caused the onFaceCapture callback to crash on some devices.
v1.0.0
- Added documentation;
- Added face detection;
- Added auto capture.