react-native-vision-camera-spoof-detector 1.0.1 → 1.0.3

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 (133) hide show
  1. package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_deps +0 -0
  2. package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_log +125 -125
  3. package/android/.cxx/Debug/2a1it684/arm64-v8a/CMakeFiles/CMakeOutput.log +22 -22
  4. package/android/.cxx/Debug/2a1it684/arm64-v8a/configure_fingerprint.bin +15 -15
  5. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_deps +0 -0
  6. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_log +176 -119
  7. package/android/.cxx/Debug/2a1it684/armeabi-v7a/CMakeFiles/CMakeOutput.log +20 -20
  8. package/android/.cxx/Debug/2a1it684/armeabi-v7a/configure_fingerprint.bin +15 -15
  9. package/android/.cxx/Debug/2a1it684/x86/.ninja_deps +0 -0
  10. package/android/.cxx/Debug/2a1it684/x86/.ninja_log +164 -111
  11. package/android/.cxx/Debug/2a1it684/x86/CMakeFiles/CMakeOutput.log +20 -20
  12. package/android/.cxx/Debug/2a1it684/x86/configure_fingerprint.bin +15 -15
  13. package/android/.cxx/Debug/2a1it684/x86_64/.ninja_deps +0 -0
  14. package/android/.cxx/Debug/2a1it684/x86_64/.ninja_log +109 -109
  15. package/android/.cxx/Debug/2a1it684/x86_64/CMakeFiles/CMakeOutput.log +20 -20
  16. package/android/.cxx/Debug/2a1it684/x86_64/configure_fingerprint.bin +15 -15
  17. package/android/build/.transforms/4ebf3dd0dca4f83fe1776360e21ded81/transformed/classes/classes_dex/classes.dex +0 -0
  18. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/AntiSpoofingResult.dex +0 -0
  19. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/ByteBufferPool$Companion.dex +0 -0
  20. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/ByteBufferPool.dex +0 -0
  21. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofFrameProcessor.dex +0 -0
  22. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.dex +0 -0
  23. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.dex +0 -0
  24. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.dex +0 -0
  25. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced.dex +0 -0
  26. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/JpegEncoder$Companion.dex +0 -0
  27. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/JpegEncoder.dex +0 -0
  28. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/YuvToFloatArrayConverter.dex +0 -0
  29. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  30. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/build_stdout_targets.txt +45 -45
  31. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_715_timing.txt +9 -0
  32. package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/build_stdout_targets.txt +46 -46
  33. package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/{generate_cxx_metadata_9843_timing.txt → generate_cxx_metadata_716_timing.txt} +1 -1
  34. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/build_stdout_targets.txt +43 -43
  35. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/{generate_cxx_metadata_9843_timing.txt → generate_cxx_metadata_716_timing.txt} +1 -1
  36. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/build_stdout_targets.txt +37 -37
  37. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_714_timing.txt +9 -0
  38. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  39. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/AntiSpoofingResult.class +0 -0
  40. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/ByteBufferPool$Companion.class +0 -0
  41. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/ByteBufferPool.class +0 -0
  42. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofFrameProcessor.class +0 -0
  43. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.class +0 -0
  44. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.class +0 -0
  45. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
  46. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
  47. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/JpegEncoder$Companion.class +0 -0
  48. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/JpegEncoder.class +0 -0
  49. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
  50. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  109. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  110. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/AntiSpoofingResult.class +0 -0
  111. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/ByteBufferPool$Companion.class +0 -0
  112. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/ByteBufferPool.class +0 -0
  113. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofFrameProcessor.class +0 -0
  114. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.class +0 -0
  115. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.class +0 -0
  116. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
  117. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
  118. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/JpegEncoder$Companion.class +0 -0
  119. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/JpegEncoder.class +0 -0
  120. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
  121. package/android/src/main/java/com/faceantispoof/ByteBufferPool.kt +68 -0
  122. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofFrameProcessor.kt +2 -1
  123. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +367 -50
  124. package/android/src/main/java/com/faceantispoof/JpegEncoder.kt +204 -0
  125. package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +62 -0
  126. package/package.json +3 -3
  127. package/CHANGELOG.md +0 -48
  128. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_9843_timing.txt +0 -9
  129. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_9843_timing.txt +0 -9
  130. /package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{index-2025-11-18T10-25-37-0358.json → index-2025-11-24T09-46-02-0941.json} +0 -0
  131. /package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{index-2025-11-18T10-12-50-0571.json → index-2025-11-18T18-09-57-0510.json} +0 -0
  132. /package/android/.cxx/Debug/2a1it684/x86/.cmake/api/v1/reply/{index-2025-11-18T10-13-00-0335.json → index-2025-11-18T18-10-04-0841.json} +0 -0
  133. /package/android/.cxx/Debug/2a1it684/x86_64/.cmake/api/v1/reply/{index-2025-11-18T10-26-26-0673.json → index-2025-11-24T09-46-22-0202.json} +0 -0
