capacitor-plugin-faceantispoofing 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CapacitorPluginFaceantispoofing.podspec +20 -0
  2. package/Package.swift +28 -0
  3. package/README.md +175 -0
  4. package/android/build.gradle +64 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/assets/FaceAntiSpoofing.tflite +0 -0
  7. package/android/src/main/assets/onet.tflite +0 -0
  8. package/android/src/main/assets/pnet.tflite +0 -0
  9. package/android/src/main/assets/rnet.tflite +0 -0
  10. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/FaceAntiSpoofing.java +112 -0
  11. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/FaceAntiSpoofingPlugin.java +178 -0
  12. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/MyUtil.java +174 -0
  13. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/mtcnn/Align.java +28 -0
  14. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/mtcnn/Box.java +73 -0
  15. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/mtcnn/MTCNN.java +268 -0
  16. package/android/src/main/java/io/github/asephermann/plugins/faceantispoofing/mtcnn/Utils.java +25 -0
  17. package/android/src/main/res/.gitkeep +0 -0
  18. package/dist/docs.json +104 -0
  19. package/dist/esm/definitions.d.ts +22 -0
  20. package/dist/esm/definitions.js +2 -0
  21. package/dist/esm/definitions.js.map +1 -0
  22. package/dist/esm/index.d.ts +4 -0
  23. package/dist/esm/index.js +7 -0
  24. package/dist/esm/index.js.map +1 -0
  25. package/dist/esm/web.d.ts +5 -0
  26. package/dist/esm/web.js +14 -0
  27. package/dist/esm/web.js.map +1 -0
  28. package/dist/plugin.cjs.js +28 -0
  29. package/dist/plugin.cjs.js.map +1 -0
  30. package/dist/plugin.js +31 -0
  31. package/dist/plugin.js.map +1 -0
  32. package/ios/Sources/FaceAntiSpoofingPlugin/Align.swift +41 -0
  33. package/ios/Sources/FaceAntiSpoofingPlugin/Box.swift +70 -0
  34. package/ios/Sources/FaceAntiSpoofingPlugin/FaceAntiSpoofing.swift +105 -0
  35. package/ios/Sources/FaceAntiSpoofingPlugin/FaceAntiSpoofing.tflite +0 -0
  36. package/ios/Sources/FaceAntiSpoofingPlugin/FaceAntiSpoofingPlugin.swift +166 -0
  37. package/ios/Sources/FaceAntiSpoofingPlugin/MTCNN.swift +407 -0
  38. package/ios/Sources/FaceAntiSpoofingPlugin/Tools.swift +103 -0
  39. package/ios/Sources/FaceAntiSpoofingPlugin/onet.tflite +0 -0
  40. package/ios/Sources/FaceAntiSpoofingPlugin/pnet.tflite +0 -0
  41. package/ios/Sources/FaceAntiSpoofingPlugin/rnet.tflite +0 -0
  42. package/ios/Tests/FaceAntiSpoofingPluginTests/FaceAntiSpoofingTests.swift +15 -0
  43. package/package.json +80 -0
