react-native-vision-camera-spoof-detector 1.1.22 → 1.1.23

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.
@@ -260,6 +260,50 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
260
260
  }
261
261
  }
262
262
 
263
+ fun advancedAntiSpoofingRGBA(buffer: ByteBuffer, width: Int, height: Int, rowStride: Int, pixelStride: Int): AntiSpoofingResult {
264
+ if (!isInitialized) {
265
+ return AntiSpoofingResult(
266
+ neuralNetworkScore = 0f,
267
+ laplacianScore = 0,
268
+ textureComplexity = 0,
269
+ temporalConsistency = 0f,
270
+ colorSaturation = 0f,
271
+ combinedScore = 0f,
272
+ confidence = 0f
273
+ )
274
+ }
275
+
276
+ return try {
277
+ val sampled = sampleRgbaBufferToModelAndGray(buffer, width, height, rowStride, pixelStride)
278
+ val nnResult = neuralNetworkAntiSpoofingSampled(sampled.rgbBuffer)
279
+ val nnScore = nnResult.getOrNull() ?: throw IllegalStateException(nnResult.exceptionOrNull()?.message ?: "NN inference failed")
280
+ val laplacianScore = calculateLaplacianFromGray(sampled.grayBuffer)
281
+ val combinedScore = calculateCombinedScore(nnScore, laplacianScore)
282
+ val confidence = calculateConfidence(nnScore, laplacianScore)
283
+
284
+ AntiSpoofingResult(
285
+ neuralNetworkScore = nnScore,
286
+ laplacianScore = laplacianScore,
287
+ textureComplexity = 0,
288
+ temporalConsistency = 0f,
289
+ colorSaturation = 0f,
290
+ combinedScore = combinedScore,
291
+ confidence = confidence
292
+ )
293
+ } catch (e: Exception) {
294
+ Log.e(TAG, "Error in advancedAntiSpoofingRGBA buffer path", e)
295
+ AntiSpoofingResult(
296
+ neuralNetworkScore = 0f,
297
+ laplacianScore = 0,
298
+ textureComplexity = 0,
299
+ temporalConsistency = 0f,
300
+ colorSaturation = 0f,
301
+ combinedScore = 0f,
302
+ confidence = 0f
303
+ )
304
+ }
305
+ }
306
+
263
307
  // FROM WORKING VERSION: neuralNetworkAntiSpoofing with Bitmap