@@ -8,17 +8,19 @@ import java.io.IOException
8
8
  import java.nio.MappedByteBuffer
9
9
  import java.nio.channels.FileChannel
10
10
  import java.io.FileInputStream
11
- import kotlin.math.abs
11
+ import kotlin.math.*
12
12
  import java.nio.ByteBuffer
13
13
  import java.nio.ByteOrder
14
- import java.util.HashMap
14
+ import java.util.*
15
15
  import android.util.Log
16
+ import kotlin.collections.ArrayList
17
+ import android.util.Base64
16
18
 
17
19
  class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
18
20
 
19
21
  companion object {
20
22
  const val INPUT_IMAGE_SIZE = 256
21
- const val THRESHOLD = 0.2f
23
+ const val BASE_THRESHOLD = 0.2f
22
24
  const val ROUTE_INDEX = 6
23
25
  const val LAPLACE_THRESHOLD = 50
24
26
  const val LAPLACIAN_THRESHOLD = 1000
@@ -29,6 +31,16 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
29
31
  const val ACCELERATOR_CPU = "CPU"
30
32
  const val ACCELERATOR_GPU = "GPU"
31
33
  const val ACCELERATOR_NNAPI = "NNAPI"
34
+
35
+ // Advanced spoof detection constants
36
+ private const val HISTORY_SIZE = 10
37
+ private const val TEMPORAL_CONSISTENCY_THRESHOLD = 0.08f
38
+ private const val SCORE_VARIANCE_THRESHOLD = 0.02f
39
+ private const val MIN_FRAMES_FOR_ANALYSIS = 5
40
+ private const val DYNAMIC_THRESHOLD_MULTIPLIER = 1.3f
41
+ private const val TEXTURE_COMPLEXITY_THRESHOLD = 800
42
+ private const val COLOR_SATURATION_THRESHOLD = 0.4f
43
+ private const val DEPTH_VARIATION_THRESHOLD = 0.1f
32
44
  }
33
45
 
34
46
  private var interpreter: Interpreter? = null
@@ -48,6 +60,21 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
48
60
  private var grayscaleBuffer: IntArray? = null
49
61
  private val yuvConverter = YuvToFloatArrayConverter()
50
62
 
63
+ // Advanced spoof detection fields
64
+ private val scoreHistory = LinkedList<Float>()
65
+ private val laplacianHistory = LinkedList<Int>()
66
+ private var frameCount = 0
67
+ private var adaptiveThreshold = BASE_THRESHOLD
68
+ private var environmentAssessment = EnvironmentAssessment()
69
+
70
+ data class EnvironmentAssessment(
71
+ var avgBrightness: Float = 0f,
72
+ var brightnessVariance: Float = 0f,
73
+ var estimatedDistance: Float = 0f,
74
+ var lightingConsistency: Float = 0f,
75
+ var lastAssessmentTime: Long = 0
76
+ )
77
+
51
78
  init {
52
79
  initializeInterpreter()
53
80
  }
@@ -58,13 +85,11 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
58
85
  val options = Interpreter.Options()
59
86
  options.setNumThreads(4)
60
87
 
61
- // Try to add GPU delegate first
62
88
  if (tryAddGpuDelegate(options)) {
63
89
  currentAccelerator = ACCELERATOR_GPU
64
90
  Log.i(TAG, "GPU delegate enabled successfully")
65
91
  }
