react-native-vision-camera-spoof-detector 1.1.16 → 1.1.18

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.
@@ -198,8 +198,10 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
198
198
  }
199
199
 
200
200
  return try {
201
- val nnScore = neuralNetworkAntiSpoofingRGBA(rgba, width, height).getOrThrow()
202
- val laplacianScore = laplacianFromRgba(rgba, width, height).getOrThrow()
201
+ val sampled = sampleRgbaToModelAndGray(rgba, width, height)
202
+ val nnResult = neuralNetworkAntiSpoofingSampled(sampled.rgbBuffer)
203
+ val nnScore = nnResult.getOrNull() ?: throw IllegalStateException(nnResult.exceptionOrNull()?.message ?: "NN inference failed")
204
+ val laplacianScore = calculateLaplacianFromGray(sampled.grayBuffer)
203
205
  val isLive = nnScore < BASE_THRESHOLD && laplacianScore > LAPLACIAN_THRESHOLD
204
206
  val combinedScore = calculateCombinedScore(nnScore, laplacianScore)
205
207
  val confidence = calculateConfidence(nnScore, laplacianScore)
@@ -282,6 +284,23 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
282
284
  }
283
285
  }
284
286
 
287
+ private fun neuralNetworkAntiSpoofingSampled(rgbBuffer: Array<Array<FloatArray>>): Result<Float> {
288
+ return try {
289
+ val input = arrayOf(rgbBuffer)
290
+ clssPred[0].fill(0f)
291
+ leafNodeMask[0].fill(0f)
292
+ outputs[outputIndex0] = clssPred
293
+ outputs[outputIndex1] = leafNodeMask
294
+ interpreter!!.runForMultipleInputsOutputs(arrayOf(input), outputs)
295
+ Log.i(TAG, "clss_pred: [${clssPred[0].joinToString()}]")
296
+ Log.i(TAG, "leaf_node_mask: [${leafNodeMask[0].joinToString()}]")
297
+ Result.success(leafScore1(clssPred, leafNodeMask))
298
+ } catch (e: Exception) {
299
+ Log.e(TAG, "Error in neuralNetworkAntiSpoofingSampled", e)
300
+ Result.failure(e)
301
+ }
302
+ }
303
+
285
304
  // FROM WORKING VERSION: leafScore1
286
305
  private fun leafScore1(clssPred: Array<FloatArray>, leafNodeMask: Array<FloatArray>): Float {
287
306
  var score = 0f
@@ -340,6 +359,40 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
340
359
  return floatValues
341
360
  }
342
361
 
362
+ private data class SampledFrame(
363
+ val rgbBuffer: Array<Array<FloatArray>>,
364
+ val grayBuffer: Array<IntArray>
365
+ )
366
+
367
+ private fun sampleRgbaToModelAndGray(
368
+ rgba: ByteArray,
369
+ srcWidth: Int,
370
+ srcHeight: Int
371
+ ): SampledFrame {
372
+ val targetWidth = INPUT_IMAGE_SIZE
373
+ val targetHeight = INPUT_IMAGE_SIZE
374
+ val rgbBuffer = Array(targetHeight) { Array(targetWidth) { FloatArray(3) } }
375
+ val grayBuffer = Array(targetHeight) { IntArray(targetWidth) }
376
+ val scaleX = srcWidth.toFloat() / targetWidth
377
+ val scaleY = srcHeight.toFloat() / targetHeight
378
+
379
+ for (targetY in 0 until targetHeight) {
380
+ val srcY = (targetY * scaleY).toInt().coerceIn(0, srcHeight - 1)
381
+ for (targetX in 0 until targetWidth) {
382
+ val srcX = (targetX * scaleX).toInt().coerceIn(0, srcWidth - 1)
383
+ val srcIndex = (srcY * srcWidth + srcX) * 4
384
+
385
+ val r = (rgba[srcIndex].toInt() and 0xFF) / 255f
386
+ val g = (rgba[srcIndex + 1].toInt() and 0xFF) / 255f
387
+ val b = (rgba[srcIndex + 2].toInt() and 0xFF) / 255f
388
+ rgbBuffer[targetY][targetX] = floatArrayOf(r, g, b)
389
+ grayBuffer[targetY][targetX] = (0.299f * rgba[srcIndex].toInt() + 0.587f * rgba[srcIndex + 1].toInt() + 0.114f * rgba[srcIndex + 2].toInt()).toInt()
390
+ }
391
+ }
392
+
393
+ return SampledFrame(rgbBuffer, grayBuffer)
394
+ }
395
+
343
396
  // FROM WORKING VERSION: laplacian with Bitmap