264
308
  private fun neuralNetworkAntiSpoofing(bitmap: Bitmap): Result<Float> {
265
309
  return try {
@@ -381,6 +425,45 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
381
425
  return SampledFrame(modelInputBuffer, grayBuffer)
382
426
  }
383
427
 
428
+ private fun sampleRgbaBufferToModelAndGray(
429
+ buffer: ByteBuffer,
430
+ srcWidth: Int,
431
+ srcHeight: Int,
432
+ rowStride: Int,
433
+ pixelStride: Int
434
+ ): SampledFrame {
435
+ val targetWidth = INPUT_IMAGE_SIZE
436
+ val targetHeight = INPUT_IMAGE_SIZE
437
+ val scaleX = srcWidth.toFloat() / targetWidth
438
+ val scaleY = srcHeight.toFloat() / targetHeight
439
+ val duplicated = buffer.duplicate()
440
+ duplicated.order(ByteOrder.nativeOrder())
441
+
442
+ for (targetY in 0 until targetHeight) {
443
+ val srcY = (targetY * scaleY).toInt().coerceIn(0, srcHeight - 1)
444
+ val rowOffset = srcY * rowStride
445
+ for (targetX in 0 until targetWidth) {
446
+ val srcX = (targetX * scaleX).toInt().coerceIn(0, srcWidth - 1)
447
+ val pixelIndex = rowOffset + srcX * pixelStride
448
+
449
+ val r = duplicated.get(pixelIndex).toInt() and 0xFF
450
+ val g = duplicated.get(pixelIndex + 1).toInt() and 0xFF
451
+ val b = duplicated.get(pixelIndex + 2).toInt() and 0xFF
452
+
453
+ modelInputBuffer[targetY][targetX][0] = r / 255f
454
+ modelInputBuffer[targetY][targetX][1] = g / 255f
455
+ modelInputBuffer[targetY][targetX][2] = b / 255f
456
+ grayBuffer[targetY][targetX] = (
457
+ 0.299f * r +
458
+ 0.587f * g +
459
+ 0.114f * b
460
+ ).toInt()
461
+ }
462
+ }
463
+
464
+ return SampledFrame(modelInputBuffer, grayBuffer)
465
+ }
466
+
384
467
  // FROM WORKING VERSION: laplacian with Bitmap
385
468
  private fun laplacian(bitmap: Bitmap): Result<Int> {
386
469
  return try {
@@ -83,12 +83,21 @@ class HybridSpoofDetectorOutput(
83
83
  return
84
84
  }
85
85
  try {
86
- val rgba = extractRgbaBytes(image)
87
- if (!shouldProcessFrame(rgba, image.width, image.height)) {
86
+ val plane = image.planes.firstOrNull() ?: throw IllegalStateException("RGBA image has no planes")
87
+ val rgba = plane.buffer.duplicate()
88
+ rgba.order(java.nio.ByteOrder.nativeOrder())
89
+
90
+ if (!shouldProcessFrameFromPlane(rgba, image.width, image.height, plane.rowStride, plane.pixelStride)) {
88
91
  return
89
92
  }
90
93
 
91
- val result = detector.advancedAntiSpoofingRGBA(rgba, image.width, image.height)
94
+ val result = detector.advancedAntiSpoofingRGBA(
95
+ rgba,
96
+ image.width,
97
+ image.height,
98
+ plane.rowStride,
99
+ plane.pixelStride,
100
+ )
92
101
  val isLive = result.neuralNetworkScore < FaceAntiSpoofingAdvanced.BASE_THRESHOLD &&
93
102
  result.laplacianScore > FaceAntiSpoofingAdvanced.LAPLACIAN_THRESHOLD
94
103
  options.onResult.invoke(
@@ -109,7 +118,13 @@ class HybridSpoofDetectorOutput(
109
118
  }
110
119
  }
111
120
 
112
- private fun shouldProcessFrame(rgba: ByteArray, width: Int, height: Int): Boolean {
121
+ private fun shouldProcessFrameFromPlane(
122
+ buffer: java.nio.ByteBuffer,
123
+ width: Int,
124
+ height: Int,
125
+ rowStride: Int,
126
+ pixelStride: Int,
127
+ ): Boolean {
113
128
  val frameIndex = frameCounter.incrementAndGet()
114
129
  if (frameIndex % FRAME_EVALUATION_INTERVAL != 0) {
115
130
  return false
@@ -121,14 +136,14 @@ class HybridSpoofDetectorOutput(
121
136
  var sampleCount = 0
122
137
 
123
138
  for (y in 0 until height step sampleStep) {
124
- val rowOffset = y * width * 4
139
+ val rowOffset = y * rowStride
125
140
  for (x in 0 until width step sampleStep) {
126
- val index = rowOffset + x * 4
127
- if (index + 2 >= rgba.size) continue
141
+ val index = rowOffset + x * pixelStride
142
+ if (index + 2 >= buffer.capacity()) continue
128
143
 
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
144
+ val r = buffer.get(index).toInt() and 0xFF
145
+ val g = buffer.get(index + 1).toInt() and 0xFF
146
+ val b = buffer.get(index + 2).toInt() and 0xFF
132
147
  val luma = (0.299 * r + 0.587 * g + 0.114 * b).toInt()
133
148
 
134
149
  brightnessSum += luma.toLong()
@@ -143,7 +158,7 @@ class HybridSpoofDetectorOutput(
143
158
 
144
159
  val avgBrightness = brightnessSum.toFloat() / sampleCount
145
160
  val variance = (brightnessSquaredSum.toFloat() / sampleCount) - (avgBrightness * avgBrightness)
146
- val brightnessDelta = kotlin.math.abs(avgBrightness - lastFrameBrightness)
161
+ val brightnessDelta = abs(avgBrightness - lastFrameBrightness)
147
162
  lastFrameBrightness = avgBrightness
148
163
 
149
164
  val brightnessOk = avgBrightness in MIN_FRAME_BRIGHTNESS..MAX_FRAME_BRIGHTNESS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vision-camera-spoof-detector",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
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": {