66
92
  else {
67
- // GPU not available, enable NNAPI through interpreter options
68
93
  try {
69
94
  options.setUseNNAPI(true)
70
95
  currentAccelerator = ACCELERATOR_NNAPI
@@ -77,12 +102,10 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
77
102
 
78
103
  interpreter = Interpreter(modelBuffer, options)
79
104
 
80
- // Try to find output indices once and prepare outputs map
81
105
  try {
82
106
  outputIndex0 = interpreter!!.getOutputIndex("Identity")
83
107
  outputIndex1 = interpreter!!.getOutputIndex("Identity_1")
84
108
  } catch (e: Exception) {
85
- // Keep defaults (0 and 1) if named outputs not available
86
109
  Log.w(TAG, "Could not resolve named output indices, using defaults: ${e.message}")
87
110
  }
88
111
 
@@ -97,13 +120,8 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
97
120
  }
98
121
  }
99
122
 
100
- /**
101
- * Try to enable GPU delegate for modern devices
102
- * Returns true if GPU delegate was successfully added
103
- */
104
123
  private fun tryAddGpuDelegate(options: Interpreter.Options): Boolean {
105
124
  return try {
106
- // Check if device supports GPU delegate
107
125
  val compatibilityList = CompatibilityList()
108
126
  if (compatibilityList.isDelegateSupportedOnThisDevice) {
109
127
  gpuDelegate = GpuDelegate(compatibilityList.bestOptionsForThisDevice)
@@ -122,9 +140,6 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
122
140
  }
123
141
  }
124
142
 
125
- /**
126
- * Get the currently active accelerator type
127
- */
128
143
  fun getAccelerator(): String = currentAccelerator
129
144
 
130
145
  fun advancedAntiSpoofing(nv21: ByteArray, width: Int, height: Int): AntiSpoofingResult {
@@ -132,58 +147,107 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
132
147
  return AntiSpoofingResult(
133
148
  neuralNetworkScore = 0f,
134
149
  laplacianScore = 0,
150
+ textureComplexity = 0,
151
+ temporalConsistency = 0f,
152
+ colorSaturation = 0f,
135
153
  combinedScore = 0f,
136
154
  isLive = false,
137
- label = "Not Initialized"
155
+ confidence = 0f,
156
+ label = "Not Initialized",
157
+ spoofType = "UNKNOWN"
138
158
  )
139
159
  }
140
160
 
141
- // Validate input parameters
142
- if (nv21 == null || nv21.isEmpty()) {
143
- Log.e(TAG, "Invalid NV21 buffer: null or empty")
144
- return AntiSpoofingResult(0f, 0, 0f, false, "Error: Invalid NV21 buffer")
145
- }
146
-
147
- if (width <= 0 || height <= 0) {
148
- Log.e(TAG, "Invalid frame dimensions: width=$width, height=$height")
149
- return AntiSpoofingResult(0f, 0, 0f, false, "Error: Invalid dimensions")
161
+ // Enhanced input validation
162
+ if (nv21.isEmpty()) {
163
+ return AntiSpoofingResult(0f, 0, 0, 0f, 0f, 0f, false, 0f, "Error: Invalid NV21 buffer", "INVALID_INPUT")
150
164
  }
151
165
 
152
166
  val expectedSize = width * height + (width * height) / 2
153
167
  if (nv21.size < expectedSize) {
154
- Log.e(TAG, "NV21 buffer too small: expected >= $expectedSize, got ${nv21.size}")
155
- return AntiSpoofingResult(0f, 0, 0f, false, "Error: Buffer size mismatch")
168
+ return AntiSpoofingResult(0f, 0, 0, 0f, 0f, 0f, false, 0f, "Error: Buffer size mismatch", "INVALID_INPUT")
156
169
  }
157
170
 
158
171
  return try {
172
+ // Multiple detection techniques
159
173
  val nnScore = neuralNetworkAntiSpoofing(nv21, width, height)
160
174
  val laplacianScore = laplacian(nv21, width, height)
175
+ val textureScore = calculateTextureComplexity(nv21, width, height)
176
+ val colorSat = calculateColorSaturation(nv21, width, height)
177
+ val temporalConsistency = calculateTemporalConsistency(nnScore)
178
+
179
+ // Update adaptive threshold based on environment
180
+ updateAdaptiveThreshold(nv21, width, height)
181
+
182
+ // Multi-factor spoof detection
183
+ val spoofDetection = detectSpoofType(
184
+ nnScore,
185
+ laplacianScore,
186
+ textureScore,
187
+ colorSat,
188
+ temporalConsistency
189
+ )
190
+
191
+ val confidence = calculateConfidence(nnScore, laplacianScore, textureScore, temporalConsistency)
192
+ val combinedScore = calculateCombinedScore(nnScore, laplacianScore, textureScore, temporalConsistency)
193
+
194
+ // Final decision with multiple safeguards
195
+ val isLive = isLiveFace(
196
+ nnScore,
197
+ laplacianScore,
198
+ textureScore,
199
+ colorSat,
200
+ temporalConsistency,
201
+ spoofDetection
202
+ )
161
203
 
162
- val isLive = nnScore < THRESHOLD && laplacianScore > LAPLACIAN_THRESHOLD
163
- val combinedScore = calculateCombinedScore(nnScore, laplacianScore)
204
+ // Generate 640×640 JPEG Base64 for Recognition API when face is live
205
+ var recognitionImageBase64: String? = null
206
+ val DEBUG_FORCE_BASE64 = false // Set to true to force Base64 generation for testing
207
+
208
+ if (isLive || DEBUG_FORCE_BASE64) {
209
+ Log.d(TAG, "🔍 isLive=$isLive (DEBUG_FORCE_BASE64=$DEBUG_FORCE_BASE64) - generating 640x640 JPEG Base64...")
210
+ try {
211
+ recognitionImageBase64 = generate640x640JpegBase64(nv21, width, height)
212
+ if (recognitionImageBase64 != null) {
213
+ Log.d(TAG, " Generated 640x640 JPEG Base64 for recognition API (size: ${recognitionImageBase64.length} bytes)")
214
+ } else {
215
+ Log.w(TAG, " generate640x640JpegBase64 returned null")
216
+ }
217
+ } catch (e: Exception) {
218
+ Log.w(TAG, " Failed to generate 640x640 JPEG Base64: ${e.message}", e)
219
+ // Continue without the image - it's not critical
220
+ }
221
+ } else {
222
+ Log.d(TAG, " isLive is FALSE - skipping Base64 generation")
223
+ }
164
224
 
165
225
  AntiSpoofingResult(
166
226
  neuralNetworkScore = nnScore,
167
227
  laplacianScore = laplacianScore,
228
+ textureComplexity = textureScore,
229
+ temporalConsistency = temporalConsistency,
230
+ colorSaturation = colorSat,
168
231
  combinedScore = combinedScore,
169
232
  isLive = isLive,
170
- label = if (isLive) "Live Face" else "Spoof Face"
233
+ confidence = confidence,
234
+ label = if (isLive) "Live Face" else "Spoof Face: ${spoofDetection.type}",
235
+ spoofType = spoofDetection.type,
236
+ recognitionImageBase64 = recognitionImageBase64
171
237
  )
172
238
  } catch (e: Exception) {
173
239
  Log.e(TAG, "Error in advancedAntiSpoofing", e)
174
- AntiSpoofingResult(0f, 0, 0f, false, "Error: ${e.message}")
240
+ AntiSpoofingResult(0f, 0, 0, 0f, 0f, 0f, false, 0f, "Error: ${e.message}", "PROCESSING_ERROR")
175
241
  }
176
242
  }
177
243
 
178
244
  private fun neuralNetworkAntiSpoofing(nv21: ByteArray, width: Int, height: Int): Float {
179
245
  return try {
180
- // Validate NV21 buffer before processing
181
246
  val expectedSize = width * height + (width * height) / 2
182
247
  if (nv21.size < expectedSize) {
183
248
  throw IllegalArgumentException("NV21 buffer size mismatch: expected $expectedSize, got ${nv21.size}")
184
249
  }
185
250
 
186
- // Convert NV21 YUV to RGB float array, resized to model input size
187
251
  if (rgbFloatBuffer == null || rgbFloatBuffer!!.size < INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE * 3) {
188
252
  rgbFloatBuffer = FloatArray(INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE * 3)
189
253
  }
@@ -197,32 +261,87 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
197
261
  throw Exception("RGB conversion returned empty array")
198
262
  }
199
263
 
200
- // Fill reusable inputBuffer with normalized floats (row-major RGB)
201
264
  inputBuffer.rewind()
202
265
  for (i in 0 until (INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE * 3)) {
203
266
  inputBuffer.putFloat(rgbFloats[i])
204
267
  }
205
268
  inputBuffer.rewind()
206
269
 
207
- // Validate interpreter
208
270
  if (interpreter == null) {
209
271
  throw Exception("Interpreter is null")
210
272
  }
211
273
 
212
- // Reuse preallocated outputs map and arrays
213
274
  outputs[outputIndex0] = clssPred
214
275
  outputs[outputIndex1] = leafNodeMask
215
276
 
216
- // Run model with ByteBuffer input (single input)
217
277
  interpreter!!.runForMultipleInputsOutputs(arrayOf<Any>(inputBuffer), outputs)
218
278
 
219
- leafScore1(clssPred, leafNodeMask)
279
+ val rawScore = leafScore1(clssPred, leafNodeMask)
280
+
281
+ // Apply non-linear scaling to make high scores more sensitive
282
+ applyNonLinearScaling(rawScore)
220
283
  } catch (e: Exception) {
221
284
  Log.e(TAG, "Error in neuralNetworkAntiSpoofing: ${e.message}", e)
222
285
  throw e
223
286
  }
224
287
  }