@@ -0,0 +1,20 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorPluginFaceantispoofing'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.resource_bundles = {'CapacitorPluginFaceantispoofing' => ['ios/Sources/**/*.tflite']}
15
+ s.ios.deployment_target = '15.0'
16
+ s.dependency 'Capacitor'
17
+ s.dependency 'TensorFlowLiteSwift', '~> 2.17.0'
18
+ s.dependency 'TensorFlowLiteC', '~> 2.17.0'
19
+ s.swift_version = '5.1'
20
+ end
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapacitorPluginFaceantispoofing",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapacitorPluginFaceantispoofing",
10
+ targets: ["FaceAntiSpoofingPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "FaceAntiSpoofingPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/FaceAntiSpoofingPlugin"),
23
+ .testTarget(
24
+ name: "FaceAntiSpoofingPluginTests",
25
+ dependencies: ["FaceAntiSpoofingPlugin"],
26
+ path: "ios/Tests/FaceAntiSpoofingPluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # capacitor-plugin-faceantispoofing
2
+
3
+ Capacitor plugin for Passive Liveness Detection and Face Anti-Spoofing using TensorFlow Lite models.
4
+
5
+ This plugin has been migrated from [cordova-plugin-facespoofing](https://github.com/rakaraka029/cordova-plugin-facespoofing).
6
+
7
+ ## Features
8
+
9
+ - **Face Detection**: Uses MTCNN (Multi-task Cascaded Convolutional Networks) for accurate face detection
10
+ - **Anti-Spoofing Detection**: Passive liveness detection using TensorFlow Lite models
11
+ - **Blur Detection**: Laplacian variance-based image quality assessment
12
+ - **Cross-Platform**: Support for both Android and iOS
13
+
14
+ ## Install
15
+
16
+ ### Using npm
17
+
18
+ ```bash
19
+ npm install capacitor-plugin-faceantispoofing
20
+ npx cap sync
21
+ ```
22
+
23
+ ### Using yarn
24
+
25
+ ```bash
26
+ yarn add capacitor-plugin-faceantispoofing
27
+ npx cap sync
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```typescript
33
+ import { FaceAntiSpoofing } from 'capacitor-plugin-faceantispoofing';
34
+
35
+ // Detect face liveness from base64 image
36
+ const detectLiveness = async (base64Image: string) => {
37
+ try {
38
+ const result = await FaceAntiSpoofing.detect({
39
+ image: base64Image // Base64 encoded image (without data:image/...;base64, prefix)
40
+ });
41
+
42
+ console.log('Error:', result.error);
43
+ console.log('Liveness:', result.liveness);
44
+ console.log('Score:', result.score);
45
+ console.log('Threshold:', result.threshold);
46
+ console.log('Message:', result.message);
47
+
48
+ if (result.liveness) {
49
+ console.log('Real face detected!');
50
+ } else {
51
+ console.log('Spoof detected or no face found!');
52
+ }
53
+ } catch (error) {
54
+ console.error('Detection failed:', error);
55
+ }
56
+ };
57
+ ```
58
+
59
+ ## API
60
+
61
+ ### `detect(options)`
62
+
63
+ Detects face liveness and performs anti-spoofing detection on the provided image.
64
+
65
+ | Param | Type | Description |
66
+ | ------------ | ----------------------------- | -------------------------------------------------- |
67
+ | **options** | `<DetectOptions>` | Options for the detection |
68
+ | **image** | `<string>` | Base64 encoded image data or file URI |
69
+
70
+ **Returns:** `Promise<DetectionResult>`
71
+
72
+ ### DetectOptions
73
+
74
+ | Prop | Type | Description |
75
+ | ------- | ------------------- | ---------------------------------------- |
76
+ | image | `string` | Base64 encoded image (without `data:image/...;base64,` prefix) or file URI starting with `file://` |
77
+
78
+ ### DetectionResult
79
+
80
+ | Prop | Type | Description |
81
+ | --------- | ------------------- | ---------------------------------------- |
82
+ | error | `boolean` | Whether an error occurred |
83
+ | liveness | `boolean` | Whether the face is detected as real |
84
+ | score | `string` | The liveness score |
85
+ | threshold | `string` | The threshold used for classification |
86
+ | message | `string` | Additional information message |
87
+
88
+ ## Detection Flow
89
+
90
+ 1. **Face Detection**: Uses MTCNN (P-Net → R-Net → O-Net) to detect faces in the image
91
+ 2. **Face Alignment**: Aligns the detected face based on facial landmarks
92
+ 3. **Blur Detection**: Checks image clarity using Laplacian variance
93
+ 4. **Anti-Spoofing**: If image is clear enough, runs the anti-spoofing model to classify as real or fake
94
+
95
+ ## Response Values
96
+
97
+ - **No faces detected**: `error: true`, `message: "No faces detected"`
98
+ - **Image too blurry**: `error: false`, `liveness: false`, score contains Laplacian value
99
+ - **Real face detected**: `error: false`, `liveness: true`, score < threshold
100
+ - **Spoof detected**: `error: false`, `liveness: false`, score >= threshold
101
+
102
+ ## Platform Support
103
+
104
+ | Platform | Supported |
105
+ | -------- | --------- |
106
+ | Android | ✅ Yes |
107
+ | iOS | ✅ Yes |
108
+ | Web | ❌ No* |
109
+
110
+ *Web is not supported as native TensorFlow Lite is required.
111
+
112
+ ## Dependencies
113
+
114
+ ### Android
115
+ - Google AI Edge LITErt (TensorFlow Lite) 1.4.0
116
+ - minSdkVersion: 24
117
+ - compileSdkVersion: 36
118
+
119
+ ### iOS
120
+ - TensorFlowLiteSwift ~ 2.17.0
121
+ - TensorFlowLiteC ~ 2.17.0
122
+ - iOS Deployment Target: 15.0
123
+
124
+ ## Model Files
125
+
126
+ This plugin includes the following TensorFlow Lite models:
127
+
128
+ - `FaceAntiSpoofing.tflite` - Anti-spoofing classification model
129
+ - `pnet.tflite` - MTCNN Proposal Network
130
+ - `rnet.tflite` - MTCNN Refine Network
131
+ - `onet.tflite` - MTCNN Output Network
132
+
133
+ ## Threshold Values
134
+
135
+ - **Android**: THRESHOLD = 0.5, LAPLACIAN_THRESHOLD = 0
136
+ - **iOS**: threshold = 0.5, laplacianThreshold = 0
137
+
138
+ Note: These values have been normalized from the original Cordova plugin.
139
+
140
+ ## Example Image Format
141
+
142
+ The plugin accepts images in the following formats:
143
+
144
+ 1. **Base64 without prefix**: `iVBORw0KGgoAAAANSUhEUgAA...`
145
+ 2. **Base64 with data URI**: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`
146
+ 3. **File URI**: `file:///path/to/image.png`
147
+
148
+ ## Error Handling
149
+
150
+ ```typescript
151
+ try {
152
+ const result = await FaceAntiSpoofing.detect({ image: base64Data });
153
+
154
+ if (result.error) {
155
+ console.error('Detection error:', result.message);
156
+ return;
157
+ }
158
+
159
+ if (result.liveness) {
160
+ // Real face detected
161
+ } else {
162
+ // Spoof or no face detected
163
+ }
164
+ } catch (e) {
165
+ console.error('Plugin error:', e);
166
+ }
167
+ ```
168
+
169
+ ## License
170
+
171
+ MIT
172
+
173
+ ## Credits
174
+
175
+ Originally based on [cordova-plugin-facespoofing](https://github.com/rakaraka029/cordova-plugin-facespoofing) by rakaraka029.
@@ -0,0 +1,64 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace = "io.github.asephermann.plugins.faceantispoofing"
22
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
26
+ versionCode 1
27
+ versionName "1.0"
28
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError = false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_21
41
+ targetCompatibility JavaVersion.VERSION_21
42
+ }
43
+ aaptOptions {
44
+ noCompress "tflite"
45
+ noCompress "lite"
46
+ }
47
+ }
48
+
49
+
50
+ repositories {
51
+ google()
52
+ mavenCentral()
53
+ }
54
+
55
+
56
+ dependencies {
57
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
58
+ implementation project(':capacitor-android')
59
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
60
+ testImplementation "junit:junit:$junitVersion"
61
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
62
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
63
+ implementation 'com.google.ai.edge.litert:litert:1.4.0'
64
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,112 @@
1
+ package io.github.asephermann.plugins.faceantispoofing;
2
+
3
+ import android.content.res.AssetManager;
4
+ import android.graphics.Bitmap;
5
+ import android.util.Log;
6
+
7
+ import com.google.ai.edge.litert.Interpreter;
8
+
9
+ import java.io.IOException;
10
+ import java.util.HashMap;
11
+ import java.util.Map;
12
+
13
+ public class FaceAntiSpoofing {
14
+ private static final String MODEL_FILE = "FaceAntiSpoofing.tflite";
15
+
16
+ public static final int INPUT_IMAGE_SIZE = 256;
17
+ public static final float THRESHOLD = 0.5f;
18
+ public static final int ROUTE_INDEX = 6;
19
+ public static final int LAPLACE_THRESHOLD = 50;
20
+ public static final int LAPLACIAN_THRESHOLD = 0;
21
+
22
+ private Interpreter interpreter;
23
+
24
+ public FaceAntiSpoofing(AssetManager assetManager) throws IOException {
25
+ Interpreter.Options options = new Interpreter.Options();
26
+ options.setNumThreads(4);
27
+ interpreter = new Interpreter(MyUtil.loadModelFile(assetManager, MODEL_FILE), options);
28
+ }
29
+
30
+ public float antiSpoofing(Bitmap bitmap) {
31
+ Bitmap bitmapScale = Bitmap.createScaledBitmap(bitmap, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE, true);
32
+
33
+ float[][][] img = normalizeImage(bitmapScale);
34
+ float[][][][] input = new float[1][][][];
35
+ input[0] = img;
36
+ float[][] clss_pred = new float[1][8];
37
+ float[][] leaf_node_mask = new float[1][8];
38
+ Map<Integer, Object> outputs = new HashMap<>();
39
+ outputs.put(interpreter.getOutputIndex("Identity"), clss_pred);
40
+ outputs.put(interpreter.getOutputIndex("Identity_1"), leaf_node_mask);
41
+ interpreter.runForMultipleInputsOutputs(new Object[]{input}, outputs);
42
+
43
+ Log.i("FaceAntiSpoofing", "[" + clss_pred[0][0] + ", " + clss_pred[0][1] + ", "
44
+ + clss_pred[0][2] + ", " + clss_pred[0][3] + ", " + clss_pred[0][4] + ", "
45
+ + clss_pred[0][5] + ", " + clss_pred[0][6] + ", " + clss_pred[0][7] + "]");
46
+ Log.i("FaceAntiSpoofing", "[" + leaf_node_mask[0][0] + ", " + leaf_node_mask[0][1] + ", "
47
+ + leaf_node_mask[0][2] + ", " + leaf_node_mask[0][3] + ", " + leaf_node_mask[0][4] + ", "
48
+ + leaf_node_mask[0][5] + ", " + leaf_node_mask[0][6] + ", " + leaf_node_mask[0][7] + "]");
49
+
50
+ return leaf_score1(clss_pred, leaf_node_mask);
51
+ }
52
+
53
+ private float leaf_score1(float[][] clss_pred, float[][] leaf_node_mask) {
54
+ float score = 0;
55
+ for (int i = 0; i < 8; i++) {
56
+ score += Math.abs(clss_pred[0][i]) * leaf_node_mask[0][i];
57
+ }
58
+ return score;
59
+ }
60
+
61
+ private float leaf_score2(float[][] clss_pred) {
62
+ return clss_pred[0][ROUTE_INDEX];
63
+ }
64
+
65
+ public static float[][][] normalizeImage(Bitmap bitmap) {
66
+ int h = bitmap.getHeight();
67
+ int w = bitmap.getWidth();
68
+ float[][][] floatValues = new float[h][w][3];
69
+
70
+ float imageStd = 255;
71
+ int[] pixels = new int[h * w];
72
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, w, h);
73
+ for (int i = 0; i < h; i++) {
74
+ for (int j = 0; j < w; j++) {
75
+ final int val = pixels[i * w + j];
76
+ float r = ((val >> 16) & 0xFF) / imageStd;
77
+ float g = ((val >> 8) & 0xFF) / imageStd;
78
+ float b = (val & 0xFF) / imageStd;
79
+
80
+ float[] arr = {r, g, b};
81
+ floatValues[i][j] = arr;
82
+ }
83
+ }
84
+ return floatValues;
85
+ }
86
+
87
+ public int laplacian(Bitmap bitmap) {
88
+ Bitmap bitmapScale = Bitmap.createScaledBitmap(bitmap, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE, true);
89
+
90
+ int[][] laplace = {{0, 1, 0}, {1, -4, 1}, {0, 1, 0}};
91
+ int size = laplace.length;
92
+ int[][] img = MyUtil.convertGreyImg(bitmapScale);
93
+ int height = img.length;
94
+ int width = img[0].length;
95
+
96
+ int score = 0;
97
+ for (int x = 0; x < height - size + 1; x++){
98
+ for (int y = 0; y < width - size + 1; y++){
99
+ int result = 0;
100
+ for (int i = 0; i < size; i++){
101
+ for (int j = 0; j < size; j++){
102
+ result += (img[x + i][y + j] & 0xFF) * laplace[i][j];
103
+ }
104
+ }
105
+ if (result > LAPLACE_THRESHOLD) {
106
+ score++;
107
+ }
108
+ }
109
+ }
110
+ return score;
111
+ }
112
+ }
@@ -0,0 +1,178 @@
1
+ package io.github.asephermann.plugins.faceantispoofing;
2
+
3
+ import android.graphics.Bitmap;
4
+ import android.graphics.BitmapFactory;
5
+ import android.util.Base64;
6
+ import android.util.Log;
7
+
8
+ import com.getcapacitor.JSObject;
9
+ import com.getcapacitor.Plugin;
10
+ import com.getcapacitor.PluginCall;
11
+ import com.getcapacitor.PluginMethod;
12
+ import com.getcapacitor.annotation.CapacitorPlugin;
13
+
14
+ import java.io.File;
15
+ import java.io.FileInputStream;
16
+ import java.io.IOException;
17
+ import java.util.Vector;
18
+
19
+ import io.github.asephermann.plugins.faceantispoofing.mtcnn.Align;
20
+ import io.github.asephermann.plugins.faceantispoofing.mtcnn.Box;
21
+ import io.github.asephermann.plugins.faceantispoofing.mtcnn.MTCNN;
22
+
23
+ @CapacitorPlugin(name = "FaceAntiSpoofing")
24
+ public class FaceAntiSpoofingPlugin extends Plugin {
25
+
26
+ private static final String TAG = "FaceAntiSpoofing";
27
+
28
+ private FaceAntiSpoofing faceAntiSpoofing;
29
+ private MTCNN mtcnn;
30
+
31
+ @Override
32
+ public void load() {
33
+ super.load();
34
+ try {
35
+ faceAntiSpoofing = new FaceAntiSpoofing(getContext().getAssets());
36
+ mtcnn = new MTCNN(getContext().getAssets());
37
+ Log.d(TAG, "Models loaded successfully");
38
+ } catch (IOException e) {
39
+ Log.e(TAG, "Failed to load models", e);
40
+ }
41
+ }
42
+
43
+ @PluginMethod
44
+ public void detect(PluginCall call) {
45
+ String image = call.getString("image");
46
+
47
+ if (image == null || image.isEmpty()) {
48
+ call.reject("Image data is required");
49
+ return;
50
+ }
51
+
52
+ if (faceAntiSpoofing == null || mtcnn == null) {
53
+ call.reject("Models not loaded. Please ensure the plugin is properly initialized.");
54
+ return;
55
+ }
56
+
57
+ try {
58
+ Bitmap bitmap = decodeImage(image);
59
+ if (bitmap == null) {
60
+ call.reject("Failed to decode image");
61
+ return;
62
+ }
63
+
64
+ DetectionResult result = performAntiSpoofing(bitmap);
65
+ call.resolve(resultToJSObject(result));
66
+ } catch (Exception e) {
67
+ Log.e(TAG, "Error during detection", e);
68
+ call.reject("Error during detection: " + e.getMessage());
69
+ }
70
+ }
71
+
72
+ private Bitmap decodeImage(String imageData) throws IOException {
73
+ // Check if it's a base64 string or file path
74
+ if (imageData.startsWith("data:image/")) {
75
+ // Remove data URI prefix if present
76
+ int commaIndex = imageData.indexOf(',');
77
+ if (commaIndex != -1) {
78
+ imageData = imageData.substring(commaIndex + 1);
79
+ }
80
+ byte[] decodedString = Base64.decode(imageData, Base64.DEFAULT);
81
+ return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
82
+ } else if (imageData.startsWith("file://")) {
83
+ String filePath = imageData.substring(7);
84
+ File file = new File(filePath);
85
+ if (file.exists()) {
86
+ FileInputStream fis = new FileInputStream(file);
87
+ Bitmap bitmap = BitmapFactory.decodeStream(fis);
88
+ fis.close();
89
+ return bitmap;
90
+ }
91
+ } else {
92
+ // Try as base64
93
+ byte[] decodedString = Base64.decode(imageData, Base64.DEFAULT);
94
+ return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
95
+ }
96
+ return null;
97
+ }
98
+
99
+ private DetectionResult performAntiSpoofing(Bitmap bitmap) {
100
+ DetectionResult result = new DetectionResult();
101
+
102
+ if (bitmap == null) {
103
+ result.error = true;
104
+ result.message = "Please detect face first";
105
+ return result;
106
+ }
107
+
108
+ Bitmap bitmapTemp1 = bitmap.copy(bitmap.getConfig(), false);
109
+ Vector<Box> boxes1 = mtcnn.detectFaces(bitmapTemp1, bitmapTemp1.getWidth() / 8);
110
+
111
+ if (boxes1.size() == 0) {
112
+ result.error = true;
113
+ result.message = "No faces detected";
114
+ return result;
115
+ }
116
+
117
+ Box box1 = boxes1.get(0);
118
+ bitmapTemp1 = Align.face_align(bitmapTemp1, box1.landmark);
119
+ boxes1 = mtcnn.detectFaces(bitmapTemp1, bitmapTemp1.getWidth() / 8);
120
+
121
+ if (boxes1.size() == 0) {
122
+ result.error = true;
123
+ result.message = "No faces detected";
124
+ return result;
125
+ }
126
+
127
+ box1 = boxes1.get(0);
128
+ box1.toSquareShape();
129
+ box1.limitSquare(bitmapTemp1.getWidth(), bitmapTemp1.getHeight());
130
+ android.graphics.Rect rect1 = box1.transform2Rect();
131
+ Bitmap bitmapCrop1 = MyUtil.crop(bitmapTemp1, rect1);
132
+
133
+ int laplace2 = faceAntiSpoofing.laplacian(bitmapCrop1);
134
+
135
+ if (laplace2 < FaceAntiSpoofing.LAPLACIAN_THRESHOLD) {
136
+ result.error = false;
137
+ result.score = String.valueOf(laplace2);
138
+ result.threshold = String.valueOf(FaceAntiSpoofing.LAPLACIAN_THRESHOLD);
139
+ result.liveness = false;
140
+ result.message = "";
141
+ } else {
142
+ float score2 = faceAntiSpoofing.antiSpoofing(bitmapCrop1);
143
+ if (score2 < FaceAntiSpoofing.THRESHOLD) {
144
+ result.error = false;
145
+ result.score = String.valueOf(score2);
146
+ result.threshold = String.valueOf(FaceAntiSpoofing.THRESHOLD);
147
+ result.liveness = true;
148
+ result.message = "";
149
+ } else {
150
+ result.error = false;
151
+ result.score = String.valueOf(score2);
152
+ result.threshold = String.valueOf(FaceAntiSpoofing.THRESHOLD);
153
+ result.liveness = false;
154
+ result.message = "Sorry, we can't find a face or indicate that the photo is real.";
155
+ }
156
+ }
157
+
158
+ return result;
159
+ }
160
+
161
+ private JSObject resultToJSObject(DetectionResult result) {
162
+ JSObject jsObject = new JSObject();
163
+ jsObject.put("error", result.error);
164
+ jsObject.put("liveness", result.liveness);
165
+ jsObject.put("score", result.score);
166
+ jsObject.put("threshold", result.threshold);
167
+ jsObject.put("message", result.message);
168
+ return jsObject;
169
+ }
170
+
171
+ private static class DetectionResult {
172
+ boolean error = false;
173
+ boolean liveness = false;
174
+ String score = "0";
175
+ String threshold = "0";
176
+ String message = "";
177
+ }
178
+ }