├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── faceplugin │ │ └── facerecognition │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── faceplugin │ │ │ └── facerecognition │ │ │ ├── AboutActivity.kt │ │ │ ├── AttributeActivity.kt │ │ │ ├── CameraActivity.java │ │ │ ├── CameraActivityKt.kt │ │ │ ├── CaptureActivity.java │ │ │ ├── CaptureView.java │ │ │ ├── ContactUsActivity.kt │ │ │ ├── DBManager.java │ │ │ ├── FACE_CAPTURE_STATE.java │ │ │ ├── FaceView.java │ │ │ ├── IdentifyDetailsActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MyGlobal.java │ │ │ ├── Person.java │ │ │ ├── PersonAdapter.java │ │ │ ├── ResultActivity.kt │ │ │ ├── SettingsActivity.kt │ │ │ ├── SplashActivity.java │ │ │ └── Utils.java │ └── res │ │ ├── drawable │ │ ├── attributr.png │ │ ├── capture.png │ │ ├── enroll.png │ │ ├── gradient_back.png │ │ ├── ic_camera.xml │ │ ├── ic_delete.xml │ │ ├── ic_email.xml │ │ ├── ic_faceplugin.png │ │ ├── ic_github.png │ │ ├── ic_launcher_background.xml │ │ ├── ic_person_search.xml │ │ ├── ic_register.xml │ │ ├── ic_reset.xml │ │ ├── ic_skype.png │ │ ├── ic_telegram.png │ │ ├── ic_threshold.xml │ │ ├── ic_tip.xml │ │ ├── ic_whatsapp.png │ │ ├── identify.png │ │ ├── information.png │ │ ├── ripple.xml │ │ ├── rounded_btn_bg.xml │ │ ├── rounded_edge.xml │ │ ├── rounded_image.xml │ │ ├── settings.png │ │ └── splash.png │ │ ├── layout │ │ ├── activity_about_new.xml │ │ ├── activity_attribute.xml │ │ ├── activity_camera.xml │ │ ├── activity_camera_kt.xml │ │ ├── activity_capture.xml │ │ ├── activity_contacus.xml │ │ ├── activity_identify_details.xml │ │ ├── activity_main.xml │ │ ├── activity_result_new.xml │ │ ├── activity_settings.xml │ │ ├── activity_splash.xml │ │ └── item_person.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── root_preferences.xml │ └── test │ └── java │ └── com │ └── faceplugin │ └── facerecognition │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libfacesdk ├── build.gradle ├── build │ └── .transforms │ │ └── 13753554662a2f3c3d39dbdbb6d04141 │ │ └── transformed │ │ └── jetified-facesdk │ │ └── AndroidManifest.xml └── facesdk.aar └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /.idea/ 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | #### Hugging Face - [Here](https://huggingface.co/spaces/FacePlugin-Ltd/FaceRecognition-LivenessDetection-SDK) 6 | #### Documentation- [Here](https://doc.faceplugin.com) 7 | 8 | # Face Recognition SDK Android with 3D Passive Liveness Detection - Fully On Premise 9 | ## Overview 10 | Explore `face recognition SDK` from [Faceplugin](https://faceplugin.com/) Top-ranked on NIST FRVT , coupled with an advanced `iBeta level 2 liveness detection` engine that effectively safeguards against **printed photos, video replay, 3D masks, and deepfake threats**, ensuring top-tier security. 11 |
This is `on-premise face recognition SDK` which means everything is processed in your phone and **NO** data leaves the device. 12 |

13 | 14 | ## Try this APP on Google Play 15 | 16 | 17 | 18 |

19 | 20 |
21 | Awesome Badge 22 | Star Badge 23 | issue 24 | pr 25 |
26 | 27 | ## Screenshots 28 |
29 | 30 | 31 | 32 | 33 |
34 | 35 | ## On the Youtube 36 |
37 | 38 | Watch the video 39 | 40 |
41 | 42 | ## Install License 43 | The code below shows how to use the license: https://github.com/Faceplugin-ltd/FaceRecognition-Android/blob/370ecadae564788eaa84f288e342da742fde0c1a/app/src/main/java/com/faceplugin/facerecognition/MainActivity.kt#L30-L45 44 | 45 | Please [contact us](#contact) to get the license. 46 | 47 | ## Documentation 48 |
49 | 50 |

Setup

51 | Copy the SDK (`libfacesdk` folder) to the `root` folder in your project. 52 | 53 | Add SDK to the project in `settings.gradle` 54 | 55 | ``` 56 | rootProject.name = "YourProjectName" 57 | include ':app' 58 | include ':libfacesdk' 59 | ``` 60 | Add dependency to your build.gradle 61 | 62 | ``` 63 | implementation project(path: ':libfacesdk') 64 | ``` 65 | 66 |

APIs

67 |

Activate SDK using license

68 | 69 | ```java 70 | public static native int setActivation(java.lang.String s); 71 | ``` 72 | 73 |

Init model for face recognition and liveness detection

74 | 75 | ```java 76 | public static native int init(AssetManager var0); 77 | ``` 78 |

Convert camera frame in YUV to Bitmap

79 | 80 | ```java 81 | public static native Bitmap yuv2Bitmap(byte[] var0, int var1, int var2, int var3); 82 | ``` 83 |

Run face recognition and liveness detection

84 | 85 | ```java 86 | public static native List faceDetection(Bitmap var0, FaceDetectionParam var1); 87 | ``` 88 |

Extract feature vector for the enrollment

89 | 90 | ```java 91 | public static native byte[] templateExtraction(Bitmap var0, FaceBox var1); 92 | ``` 93 |

Calculate cosine similarity for the matching

94 | 95 | ```java 96 | public static native float similarityCalculation(byte[] var0, byte[] var1); 97 | ``` 98 | 99 |

SDK Codes

100 | 101 | | Code | Status | 102 | |:------:|------| 103 | |0|Activate SDK successfully| 104 | |-1|Invalid License Key | 105 | |-2|Invalid AppID | 106 | |-3|Expired License Key| 107 | |-4|Activation Failed| 108 | |-5|SDK Failed| 109 | 110 |

Classes

111 |

FaceResult