225
288
 
289
+ private fun applyNonLinearScaling(score: Float): Float {
290
+ // Exponential scaling to make scores > 0.1 much more sensitive
291
+ return if (score > 0.1f) {
292
+ score * (1 + (score - 0.1f) * 2.0f) // Amplify higher scores
293
+ } else {
294
+ score
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Generate 640×640 JPEG image as Base64 string for Recognition API.
300
+ * Optimized for high-speed encoding without intermediate allocations.
301
+ *
302
+ * @param nv21 NV21 encoded YUV frame
303
+ * @param width Original frame width
304
+ * @param height Original frame height
305
+ * @return Base64 encoded JPEG string (640×640), or null if generation fails
306
+ */
307
+ private fun generate640x640JpegBase64(nv21: ByteArray, width: Int, height: Int): String? {
308
+ return try {
309
+ val TARGET_SIZE = 640
310
+ Log.d(TAG, "Starting 640x640 JPEG generation. Input: ${width}x${height}, NV21 size: ${nv21.size}")
311
+
312
+ // Convert NV21 to RGB byte array directly (640×640)
313
+ val rgbBytes = ByteArray(TARGET_SIZE * TARGET_SIZE * 3)
314
+ Log.d(TAG, "Allocated RGB buffer: ${rgbBytes.size} bytes")
315
+
316
+ val startConvert = System.currentTimeMillis()
317
+ val convertedRgb = yuvConverter.nv21ToRgbByteArray(
318
+ nv21, width, height,
319
+ TARGET_SIZE, TARGET_SIZE,
320
+ rgbBytes
321
+ )
322
+ val convertTime = System.currentTimeMillis() - startConvert
323
+ Log.d(TAG, "YUV to RGB conversion completed in ${convertTime}ms. RGB data: ${convertedRgb.size} bytes")
324
+
325
+ // Encode RGB to JPEG Base64 with quality 85
326
+ val startJpeg = System.currentTimeMillis()
327
+ val base64Result = JpegEncoder.rgbToJpegBase64(convertedRgb, TARGET_SIZE, TARGET_SIZE, quality = 85)
328
+ val jpegTime = System.currentTimeMillis() - startJpeg
329
+
330
+ if (base64Result != null) {
331
+ Log.d(TAG, "JPEG encoding completed in ${jpegTime}ms. Base64 size: ${base64Result.length} bytes")
332
+ Log.d(TAG, " Generated 640x640 JPEG Base64 for recognition API (total: ${convertTime + jpegTime}ms)")
333
+ } else {
334
+ Log.w(TAG, " JpegEncoder returned null")
335
+ }
336
+
337
+ base64Result
338
+ } catch (e: Exception) {
339
+ Log.e(TAG, " Error generating 640x640 JPEG Base64: ${e.message}", e)
340
+ e.printStackTrace()
341
+ null
342
+ }
343
+ }
344
+
226
345
  private fun leafScore1(clssPred: Array<FloatArray>, leafNodeMask: Array<FloatArray>): Float {
227
346
  var score = 0f
228
347
  for (i in 0 until 8) {
@@ -235,19 +354,17 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
235
354
  return try {
236
355
  calculateLaplacianScore(nv21, width, height)
237
356
  } catch (e: Exception) {
238
- android.util.Log.e(TAG, "Error in laplacian calculation", e)
357
+ Log.e(TAG, "Error in laplacian calculation", e)
239
358
  throw e
240
359
  }
241
360
  }
242
361
 
243
362
  private fun calculateLaplacianScore(nv21: ByteArray, width: Int, height: Int): Int {
244
- // Validate NV21 buffer
245
363
  val expectedSize = width * height + (width * height) / 2
246
364
  if (nv21.size < expectedSize) {
247
365
  throw IllegalArgumentException("NV21 buffer size mismatch for Laplacian: expected $expectedSize, got ${nv21.size}")
248
366
  }
249
367
 
250
- // Convert to grayscale intensity array (resized to model input size)
251
368
  if (grayscaleBuffer == null || grayscaleBuffer!!.size < INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE) {
252
369
  grayscaleBuffer = IntArray(INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE)
253
370
  }
@@ -263,7 +380,6 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
263
380
  }
264
381
 
265
382
  var score = 0
266
- // Apply 3x3 laplacian kernel without extra allocation
267
383
  for (y in 1 until INPUT_IMAGE_SIZE - 1) {
268
384
  val rowOffset = y * INPUT_IMAGE_SIZE
269
385
  for (x in 1 until INPUT_IMAGE_SIZE - 1) {
@@ -280,9 +396,199 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
280
396
  return score
281
397
  }
282
398
 
283
- private fun calculateCombinedScore(neuralScore: Float, laplacianScore: Int): Float {
399
+ private fun calculateTextureComplexity(nv21: ByteArray, width: Int, height: Int): Int {
400
+ // Enhanced texture analysis using multiple filters
401
+ if (grayscaleBuffer == null || grayscaleBuffer!!.size < INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE) {
402
+ grayscaleBuffer = IntArray(INPUT_IMAGE_SIZE * INPUT_IMAGE_SIZE)
403
+ }
404
+
405
+ val grayscale = yuvConverter.nv21ToGrayscaleIntArray(
406
+ nv21, width, height,
407
+ INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE,
408
+ grayscaleBuffer
409
+ ) ?: return 0
410
+
411
+ var complexity = 0
412
+
413
+ // Sobel edge detection for texture analysis
414
+ for (y in 1 until INPUT_IMAGE_SIZE - 1) {
415
+ val rowOffset = y * INPUT_IMAGE_SIZE
416
+ for (x in 1 until INPUT_IMAGE_SIZE - 1) {
417
+ val gx = (-1 * grayscale[rowOffset - INPUT_IMAGE_SIZE + x - 1] +
418
+ 1 * grayscale[rowOffset - INPUT_IMAGE_SIZE + x + 1] +
419
+ -2 * grayscale[rowOffset + x - 1] +
420
+ 2 * grayscale[rowOffset + x + 1] +
421
+ -1 * grayscale[rowOffset + INPUT_IMAGE_SIZE + x - 1] +
422
+ 1 * grayscale[rowOffset + INPUT_IMAGE_SIZE + x + 1])
423
+
424
+ val gy = (-1 * grayscale[rowOffset - INPUT_IMAGE_SIZE + x - 1] +
425
+ -2 * grayscale[rowOffset - INPUT_IMAGE_SIZE + x] +
426
+ -1 * grayscale[rowOffset - INPUT_IMAGE_SIZE + x + 1] +
427
+ 1 * grayscale[rowOffset + INPUT_IMAGE_SIZE + x - 1] +
428
+ 2 * grayscale[rowOffset + INPUT_IMAGE_SIZE + x] +
429
+ 1 * grayscale[rowOffset + INPUT_IMAGE_SIZE + x + 1])
430
+
431
+ val gradient = sqrt((gx * gx + gy * gy).toDouble()).toInt()
432
+ if (gradient > 50) complexity++
433
+ }
434
+ }
435
+
436
+ return complexity
437
+ }
438
+
439
+ private fun calculateColorSaturation(nv21: ByteArray, width: Int, height: Int): Float {
440
+ // Calculate average color saturation to detect printed attacks
441
+ val uvStart = width * height
442
+ var totalSaturation = 0f
443
+ var sampleCount = 0
444
+
445
+ for (i in uvStart until nv21.size step 2) {
446
+ val u = (nv21[i].toInt() and 0xFF) - 128
447
+ val v = (nv21[i + 1].toInt() and 0xFF) - 128
448
+ val saturation = sqrt((u * u + v * v).toDouble()).toFloat()
449
+ totalSaturation += saturation
450
+ sampleCount++
451
+ }
452
+
453
+ return if (sampleCount > 0) totalSaturation / sampleCount / 128.0f else 0f
454
+ }
455
+
456
+ private fun calculateTemporalConsistency(currentScore: Float): Float {
457
+ // Analyze score consistency over time
458
+ scoreHistory.addFirst(currentScore)
459
+ if (scoreHistory.size > HISTORY_SIZE) {
460
+ scoreHistory.removeLast()
461
+ }
462
+
463
+ if (scoreHistory.size < MIN_FRAMES_FOR_ANALYSIS) {
464
+ return 1.0f // Maximum consistency initially
465
+ }
466
+
467
+ // Calculate variance of recent scores
468
+ val mean = scoreHistory.average().toFloat()
469
+ val variance = scoreHistory.map { (it - mean) * (it - mean) }.average().toFloat()
470
+
471
+ // Convert variance to consistency score (lower variance = higher consistency)
472
+ return max(0f, 1.0f - (variance / SCORE_VARIANCE_THRESHOLD))
473
+ }
474
+
475
+ private fun updateAdaptiveThreshold(nv21: ByteArray, width: Int, height: Int) {
476
+ // Adjust threshold based on environmental factors
477
+ val brightness = calculateBrightness(nv21, width, height)
478
+ val currentTime = System.currentTimeMillis()
479
+
480
+ if (currentTime - environmentAssessment.lastAssessmentTime > 1000) { // Assess every second
481
+ environmentAssessment.avgBrightness = brightness
482
+ environmentAssessment.lastAssessmentTime = currentTime
483
+
484
+ // Make threshold more strict in good lighting conditions
485
+ adaptiveThreshold = if (brightness > 0.6f) {
486
+ BASE_THRESHOLD * 0.8f // Stricter in good light
487
+ } else if (brightness < 0.3f) {
488
+ BASE_THRESHOLD * 1.2f // Slightly more lenient in poor light
489
+ } else {
490
+ BASE_THRESHOLD
491
+ }
492
+ }
493
+ }
494
+
495
+ private fun calculateBrightness(nv21: ByteArray, width: Int, height: Int): Float {
496
+ // Calculate average brightness from Y channel
497
+ var totalLuma = 0f
498
+ for (i in 0 until width * height) {
499
+ totalLuma += (nv21[i].toInt() and 0xFF) / 255.0f
500
+ }
501
+ return totalLuma / (width * height)
502
+ }
503
+
504
+ data class SpoofDetectionResult(
505
+ val type: String,
506
+ val confidence: Float
507
+ )
508
+
509
+ private fun detectSpoofType(
510
+ nnScore: Float,
511
+ laplacianScore: Int,
512
+ textureScore: Int,
513
+ colorSat: Float,
514
+ temporalConsistency: Float
515
+ ): SpoofDetectionResult {
516
+
517
+ // Detect specific spoof types based on feature patterns
518
+ return when {
519
+ // Printed photo detection (low texture, low laplacian)
520
+ textureScore < TEXTURE_COMPLEXITY_THRESHOLD * 0.6 &&
521
+ laplacianScore < LAPLACIAN_THRESHOLD * 0.7 ->
522
+ SpoofDetectionResult("PRINTED_PHOTO", 0.8f)
523
+
524
+ // Screen replay detection (high color saturation, medium texture)
525
+ colorSat > COLOR_SATURATION_THRESHOLD * 1.2 &&
526
+ textureScore > TEXTURE_COMPLEXITY_THRESHOLD * 0.8 ->
527
+ SpoofDetectionResult("SCREEN_REPLAY", 0.7f)
528
+
529
+ // 3D mask detection (medium scores across all features)
530
+ nnScore > BASE_THRESHOLD * 1.5 &&
531
+ temporalConsistency < 0.7f ->
532
+ SpoofDetectionResult("3D_MASK", 0.6f)
533
+
534
+ // General spoof
535
+ nnScore > BASE_THRESHOLD ->
536
+ SpoofDetectionResult("GENERAL_SPOOF", 0.9f)
537
+
538
+ else -> SpoofDetectionResult("UNKNOWN", 0.5f)
539
+ }
540
+ }
541
+
542
+ private fun calculateConfidence(
543
+ nnScore: Float,
544
+ laplacianScore: Int,
545
+ textureScore: Int,
546
+ temporalConsistency: Float
547
+ ): Float {
548
+ val nnConfidence = 1.0f - (nnScore / BASE_THRESHOLD).coerceIn(0f, 1f)
549
+ val textureConfidence = (textureScore.toFloat() / TEXTURE_COMPLEXITY_THRESHOLD).coerceIn(0f, 1f)
550
+ val laplacianConfidence = (laplacianScore.toFloat() / LAPLACIAN_THRESHOLD).coerceIn(0f, 1f)
551
+
552
+ return (nnConfidence * 0.4f + textureConfidence * 0.3f +
553
+ laplacianConfidence * 0.2f + temporalConsistency * 0.1f)
554
+ }
555
+
556
+ private fun calculateCombinedScore(
557
+ neuralScore: Float,
558
+ laplacianScore: Int,
559
+ textureScore: Int,
560
+ temporalConsistency: Float
561
+ ): Float {
284
562
  val normalizedLaplacian = laplacianScore.toFloat() / LAPLACIAN_THRESHOLD
285
- return (1 - neuralScore) * 0.7f + (normalizedLaplacian * 0.3f)
563
+ val normalizedTexture = textureScore.toFloat() / TEXTURE_COMPLEXITY_THRESHOLD
564
+
565
+ return (1 - neuralScore) * 0.5f +
566
+ normalizedLaplacian * 0.25f +
567
+ normalizedTexture * 0.15f +
568
+ temporalConsistency * 0.1f
569
+ }
570
+
571
+ private fun isLiveFace(
572
+ nnScore: Float,
573
+ laplacianScore: Int,
574
+ textureScore: Int,
575
+ colorSat: Float,
576
+ temporalConsistency: Float,
577
+ spoofDetection: SpoofDetectionResult
578
+ ): Boolean {
579
+
580
+ // Multi-layered spoof detection
581
+ val layer1 = nnScore < adaptiveThreshold
582
+ val layer2 = laplacianScore > LAPLACIAN_THRESHOLD
583
+ val layer3 = textureScore > TEXTURE_COMPLEXITY_THRESHOLD
584
+ val layer4 = temporalConsistency > 0.7f
585
+ val layer5 = colorSat < COLOR_SATURATION_THRESHOLD * 1.5f
586
+
587
+ // Require majority of layers to pass, with neural network score being most important
588
+ val passedLayers = listOf(layer1, layer2, layer3, layer4, layer5).count { it }
589
+
590
+ // Stricter requirement: must pass at least 4 out of 5 layers, including neural network
591
+ return passedLayers >= 4 && layer1
286
592
  }
287
593
 
288
594
  @Throws(IOException::class)
@@ -297,23 +603,28 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
297
603
  fileDescriptor.declaredLength
298
604
  )
299
605
  } catch (e: Exception) {
300
- android.util.Log.e(TAG, "Error loading model file", e)
606
+ Log.e(TAG, "Error loading model file", e)
301
607
  throw e
302
608
  }
303
609
  }
304
610
 
305
611
  fun isInitialized(): Boolean = isInitialized
306
612
 
613
+ fun resetHistory() {
614
+ scoreHistory.clear()
615
+ laplacianHistory.clear()
616
+ frameCount = 0
617
+ adaptiveThreshold = BASE_THRESHOLD
618
+ }
619
+
307
620
  fun close() {
308
621
  try {
309
622
  interpreter?.close()
310
623
  interpreter = null
311
-
312
- // Clean up GPU delegate
313
624
  gpuDelegate?.close()
314
625
  gpuDelegate = null
315
-
316
626
  currentAccelerator = ACCELERATOR_CPU
627
+ resetHistory()
317
628
  Log.i(TAG, "Interpreter and delegates closed successfully")
318
629
  } catch (e: Exception) {
319
630
  Log.e(TAG, "Error closing interpreter", e)
@@ -324,7 +635,13 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
324
635
  data class AntiSpoofingResult(
325
636
  val neuralNetworkScore: Float,
326
637
  val laplacianScore: Int,
638
+ val textureComplexity: Int,
639
+ val temporalConsistency: Float,
640
+ val colorSaturation: Float,
327
641
  val combinedScore: Float,
328
642
  val isLive: Boolean,
329
- val label: String
330
- )
643
+ val confidence: Float,
644
+ val label: String,
645
+ val spoofType: String,
646
+ val recognitionImageBase64: String? = null
647
+ )