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
|
|
202
|
-
val
|
|
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 {
|
package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorOutput.kt
CHANGED
|
@@ -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
|
|
104
|
-
val
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for (
|
|
122
|
-
val
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
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.
|
|
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": {
|