react-native-vision-camera-spoof-detector 1.1.0 → 1.1.7
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.
Potentially problematic release.
This version of react-native-vision-camera-spoof-detector might be problematic. Click here for more details.
- package/.npmignore +16 -0
- package/android/CMakeLists.txt +29 -0
- package/android/build.gradle +26 -10
- package/android/fix-prefab.gradle +4 -0
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/cpp/CMakeLists.txt +18 -86
- package/android/src/main/cpp/FaceAntiSpoofJSI.cpp +80 -6
- package/android/src/main/cpp/cpp-adapter.cpp +9 -0
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofModule.kt +53 -103
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofPackage.kt +15 -8
- package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorFactory.kt +15 -0
- package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorOutput.kt +107 -0
- package/index.js +23 -42
- package/nitro.json +20 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake +81 -0
- package/nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.gradle +27 -0
- package/nitrogen/generated/android/VisionCameraSpoofDetectorOnLoad.cpp +58 -0
- package/nitrogen/generated/android/VisionCameraSpoofDetectorOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JAntiSpoofingResult.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_AntiSpoofingResult.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__exception_ptr.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridSpoofDetectorFactorySpec.cpp +75 -0
- package/nitrogen/generated/android/c++/JHybridSpoofDetectorFactorySpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JSize.hpp +61 -0
- package/nitrogen/generated/android/c++/JSpoofDetectorOutputOptions.hpp +91 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/AntiSpoofingResult.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Func_void_AntiSpoofingResult.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Func_void_std__exception_ptr.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorFactorySpec.kt +57 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Size.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/SpoofDetectorOutputOptions.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/VisionCameraSpoofDetectorOnLoad.kt +35 -0
- package/nitrogen/generated/shared/c++/AntiSpoofingResult.hpp +103 -0
- package/nitrogen/generated/shared/c++/HybridSpoofDetectorFactorySpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridSpoofDetectorFactorySpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/Size.hpp +87 -0
- package/nitrogen/generated/shared/c++/SpoofDetectorOutputOptions.hpp +98 -0
- package/package.json +10 -8
- package/src/specs/SpoofDetectorFactory.nitro.ts +23 -0
- package/android/src/main/assets/spoof_model_scale_2_7.tflite +0 -0
- package/android/src/main/assets/spoof_model_scale_4_0.tflite +0 -0
- /package/{android/src/main → lib}/assets/FaceAntiSpoofing.tflite +0 -0
package/.npmignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
project(VisionCameraSpoofDetector)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME VisionCameraSpoofDetector)
|
|
5
|
+
set(CMAKE_SUPPRESS_REGENERATION ON)
|
|
6
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
7
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
8
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
9
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
10
|
+
set(CMAKE_ANDROID_STL_TYPE c++_shared)
|
|
11
|
+
|
|
12
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
13
|
+
src/main/cpp/cpp-adapter.cpp
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Nitrogen-generated specs: this path is valid because the project root is android/
|
|
17
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake)
|
|
18
|
+
|
|
19
|
+
find_library(LOG_LIB log)
|
|
20
|
+
find_package(react-native-vision-camera REQUIRED)
|
|
21
|
+
|
|
22
|
+
target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
|
|
23
|
+
|
|
24
|
+
target_link_libraries(
|
|
25
|
+
${PACKAGE_NAME}
|
|
26
|
+
${LOG_LIB}
|
|
27
|
+
android
|
|
28
|
+
react-native-vision-camera::VisionCamera
|
|
29
|
+
)
|
package/android/build.gradle
CHANGED
|
@@ -19,8 +19,15 @@ buildscript {
|
|
|
19
19
|
|
|
20
20
|
apply plugin: 'com.android.library'
|
|
21
21
|
apply plugin: 'kotlin-android'
|
|
22
|
+
apply from: '../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.gradle'
|
|
23
|
+
|
|
24
|
+
def reactNativeArchitectures() {
|
|
25
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
26
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
27
|
+
}
|
|
22
28
|
|
|
23
29
|
android {
|
|
30
|
+
namespace "com.margelo.nitro.camera.spoofdetector"
|
|
24
31
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
|
25
32
|
|
|
26
33
|
defaultConfig {
|
|
@@ -35,7 +42,9 @@ android {
|
|
|
35
42
|
// Needed for JSI / C++ build
|
|
36
43
|
externalNativeBuild {
|
|
37
44
|
cmake {
|
|
38
|
-
cppFlags "-
|
|
45
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
46
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
47
|
+
abiFilters (*reactNativeArchitectures())
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
}
|
|
@@ -61,7 +70,7 @@ android {
|
|
|
61
70
|
|
|
62
71
|
sourceSets {
|
|
63
72
|
main {
|
|
64
|
-
assets.srcDirs = ['
|
|
73
|
+
assets.srcDirs = ['../lib/assets']
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
|
|
@@ -91,10 +100,14 @@ android {
|
|
|
91
100
|
|
|
92
101
|
externalNativeBuild {
|
|
93
102
|
cmake {
|
|
94
|
-
path "
|
|
95
|
-
version "3.22.1"
|
|
103
|
+
path "CMakeLists.txt"
|
|
104
|
+
version "3.22.1"
|
|
96
105
|
}
|
|
97
106
|
}
|
|
107
|
+
|
|
108
|
+
buildFeatures {
|
|
109
|
+
prefab true
|
|
110
|
+
}
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
def isNewArchitectureEnabled() {
|
|
@@ -109,8 +122,10 @@ repositories {
|
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
dependencies {
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
implementation "androidx.camera:camera-core:1.7.0-alpha03"
|
|
126
|
+
|
|
127
|
+
// React Native and VisionCamera provide their native Prefab libraries.
|
|
128
|
+
compileOnly "com.facebook.react:react-android"
|
|
114
129
|
|
|
115
130
|
// Kotlin
|
|
116
131
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
|
@@ -130,8 +145,9 @@ dependencies {
|
|
|
130
145
|
// AndroidX - EXIF Support for image orientation
|
|
131
146
|
implementation "androidx.exifinterface:exifinterface:1.3.6"
|
|
132
147
|
|
|
133
|
-
// VisionCamera 5
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
// VisionCamera 5 and Nitro provide these native libraries to the consuming app.
|
|
149
|
+
compileOnly project(':react-native-vision-camera')
|
|
150
|
+
compileOnly project(':react-native-vision-camera-worklets')
|
|
151
|
+
compileOnly project(':react-native-worklets')
|
|
152
|
+
compileOnly project(':react-native-nitro-modules')
|
|
137
153
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// This package does not need the forced prefab reconfigure workaround.
|
|
2
|
+
// Leaving the task outputs as a normal Gradle up-to-date state prevents the
|
|
3
|
+
// infinite CMake regeneration loop that was triggering
|
|
4
|
+
// "ninja: error: manifest 'build.ninja' still dirty after 100 tries".
|
|
@@ -1,101 +1,33 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.4.1)
|
|
2
|
-
|
|
3
|
-
project(fastyuv)
|
|
4
|
-
|
|
5
|
-
set(SOURCE_FILES
|
|
6
|
-
FastYuvJni.cpp
|
|
7
|
-
fast_yuv.cpp
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
add_library(fastyuv SHARED ${SOURCE_FILES})
|
|
11
|
-
|
|
12
|
-
# Enable NEON SIMD optimizations for ARM architectures
|
|
13
|
-
if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
|
14
|
-
# ARM NEON optimization flags
|
|
15
|
-
target_compile_options(fastyuv PRIVATE
|
|
16
|
-
-fPIC
|
|
17
|
-
-O3
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
# ARM-specific NEON enablement (clang doesn't support -march=native for cross-compilation)
|
|
21
|
-
if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7")
|
|
22
|
-
# ARMv7 with NEON (32-bit ARM)
|
|
23
|
-
target_compile_options(fastyuv PRIVATE -march=armv7-a -mfpu=neon -mfloat-abi=softfp)
|
|
24
|
-
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
|
25
|
-
# ARMv8 with full SIMD support (64-bit ARM)
|
|
26
|
-
target_compile_options(fastyuv PRIVATE -march=armv8-a)
|
|
27
|
-
endif()
|
|
28
|
-
endif()
|
|
29
|
-
|
|
30
|
-
# Always add libyuv include dir so headers like libyuv/convert.h are found.
|
|
31
|
-
target_include_directories(fastyuv PRIVATE ${CMAKE_SOURCE_DIR}/libyuv/include)
|
|
32
|
-
|
|
33
|
-
# Prefer using libyuv subproject if available.
|
|
34
|
-
if (EXISTS ${CMAKE_SOURCE_DIR}/libyuv/CMakeLists.txt)
|
|
35
|
-
add_subdirectory(libyuv)
|
|
36
|
-
# Link against the libyuv target produced by that CMake. The lib creates
|
|
37
|
-
# targets named 'yuv' (static) and 'yuv_shared' (shared) whose output
|
|
38
|
-
# library name is 'libyuv'. Prefer the shared target when available.
|
|
39
|
-
if (TARGET yuv_shared)
|
|
40
|
-
target_link_libraries(fastyuv PRIVATE yuv_shared)
|
|
41
|
-
elseif (TARGET yuv)
|
|
42
|
-
target_link_libraries(fastyuv PRIVATE yuv)
|
|
43
|
-
endif()
|
|
44
|
-
endif()
|
|
45
|
-
|
|
46
|
-
# 支持 Android 15+ 的 16KB 页面大小
|
|
47
|
-
# https://developer.android.com/guide/practices/page-sizes
|
|
48
|
-
target_link_options(fastyuv PRIVATE "-Wl,-z,max-page-size=16384")
|
|
49
|
-
|
|
50
|
-
# Android log
|
|
51
|
-
find_library(log-lib log)
|
|
52
|
-
target_link_libraries(fastyuv PRIVATE ${log-lib})
|
|
53
1
|
cmake_minimum_required(VERSION 3.18)
|
|
54
|
-
project(
|
|
2
|
+
project(VisionCameraSpoofDetector)
|
|
55
3
|
|
|
56
4
|
set(CMAKE_CXX_STANDARD 17)
|
|
57
5
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_SUPPRESS_REGENERATION ON)
|
|
58
7
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
8
|
+
set(CMAKE_ANDROID_STL_TYPE c++_shared)
|
|
59
9
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
get_filename_component(PARENT_1 "${CMAKE_SOURCE_ABS}/.." ABSOLUTE) # main
|
|
63
|
-
get_filename_component(PARENT_2 "${PARENT_1}/.." ABSOLUTE) # src
|
|
64
|
-
get_filename_component(PARENT_3 "${PARENT_2}/.." ABSOLUTE) # android
|
|
65
|
-
get_filename_component(PARENT_4 "${PARENT_3}/.." ABSOLUTE) # react-native-vision-camera-spoof-detector
|
|
66
|
-
get_filename_component(NODE_MODULES_ABS "${PARENT_4}/.." ABSOLUTE) # node_modules
|
|
67
|
-
|
|
68
|
-
set(REACT_NATIVE_DIR "${NODE_MODULES_ABS}/react-native")
|
|
69
|
-
set(REACT_COMMON_DIR "${REACT_NATIVE_DIR}/ReactCommon")
|
|
70
|
-
|
|
71
|
-
message(STATUS "NODE_MODULES_ABS: ${NODE_MODULES_ABS}")
|
|
72
|
-
message(STATUS "REACT_NATIVE_DIR: ${REACT_NATIVE_DIR}")
|
|
73
|
-
message(STATUS "REACT_COMMON_DIR: ${REACT_COMMON_DIR}")
|
|
10
|
+
set(PACKAGE_NAME VisionCameraSpoofDetector)
|
|
11
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
74
12
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
add_library(faceantispoof SHARED FaceAntiSpoofJSI.cpp ${JSI_CPP})
|
|
79
|
-
|
|
80
|
-
target_include_directories(faceantispoof PRIVATE
|
|
81
|
-
"${REACT_COMMON_DIR}/jsi"
|
|
82
|
-
"${REACT_COMMON_DIR}/cxxreact"
|
|
83
|
-
"${REACT_COMMON_DIR}/callinvoker/ReactCommon"
|
|
84
|
-
"${REACT_COMMON_DIR}/runtimeexecutor/ReactCommon"
|
|
85
|
-
"${REACT_COMMON_DIR}"
|
|
13
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
14
|
+
cpp-adapter.cpp
|
|
86
15
|
)
|
|
87
16
|
|
|
88
|
-
|
|
17
|
+
# Nitrogen-generated hybrid bindings
|
|
18
|
+
include(${CMAKE_SOURCE_DIR}/../../../../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake)
|
|
19
|
+
|
|
20
|
+
find_library(LOG_LIB log)
|
|
21
|
+
find_package(react-native-vision-camera REQUIRED)
|
|
89
22
|
|
|
90
|
-
#
|
|
91
|
-
target_link_options(
|
|
23
|
+
# Android 15+ 16KB page support
|
|
24
|
+
target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
|
|
92
25
|
|
|
93
26
|
target_link_libraries(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
m
|
|
27
|
+
${PACKAGE_NAME}
|
|
28
|
+
${LOG_LIB}
|
|
29
|
+
android
|
|
30
|
+
react-native-vision-camera::VisionCamera
|
|
99
31
|
)
|
|
100
32
|
|
|
101
33
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#include <jni.h>
|
|
2
2
|
#include <jsi/jsi.h>
|
|
3
3
|
#include <string>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <algorithm>
|
|
4
6
|
#include <android/log.h>
|
|
5
7
|
|
|
6
8
|
using namespace facebook;
|
|
@@ -8,18 +10,90 @@ using namespace facebook;
|
|
|
8
10
|
// Logging macro
|
|
9
11
|
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FaceAntiSpoofJSI", __VA_ARGS__))
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
static JavaVM *javaVm = nullptr;
|
|
14
|
+
|
|
12
15
|
jsi::Value faceAntiSpoof(jsi::Runtime &runtime, const jsi::Value &frame) {
|
|
13
|
-
|
|
16
|
+
if (!frame.isObject()) {
|
|
17
|
+
throw jsi::JSError(runtime, "FaceAntiSpoof expected a Frame object");
|
|
18
|
+
}
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
auto frameObject = frame.asObject(runtime);
|
|
21
|
+
auto planesValue = frameObject.getProperty(runtime, "getPlanes").asObject(runtime)
|
|
22
|
+
.asFunction(runtime).call(runtime, frameObject);
|
|
23
|
+
auto planes = planesValue.asObject(runtime);
|
|
24
|
+
auto length = static_cast<size_t>(planes.getProperty(runtime, "length").asNumber());
|
|
25
|
+
if (length < 3) {
|
|
26
|
+
throw jsi::JSError(runtime, "FaceAntiSpoof expected Y, U and V planes");
|
|
27
|
+
}
|
|
19
28
|
|
|
29
|
+
std::vector<std::vector<uint8_t>> planeData(3);
|
|
30
|
+
jint rowStrides[3] = {};
|
|
31
|
+
jint pixelStrides[3] = {};
|
|
32
|
+
for (size_t index = 0; index < 3; index++) {
|
|
33
|
+
auto plane = planes.getProperty(runtime, jsi::Value(static_cast<double>(index))).asObject(runtime);
|
|
34
|
+
auto buffer = plane.getProperty(runtime, "getPixelBuffer").asObject(runtime)
|
|
35
|
+
.asFunction(runtime).call(runtime, plane).asObject(runtime);
|
|
36
|
+
auto arrayBuffer = std::move(buffer).getArrayBuffer(runtime);
|
|
37
|
+
auto *data = static_cast<const uint8_t *>(arrayBuffer.data(runtime));
|
|
38
|
+
planeData[index].assign(data, data + arrayBuffer.size(runtime));
|
|
39
|
+
rowStrides[index] = static_cast<jint>(plane.getProperty(runtime, "bytesPerRow").asNumber());
|
|
40
|
+
auto planeWidth = plane.getProperty(runtime, "width").asNumber();
|
|
41
|
+
pixelStrides[index] = std::max(1, static_cast<jint>(rowStrides[index] / planeWidth));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
JNIEnv *env = nullptr;
|
|
45
|
+
bool attached = false;
|
|
46
|
+
if (javaVm == nullptr) {
|
|
47
|
+
throw jsi::JSError(runtime, "FaceAntiSpoof Android runtime is unavailable");
|
|
48
|
+
}
|
|
49
|
+
if (javaVm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
|
|
50
|
+
if (javaVm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
|
|
51
|
+
throw jsi::JSError(runtime, "FaceAntiSpoof could not attach to Android runtime");
|
|
52
|
+
}
|
|
53
|
+
attached = true;
|
|
54
|
+
}
|
|
55
|
+
auto moduleClass = env->FindClass("com/faceantispoof/FaceAntiSpoofModule");
|
|
56
|
+
auto process = env->GetStaticMethodID(moduleClass, "processFrameFromJSI", "([[BII[I[I)[D");
|
|
57
|
+
auto javaPlanes = env->NewObjectArray(3, env->FindClass("[B"), nullptr);
|
|
58
|
+
auto javaRowStrides = env->NewIntArray(3);
|
|
59
|
+
auto javaPixelStrides = env->NewIntArray(3);
|
|
60
|
+
env->SetIntArrayRegion(javaRowStrides, 0, 3, rowStrides);
|
|
61
|
+
env->SetIntArrayRegion(javaPixelStrides, 0, 3, pixelStrides);
|
|
62
|
+
for (int index = 0; index < 3; index++) {
|
|
63
|
+
auto bytes = env->NewByteArray(static_cast<jsize>(planeData[index].size()));
|
|
64
|
+
env->SetByteArrayRegion(bytes, 0, static_cast<jsize>(planeData[index].size()),
|
|
65
|
+
reinterpret_cast<const jbyte *>(planeData[index].data()));
|
|
66
|
+
env->SetObjectArrayElement(javaPlanes, index, bytes);
|
|
67
|
+
env->DeleteLocalRef(bytes);
|
|
68
|
+
}
|
|
69
|
+
auto values = static_cast<jdoubleArray>(env->CallStaticObjectMethod(
|
|
70
|
+
moduleClass, process, javaPlanes, static_cast<jint>(frameObject.getProperty(runtime, "width").asNumber()),
|
|
71
|
+
static_cast<jint>(frameObject.getProperty(runtime, "height").asNumber()),
|
|
72
|
+
javaRowStrides, javaPixelStrides));
|
|
73
|
+
jdouble output[5] = {};
|
|
74
|
+
if (values != nullptr) env->GetDoubleArrayRegion(values, 0, 5, output);
|
|
75
|
+
env->DeleteLocalRef(values);
|
|
76
|
+
env->DeleteLocalRef(javaPixelStrides);
|
|
77
|
+
env->DeleteLocalRef(javaRowStrides);
|
|
78
|
+
env->DeleteLocalRef(javaPlanes);
|
|
79
|
+
env->DeleteLocalRef(moduleClass);
|
|
80
|
+
if (attached) javaVm->DetachCurrentThread();
|
|
81
|
+
|
|
82
|
+
jsi::Object result(runtime);
|
|
83
|
+
result.setProperty(runtime, "isLive", output[0] != 0.0);
|
|
84
|
+
result.setProperty(runtime, "label", output[0] != 0.0 ? "Live Face" : "Spoof Face");
|
|
85
|
+
result.setProperty(runtime, "neuralNetworkScore", output[1]);
|
|
86
|
+
result.setProperty(runtime, "laplacianScore", output[2]);
|
|
87
|
+
result.setProperty(runtime, "combinedScore", output[3]);
|
|
88
|
+
result.setProperty(runtime, "confidence", output[4]);
|
|
20
89
|
return result;
|
|
21
90
|
}
|
|
22
91
|
|
|
92
|
+
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
|
|
93
|
+
javaVm = vm;
|
|
94
|
+
return JNI_VERSION_1_6;
|
|
95
|
+
}
|
|
96
|
+
|
|
23
97
|
// JSI registration function
|
|
24
98
|
extern "C"
|
|
25
99
|
JNIEXPORT void JNICALL
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#include "VisionCameraSpoofDetectorOnLoad.hpp"
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
3
|
+
#include <jni.h>
|
|
4
|
+
|
|
5
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
6
|
+
return facebook::jni::initialize(vm, []() {
|
|
7
|
+
margelo::nitro::camera::spoofdetector::registerAllNatives();
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -13,17 +13,27 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
|
|
|
13
13
|
|
|
14
14
|
companion object {
|
|
15
15
|
const val NAME = "FaceAntiSpoof"
|
|
16
|
+
private var activeModule: FaceAntiSpoofModule? = null
|
|
17
|
+
|
|
18
|
+
@JvmStatic
|
|
19
|
+
fun processFrameFromJSI(
|
|
20
|
+
planes: Array<ByteArray>,
|
|
21
|
+
width: Int,
|
|
22
|
+
height: Int,
|
|
23
|
+
rowStrides: IntArray,
|
|
24
|
+
pixelStrides: IntArray
|
|
25
|
+
): DoubleArray {
|
|
26
|
+
val module = activeModule
|
|
27
|
+
?: return doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
|
|
28
|
+
return module.processNativeFrame(planes, width, height, rowStrides, pixelStrides)
|
|
29
|
+
}
|
|
16
30
|
}
|
|
17
31
|
|
|
18
32
|
private var faceAntiSpoofing: FaceAntiSpoofingAdvanced? = null
|
|
19
33
|
private var isModuleInitialized = false
|
|
20
34
|
|
|
21
35
|
init {
|
|
22
|
-
|
|
23
|
-
System.loadLibrary("faceantispoof")
|
|
24
|
-
} catch (e: Exception) {
|
|
25
|
-
android.util.Log.e("FaceAntiSpoof", "Error loading native library", e)
|
|
26
|
-
}
|
|
36
|
+
activeModule = this
|
|
27
37
|
|
|
28
38
|
try {
|
|
29
39
|
// Initialize face anti-spoofing with app assets
|
|
@@ -35,57 +45,6 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
|
|
|
35
45
|
isModuleInitialized = false
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
try {
|
|
39
|
-
tryRegisterJSI()
|
|
40
|
-
} catch (e: Exception) {
|
|
41
|
-
android.util.Log.e("FaceAntiSpoof", "Error starting JSI registration", e)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private fun tryRegisterJSI() {
|
|
46
|
-
try {
|
|
47
|
-
Thread {
|
|
48
|
-
try {
|
|
49
|
-
Thread.sleep(500)
|
|
50
|
-
val holder = reactApplicationContext.javaScriptContextHolder ?: return@Thread
|
|
51
|
-
|
|
52
|
-
// Try nativePointer
|
|
53
|
-
try {
|
|
54
|
-
val field = holder.javaClass.getDeclaredField("nativePointer")
|
|
55
|
-
field.isAccessible = true
|
|
56
|
-
val jsContextPtr = field.getLong(holder)
|
|
57
|
-
if (jsContextPtr != 0L) {
|
|
58
|
-
try {
|
|
59
|
-
registerJSIFunction(jsContextPtr)
|
|
60
|
-
} catch (e: Exception) {
|
|
61
|
-
android.util.Log.d("FaceAntiSpoof", "JSI registration not available: ${e.message}")
|
|
62
|
-
}
|
|
63
|
-
return@Thread
|
|
64
|
-
}
|
|
65
|
-
} catch (_: Exception) { }
|
|
66
|
-
|
|
67
|
-
// Try mContext
|
|
68
|
-
try {
|
|
69
|
-
val field = holder.javaClass.getDeclaredField("mContext")
|
|
70
|
-
field.isAccessible = true
|
|
71
|
-
val jsContextPtr = field.getLong(holder)
|
|
72
|
-
if (jsContextPtr != 0L) {
|
|
73
|
-
try {
|
|
74
|
-
registerJSIFunction(jsContextPtr)
|
|
75
|
-
} catch (e: Exception) {
|
|
76
|
-
android.util.Log.d("FaceAntiSpoof", "JSI registration not available: ${e.message}")
|
|
77
|
-
}
|
|
78
|
-
return@Thread
|
|
79
|
-
}
|
|
80
|
-
} catch (_: Exception) { }
|
|
81
|
-
|
|
82
|
-
} catch (e: Exception) {
|
|
83
|
-
android.util.Log.d("FaceAntiSpoof", "Error in JSI registration thread: ${e.message}")
|
|
84
|
-
}
|
|
85
|
-
}.start()
|
|
86
|
-
} catch (e: Exception) {
|
|
87
|
-
android.util.Log.d("FaceAntiSpoof", "Failed to start thread for JSI registration: ${e.message}")
|
|
88
|
-
}
|
|
89
48
|
}
|
|
90
49
|
|
|
91
50
|
override fun getName(): String = NAME
|
|
@@ -181,6 +140,41 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
|
|
|
181
140
|
}
|
|
182
141
|
}
|
|
183
142
|
|
|
143
|
+
@Synchronized
|
|
144
|
+
private fun processNativeFrame(
|
|
145
|
+
planes: Array<ByteArray>,
|
|
146
|
+
width: Int,
|
|
147
|
+
height: Int,
|
|
148
|
+
rowStrides: IntArray,
|
|
149
|
+
pixelStrides: IntArray
|
|
150
|
+
): DoubleArray {
|
|
151
|
+
if (!isModuleInitialized || faceAntiSpoofing == null || planes.size < 3) {
|
|
152
|
+
return doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return try {
|
|
156
|
+
val result = faceAntiSpoofing!!.advancedAntiSpoofing(
|
|
157
|
+
planes,
|
|
158
|
+
width,
|
|
159
|
+
height,
|
|
160
|
+
rowStrides,
|
|
161
|
+
pixelStrides
|
|
162
|
+
)
|
|
163
|
+
val isLive = result.neuralNetworkScore < FaceAntiSpoofingAdvanced.BASE_THRESHOLD &&
|
|
164
|
+
result.laplacianScore > FaceAntiSpoofingAdvanced.LAPLACIAN_THRESHOLD
|
|
165
|
+
doubleArrayOf(
|
|
166
|
+
if (isLive) 1.0 else 0.0,
|
|
167
|
+
result.neuralNetworkScore.toDouble(),
|
|
168
|
+
result.laplacianScore.toDouble(),
|
|
169
|
+
result.combinedScore.toDouble(),
|
|
170
|
+
result.confidence.toDouble()
|
|
171
|
+
)
|
|
172
|
+
} catch (error: Exception) {
|
|
173
|
+
android.util.Log.e("FaceAntiSpoof", "Native frame processing failed", error)
|
|
174
|
+
doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
184
178
|
@ReactMethod
|
|
185
179
|
fun isAvailable(promise: Promise) {
|
|
186
180
|
try {
|
|
@@ -218,51 +212,9 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
|
|
|
218
212
|
|
|
219
213
|
@ReactMethod
|
|
220
214
|
fun install(promise: Promise) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (holder != null) {
|
|
225
|
-
// Try nativePointer
|
|
226
|
-
try {
|
|
227
|
-
val f = holder.javaClass.getDeclaredField("nativePointer")
|
|
228
|
-
f.isAccessible = true
|
|
229
|
-
val ptr = f.getLong(holder)
|
|
230
|
-
if (ptr != 0L) {
|
|
231
|
-
try {
|
|
232
|
-
registerJSIFunction(ptr)
|
|
233
|
-
promise.resolve(true)
|
|
234
|
-
return
|
|
235
|
-
} catch (e: Exception) {
|
|
236
|
-
android.util.Log.d("FaceAntiSpoof", "JSI not available: ${e.message}")
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
} catch (_: Exception) {}
|
|
240
|
-
|
|
241
|
-
// Try mContext
|
|
242
|
-
try {
|
|
243
|
-
val f = holder.javaClass.getDeclaredField("mContext")
|
|
244
|
-
f.isAccessible = true
|
|
245
|
-
val ptr = f.getLong(holder)
|
|
246
|
-
if (ptr != 0L) {
|
|
247
|
-
try {
|
|
248
|
-
registerJSIFunction(ptr)
|
|
249
|
-
promise.resolve(true)
|
|
250
|
-
return
|
|
251
|
-
} catch (e: Exception) {
|
|
252
|
-
android.util.Log.d("FaceAntiSpoof", "JSI not available: ${e.message}")
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
} catch (_: Exception) {}
|
|
256
|
-
|
|
257
|
-
promise.resolve(false)
|
|
258
|
-
} else {
|
|
259
|
-
promise.resolve(false)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
} catch (e: Exception) {
|
|
263
|
-
android.util.Log.e("FaceAntiSpoof", "install() error", e)
|
|
264
|
-
promise.reject("INSTALL_ERROR", "Failed to install JSI function", e)
|
|
265
|
-
}
|
|
215
|
+
// VisionCamera 5 uses the Nitro CameraOutput. The old reflective JSI
|
|
216
|
+
// installer targets a native library that is no longer part of this module.
|
|
217
|
+
promise.resolve(false)
|
|
266
218
|
}
|
|
267
219
|
|
|
268
220
|
@ReactMethod
|
|
@@ -292,6 +244,4 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
|
|
|
292
244
|
super.onCatalystInstanceDestroy()
|
|
293
245
|
}
|
|
294
246
|
|
|
295
|
-
// Native JSI registration - optional, may not be available
|
|
296
|
-
private external fun registerJSIFunction(jsContext: Long)
|
|
297
247
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.margelo.nitro.camera.spoofdetector
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
|
+
import com.faceantispoof.FaceAntiSpoofModule
|
|
7
8
|
|
|
8
|
-
class FaceAntiSpoofPackage :
|
|
9
|
-
override fun
|
|
10
|
-
|
|
9
|
+
class FaceAntiSpoofPackage : BaseReactPackage() {
|
|
10
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
|
|
11
|
+
if (name == FaceAntiSpoofModule.NAME) FaceAntiSpoofModule(reactContext) else null
|
|
11
12
|
|
|
12
|
-
override fun
|
|
13
|
-
|
|
13
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
|
|
14
|
+
HashMap()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
companion object {
|
|
18
|
+
init {
|
|
19
|
+
VisionCameraSpoofDetectorOnLoad.initializeNative()
|
|
20
|
+
}
|
|
14
21
|
}
|
|
15
22
|
}
|
package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorFactory.kt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.margelo.nitro.camera.spoofdetector
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Keep
|
|
4
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
5
|
+
import com.margelo.nitro.camera.HybridCameraOutputSpec
|
|
6
|
+
|
|
7
|
+
@DoNotStrip
|
|
8
|
+
@Keep
|
|
9
|
+
class HybridSpoofDetectorFactory : HybridSpoofDetectorFactorySpec() {
|
|
10
|
+
@DoNotStrip
|
|
11
|
+
@Keep
|
|
12
|
+
override fun createSpoofDetectorOutput(options: SpoofDetectorOutputOptions): HybridCameraOutputSpec {
|
|
13
|
+
return HybridSpoofDetectorOutput(options)
|
|
14
|
+
}
|
|
15
|
+
}
|