react-native-vision-camera-spoof-detector 1.1.19 → 1.1.21

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.
@@ -55,31 +55,60 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
55
55
  private fun initializeInterpreter() {
56
56
  try {
57
57
  val modelBuffer = loadModelFile(assetManager, MODEL_FILE)
58
- val options = Interpreter.Options()
59
-
60
- // Use optimal settings
61
- val optimalThreads = 4 // Fixed as in working version
62
- options.setNumThreads(optimalThreads)
63
-
64
- currentAccelerator = ACCELERATOR_CPU
65
- Log.i(TAG, "CPU-ONLY MODE - Using $optimalThreads threads")
66
-
67
- interpreter = Interpreter(modelBuffer, options)
68
-
58
+ val optimalThreads = 4
59
+ val compatibilityList = CompatibilityList()
60
+ var selectedAccelerator = ACCELERATOR_CPU
61
+
62
+ try {
63
+ if (compatibilityList.isDelegateSupportedOnThisDevice) {
64
+ val delegate = GpuDelegate(compatibilityList.bestOptionsForThisDevice)
65
+ gpuDelegate = delegate
66
+ val options = Interpreter.Options().apply {
67
+ setNumThreads(optimalThreads)
68
+ addDelegate(delegate)
69
+ }
70
+ interpreter = Interpreter(modelBuffer, options)
71
+ selectedAccelerator = ACCELERATOR_GPU
72
+ Log.i(TAG, "Face Anti-Spoof initialized | GPU delegate enabled | Threads: $optimalThreads")
73
+ } else {
74
+ throw IllegalStateException("GPU delegate not supported")
75
+ }
76
+ } catch (e: Exception) {
77
+ Log.w(TAG, "GPU delegate unavailable or failed: ${e.message}", e)
78
+ try {
79
+ val nnapiOptions = org.tensorflow.lite.nnapi.NnApiDelegate()
80
+ val options = Interpreter.Options().apply {
81
+ setNumThreads(optimalThreads)
82
+ addDelegate(nnapiOptions)
83
+ }
84
+ interpreter = Interpreter(modelBuffer, options)
85
+ selectedAccelerator = ACCELERATOR_NNAPI
86
+ Log.i(TAG, "Face Anti-Spoof initialized | NNAPI delegate enabled | Threads: $optimalThreads")
87
+ } catch (nnapiError: Exception) {
88
+ Log.w(TAG, "NNAPI delegate unavailable or failed: ${nnapiError.message}", nnapiError)
89
+ val options = Interpreter.Options().apply {
90
+ setNumThreads(optimalThreads)
91
+ }
92
+ interpreter = Interpreter(modelBuffer, options)
93
+ selectedAccelerator = ACCELERATOR_CPU
94
+ Log.i(TAG, "Face Anti-Spoof initialized | CPU fallback enabled | Threads: $optimalThreads")
95
+ }
96
+ }
97
+
98
+ currentAccelerator = selectedAccelerator
99
+
69
100
  try {
70
101
  outputIndex0 = interpreter!!.getOutputIndex("Identity")
71
102
  outputIndex1 = interpreter!!.getOutputIndex("Identity_1")
72
103
  } catch (e: Exception) {
73
104
  Log.w(TAG, "Could not resolve named output indices, using defaults: ${e.message}")
74
- // Fallback to default indices
75
105
  outputIndex0 = 0
76
106
  outputIndex1 = 1
77
107
  }
78
-
108
+
79
109
  outputs[outputIndex0] = clssPred
80
110
  outputs[outputIndex1] = leafNodeMask
81
111
  isInitialized = true
82
- Log.i(TAG, "Face Anti-Spoof initialized | CPU-ONLY MODE | Threads: $optimalThreads")
83
112
  } catch (e: Exception) {
84
113
  Log.e(TAG, "FAILED to load TFLite model: ${e.message}", e)
85
114
  isInitialized = false
@@ -13,6 +13,9 @@ import com.margelo.nitro.camera.extensions.setAllowDroppingLateFrames
13
13
  import com.margelo.nitro.camera.extensions.surfaceRotation
14
14
  import com.margelo.nitro.camera.public.NativeCameraOutput
15
15
  import com.faceantispoof.FaceAntiSpoofingAdvanced
16
+ import kotlin.math.abs
17
+ import kotlin.math.max
18
+ import kotlin.math.min
16
19
  import java.util.concurrent.Executors
17
20
  import java.util.concurrent.atomic.AtomicBoolean
18
21
 
@@ -32,10 +35,19 @@ class HybridSpoofDetectorOutput(
32
35
  private val executor = Executors.newSingleThreadExecutor()
33
36
  private val busy = AtomicBoolean(false)
34
37
  private val disposed = AtomicBoolean(false)
38
+ private val frameCounter = java.util.concurrent.atomic.AtomicInteger(0)
39
+ private var lastFrameBrightness = 0f
35
40
  private val detector = FaceAntiSpoofingAdvanced(
36
41
  NitroModules.applicationContext?.assets ?: error("FaceAntiSpoof: no application context")
37
42
  )
38
43
  private var imageAnalysis: ImageAnalysis? = null
44
+ private companion object {
45
+ private const val FRAME_EVALUATION_INTERVAL = 3
46
+ private const val MIN_FRAME_BRIGHTNESS = 24f
47
+ private const val MAX_FRAME_BRIGHTNESS = 220f
48
+ private const val MIN_VARIANCE_FOR_ANALYSIS = 1500f
49
+ private const val MIN_BRIGHTNESS_DELTA = 8f
50
+ }
39
51
 
40
52
  override fun createUseCase(
41
53
  mirrorMode: MirrorMode,
@@ -72,6 +84,10 @@ class HybridSpoofDetectorOutput(
72
84
  }
73
85
  try {
74
86
  val rgba = extractRgbaBytes(image)
87
+ if (!shouldProcessFrame(rgba, image.width, image.height)) {
88
+ return
89
+ }
90
+
75
91
  val result = detector.advancedAntiSpoofingRGBA(rgba, image.width, image.height)
76
92
  val isLive = result.neuralNetworkScore < FaceAntiSpoofingAdvanced.BASE_THRESHOLD &&
77
93
  result.laplacianScore > FaceAntiSpoofingAdvanced.LAPLACIAN_THRESHOLD
@@ -93,6 +109,50 @@ class HybridSpoofDetectorOutput(
93
109
  }
94
110
  }
95
111
 
112
+ private fun shouldProcessFrame(rgba: ByteArray, width: Int, height: Int): Boolean {
113
+ val frameIndex = frameCounter.incrementAndGet()
114
+ if (frameIndex % FRAME_EVALUATION_INTERVAL != 0) {
115
+ return false
116
+ }
117
+
118
+ val sampleStep = max(1, min(width, height) / 32)
119
+ var brightnessSum = 0L
120
+ var brightnessSquaredSum = 0L
121
+ var sampleCount = 0
122
+
123
+ for (y in 0 until height step sampleStep) {
124
+ val rowOffset = y * width * 4
125
+ for (x in 0 until width step sampleStep) {
126
+ val index = rowOffset + x * 4
127
+ if (index + 2 >= rgba.size) continue
128
+
129
+ val r = rgba[index].toInt() and 0xFF
130
+ val g = rgba[index + 1].toInt() and 0xFF
131
+ val b = rgba[index + 2].toInt() and 0xFF
132
+ val luma = (0.299 * r + 0.587 * g + 0.114 * b).toInt()
133
+
134
+ brightnessSum += luma.toLong()
135
+ brightnessSquaredSum += (luma.toLong() * luma.toLong())
136
+ sampleCount += 1
137
+ }
138
+ }
139
+
140
+ if (sampleCount == 0) {
141
+ return false
142
+ }
143
+
144
+ val avgBrightness = brightnessSum.toFloat() / sampleCount
145
+ val variance = (brightnessSquaredSum.toFloat() / sampleCount) - (avgBrightness * avgBrightness)
146
+ val brightnessDelta = kotlin.math.abs(avgBrightness - lastFrameBrightness)
147
+ lastFrameBrightness = avgBrightness
148
+
149
+ val brightnessOk = avgBrightness in MIN_FRAME_BRIGHTNESS..MAX_FRAME_BRIGHTNESS
150
+ val textureOk = variance >= MIN_VARIANCE_FOR_ANALYSIS
151
+ val motionLikely = brightnessDelta >= MIN_BRIGHTNESS_DELTA || lastFrameBrightness == 0f
152
+
153
+ return brightnessOk && textureOk && motionLikely
154
+ }
155
+
96
156
  private fun extractRgbaBytes(image: ImageProxy): ByteArray {
97
157
  val plane = image.planes.firstOrNull() ?: throw IllegalStateException("RGBA image has no planes")
98
158
  val rowStride = plane.rowStride
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vision-camera-spoof-detector",
3
- "version": "1.1.19",
3
+ "version": "1.1.21",
4
4
  "description": "High-performance face anti-spoofing and liveness detection module for React Native Vision Camera. Uses TensorFlow Lite with GPU acceleration and optimized YUV processing.",
5
5
  "homepage": "https://github.com/dpraful/react-native-vision-camera-spoof-detector",
6
6
  "repository": {