112 | 113 | | Type | Name | Description | 114 | |------------------|------------------|------------------| 115 | | Rect | rect | Bounding box for face | 116 | | int | liveness | Liveness status: 0 for spoof, 1 for real, less than 0 for unknown | 117 | | int | gender | Gender classification result | 118 | | int | mask | Mask presence: 0 for no mask, 1 for mask | 119 | | int | age | Age estimation result | 120 | | float | yaw | Yaw angle: -45 to 45 degrees | 121 | | float | roll | Roll angle: -45 to 45 degrees | 122 | | float | pitch | Pitch angle: -45 to 45 degrees | 123 | | byte[] | feature | 2056-byte facial feature data | 124 | | byte[] | faceData | Encrypted facial data | 125 | | int | orient | Face orientation: 1 for no rotation, 2 for 90° rotation, 3 for 270° rotation, 4 for 180° rotation | 126 | | int | faceId | Face ID in the tracking face mode | 127 | 128 | ```java 129 | public class FaceResult { 130 | public Rect rect; 131 | public int liveness; 132 | public int gender; 133 | public int mask; 134 | public int age; 135 | public float yaw; 136 | public float roll; 137 | public float pitch; 138 | public byte[] feature; 139 | public byte[] faceData; 140 | public int orient; 141 | public int faceId; 142 | 143 | public FaceResult() { 144 | } 145 | } 146 | ``` 147 |
148 | 149 | Here's our official [document](https://doc.faceplugin.com/) 150 | 151 | ## List of our Products 152 | 153 | * **[Face Recognition with Liveness Detection-Android (Java, Kotlin)](https://github.com/Faceplugin-ltd/FaceRecognition-Android)** 154 | * **[Face Recognition with Liveness Detection-iOS (Objective C, Swift)](https://github.com/Faceplugin-ltd/FaceRecognition-iOS)** 155 | * **[Face Recognition with Liveness Detection-React Native](https://github.com/Faceplugin-ltd/FaceRecognition-React-Native)** 156 | * **[Face Recognition with Liveness Detection-Flutter](https://github.com/Faceplugin-ltd/FaceRecognition-Flutter)** 157 | * **[Face Recognition with Liveness Detection-Ionic Cordova](https://github.com/Faceplugin-ltd/FaceRecognition-Ionic-Cordova)** 158 | * **[Face Recognition with Liveness Detection-.Net MAUI](https://github.com/Faceplugin-ltd/FaceRecognition-.Net)** 159 | * **[Face Recognition with Liveness Detection-.Net WPF](https://github.com/Faceplugin-ltd/FaceRecognition-WPF-.Net)** 160 | * **[Face Recognition with Liveness Detection-Javascript](https://github.com/Faceplugin-ltd/FaceRecognition-LivenessDetection-Javascript)** 161 | * **[Face Recognition with LivenessDetection-React](https://github.com/Faceplugin-ltd/FaceRecognition-LivenessDetection-React)** 162 | * **[Face Recognition with LivenessDetection-Vue](https://github.com/Faceplugin-ltd/FaceRecognition-LivenessDetection-Vue)** 163 | * **[Face Liveness Detection-Android (Java, Kotlin)](https://github.com/Faceplugin-ltd/FaceLivenessDetection-Android)** 164 | * **[Face Liveness Detection-iOS (Objective C, Swift)](https://github.com/Faceplugin-ltd/FaceLivenessDetection-iOS)** 165 | * **[Face Liveness Detection-Linux](https://github.com/Faceplugin-ltd/FaceLivenessDetection-Linux)** 166 | * **[Face Liveness Detection-Docker](https://github.com/Faceplugin-ltd/FaceLivenessDetection-Docker)** 167 | * **[Open Source Face Recognition SDK](https://github.com/Faceplugin-ltd/Open-Source-Face-Recognition-SDK)** 168 | * **[Face Recognition SDK](https://github.com/Faceplugin-ltd/Face-Recognition-SDK)** 169 | * **[Liveness Detection SDK](https://github.com/Faceplugin-ltd/Face-Liveness-Detection-SDK)** 170 | * **[Palm Recognition SDK](https://github.com/Faceplugin-ltd/Palm-Recognition)** 171 | * **[ID Card Recognition](https://github.com/Faceplugin-ltd/ID-Card-Recognition)** 172 | * **[ID Document Liveness Detection](https://github.com/Faceplugin-ltd/ID-Document-Liveness-Detection)** 173 | 174 | ## Contact 175 |
176 | faceplugin.com  177 | faceplugin.com  178 | faceplugin.com 179 |
180 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.faceplugin.facerecognition' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.faceplugin.facerecognition" 12 | minSdk 24 13 | targetSdk 33 14 | versionCode 2 15 | versionName "1.1" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | 19 | ndk { 20 | abiFilters 'arm64-v8a', 'armeabi-v7a' 21 | } 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | kotlinOptions { 35 | jvmTarget = '1.8' 36 | } 37 | } 38 | 39 | dependencies { 40 | 41 | 42 | implementation 'androidx.appcompat:appcompat:1.6.1' 43 | implementation 'com.google.android.material:material:1.8.0' 44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 45 | implementation 'androidx.preference:preference:1.2.0' 46 | implementation 'androidx.preference:preference-ktx:1.2.0' 47 | 48 | implementation "androidx.camera:camera-core:1.0.0-beta12" 49 | implementation "androidx.camera:camera-camera2:1.0.0-beta12" 50 | implementation "androidx.camera:camera-lifecycle:1.0.0-beta12" 51 | implementation 'androidx.camera:camera-view:1.0.0-alpha19' 52 | 53 | implementation project(path: ':libfacesdk') 54 | 55 | implementation 'io.fotoapparat:fotoapparat:2.7.0' 56 | 57 | testImplementation 'junit:junit:4.13.2' 58 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 59 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 60 | implementation 'com.github.roynx98:transition-button-android:c1bf882dc3' 61 | 62 | implementation 'com.github.ybq:Android-SpinKit:1.4.0' 63 | implementation 'com.google.android.material:material:1.2.0-alpha02' 64 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/faceplugin/facerecognition/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.faceplugin.faceattribute", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 40 | 44 | 48 | 52 | 56 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Intent 4 | import android.content.pm.ResolveInfo 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import android.widget.Button 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.google.android.material.bottomsheet.BottomSheetDialog 11 | 12 | 13 | class AboutActivity : AppCompatActivity() { 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_about_new) 17 | 18 | findViewById(R.id.btn_contact).setOnClickListener { 19 | val bottomSheet = ContactUsActivity() 20 | bottomSheet.show(supportFragmentManager, "ContactUsActivity") 21 | } 22 | 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/AttributeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.graphics.Bitmap 4 | import android.os.Bundle 5 | import android.widget.ImageView 6 | import android.widget.TextView 7 | import androidx.appcompat.app.AppCompatActivity 8 | 9 | class AttributeActivity : AppCompatActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_attribute) 13 | 14 | val faceImage = intent.getParcelableExtra("face_image") as? Bitmap 15 | val livenessScore = intent.getFloatExtra("liveness", 0f) 16 | val yaw = intent.getFloatExtra("yaw", 0f) 17 | val roll = intent.getFloatExtra("roll", 0f) 18 | val pitch = intent.getFloatExtra("pitch", 0f) 19 | val face_quality = intent.getFloatExtra("face_quality", 0f) 20 | val face_luminance = intent.getFloatExtra("face_luminance", 0f) 21 | val left_eye_closed = intent.getFloatExtra("left_eye_closed", 0f) 22 | val right_eye_closed = intent.getFloatExtra("right_eye_closed", 0f) 23 | val face_occlusion = intent.getFloatExtra("face_occlusion", 0f) 24 | val mouth_opened = intent.getFloatExtra("mouth_opened", 0f) 25 | val age = intent.getIntExtra("age", 0) 26 | val gender = intent.getIntExtra("gender", 0) 27 | 28 | findViewById(R.id.imageFace).setImageBitmap(faceImage) 29 | 30 | if (livenessScore > SettingsActivity.getLivenessThreshold(this)) { 31 | val msg = String.format("Liveness: Real, score = %.03f", livenessScore) 32 | findViewById(R.id.txtLiveness).text = msg 33 | } else { 34 | val msg = String.format("Liveness: Spoof, score = %.03f", livenessScore) 35 | findViewById(R.id.txtLiveness).text = msg 36 | } 37 | 38 | if (face_quality < 0.5f) { 39 | val msg = String.format("Quality: Low, score = %.03f", face_quality) 40 | findViewById(R.id.txtQuality).text = msg 41 | } else if(face_quality < 0.75f){ 42 | val msg = String.format("Quality: Medium, score = %.03f", face_quality) 43 | findViewById(R.id.txtQuality).text = msg 44 | } else { 45 | val msg = String.format("Quality: High, score = %.03f", face_quality) 46 | findViewById(R.id.txtQuality).text = msg 47 | } 48 | 49 | var msg = String.format("Luminance: %.03f", face_luminance) 50 | findViewById(R.id.txtLuminance).text = msg 51 | 52 | msg = String.format("Angles: yaw = %.03f, roll = %.03f, pitch = %.03f", yaw, roll, pitch) 53 | findViewById(R.id.txtAngles).text = msg 54 | 55 | if (face_occlusion > SettingsActivity.getOcclusionThreshold(this)) { 56 | msg = String.format("Face occluded: score = %.03f", face_occlusion) 57 | findViewById(R.id.txtOcclusion).text = msg 58 | } else { 59 | msg = String.format("Face not occluded: score = %.03f", face_occlusion) 60 | findViewById(R.id.txtOcclusion).text = msg 61 | } 62 | 63 | msg = String.format("Left eye closed: %b, %.03f, Right eye closed: %b, %.03f", left_eye_closed > SettingsActivity.getEyecloseThreshold(this), 64 | left_eye_closed, right_eye_closed > SettingsActivity.getEyecloseThreshold(this), right_eye_closed) 65 | findViewById(R.id.txtEyeClosed).text = msg 66 | 67 | msg = String.format("Mouth opened: %b, %.03f", mouth_opened > SettingsActivity.getMouthopenThreshold(this), mouth_opened) 68 | findViewById(R.id.txtMouthOpened).text = msg 69 | 70 | msg = String.format("Age: %d", age) 71 | findViewById(R.id.txtAge).text = msg 72 | 73 | if(gender == 0) { 74 | msg = String.format("Gender: Male") 75 | } else { 76 | msg = String.format("Gender: Female") 77 | } 78 | findViewById(R.id.txtGender).text = msg 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/CameraActivity.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | 4 | import static androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST; 5 | 6 | import android.Manifest; 7 | import android.annotation.SuppressLint; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.pm.PackageManager; 11 | import android.graphics.Bitmap; 12 | import android.media.Image; 13 | import android.os.Bundle; 14 | import android.util.Size; 15 | 16 | import androidx.annotation.NonNull; 17 | import androidx.appcompat.app.AppCompatActivity; 18 | import androidx.camera.core.Camera; 19 | import androidx.camera.core.CameraSelector; 20 | import androidx.camera.core.ImageAnalysis; 21 | import androidx.camera.core.ImageProxy; 22 | import androidx.camera.core.Preview; 23 | import androidx.camera.lifecycle.ProcessCameraProvider; 24 | import androidx.camera.view.PreviewView; 25 | import androidx.core.app.ActivityCompat; 26 | import androidx.core.content.ContextCompat; 27 | 28 | import com.google.common.util.concurrent.ListenableFuture; 29 | import com.ocp.facesdk.FaceBox; 30 | import com.ocp.facesdk.FaceDetectionParam; 31 | import com.ocp.facesdk.FaceSDK; 32 | 33 | import java.nio.ByteBuffer; 34 | import java.util.List; 35 | import java.util.concurrent.ExecutionException; 36 | import java.util.concurrent.ExecutorService; 37 | import java.util.concurrent.Executors; 38 | 39 | public class CameraActivity extends AppCompatActivity { 40 | 41 | static String TAG = CameraActivity.class.getSimpleName(); 42 | static int PREVIEW_WIDTH = 720; 43 | static int PREVIEW_HEIGHT = 1280; 44 | 45 | private ExecutorService cameraExecutorService; 46 | private PreviewView viewFinder; 47 | private Preview preview = null; 48 | private ImageAnalysis imageAnalyzer = null; 49 | private Camera camera = null; 50 | private CameraSelector cameraSelector = null; 51 | private ProcessCameraProvider cameraProvider = null; 52 | 53 | private FaceView faceView; 54 | 55 | private Context context; 56 | 57 | private Boolean recognized = false; 58 | 59 | @Override 60 | protected void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setContentView(R.layout.activity_camera); 63 | 64 | context = this; 65 | 66 | viewFinder = findViewById(R.id.preview); 67 | faceView = findViewById(R.id.faceView); 68 | cameraExecutorService = Executors.newFixedThreadPool(1); 69 | 70 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 71 | == PackageManager.PERMISSION_DENIED) { 72 | 73 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1); 74 | } else { 75 | viewFinder.post(() -> 76 | { 77 | setUpCamera(); 78 | }); 79 | } 80 | } 81 | 82 | @Override 83 | public void onResume() { 84 | super.onResume(); 85 | 86 | recognized = false; 87 | } 88 | 89 | @Override 90 | public void onPause() { 91 | super.onPause(); 92 | 93 | faceView.setFaceBoxes(null); 94 | } 95 | 96 | @Override 97 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 98 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 99 | 100 | if(requestCode == 1) { 101 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 102 | == PackageManager.PERMISSION_GRANTED) { 103 | 104 | viewFinder.post(() -> 105 | { 106 | setUpCamera(); 107 | }); 108 | } 109 | } 110 | } 111 | 112 | private void setUpCamera() 113 | { 114 | ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(CameraActivity.this); 115 | cameraProviderFuture.addListener(() -> { 116 | 117 | // CameraProvider 118 | try { 119 | cameraProvider = cameraProviderFuture.get(); 120 | } catch (ExecutionException e) { 121 | } catch (InterruptedException e) { 122 | } 123 | 124 | // Build and bind the camera use cases 125 | bindCameraUseCases(); 126 | 127 | }, ContextCompat.getMainExecutor(CameraActivity.this)); 128 | } 129 | 130 | @SuppressLint({"RestrictedApi", "UnsafeExperimentalUsageError"}) 131 | private void bindCameraUseCases() 132 | { 133 | int rotation = viewFinder.getDisplay().getRotation(); 134 | 135 | cameraSelector = new CameraSelector.Builder().requireLensFacing(SettingsActivity.getCameraLens(this)).build(); 136 | 137 | preview = new Preview.Builder() 138 | .setTargetResolution(new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)) 139 | .setTargetRotation(rotation) 140 | .build(); 141 | 142 | imageAnalyzer = new ImageAnalysis.Builder() 143 | .setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST) 144 | .setTargetResolution(new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)) 145 | // Set initial target rotation, we will have to call this again if rotation changes 146 | // during the lifecycle of this use case 147 | .setTargetRotation(rotation) 148 | .build(); 149 | 150 | imageAnalyzer.setAnalyzer(cameraExecutorService, new FaceAnalyzer()); 151 | 152 | cameraProvider.unbindAll(); 153 | 154 | try { 155 | camera = cameraProvider.bindToLifecycle( 156 | this, cameraSelector, preview, imageAnalyzer); 157 | 158 | preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); 159 | } catch (Exception exc) { 160 | } 161 | } 162 | 163 | class FaceAnalyzer implements ImageAnalysis.Analyzer 164 | { 165 | @SuppressLint("UnsafeExperimentalUsageError") 166 | @Override 167 | public void analyze(@NonNull ImageProxy imageProxy) 168 | { 169 | analyzeImage(imageProxy); 170 | } 171 | } 172 | 173 | @SuppressLint("UnsafeExperimentalUsageError") 174 | private void analyzeImage(ImageProxy imageProxy) 175 | { 176 | if(recognized == true) { 177 | imageProxy.close(); 178 | return; 179 | } 180 | 181 | try 182 | { 183 | Image image = imageProxy.getImage(); 184 | 185 | Image.Plane[] planes = image.getPlanes(); 186 | ByteBuffer yBuffer = planes[0].getBuffer(); 187 | ByteBuffer uBuffer = planes[1].getBuffer(); 188 | ByteBuffer vBuffer = planes[2].getBuffer(); 189 | 190 | int ySize = yBuffer.remaining(); 191 | int uSize = uBuffer.remaining(); 192 | int vSize = vBuffer.remaining(); 193 | 194 | byte[] nv21 = new byte[ySize + uSize + vSize]; 195 | yBuffer.get(nv21, 0, ySize); 196 | vBuffer.get(nv21, ySize, vSize); 197 | uBuffer.get(nv21, ySize + vSize, uSize); 198 | 199 | int cameraMode = 7; 200 | if(SettingsActivity.getCameraLens(context) == CameraSelector.LENS_FACING_BACK) { 201 | cameraMode = 6; 202 | } 203 | Bitmap bitmap = FaceSDK.yuv2Bitmap(nv21, image.getWidth(), image.getHeight(), cameraMode); 204 | 205 | FaceDetectionParam faceDetectionParam = new FaceDetectionParam(); 206 | faceDetectionParam.check_liveness = true; 207 | faceDetectionParam.check_liveness_level = SettingsActivity.getLivenessLevel(this); 208 | List faceBoxes = FaceSDK.faceDetection(bitmap, faceDetectionParam); 209 | 210 | runOnUiThread(new Runnable() { 211 | @Override 212 | public void run() { 213 | faceView.setFrameSize(new Size(bitmap.getWidth(), bitmap.getHeight())); 214 | faceView.setFaceBoxes(faceBoxes); 215 | } 216 | }); 217 | 218 | if(faceBoxes.size() > 0) { 219 | FaceBox faceBox = faceBoxes.get(0); 220 | if(faceBox.liveness > SettingsActivity.getLivenessThreshold(context)) { 221 | byte[] templates = FaceSDK.templateExtraction(bitmap, faceBox); 222 | 223 | float maxSimiarlity = 0; 224 | Person maximiarlityPerson = null; 225 | for(Person person : DBManager.personList) { 226 | float similarity = FaceSDK.similarityCalculation(templates, person.templates); 227 | if(similarity > maxSimiarlity) { 228 | maxSimiarlity = similarity; 229 | maximiarlityPerson = person; 230 | } 231 | } 232 | 233 | if(maxSimiarlity > SettingsActivity.getIdentifyThreshold(this)) { 234 | recognized = true; 235 | final Person identifiedPerson = maximiarlityPerson; 236 | final float identifiedSimilarity = maxSimiarlity; 237 | 238 | runOnUiThread(new Runnable() { 239 | @Override 240 | public void run() { 241 | Bitmap faceImage = Utils.cropFace(bitmap, faceBox); 242 | 243 | Intent intent = new Intent(context, ResultActivity.class); 244 | intent.putExtra("identified_face", faceImage); 245 | intent.putExtra("enrolled_face", identifiedPerson.face); 246 | intent.putExtra("identified_name", identifiedPerson.name); 247 | intent.putExtra("similarity", identifiedSimilarity); 248 | intent.putExtra("liveness", faceBox.liveness); 249 | intent.putExtra("yaw", faceBox.yaw); 250 | intent.putExtra("roll", faceBox.roll); 251 | intent.putExtra("pitch", faceBox.pitch); 252 | intent.putExtra("face_quality", faceBox.face_quality); 253 | intent.putExtra("face_luminance", faceBox.face_luminance); 254 | 255 | startActivity(intent); 256 | } 257 | }); 258 | } 259 | } 260 | } 261 | } 262 | catch (Exception e) 263 | { 264 | e.printStackTrace(); 265 | } 266 | finally 267 | { 268 | imageProxy.close(); 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/CameraActivityKt.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.os.Bundle 8 | import android.util.Size 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.camera.core.CameraSelector 11 | import androidx.core.app.ActivityCompat 12 | import androidx.core.content.ContextCompat 13 | import com.ocp.facesdk.FaceDetectionParam 14 | import com.ocp.facesdk.FaceSDK 15 | import io.fotoapparat.Fotoapparat 16 | import io.fotoapparat.parameter.Resolution 17 | import io.fotoapparat.preview.Frame 18 | import io.fotoapparat.preview.FrameProcessor 19 | import io.fotoapparat.selector.front 20 | import io.fotoapparat.view.CameraView 21 | 22 | class CameraActivityKt : AppCompatActivity() { 23 | 24 | val TAG = CameraActivityKt::class.java.simpleName 25 | val PREVIEW_WIDTH = 720 26 | val PREVIEW_HEIGHT = 1280 27 | 28 | private lateinit var cameraView: CameraView 29 | private lateinit var faceView: FaceView 30 | private lateinit var fotoapparat: Fotoapparat 31 | private lateinit var context: Context 32 | 33 | private var recognized = false 34 | 35 | override fun onCreate(savedInstanceState: Bundle?) { 36 | super.onCreate(savedInstanceState) 37 | setContentView(R.layout.activity_camera_kt) 38 | 39 | context = this 40 | cameraView = findViewById(R.id.preview) 41 | faceView = findViewById(R.id.faceView) 42 | 43 | fotoapparat = Fotoapparat.with(this) 44 | .into(cameraView) 45 | .lensPosition(front()) 46 | .frameProcessor(FaceFrameProcessor()) 47 | .previewResolution { Resolution(PREVIEW_HEIGHT,PREVIEW_WIDTH) } 48 | .build() 49 | 50 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 51 | == PackageManager.PERMISSION_DENIED 52 | ) { 53 | ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1) 54 | } else { 55 | fotoapparat.start() 56 | } 57 | } 58 | 59 | override fun onResume() { 60 | super.onResume() 61 | recognized = false 62 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 63 | == PackageManager.PERMISSION_GRANTED 64 | ) { 65 | fotoapparat.start() 66 | } 67 | } 68 | 69 | override fun onPause() { 70 | super.onPause() 71 | fotoapparat.stop() 72 | faceView.setFaceBoxes(null) 73 | } 74 | 75 | override fun onRequestPermissionsResult( 76 | requestCode: Int, 77 | permissions: Array, 78 | grantResults: IntArray 79 | ) { 80 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 81 | if (requestCode == 1) { 82 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 83 | == PackageManager.PERMISSION_GRANTED 84 | ) { 85 | fotoapparat.start() 86 | } 87 | } 88 | } 89 | 90 | inner class FaceFrameProcessor : FrameProcessor { 91 | 92 | override fun process(frame: Frame) { 93 | 94 | if(recognized == true) { 95 | return 96 | } 97 | 98 | var cameraMode = 7 99 | if (SettingsActivity.getCameraLens(context) == CameraSelector.LENS_FACING_BACK) { 100 | cameraMode = 6 101 | } 102 | 103 | val bitmap = FaceSDK.yuv2Bitmap(frame.image, frame.size.width, frame.size.height, cameraMode) 104 | 105 | val faceDetectionParam = FaceDetectionParam() 106 | faceDetectionParam.check_liveness = true 107 | faceDetectionParam.check_liveness_level = SettingsActivity.getLivenessLevel(context) 108 | val faceBoxes = FaceSDK.faceDetection(bitmap, faceDetectionParam) 109 | 110 | runOnUiThread { 111 | faceView.setFrameSize(Size(bitmap.width, bitmap.height)) 112 | faceView.setFaceBoxes(faceBoxes) 113 | } 114 | 115 | if(faceBoxes.size > 0) { 116 | val faceBox = faceBoxes[0] 117 | if (faceBox.liveness > SettingsActivity.getLivenessThreshold(context)) { 118 | val templates = FaceSDK.templateExtraction(bitmap, faceBox) 119 | 120 | var maxSimiarlity = 0f 121 | var maximiarlityPerson: Person? = null 122 | for (person in DBManager.personList) { 123 | val similarity = FaceSDK.similarityCalculation(templates, person.templates) 124 | if (similarity > maxSimiarlity) { 125 | maxSimiarlity = similarity 126 | maximiarlityPerson = person 127 | } 128 | } 129 | if (maxSimiarlity > SettingsActivity.getIdentifyThreshold(context)) { 130 | recognized = true 131 | val identifiedPerson = maximiarlityPerson 132 | val identifiedSimilarity = maxSimiarlity 133 | 134 | runOnUiThread { 135 | val faceImage = Utils.cropFace(bitmap, faceBox) 136 | val intent = Intent(context, ResultActivity::class.java) 137 | intent.putExtra("identified_face", faceImage) 138 | intent.putExtra("enrolled_face", identifiedPerson!!.face) 139 | intent.putExtra("identified_name", identifiedPerson!!.name) 140 | intent.putExtra("similarity", identifiedSimilarity) 141 | intent.putExtra("liveness", faceBox.liveness) 142 | intent.putExtra("yaw", faceBox.yaw) 143 | intent.putExtra("roll", faceBox.roll) 144 | intent.putExtra("pitch", faceBox.pitch) 145 | intent.putExtra("face_quality", faceBox.face_quality) 146 | intent.putExtra("face_luminance", faceBox.face_luminance) 147 | startActivity(intent) 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/CaptureActivity.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | 4 | import static androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST; 5 | 6 | import android.Manifest; 7 | import android.annotation.SuppressLint; 8 | import android.content.Context; 9 | import android.content.pm.PackageManager; 10 | import android.graphics.Bitmap; 11 | import android.graphics.RectF; 12 | import android.media.Image; 13 | import android.os.Bundle; 14 | import android.util.Size; 15 | import android.view.View; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import androidx.annotation.NonNull; 20 | import androidx.appcompat.app.AppCompatActivity; 21 | import androidx.camera.core.Camera; 22 | import androidx.camera.core.CameraSelector; 23 | import androidx.camera.core.ImageAnalysis; 24 | import androidx.camera.core.ImageProxy; 25 | import androidx.camera.core.Preview; 26 | import androidx.camera.lifecycle.ProcessCameraProvider; 27 | import androidx.camera.view.PreviewView; 28 | import androidx.constraintlayout.widget.ConstraintLayout; 29 | import androidx.core.app.ActivityCompat; 30 | import androidx.core.content.ContextCompat; 31 | 32 | import com.google.common.util.concurrent.ListenableFuture; 33 | import com.ocp.facesdk.FaceBox; 34 | import com.ocp.facesdk.FaceDetectionParam; 35 | import com.ocp.facesdk.FaceSDK; 36 | 37 | import java.nio.ByteBuffer; 38 | import java.util.List; 39 | import java.util.Random; 40 | import java.util.concurrent.ExecutionException; 41 | import java.util.concurrent.ExecutorService; 42 | import java.util.concurrent.Executors; 43 | 44 | 45 | 46 | public class CaptureActivity extends AppCompatActivity implements CaptureView.ViewModeChanged{ 47 | 48 | static String TAG = CaptureActivity.class.getSimpleName(); 49 | static int PREVIEW_WIDTH = 720; 50 | static int PREVIEW_HEIGHT = 1280; 51 | 52 | private ExecutorService cameraExecutorService; 53 | private PreviewView viewFinder; 54 | private Preview preview = null; 55 | private ImageAnalysis imageAnalyzer = null; 56 | private Camera camera = null; 57 | private CameraSelector cameraSelector = null; 58 | private ProcessCameraProvider cameraProvider = null; 59 | 60 | private CaptureView captureView; 61 | 62 | private TextView warningTxt; 63 | 64 | private TextView livenessTxt; 65 | 66 | private TextView qualityTxt; 67 | 68 | private TextView luminaceTxt; 69 | 70 | private ConstraintLayout lytCaptureResult; 71 | 72 | private Context context; 73 | 74 | private Bitmap capturedBitmap = null; 75 | 76 | private FaceBox capturedFace = null; 77 | 78 | @Override 79 | protected void onCreate(Bundle savedInstanceState) { 80 | super.onCreate(savedInstanceState); 81 | setContentView(R.layout.activity_capture); 82 | 83 | context = this; 84 | 85 | viewFinder = findViewById(R.id.preview); 86 | captureView = findViewById(R.id.captureView); 87 | warningTxt = findViewById(R.id.txtWarning); 88 | livenessTxt = findViewById(R.id.txtLiveness); 89 | qualityTxt = findViewById(R.id.txtQuality); 90 | luminaceTxt = findViewById(R.id.txtLuminance); 91 | lytCaptureResult = findViewById(R.id.lytCaptureResult); 92 | cameraExecutorService = Executors.newFixedThreadPool(1); 93 | 94 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 95 | == PackageManager.PERMISSION_DENIED) { 96 | 97 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1); 98 | } else { 99 | viewFinder.post(() -> 100 | { 101 | setUpCamera(); 102 | }); 103 | } 104 | 105 | captureView.setViewModeInterface(this); 106 | captureView.setViewMode(CaptureView.VIEW_MODE.NO_FACE_PREPARE); 107 | 108 | findViewById(R.id.buttonEnroll).setOnClickListener(new View.OnClickListener() { 109 | @Override 110 | public void onClick(View view) { 111 | Bitmap faceImage = Utils.cropFace(capturedBitmap, capturedFace); 112 | byte[] templates = FaceSDK.templateExtraction(capturedBitmap, capturedFace); 113 | 114 | DBManager dbManager = new DBManager(context); 115 | final int min = 10000; 116 | final int max = 20000; 117 | final int random = new Random().nextInt((max - min) + 1) + min; 118 | 119 | dbManager.insertPerson("Person" + random, faceImage, templates); 120 | Toast.makeText(context, getString(R.string.person_enrolled), Toast.LENGTH_SHORT).show(); 121 | finish(); 122 | } 123 | }); 124 | } 125 | 126 | @Override 127 | public void onResume() { 128 | super.onResume(); 129 | } 130 | 131 | @Override 132 | public void onPause() { 133 | super.onPause(); 134 | 135 | captureView.setFaceBoxes(null); 136 | } 137 | 138 | @Override 139 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 140 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 141 | 142 | if(requestCode == 1) { 143 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 144 | == PackageManager.PERMISSION_GRANTED) { 145 | 146 | viewFinder.post(() -> 147 | { 148 | setUpCamera(); 149 | }); 150 | } 151 | } 152 | } 153 | 154 | private void setUpCamera() 155 | { 156 | ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(CaptureActivity.this); 157 | cameraProviderFuture.addListener(() -> { 158 | 159 | // CameraProvider 160 | try { 161 | cameraProvider = cameraProviderFuture.get(); 162 | } catch (ExecutionException e) { 163 | } catch (InterruptedException e) { 164 | } 165 | 166 | // Build and bind the camera use cases 167 | bindCameraUseCases(); 168 | 169 | }, ContextCompat.getMainExecutor(CaptureActivity.this)); 170 | } 171 | 172 | @SuppressLint({"RestrictedApi", "UnsafeExperimentalUsageError"}) 173 | private void bindCameraUseCases() 174 | { 175 | int rotation = viewFinder.getDisplay().getRotation(); 176 | 177 | cameraSelector = new CameraSelector.Builder().requireLensFacing(SettingsActivity.getCameraLens(this)).build(); 178 | 179 | preview = new Preview.Builder() 180 | .setTargetResolution(new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)) 181 | .setTargetRotation(rotation) 182 | .build(); 183 | 184 | imageAnalyzer = new ImageAnalysis.Builder() 185 | .setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST) 186 | .setTargetResolution(new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)) 187 | // Set initial target rotation, we will have to call this again if rotation changes 188 | // during the lifecycle of this use case 189 | .setTargetRotation(rotation) 190 | .build(); 191 | 192 | imageAnalyzer.setAnalyzer(cameraExecutorService, new FaceAnalyzer()); 193 | 194 | cameraProvider.unbindAll(); 195 | 196 | try { 197 | camera = cameraProvider.bindToLifecycle( 198 | this, cameraSelector, preview, imageAnalyzer); 199 | 200 | preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); 201 | } catch (Exception exc) { 202 | } 203 | } 204 | 205 | @Override 206 | public void view5_finished() { 207 | 208 | FaceDetectionParam param = new FaceDetectionParam(); 209 | param.check_liveness = true; 210 | param.check_liveness_level = SettingsActivity.getLivenessLevel(this); 211 | 212 | List faceBoxes = FaceSDK.faceDetection(capturedBitmap, param); 213 | if(faceBoxes != null && faceBoxes.size() > 0) { 214 | if(faceBoxes.get(0).liveness > SettingsActivity.getLivenessThreshold(context)) { 215 | String msg = String.format("Liveness: Real, score = %.03f", faceBoxes.get(0).liveness); 216 | livenessTxt.setText(msg); 217 | } 218 | else { 219 | String msg = String.format("Liveness: Spoof, score = %.03f", faceBoxes.get(0).liveness); 220 | livenessTxt.setText(msg); 221 | } 222 | } 223 | 224 | if(capturedFace.face_quality < 0.5f) { 225 | String msg = String.format("Quality: Low, score = %.03f", capturedFace.face_quality); 226 | qualityTxt.setText(msg); 227 | } else if(capturedFace.face_quality < 0.75f) { 228 | String msg = String.format("Quality: Medium, score = %.03f", capturedFace.face_quality); 229 | qualityTxt.setText(msg); 230 | } else { 231 | String msg = String.format("Quality: High, score = %.03f", capturedFace.face_quality); 232 | qualityTxt.setText(msg); 233 | } 234 | 235 | String msg = String.format("Luminance: %.03f", capturedFace.face_luminance); 236 | luminaceTxt.setText(msg); 237 | 238 | lytCaptureResult.setVisibility(View.VISIBLE); 239 | } 240 | 241 | class FaceAnalyzer implements ImageAnalysis.Analyzer 242 | { 243 | @SuppressLint("UnsafeExperimentalUsageError") 244 | @Override 245 | public void analyze(@NonNull ImageProxy imageProxy) 246 | { 247 | analyzeImage(imageProxy); 248 | } 249 | } 250 | 251 | @SuppressLint("UnsafeExperimentalUsageError") 252 | private void analyzeImage(ImageProxy imageProxy) 253 | { 254 | if(captureView.viewMode == CaptureView.VIEW_MODE.NO_FACE_PREPARE) { 255 | imageProxy.close(); 256 | return; 257 | } 258 | 259 | try 260 | { 261 | Image image = imageProxy.getImage(); 262 | 263 | Image.Plane[] planes = image.getPlanes(); 264 | ByteBuffer yBuffer = planes[0].getBuffer(); 265 | ByteBuffer uBuffer = planes[1].getBuffer(); 266 | ByteBuffer vBuffer = planes[2].getBuffer(); 267 | 268 | int ySize = yBuffer.remaining(); 269 | int uSize = uBuffer.remaining(); 270 | int vSize = vBuffer.remaining(); 271 | 272 | byte[] nv21 = new byte[ySize + uSize + vSize]; 273 | yBuffer.get(nv21, 0, ySize); 274 | vBuffer.get(nv21, ySize, vSize); 275 | uBuffer.get(nv21, ySize + vSize, uSize); 276 | 277 | Bitmap bitmap = FaceSDK.yuv2Bitmap(nv21, image.getWidth(), image.getHeight(), 7); 278 | 279 | FaceDetectionParam param = new FaceDetectionParam(); 280 | param.check_face_occlusion = true; 281 | param.check_eye_closeness = true; 282 | param.check_mouth_opened = true; 283 | 284 | List faceBoxes = FaceSDK.faceDetection(bitmap, param); 285 | FACE_CAPTURE_STATE faceCaptureState = checkFace(faceBoxes, this); 286 | 287 | if(captureView.viewMode == CaptureView.VIEW_MODE.REPEAT_NO_FACE_PREPARE) { 288 | if(faceCaptureState.compareTo(FACE_CAPTURE_STATE.NO_FACE) > 0) { 289 | runOnUiThread(new Runnable() { 290 | @Override 291 | public void run() { 292 | captureView.setViewMode(CaptureView.VIEW_MODE.TO_FACE_CIRCLE); 293 | } 294 | }); 295 | } 296 | } else if(captureView.viewMode == CaptureView.VIEW_MODE.FACE_CIRCLE) { 297 | runOnUiThread(new Runnable() { 298 | @Override 299 | public void run() { 300 | captureView.setFrameSize(new Size(bitmap.getWidth(), bitmap.getHeight())); 301 | captureView.setFaceBoxes(faceBoxes); 302 | 303 | if(faceCaptureState == FACE_CAPTURE_STATE.NO_FACE) { 304 | warningTxt.setText(""); 305 | 306 | captureView.setViewMode(CaptureView.VIEW_MODE.FACE_CIRCLE_TO_NO_FACE); 307 | } 308 | else if(faceCaptureState == FACE_CAPTURE_STATE.MULTIPLE_FACES) 309 | warningTxt.setText("Multiple face detected!"); 310 | else if(faceCaptureState == FACE_CAPTURE_STATE.FIT_IN_CIRCLE) 311 | warningTxt.setText("Fit in circle!"); 312 | else if(faceCaptureState == FACE_CAPTURE_STATE.MOVE_CLOSER) 313 | warningTxt.setText("Move closer!"); 314 | else if(faceCaptureState == FACE_CAPTURE_STATE.NO_FRONT) 315 | warningTxt.setText("Not fronted face!"); 316 | else if(faceCaptureState == FACE_CAPTURE_STATE.FACE_OCCLUDED) 317 | warningTxt.setText("Face occluded!"); 318 | else if(faceCaptureState == FACE_CAPTURE_STATE.EYE_CLOSED) 319 | warningTxt.setText("Eye closed!"); 320 | else if(faceCaptureState == FACE_CAPTURE_STATE.MOUTH_OPENED) 321 | warningTxt.setText("Mouth opened!"); 322 | else if(faceCaptureState == FACE_CAPTURE_STATE.SPOOFED_FACE) 323 | warningTxt.setText("Spoof face"); 324 | else { 325 | warningTxt.setText(""); 326 | captureView.setViewMode(CaptureView.VIEW_MODE.FACE_CAPTURE_PREPARE); 327 | 328 | capturedBitmap = bitmap; 329 | capturedFace = faceBoxes.get(0); 330 | captureView.setCapturedBitmap(capturedBitmap); 331 | } 332 | } 333 | }); 334 | } else if(captureView.viewMode == CaptureView.VIEW_MODE.FACE_CAPTURE_PREPARE) { 335 | if(faceCaptureState == FACE_CAPTURE_STATE.CAPTURE_OK) { 336 | if(faceBoxes.get(0).face_quality > capturedFace.face_quality) { 337 | capturedBitmap = bitmap; 338 | capturedFace = faceBoxes.get(0); 339 | captureView.setCapturedBitmap(capturedBitmap); 340 | } 341 | } 342 | } else if(captureView.viewMode == CaptureView.VIEW_MODE.FACE_CAPTURE_DONE) { 343 | runOnUiThread(new Runnable() { 344 | @Override 345 | public void run() { 346 | cameraProvider.unbindAll(); 347 | } 348 | }); 349 | } 350 | } 351 | catch (Exception e) 352 | { 353 | e.printStackTrace(); 354 | } 355 | finally 356 | { 357 | imageProxy.close(); 358 | } 359 | } 360 | 361 | public static FACE_CAPTURE_STATE checkFace(List faceBoxes, Context context) { 362 | if(faceBoxes == null || faceBoxes.size() == 0) 363 | return FACE_CAPTURE_STATE.NO_FACE; 364 | 365 | if(faceBoxes.size() > 1) { 366 | return FACE_CAPTURE_STATE.MULTIPLE_FACES; 367 | } 368 | 369 | FaceBox faceBox = faceBoxes.get(0); 370 | float faceLeft = Float.MAX_VALUE; 371 | float faceRight = 0f; 372 | float faceBottom = 0f; 373 | for(int i = 0; i < 68; i ++) { 374 | faceLeft = Math.min(faceLeft, faceBox.landmarks_68[i * 2]); 375 | faceRight = Math.max(faceRight, faceBox.landmarks_68[i * 2]); 376 | faceBottom = Math.max(faceBottom, faceBox.landmarks_68[i * 2 + 1]); 377 | } 378 | 379 | float sizeRate = 0.30f; 380 | float interRate = 0.03f; 381 | Size frameSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); 382 | RectF roiRect = CaptureView.getROIRect(frameSize); 383 | float centerY = (faceBox.y2 + faceBox.y1) / 2; 384 | float topY = centerY - (faceBox.y2 - faceBox.y1) * 2 / 3; 385 | float interX = Math.max(0f, roiRect.left - faceLeft) + Math.max(0f, faceRight - roiRect.right); 386 | float interY = Math.max(0f, roiRect.top - topY) + Math.max(0f, faceBottom - roiRect.bottom); 387 | if(interX / roiRect.width() > interRate || interY / roiRect.height() > interRate) { 388 | return FACE_CAPTURE_STATE.FIT_IN_CIRCLE; 389 | } 390 | 391 | if(interX / roiRect.width() > interRate || interY / roiRect.height() > interRate) { 392 | return FACE_CAPTURE_STATE.FIT_IN_CIRCLE; 393 | } 394 | 395 | if((faceBox.y2 - faceBox.y1) * (faceBox.x2 - faceBox.x1) < roiRect.width() * roiRect.height() * sizeRate) { 396 | return FACE_CAPTURE_STATE.MOVE_CLOSER; 397 | } 398 | 399 | if(Math.abs(faceBox.yaw) > SettingsActivity.getYawThreshold(context) || 400 | Math.abs(faceBox.roll) > SettingsActivity.getRollThreshold(context) || 401 | Math.abs(faceBox.pitch) > SettingsActivity.getPitchThreshold(context)) { 402 | return FACE_CAPTURE_STATE.NO_FRONT; 403 | } 404 | 405 | if(faceBox.face_occlusion > SettingsActivity.getOcclusionThreshold(context)) { 406 | return FACE_CAPTURE_STATE.FACE_OCCLUDED; 407 | } 408 | 409 | if(faceBox.left_eye_closed > SettingsActivity.getEyecloseThreshold(context) || 410 | faceBox.right_eye_closed > SettingsActivity.getEyecloseThreshold(context)) { 411 | return FACE_CAPTURE_STATE.EYE_CLOSED; 412 | } 413 | 414 | if(faceBox.mouth_opened > SettingsActivity.getMouthopenThreshold(context)) { 415 | return FACE_CAPTURE_STATE.MOUTH_OPENED; 416 | } 417 | 418 | return FACE_CAPTURE_STATE.CAPTURE_OK; 419 | } 420 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/ContactUsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Intent 4 | import android.content.pm.ResolveInfo 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.TextView 11 | import androidx.appcompat.app.AppCompatActivity 12 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 13 | 14 | 15 | class ContactUsActivity : BottomSheetDialogFragment() { 16 | 17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 18 | val view = inflater.inflate(R.layout.activity_contacus, container, false) 19 | 20 | val mailTextView = view.findViewById(R.id.txtMail) 21 | 22 | mailTextView.setOnClickListener { 23 | val intent = Intent(Intent.ACTION_SEND) 24 | intent.type = "plain/text" 25 | intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("info@faceplugin.com")) 26 | intent.putExtra(Intent.EXTRA_SUBJECT, "License Request") 27 | intent.putExtra(Intent.EXTRA_TEXT, "") 28 | startActivity(Intent.createChooser(intent, "")) 29 | //dismiss() 30 | } 31 | 32 | return view 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/DBManager.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | import java.util.ArrayList; 13 | 14 | public class DBManager extends SQLiteOpenHelper { 15 | 16 | public static ArrayList personList = new ArrayList(); 17 | 18 | public DBManager(Context context) { 19 | super(context, "mydb" , null, 1); 20 | } 21 | 22 | @Override 23 | public void onCreate(SQLiteDatabase db) { 24 | // TODO Auto-generated method stub 25 | db.execSQL( 26 | "create table person " + 27 | "(name text, face blob, templates blob)" 28 | ); 29 | } 30 | 31 | @Override 32 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 33 | // TODO Auto-generated method stub 34 | db.execSQL("DROP TABLE IF EXISTS person"); 35 | onCreate(db); 36 | } 37 | 38 | public void insertPerson (String name, Bitmap face, byte[] templates) { 39 | 40 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 41 | face.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); 42 | byte[] faceJpg = byteArrayOutputStream.toByteArray(); 43 | 44 | SQLiteDatabase db = this.getWritableDatabase(); 45 | ContentValues contentValues = new ContentValues(); 46 | contentValues.put("name", name); 47 | contentValues.put("face", faceJpg); 48 | contentValues.put("templates", templates); 49 | db.insert("person", null, contentValues); 50 | 51 | personList.add(new Person(name, face, templates)); 52 | } 53 | 54 | public Integer deletePerson (String name) { 55 | for(int i = 0; i < personList.size(); i ++) { 56 | if(personList.get(i).name == name) { 57 | personList.remove(i); 58 | i --; 59 | } 60 | } 61 | 62 | SQLiteDatabase db = this.getWritableDatabase(); 63 | return db.delete("person", 64 | "name = ? ", 65 | new String[] { name }); 66 | } 67 | 68 | public Integer clearDB () { 69 | personList.clear(); 70 | 71 | SQLiteDatabase db = this.getWritableDatabase(); 72 | db.execSQL("delete from person"); 73 | return 0; 74 | } 75 | 76 | public void loadPerson() { 77 | personList.clear(); 78 | 79 | SQLiteDatabase db = this.getReadableDatabase(); 80 | Cursor res = db.rawQuery( "select * from person", null ); 81 | res.moveToFirst(); 82 | 83 | while(res.isAfterLast() == false){ 84 | String name = res.getString(res.getColumnIndex("name")); 85 | byte[] faceJpg = res.getBlob(res.getColumnIndex("face")); 86 | byte[] templates = res.getBlob(res.getColumnIndex("templates")); 87 | Bitmap face = BitmapFactory.decodeByteArray(faceJpg, 0, faceJpg.length); 88 | 89 | Person person = new Person(name, face, templates); 90 | personList.add(person); 91 | 92 | res.moveToNext(); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/FACE_CAPTURE_STATE.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | public enum FACE_CAPTURE_STATE { 4 | NO_FACE, MULTIPLE_FACES, FIT_IN_CIRCLE, MOVE_CLOSER, NO_FRONT, FACE_OCCLUDED, EYE_CLOSED, MOUTH_OPENED, SPOOFED_FACE, CAPTURE_OK 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/FaceView.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.util.AttributeSet; 9 | import android.util.Size; 10 | import android.view.View; 11 | 12 | import androidx.annotation.Nullable; 13 | 14 | import com.ocp.facesdk.FaceBox; 15 | 16 | import java.util.List; 17 | 18 | public class FaceView extends View { 19 | 20 | private Context context; 21 | private Paint realPaint; 22 | private Paint spoofPaint; 23 | 24 | private Size frameSize; 25 | 26 | private List faceBoxes; 27 | 28 | public FaceView(Context context) { 29 | this(context, null); 30 | 31 | this.context = context; 32 | init(); 33 | } 34 | 35 | public FaceView(Context context, @Nullable AttributeSet attrs) { 36 | super(context, attrs); 37 | this.context = context; 38 | 39 | init(); 40 | } 41 | 42 | public void init() { 43 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 44 | 45 | realPaint = new Paint(); 46 | realPaint.setStyle(Paint.Style.STROKE); 47 | realPaint.setStrokeWidth(3); 48 | realPaint.setColor(Color.GREEN); 49 | realPaint.setAntiAlias(true); 50 | realPaint.setTextSize(50); 51 | 52 | spoofPaint = new Paint(); 53 | spoofPaint.setStyle(Paint.Style.STROKE); 54 | spoofPaint.setStrokeWidth(3); 55 | spoofPaint.setColor(Color.RED); 56 | spoofPaint.setAntiAlias(true); 57 | spoofPaint.setTextSize(50); 58 | } 59 | 60 | public void setFrameSize(Size frameSize) 61 | { 62 | this.frameSize = frameSize; 63 | } 64 | 65 | public void setFaceBoxes(List faceBoxes) 66 | { 67 | this.faceBoxes = faceBoxes; 68 | invalidate(); 69 | } 70 | 71 | @Override 72 | protected void onDraw(Canvas canvas) { 73 | super.onDraw(canvas); 74 | 75 | if (frameSize != null && faceBoxes != null) { 76 | float x_scale = this.frameSize.getWidth() / (float)canvas.getWidth(); 77 | float y_scale = this.frameSize.getHeight() / (float)canvas.getHeight(); 78 | 79 | for (int i = 0; i < faceBoxes.size(); i++) { 80 | FaceBox faceBox = faceBoxes.get(i); 81 | 82 | if (faceBox.liveness < SettingsActivity.getLivenessThreshold(context)) 83 | { 84 | spoofPaint.setStrokeWidth(3); 85 | spoofPaint.setStyle(Paint.Style.FILL_AND_STROKE); 86 | canvas.drawText("SPOOF " + faceBox.liveness, (faceBox.x1 / x_scale) + 10, (faceBox.y1 / y_scale) - 30, spoofPaint); 87 | 88 | spoofPaint.setStrokeWidth(5); 89 | spoofPaint.setStyle(Paint.Style.STROKE); 90 | canvas.drawRect(new Rect((int)(faceBox.x1 / x_scale), (int)(faceBox.y1 / y_scale), 91 | (int)(faceBox.x2 / x_scale), (int)(faceBox.y2 / y_scale)), spoofPaint); 92 | } 93 | else 94 | { 95 | realPaint.setStrokeWidth(3); 96 | realPaint.setStyle(Paint.Style.FILL_AND_STROKE); 97 | canvas.drawText("REAL " + faceBox.liveness, (faceBox.x1 / x_scale) + 10, (faceBox.y1 / y_scale) - 30, realPaint); 98 | 99 | realPaint.setStyle(Paint.Style.STROKE); 100 | realPaint.setStrokeWidth(5); 101 | canvas.drawRect(new Rect((int)(faceBox.x1 / x_scale), (int)(faceBox.y1 / y_scale), 102 | (int)(faceBox.x2 / x_scale), (int)(faceBox.y2 / y_scale)), realPaint); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/IdentifyDetailsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Intent 4 | import android.content.pm.ResolveInfo 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.TextView 11 | import androidx.appcompat.app.AppCompatActivity 12 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 13 | 14 | 15 | class IdentifyDetailsActivity : BottomSheetDialogFragment() { 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, 19 | container: ViewGroup?, 20 | savedInstanceState: Bundle? 21 | ): View? { 22 | val view = inflater.inflate(R.layout.activity_identify_details, container, false) 23 | 24 | val identifiedName = arguments?.getString("identified_name") 25 | val similarity = arguments?.getFloat("similarity", 0f) 26 | val livenessScore = arguments?.getFloat("liveness", 0f) 27 | val yaw = arguments?.getFloat("yaw", 0f) 28 | val roll = arguments?.getFloat("roll", 0f) 29 | val pitch = arguments?.getFloat("pitch", 0f) 30 | val face_quality = arguments?.getFloat("face_quality", 0f) 31 | val face_luminance = arguments?.getFloat("face_luminance", 0f) 32 | 33 | view.findViewById(R.id.textPerson).text = "Identified: " + identifiedName 34 | view.findViewById(R.id.textSimilarity).text = "Similarity: " + similarity 35 | view.findViewById(R.id.textLiveness).text = "Liveness score: " + livenessScore 36 | view.findViewById(R.id.textYaw).text = "Yaw: " + yaw 37 | view.findViewById(R.id.textRoll).text = "Roll: " + roll 38 | view.findViewById(R.id.textPitch).text = "Pitch: " + pitch 39 | 40 | if (face_quality != null) { 41 | if (face_quality < 0.5f) { 42 | val msg = String.format("Quality: Low, score = %.03f", face_quality) 43 | view.findViewById(R.id.txtQuality).text = msg 44 | } else if (face_quality < 0.75f) { 45 | val msg = String.format("Quality: Medium, score = %.03f", face_quality) 46 | view.findViewById(R.id.txtQuality).text = msg 47 | } else { 48 | val msg = String.format("Quality: High, score = %.03f", face_quality) 49 | view.findViewById(R.id.txtQuality).text = msg 50 | } 51 | } 52 | 53 | var msg = String.format("Luminance: %.03f", face_luminance) 54 | view.findViewById(R.id.txtLuminance).text = msg 55 | 56 | return view 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Intent 4 | import android.graphics.Bitmap 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import android.view.View 8 | import android.widget.* 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.ocp.facesdk.FaceBox 11 | import com.ocp.facesdk.FaceDetectionParam 12 | import com.ocp.facesdk.FaceSDK 13 | import kotlin.random.Random 14 | 15 | class MainActivity : AppCompatActivity() { 16 | 17 | companion object { 18 | private val SELECT_PHOTO_REQUEST_CODE = 1 19 | private val SELECT_ATTRIBUTE_REQUEST_CODE = 2 20 | } 21 | 22 | private lateinit var dbManager: DBManager 23 | private lateinit var textWarning: TextView 24 | private lateinit var textEnrolledFace: TextView 25 | private lateinit var personAdapter: PersonAdapter 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_main) 30 | 31 | textWarning = findViewById(R.id.textWarning) 32 | textEnrolledFace = findViewById(R.id.tv_enrolledface) 33 | textEnrolledFace.setVisibility(View.INVISIBLE) 34 | 35 | 36 | 37 | var ret = FaceSDK.setActivation( 38 | "Fqk7LKLbzfSCBor1Oidf0+aPu7OsAJgjxU5m6EQMP3WQ4JZ0Rt44C8T7auT27jjx9iwYmG/8l3TB\n" + 39 | "9MBZuaQKCKMiBvwu+JGfbyrQPrs0vyunAZplg0qUm3MUjz/ko1oJNDzh90jOvsdy8C+SGFWgLULQ\n" + 40 | "rA6K0dipo5B0v8uPXHkGliNVRuxdKg86iaGHpVzE9V+oqecdXqiuJyRloIqC+vWEYObQkJAocnwR\n" + 41 | "M51gg1HHqFYZ0RS9PI5DVzRNHHT4X/ws7e1tc2R0LgU22gd/4SHDYfoV8gHtyi/QdMthKgyzcJrN\n" + 42 | "p0lS+CrpoQuOzWl1toECPoSfcrbmmNP6v67ISA==" 43 | ) 44 | 45 | if (ret == FaceSDK.SDK_SUCCESS) { 46 | ret = FaceSDK.init(assets) 47 | } 48 | 49 | if (ret != FaceSDK.SDK_SUCCESS) { 50 | textWarning.setVisibility(View.VISIBLE) 51 | if (ret == FaceSDK.SDK_LICENSE_KEY_ERROR) { 52 | textWarning.setText("Invalid license!") 53 | } else if (ret == FaceSDK.SDK_LICENSE_APPID_ERROR) { 54 | textWarning.setText("Invalid error!") 55 | } else if (ret == FaceSDK.SDK_LICENSE_EXPIRED) { 56 | textWarning.setText("License expired!") 57 | } else if (ret == FaceSDK.SDK_NO_ACTIVATED) { 58 | textWarning.setText("No activated!") 59 | } else if (ret == FaceSDK.SDK_INIT_ERROR) { 60 | textWarning.setText("Init error!") 61 | } 62 | } 63 | 64 | dbManager = DBManager(this) 65 | dbManager.loadPerson() 66 | 67 | personAdapter = PersonAdapter(this, DBManager.personList, this.textEnrolledFace) 68 | val listView: ListView = findViewById(R.id.listPerson) as ListView 69 | listView.setAdapter(personAdapter) 70 | 71 | findViewById(R.id.ll_enroll).setOnClickListener { 72 | val intent = Intent() 73 | intent.setType("image/*") 74 | intent.setAction(Intent.ACTION_PICK) 75 | startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), SELECT_PHOTO_REQUEST_CODE) 76 | } 77 | 78 | findViewById(R.id.ll_identify).setOnClickListener { 79 | startActivity(Intent(this, CameraActivity::class.java)) 80 | } 81 | 82 | findViewById(R.id.ll_capture).setOnClickListener { 83 | startActivity(Intent(this, CaptureActivity::class.java)) 84 | } 85 | 86 | findViewById(R.id.ll_attribute).setOnClickListener { 87 | val intent = Intent() 88 | intent.setType("image/*") 89 | intent.setAction(Intent.ACTION_PICK) 90 | startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), SELECT_ATTRIBUTE_REQUEST_CODE) 91 | } 92 | 93 | findViewById(R.id.ll_settings).setOnClickListener { 94 | startActivity(Intent(this, SettingsActivity::class.java)) 95 | } 96 | findViewById(R.id.ll_about).setOnClickListener { 97 | startActivity(Intent(this, AboutActivity::class.java)) 98 | } 99 | 100 | findViewById(R.id.lytBrand).setOnClickListener { 101 | val browse = Intent(Intent.ACTION_VIEW, Uri.parse("https://faceplugin.com")) 102 | startActivity(browse) 103 | } 104 | } 105 | 106 | 107 | override fun onResume() { 108 | super.onResume() 109 | 110 | personAdapter.notifyDataSetChanged() 111 | if (personAdapter.count == 0){ 112 | textEnrolledFace.setVisibility(View.INVISIBLE) 113 | } else { 114 | textEnrolledFace.setVisibility(View.VISIBLE) 115 | } 116 | } 117 | 118 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 119 | if (requestCode == SELECT_PHOTO_REQUEST_CODE && resultCode == RESULT_OK) { 120 | try { 121 | var bitmap: Bitmap = Utils.getCorrectlyOrientedImage(this, data?.data!!) 122 | var faceBoxes: List? = FaceSDK.faceDetection(bitmap, null) 123 | 124 | if(faceBoxes.isNullOrEmpty()) { 125 | Toast.makeText(this, getString(R.string.no_face_detected), Toast.LENGTH_SHORT).show() 126 | } else if (faceBoxes.size > 1) { 127 | Toast.makeText(this, getString(R.string.multiple_face_detected), Toast.LENGTH_SHORT).show() 128 | } else { 129 | val faceImage = Utils.cropFace(bitmap, faceBoxes[0]) 130 | val templates = FaceSDK.templateExtraction(bitmap, faceBoxes[0]) 131 | 132 | dbManager.insertPerson("Person" + Random.nextInt(10000, 20000), faceImage, templates) 133 | personAdapter.notifyDataSetChanged() 134 | if (personAdapter.count == 0){ 135 | textEnrolledFace.setVisibility(View.INVISIBLE) 136 | } else { 137 | textEnrolledFace.setVisibility(View.VISIBLE) 138 | } 139 | Toast.makeText(this, getString(R.string.person_enrolled), Toast.LENGTH_SHORT).show() 140 | } 141 | } catch (e: java.lang.Exception) { 142 | //handle exception 143 | e.printStackTrace() 144 | } 145 | } else if (requestCode == SELECT_ATTRIBUTE_REQUEST_CODE && resultCode == RESULT_OK) { 146 | try { 147 | var bitmap: Bitmap = Utils.getCorrectlyOrientedImage(this, data?.data!!) 148 | 149 | 150 | val param = FaceDetectionParam() 151 | param.check_liveness = true 152 | param.check_liveness_level = SettingsActivity.getLivenessLevel(this) 153 | param.check_eye_closeness = true 154 | param.check_face_occlusion = true 155 | param.check_mouth_opened = true 156 | param.estimate_age_gender = true 157 | var faceBoxes: List? = FaceSDK.faceDetection(bitmap, param) 158 | 159 | if(faceBoxes.isNullOrEmpty()) { 160 | Toast.makeText(this, getString(R.string.no_face_detected), Toast.LENGTH_SHORT).show() 161 | } else if (faceBoxes.size > 1) { 162 | Toast.makeText(this, getString(R.string.multiple_face_detected), Toast.LENGTH_SHORT).show() 163 | } else { 164 | val faceImage = Utils.cropFace(bitmap, faceBoxes[0]) 165 | 166 | val intent = Intent(this, AttributeActivity::class.java) 167 | intent.putExtra("face_image", faceImage) 168 | intent.putExtra("yaw", faceBoxes[0].yaw) 169 | intent.putExtra("roll", faceBoxes[0].roll) 170 | intent.putExtra("pitch", faceBoxes[0].pitch) 171 | intent.putExtra("face_quality", faceBoxes[0].face_quality) 172 | intent.putExtra("face_luminance", faceBoxes[0].face_luminance) 173 | intent.putExtra("liveness", faceBoxes[0].liveness) 174 | intent.putExtra("left_eye_closed", faceBoxes[0].left_eye_closed) 175 | intent.putExtra("right_eye_closed", faceBoxes[0].right_eye_closed) 176 | intent.putExtra("face_occlusion", faceBoxes[0].face_occlusion) 177 | intent.putExtra("mouth_opened", faceBoxes[0].mouth_opened) 178 | intent.putExtra("age", faceBoxes[0].age) 179 | intent.putExtra("gender", faceBoxes[0].gender) 180 | 181 | startActivity(intent) 182 | } 183 | } catch (e: java.lang.Exception) { 184 | //handle exception 185 | e.printStackTrace() 186 | } 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/MyGlobal.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.ImageFormat; 9 | import android.graphics.Matrix; 10 | import android.graphics.Paint; 11 | import android.graphics.Rect; 12 | import android.graphics.RectF; 13 | import android.graphics.YuvImage; 14 | import android.media.Image; 15 | import android.net.Uri; 16 | import android.os.ParcelFileDescriptor; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.FileDescriptor; 20 | import java.io.IOException; 21 | import java.nio.ByteBuffer; 22 | import java.nio.ReadOnlyBufferException; 23 | 24 | public class MyGlobal { 25 | 26 | 27 | public static String adminEmail="admin@recotech.com"; 28 | public static String adminPwd="12345678"; 29 | 30 | 31 | public static boolean lockStatus=false; 32 | public static boolean cameraStatus=false; 33 | public static int nLockedCount=-1; 34 | public static Image imgBefore=null; 35 | 36 | public static Bitmap bitmapBefore=null; 37 | 38 | 39 | public static long lock_delay_time=5000; 40 | public static long splash_delay_time=3000; 41 | public static int curStatus=-1; 42 | public static int UNKNOWN=-1; 43 | public static int ADD_NEW=0; 44 | public static int CLOCK_IN=1; 45 | public static int CLOCK_OUT=2; 46 | public static int BREAK_IN=3; 47 | public static int BREAK_OUT=4; 48 | 49 | 50 | //-------------- 51 | public static Context context=null; 52 | 53 | public static MainActivity mainActivity=null; 54 | 55 | public static int OUTPUT_SIZE=192; //Output size of model 56 | public static int inputSize=112; 57 | public static float IMAGE_MEAN = 128.0f; 58 | public static float IMAGE_STD = 128.0f; 59 | public static float[][] embedings; 60 | 61 | public static Bitmap getBitmapFromUri(Uri uri) throws IOException { 62 | ParcelFileDescriptor parcelFileDescriptor = 63 | context.getContentResolver().openFileDescriptor(uri, "r"); 64 | FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); 65 | Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); 66 | parcelFileDescriptor.close(); 67 | return image; 68 | } 69 | 70 | //IMPORTANT. If conversion not done ,the toBitmap conversion does not work on some devices. 71 | public static byte[] YUV_420_888toNV21(Image image) { 72 | 73 | int width = image.getWidth(); 74 | int height = image.getHeight(); 75 | int ySize = width*height; 76 | int uvSize = width*height/4; 77 | 78 | byte[] nv21 = new byte[ySize + uvSize*2]; 79 | 80 | ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y 81 | ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U 82 | ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V 83 | 84 | int rowStride = image.getPlanes()[0].getRowStride(); 85 | assert(image.getPlanes()[0].getPixelStride() == 1); 86 | 87 | int pos = 0; 88 | 89 | if (rowStride == width) { // likely 90 | yBuffer.get(nv21, 0, ySize); 91 | pos += ySize; 92 | } 93 | else { 94 | long yBufferPos = -rowStride; // not an actual position 95 | for (; pos { 14 | 15 | DBManager dbManager; 16 | TextView txtEnrolledFace; 17 | public PersonAdapter(Context context, ArrayList personList, TextView textEnrolledFace) { 18 | super(context, 0, personList); 19 | 20 | dbManager = new DBManager(context); 21 | txtEnrolledFace = textEnrolledFace; 22 | } 23 | 24 | @Override 25 | public View getView(int position, View convertView, ViewGroup parent) { 26 | 27 | Person person = getItem(position); 28 | if (convertView == null) { 29 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_person, parent, false); 30 | } 31 | 32 | TextView tvName = (TextView) convertView.findViewById(R.id.textName); 33 | ImageView faceView = (ImageView) convertView.findViewById(R.id.imageFace); 34 | convertView.findViewById(R.id.buttonDelete).setOnClickListener(new View.OnClickListener() { 35 | @Override 36 | public void onClick(View view) { 37 | dbManager.deletePerson(DBManager.personList.get(position).name); 38 | notifyDataSetChanged(); 39 | if (DBManager.personList.size() == 0){ 40 | txtEnrolledFace.setVisibility(View.INVISIBLE); 41 | } else { 42 | txtEnrolledFace.setVisibility(View.VISIBLE); 43 | } 44 | } 45 | }); 46 | 47 | tvName.setText(person.name); 48 | faceView.setImageBitmap(person.face); 49 | // Return the completed view to render on screen 50 | return convertView; 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/ResultActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Intent 4 | import android.graphics.Bitmap 5 | import android.os.Bundle 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import androidx.appcompat.app.AppCompatActivity 9 | 10 | class ResultActivity : AppCompatActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(R.layout.activity_result_new) 14 | 15 | val identifyedFace = intent.getParcelableExtra("identified_face") as? Bitmap 16 | val enrolledFace = intent.getParcelableExtra("enrolled_face") as? Bitmap 17 | val identifiedName = intent.getStringExtra("identified_name") 18 | 19 | 20 | findViewById(R.id.imageEnrolled).setImageBitmap(enrolledFace) 21 | findViewById(R.id.imageIdentified).setImageBitmap(identifyedFace) 22 | findViewById(R.id.textPerson).text = "ID: " + identifiedName 23 | /*findViewById(R.id.textSimilarity).text = "Similarity: " + similarity 24 | findViewById(R.id.textLiveness).text = "Liveness score: " + livenessScore 25 | findViewById(R.id.textYaw).text = "Yaw: " + yaw 26 | findViewById(R.id.textRoll).text = "Roll: " + roll 27 | findViewById(R.id.textPitch).text = "Pitch: " + pitch 28 | 29 | if (face_quality < 0.5f) { 30 | val msg = String.format("Quality: Low, score = %.03f", face_quality) 31 | findViewById(R.id.txtQuality).text = msg 32 | } else if(face_quality < 0.75f){ 33 | val msg = String.format("Quality: Medium, score = %.03f", face_quality) 34 | findViewById(R.id.txtQuality).text = msg 35 | } else { 36 | val msg = String.format("Quality: High, score = %.03f", face_quality) 37 | findViewById(R.id.txtQuality).text = msg 38 | } 39 | 40 | var msg = String.format("Luminance: %.03f", face_luminance) 41 | findViewById(R.id.txtLuminance).text = msg*/ 42 | 43 | 44 | findViewById(R.id.btn_details).setOnClickListener { 45 | val bottomSheet = IdentifyDetailsActivity() 46 | val bundle = Bundle() 47 | bundle.putString("identified_name", identifiedName) 48 | bundle.putFloat("similarity", intent.getFloatExtra("similarity", 0f)) 49 | bundle.putFloat("liveness", intent.getFloatExtra("liveness", 0f)) 50 | bundle.putFloat("yaw", intent.getFloatExtra("yaw", 0f)) 51 | bundle.putFloat("roll", intent.getFloatExtra("roll", 0f)) 52 | bundle.putFloat("pitch", intent.getFloatExtra("pitch", 0f)) 53 | bundle.putFloat("face_quality", intent.getFloatExtra("face_quality", 0f)) 54 | bundle.putFloat("face_luminance", intent.getFloatExtra("face_luminance", 0f)) 55 | bottomSheet.arguments = bundle 56 | 57 | //startActivity(intent) 58 | bottomSheet.show(supportFragmentManager, "IdentifyDetailsActivity") 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.widget.Toast 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.camera.core.CameraSelector 8 | import androidx.preference.* 9 | 10 | 11 | class SettingsActivity : AppCompatActivity() { 12 | 13 | companion object { 14 | const val DEFAULT_CAMERA_LENS = "front" 15 | const val DEFAULT_LIVENESS_THRESHOLD = "0.7" 16 | const val DEFAULT_IDENTIFY_THRESHOLD = "0.8" 17 | const val DEFAULT_LIVENESS_LEVEL = "0" 18 | const val DEFAULT_YAW_THRESHOLD = "10.0" 19 | const val DEFAULT_ROLL_THRESHOLD = "10.0" 20 | const val DEFAULT_PITCH_THRESHOLD = "10.0" 21 | const val DEFAULT_OCCLUSION_THRESHOLD = "0.5" 22 | const val DEFAULT_EYECLOSE_THRESHOLD = "0.5" 23 | const val DEFAULT_MOUTHOPEN_THRESHOLD = "0.5" 24 | 25 | @JvmStatic 26 | fun getLivenessThreshold(context: Context): Float { 27 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 28 | return sharedPreferences.getString("liveness_threshold", SettingsActivity.DEFAULT_LIVENESS_THRESHOLD)!!.toFloat() 29 | } 30 | 31 | @JvmStatic 32 | fun getIdentifyThreshold(context: Context): Float { 33 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 34 | return sharedPreferences.getString("identify_threshold", SettingsActivity.DEFAULT_IDENTIFY_THRESHOLD)!!.toFloat() 35 | } 36 | 37 | @JvmStatic 38 | fun getCameraLens(context: Context): Int { 39 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 40 | val cameraLens = sharedPreferences.getString("camera_lens", SettingsActivity.DEFAULT_CAMERA_LENS) 41 | if(cameraLens == "back") { 42 | return CameraSelector.LENS_FACING_BACK 43 | } else { 44 | return CameraSelector.LENS_FACING_FRONT 45 | } 46 | } 47 | 48 | @JvmStatic 49 | fun getLivenessLevel(context: Context): Int { 50 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 51 | val livenessLevel = sharedPreferences.getString("liveness_level", SettingsActivity.DEFAULT_LIVENESS_LEVEL) 52 | if(livenessLevel == "0") { 53 | return 0 54 | } else { 55 | return 1 56 | } 57 | } 58 | 59 | @JvmStatic 60 | fun getYawThreshold(context: Context): Float { 61 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 62 | return sharedPreferences.getString("yaw_threshold", SettingsActivity.DEFAULT_YAW_THRESHOLD)!!.toFloat() 63 | } 64 | 65 | @JvmStatic 66 | fun getRollThreshold(context: Context): Float { 67 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 68 | return sharedPreferences.getString("roll_threshold", SettingsActivity.DEFAULT_ROLL_THRESHOLD)!!.toFloat() 69 | } 70 | 71 | @JvmStatic 72 | fun getPitchThreshold(context: Context): Float { 73 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 74 | return sharedPreferences.getString("pitch_threshold", SettingsActivity.DEFAULT_PITCH_THRESHOLD)!!.toFloat() 75 | } 76 | 77 | @JvmStatic 78 | fun getOcclusionThreshold(context: Context): Float { 79 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 80 | return sharedPreferences.getString("occlusion_threshold", SettingsActivity.DEFAULT_OCCLUSION_THRESHOLD)!!.toFloat() 81 | } 82 | 83 | @JvmStatic 84 | fun getEyecloseThreshold(context: Context): Float { 85 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 86 | return sharedPreferences.getString("eyeclose_threshold", SettingsActivity.DEFAULT_EYECLOSE_THRESHOLD)!!.toFloat() 87 | } 88 | 89 | @JvmStatic 90 | fun getMouthopenThreshold(context: Context): Float { 91 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 92 | return sharedPreferences.getString("mouthopen_threshold", SettingsActivity.DEFAULT_MOUTHOPEN_THRESHOLD)!!.toFloat() 93 | } 94 | } 95 | 96 | lateinit var dbManager: DBManager 97 | 98 | override fun onCreate(savedInstanceState: Bundle?) { 99 | super.onCreate(savedInstanceState) 100 | setContentView(R.layout.activity_settings) 101 | if (savedInstanceState == null) { 102 | supportFragmentManager 103 | .beginTransaction() 104 | .replace(R.id.settings, SettingsFragment()) 105 | .commit() 106 | } 107 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 108 | 109 | dbManager = DBManager(this) 110 | } 111 | 112 | class SettingsFragment : PreferenceFragmentCompat() { 113 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 114 | setPreferencesFromResource(R.xml.root_preferences, rootKey) 115 | 116 | val cameraLensPref = findPreference("camera_lens") 117 | val livenessThresholdPref = findPreference("liveness_threshold") 118 | val livenessLevelPref = findPreference("liveness_level") 119 | val identifyThresholdPref = findPreference("identify_threshold") 120 | val yawThresholdPref = findPreference("yaw_threshold") 121 | val rollThresholdPref = findPreference("roll_threshold") 122 | val pitchThresholdPref = findPreference("pitch_threshold") 123 | val occlusionThresholdPref = findPreference("occlusion_threshold") 124 | val eyeCloseThresholdPref = findPreference("eyeclose_threshold") 125 | val mouthOpenThresholdPref = findPreference("mouthopen_threshold") 126 | val buttonRestorePref = findPreference("restore_default_settings") 127 | 128 | livenessThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 129 | val stringPref = newValue as String 130 | try { 131 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 1.0f) { 132 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 133 | false 134 | } else { 135 | true 136 | } 137 | } catch (e:Exception) { 138 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 139 | false 140 | } 141 | } 142 | 143 | identifyThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 144 | val stringPref = newValue as String 145 | try { 146 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 1.0f) { 147 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 148 | false 149 | } else { 150 | true 151 | } 152 | } catch (e:Exception) { 153 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 154 | false 155 | } 156 | } 157 | 158 | yawThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 159 | val stringPref = newValue as String 160 | try { 161 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 30.0f) { 162 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 163 | false 164 | } else { 165 | true 166 | } 167 | } catch (e:Exception) { 168 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 169 | false 170 | } 171 | } 172 | 173 | rollThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 174 | val stringPref = newValue as String 175 | try { 176 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 30.0f) { 177 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 178 | false 179 | } else { 180 | true 181 | } 182 | } catch (e:Exception) { 183 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 184 | false 185 | } 186 | } 187 | 188 | pitchThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 189 | val stringPref = newValue as String 190 | try { 191 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 30.0f) { 192 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 193 | false 194 | } else { 195 | true 196 | } 197 | } catch (e:Exception) { 198 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 199 | false 200 | } 201 | } 202 | 203 | occlusionThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 204 | val stringPref = newValue as String 205 | try { 206 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 1.0f) { 207 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 208 | false 209 | } else { 210 | true 211 | } 212 | } catch (e:Exception) { 213 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 214 | false 215 | } 216 | } 217 | 218 | eyeCloseThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 219 | val stringPref = newValue as String 220 | try { 221 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 1.0f) { 222 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 223 | false 224 | } else { 225 | true 226 | } 227 | } catch (e:Exception) { 228 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 229 | false 230 | } 231 | } 232 | 233 | mouthOpenThresholdPref?.setOnPreferenceChangeListener{ preference, newValue -> 234 | val stringPref = newValue as String 235 | try { 236 | if(stringPref.toFloat() < 0.0f || stringPref.toFloat() > 1.0f) { 237 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 238 | false 239 | } else { 240 | true 241 | } 242 | } catch (e:Exception) { 243 | Toast.makeText(context, getString(R.string.invalid_value), Toast.LENGTH_SHORT).show() 244 | false 245 | } 246 | } 247 | 248 | buttonRestorePref?.setOnPreferenceClickListener { 249 | 250 | cameraLensPref?.value = SettingsActivity.DEFAULT_CAMERA_LENS 251 | livenessLevelPref?.value = SettingsActivity.DEFAULT_LIVENESS_LEVEL 252 | livenessThresholdPref?.text = SettingsActivity.DEFAULT_LIVENESS_THRESHOLD 253 | identifyThresholdPref?.text = SettingsActivity.DEFAULT_IDENTIFY_THRESHOLD 254 | yawThresholdPref?.text = SettingsActivity.DEFAULT_YAW_THRESHOLD 255 | rollThresholdPref?.text = SettingsActivity.DEFAULT_ROLL_THRESHOLD 256 | pitchThresholdPref?.text = SettingsActivity.DEFAULT_PITCH_THRESHOLD 257 | occlusionThresholdPref?.text = SettingsActivity.DEFAULT_OCCLUSION_THRESHOLD 258 | eyeCloseThresholdPref?.text = SettingsActivity.DEFAULT_EYECLOSE_THRESHOLD 259 | mouthOpenThresholdPref?.text = SettingsActivity.DEFAULT_MOUTHOPEN_THRESHOLD 260 | 261 | 262 | Toast.makeText(activity, getString(R.string.restored_default_settings), Toast.LENGTH_LONG).show() 263 | true 264 | } 265 | 266 | val buttonClearPref = findPreference("clear_all_person") 267 | buttonClearPref?.setOnPreferenceClickListener { 268 | val settingsActivity = activity as SettingsActivity 269 | settingsActivity.dbManager.clearDB() 270 | 271 | Toast.makeText(activity, getString(R.string.cleared_all_person), Toast.LENGTH_LONG).show() 272 | true 273 | } 274 | } 275 | } 276 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | 9 | public class SplashActivity extends AppCompatActivity { 10 | 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_splash); 16 | 17 | new Handler().postDelayed(new Runnable() { 18 | @Override 19 | public void run() { 20 | Intent intent =new Intent(SplashActivity.this, MainActivity.class); 21 | startActivity(intent); 22 | finish(); 23 | } 24 | },MyGlobal.splash_delay_time); 25 | 26 | // Intent intent =new Intent(SplashActivity.this, ChooseActivity.class); 27 | // startActivity(intent); 28 | // finish(); 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/faceplugin/facerecognition/Utils.java: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.graphics.Matrix; 8 | import android.net.Uri; 9 | import android.provider.MediaStore; 10 | 11 | import com.ocp.facesdk.FaceBox; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | public class Utils { 17 | 18 | public static Bitmap cropFace(Bitmap src, FaceBox faceBox) { 19 | int centerX = (faceBox.x1 + faceBox.x2) / 2; 20 | int centerY = (faceBox.y1 + faceBox.y2) / 2; 21 | int cropWidth = (int)((faceBox.x2 - faceBox.x1) * 1.4f); 22 | 23 | int cropX1 = centerX - cropWidth / 2; 24 | int cropY1 = centerY - cropWidth / 2; 25 | int cropX2 = centerX + cropWidth / 2; 26 | int cropY2 = centerY + cropWidth / 2; 27 | if(cropX1 < 0) cropX1 = 0; 28 | if(cropX2 >= src.getWidth()) cropX2 = src.getWidth() - 1; 29 | if(cropY1 < 0) cropY1 = 0; 30 | if(cropY2 >= src.getHeight()) cropY2 = src.getHeight() - 1; 31 | 32 | 33 | int cropScaleWidth = 200; 34 | int cropScaleHeight = 200; 35 | float scaleWidth = ((float) cropScaleWidth) / (cropX2 - cropX1 + 1); 36 | float scaleHeight = ((float) cropScaleHeight) / (cropY2 - cropY1 + 1); 37 | 38 | final Matrix m = new Matrix(); 39 | 40 | m.setScale(1.0f, 1.0f); 41 | m.postScale(scaleWidth, scaleHeight); 42 | final Bitmap cropped = Bitmap.createBitmap(src, cropX1, cropY1, (cropX2 - cropX1 + 1), (cropY2 - cropY1 + 1), m, 43 | true /* filter */); 44 | return cropped; 45 | } 46 | 47 | public static int getOrientation(Context context, Uri photoUri) { 48 | Cursor cursor = context.getContentResolver().query(photoUri, 49 | new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null); 50 | 51 | if (cursor.getCount() != 1) { 52 | return -1; 53 | } 54 | 55 | cursor.moveToFirst(); 56 | return cursor.getInt(0); 57 | } 58 | 59 | public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException { 60 | InputStream is = context.getContentResolver().openInputStream(photoUri); 61 | BitmapFactory.Options dbo = new BitmapFactory.Options(); 62 | dbo.inJustDecodeBounds = true; 63 | BitmapFactory.decodeStream(is, null, dbo); 64 | is.close(); 65 | 66 | int orientation = getOrientation(context, photoUri); 67 | 68 | Bitmap srcBitmap; 69 | is = context.getContentResolver().openInputStream(photoUri); 70 | srcBitmap = BitmapFactory.decodeStream(is); 71 | is.close(); 72 | 73 | if (orientation > 0) { 74 | Matrix matrix = new Matrix(); 75 | matrix.postRotate(orientation); 76 | 77 | srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(), 78 | srcBitmap.getHeight(), matrix, true); 79 | } 80 | 81 | return srcBitmap; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/attributr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/attributr.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/capture.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/enroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/enroll.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/gradient_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_camera.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_email.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_faceplugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/ic_faceplugin.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_search.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_register.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_reset.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/ic_skype.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_telegram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/ic_telegram.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_threshold.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_tip.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_whatsapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/ic_whatsapp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/identify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/identify.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/information.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_edge.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 10 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/drawable/splash.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 45 | 46 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_attribute.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 39 | 43 | 44 | 48 | 49 | 58 | 59 | 60 | 69 | 70 | 71 | 80 | 81 | 82 | 91 | 92 | 93 | 102 | 103 | 104 | 113 | 114 | 115 | 124 | 125 | 126 | 135 | 136 | 137 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera_kt.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_capture.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 52 | 53 | 58 | 59 | 65 | 66 | 67 | 68 | 79 | 80 | 81 | 92 | 93 | 94 | 105 | 106 | 107 | 119 | 120 | 125 | 126 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_contacus.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 20 | 21 | 22 | 32 | 33 | 34 | 44 | 45 | 54 | 55 | 56 | 66 | 67 | 77 | 78 | 79 | 89 | 90 | 100 | 101 | 102 | 112 | 113 | 123 | 124 | 131 | 132 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_identify_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 20 | 21 | 32 | 33 | 34 | 45 | 46 | 47 | 58 | 59 | 60 | 71 | 72 | 73 | 84 | 85 | 86 | 97 | 98 | 99 | 109 | 110 | 117 | 118 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | 21 | 30 | 31 | 42 | 43 | 53 | 54 | 63 | 64 | 65 | 66 | 76 | 77 | 87 | 88 | 97 | 98 | 99 | 109 | 110 | 120 | 121 | 130 | 131 | 132 | 133 | 134 | 143 | 144 | 154 | 155 | 165 | 166 | 175 | 176 | 177 | 178 | 188 | 189 | 199 | 200 | 209 | 210 | 211 | 221 | 222 | 232 | 233 | 242 | 243 | 244 | 245 | 246 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 276 | 277 | 278 | 279 | 290 | 291 | 307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_result_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 21 | 31 | 32 | 38 | 39 | 45 | 46 | 47 | 48 | 58 | 59 | 65 | 66 | 72 | 73 | 81 | 82 | 83 | 84 | 95 | 96 | 101 | 102 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 32 | 37 | 38 | 41 | 49 | 50 | 51 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_person.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 21 | 22 | 34 | 35 | 41 | 42 | 43 | 52 | 53 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Front 5 | Back 6 | 7 | 8 | 9 | front 10 | back 11 | 12 | 13 | 14 | High Accuracy 15 | Light Weight 16 | 17 | 18 | 19 | 0 20 | 1 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6750A4 4 | #6750A4 5 | #FFFFFF 6 | #EADDFF 7 | #21005D 8 | #625B71 9 | #FFFFFF 10 | #E8DEF8 11 | #1D192B 12 | #7D5260 13 | #FFFFFF 14 | #FFD8E4 15 | #31111D 16 | #B3261E 17 | #FFFFFF 18 | #F9DEDC 19 | #410E0B 20 | #79747E 21 | #FFFBFE 22 | #1C1B1F 23 | #FFFBFE 24 | #1C1B1F 25 | #E7E0EC 26 | #49454F 27 | #313033 28 | #F4EFF4 29 | #D0BCFF 30 | #000000 31 | #6750A4 32 | #CAC4D0 33 | #000000 34 | #D0BCFF 35 | #381E72 36 | #4F378B 37 | #EADDFF 38 | #CCC2DC 39 | #332D41 40 | #4A4458 41 | #E8DEF8 42 | #EFB8C8 43 | #492532 44 | #633B48 45 | #FFD8E4 46 | #F2B8B5 47 | #601410 48 | #8C1D18 49 | #F9DEDC 50 | #938F99 51 | #1C1B1F 52 | #E6E1E5 53 | #1C1B1F 54 | #E6E1E5 55 | #49454F 56 | #CAC4D0 57 | #E6E1E5 58 | #313033 59 | #6750A4 60 | #000000 61 | #D0BCFF 62 | #49454F 63 | #000000 64 | 65 | #252525 66 | #63317B 67 | #BF41E3 68 | #303033 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Face Recognition 3 | SettingsActivity 4 | Settings 5 | 6 | 7 | Camera 8 | Thresholds 9 | Reset 10 | 11 | 12 | Camera lens 13 | Liveness 14 | Liveness Level 15 | Identify 16 | Yaw 17 | Roll 18 | Pitch 19 | Face occlusion 20 | Eye closed 21 | Mouth opened 22 | Restore default settings 23 | Clear all person 24 | 25 | Select Picture 26 | 27 | Multiple face detected! 28 | No face detected! 29 | Person enrolled! 30 | Liveness check failed! 31 | Restored default settings 32 | Cleared all person 33 | Invalid value 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/root_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 15 | 16 | 17 | 18 | 21 | 22 | 28 | 29 | 36 | 37 | 42 | 43 | 48 | 49 | 54 | 55 | 60 | 61 | 66 | 67 | 72 | 73 | 78 | 79 | 80 | 83 | 84 | 87 | 88 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/test/java/com/faceplugin/facerecognition/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.faceplugin.facerecognition 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.3.1' apply false 4 | id 'com.android.library' version '8.3.1' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.8.0' apply false 6 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 19 20:09:07 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /libfacesdk/build.gradle: -------------------------------------------------------------------------------- 1 | configurations.maybeCreate("default") 2 | artifacts.add("default", file('facesdk.aar')) -------------------------------------------------------------------------------- /libfacesdk/build/.transforms/13753554662a2f3c3d39dbdbb6d04141/transformed/jetified-facesdk/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /libfacesdk/facesdk.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Faceplugin-ltd/FaceRecognition-Android/94709269b47b367022cf82d36c62ce69086e7af0/libfacesdk/facesdk.aar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | jcenter() 7 | } 8 | } 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | jcenter() 15 | maven { url 'https://www.jitpack.io'} 16 | } 17 | } 18 | rootProject.name = "FaceRecognition" 19 | include ':app' 20 | include ':libfacesdk' 21 | --------------------------------------------------------------------------------