344
397
  private fun laplacian(bitmap: Bitmap): Result<Int> {
345
398
  return try {
@@ -100,42 +100,33 @@ class HybridSpoofDetectorOutput(
100
100
  val width = image.width
101
101
  val height = image.height
102
102
  val bytesPerPixel = 4
103
- val rowBytes = width * bytesPerPixel
104
- val rgba = ByteArray(rowBytes * height)
103
+ val outputSize = width * height * bytesPerPixel
104
+ val packed = ByteArray(outputSize)
105
105
  val buffer = plane.buffer.duplicate()
106
106
  buffer.order(java.nio.ByteOrder.nativeOrder())
107
107
 
108
- for (y in 0 until height) {
109
- val srcOffset = y * rowStride
110
- val dstOffset = y * rowBytes
111
- val copyLength = rowBytes.coerceAtMost(rowStride)
112
- buffer.position(srcOffset)
113
- val slice = ByteArray(copyLength)
114
- buffer.get(slice)
115
- System.arraycopy(slice, 0, rgba, dstOffset, copyLength)
108
+ if (pixelStride == bytesPerPixel && rowStride == width * bytesPerPixel) {
109
+ buffer.get(packed)
110
+ return packed
116
111
  }
117
112
 
118
- if (pixelStride != 4) {
119
- val packed = ByteArray(width * height * bytesPerPixel)
120
- var outputIndex = 0
121
- for (y in 0 until height) {
122
- val rowOffset = y * rowStride
123
- for (x in 0 until width) {
124
- val pixelOffset = rowOffset + x * pixelStride
125
- val r = buffer.get(pixelOffset).toInt() and 0xFF
126
- val g = buffer.get(pixelOffset + 1).toInt() and 0xFF
127
- val b = buffer.get(pixelOffset + 2).toInt() and 0xFF
128
- val a = buffer.get(pixelOffset + 3).toInt() and 0xFF
129
- packed[outputIndex++] = r.toByte()
130
- packed[outputIndex++] = g.toByte()
131
- packed[outputIndex++] = b.toByte()
132
- packed[outputIndex++] = a.toByte()
133
- }
113
+ var outputIndex = 0
114
+ for (y in 0 until height) {
115
+ val rowOffset = y * rowStride
116
+ for (x in 0 until width) {
117
+ val pixelOffset = rowOffset + x * pixelStride
118
+ val r = buffer.get(pixelOffset).toInt() and 0xFF
119
+ val g = buffer.get(pixelOffset + 1).toInt() and 0xFF
120
+ val b = buffer.get(pixelOffset + 2).toInt() and 0xFF
121
+ val a = buffer.get(pixelOffset + 3).toInt() and 0xFF
122
+ packed[outputIndex++] = r.toByte()
123
+ packed[outputIndex++] = g.toByte()
124
+ packed[outputIndex++] = b.toByte()
125
+ packed[outputIndex++] = a.toByte()
134
126
  }
135
- return packed
136
127
  }
137
128
 
138
- return rgba
129
+ return packed
139
130
  }
140
131
 
141
132
  override fun dispose() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vision-camera-spoof-detector",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
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": {