react-native-vision-camera-spoof-detector 1.0.4 → 1.0.6
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.
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{codemodel-v2-390962e2784a6a84fa3e.json → codemodel-v2-6c7de80461af72f456a3.json} +2 -2
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{index-2025-11-24T13-21-59-0659.json → index-2025-11-24T17-41-56-0411.json} +2 -2
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv-Debug-1e27b8a2bdcddd47b87d.json → target-yuv-Debug-32bc4eddd343848e1c25.json} +4 -4
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv_shared-Debug-ac9194487d1fc1268a87.json → target-yuv_shared-Debug-75ea769bf76a12914d29.json} +4 -4
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_log +125 -125
- package/android/.cxx/Debug/2a1it684/arm64-v8a/configure_fingerprint.bin +14 -14
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_log +118 -119
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/configure_fingerprint.bin +14 -14
- package/android/.cxx/Debug/2a1it684/x86/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/x86/.ninja_log +110 -111
- package/android/.cxx/Debug/2a1it684/x86/configure_fingerprint.bin +14 -14
- package/android/.cxx/Debug/2a1it684/x86_64/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/x86_64/.ninja_log +109 -109
- package/android/.cxx/Debug/2a1it684/x86_64/configure_fingerprint.bin +14 -14
- package/android/build/.transforms/4ebf3dd0dca4f83fe1776360e21ded81/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/AntiSpoofingResult.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofFrameProcessor.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/ImageFormatEncoder.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/YuvToFloatArrayConverter.dex +0 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/build_stdout_targets.txt +49 -49
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_2688_timing.txt +9 -0
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/build_stdout_targets.txt +44 -44
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/{generate_cxx_metadata_2653_timing.txt → generate_cxx_metadata_2687_timing.txt} +1 -1
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/build_stdout_targets.txt +40 -40
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/{generate_cxx_metadata_2653_timing.txt → generate_cxx_metadata_2687_timing.txt} +1 -1
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/build_stdout_targets.txt +43 -43
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_2687_timing.txt +9 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/AntiSpoofingResult.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofFrameProcessor.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/ImageFormatEncoder.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/AntiSpoofingResult.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofFrameProcessor.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/ImageFormatEncoder.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofFrameProcessor.kt +25 -6
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +46 -16
- package/android/src/main/java/com/faceantispoof/ImageFormatEncoder.kt +99 -21
- package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +103 -12
- package/package.json +3 -3
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_2655_timing.txt +0 -9
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_2653_timing.txt +0 -9
- /package/android/.cxx/Debug/2a1it684/x86_64/.cmake/api/v1/reply/{index-2025-11-24T13-22-20-0304.json → index-2025-11-24T17-42-56-0679.json} +0 -0
|
@@ -88,11 +88,27 @@ object ImageFormatEncoder {
|
|
|
88
88
|
return try {
|
|
89
89
|
validateImageDimensions(width, height)
|
|
90
90
|
|
|
91
|
+
if (rgbFloats.isEmpty()) {
|
|
92
|
+
Log.e(TAG, "RGB float array is empty")
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
val expectedSize = width * height * 3
|
|
97
|
+
if (rgbFloats.size < expectedSize) {
|
|
98
|
+
Log.e(TAG, "RGB float array too small: expected $expectedSize, got ${rgbFloats.size}")
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
// Convert float array (normalized 0-1) to byte array (0-255)
|
|
92
|
-
val rgbBytes = ByteArray(
|
|
103
|
+
val rgbBytes = ByteArray(expectedSize)
|
|
93
104
|
var idx = 0
|
|
94
|
-
for (i in
|
|
95
|
-
|
|
105
|
+
for (i in 0 until expectedSize) {
|
|
106
|
+
if (idx >= rgbFloats.size) {
|
|
107
|
+
Log.e(TAG, "Float array index out of bounds at $idx")
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
val floatVal = rgbFloats[idx++].coerceIn(0f, 1f)
|
|
111
|
+
rgbBytes[i] = (floatVal * 255f).toInt().coerceIn(0, 255).toByte()
|
|
96
112
|
}
|
|
97
113
|
|
|
98
114
|
rgbToBase64(rgbBytes, width, height, format, quality)
|
|
@@ -124,12 +140,25 @@ object ImageFormatEncoder {
|
|
|
124
140
|
validateRgbData(rgbData, width, height)
|
|
125
141
|
|
|
126
142
|
val bitmap = createBitmapFromRgb(rgbData, width, height)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
?: return null
|
|
144
|
+
|
|
145
|
+
val imageBytes = try {
|
|
146
|
+
when (format) {
|
|
147
|
+
ImageFormat.JPEG -> compressBitmapToBytes(bitmap, format, quality)
|
|
148
|
+
ImageFormat.PNG -> compressBitmapToBytes(bitmap, format, 100)
|
|
149
|
+
ImageFormat.BMP -> compressBitmapToBytes(bitmap, format, 100)
|
|
150
|
+
}
|
|
151
|
+
} catch (e: Exception) {
|
|
152
|
+
Log.e(TAG, "Error compressing bitmap: ${e.message}", e)
|
|
153
|
+
null
|
|
154
|
+
} finally {
|
|
155
|
+
try {
|
|
156
|
+
bitmap.recycle()
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
Log.w(TAG, "Error recycling bitmap: ${e.message}")
|
|
159
|
+
}
|
|
131
160
|
}
|
|
132
|
-
|
|
161
|
+
|
|
133
162
|
imageBytes
|
|
134
163
|
} catch (e: Exception) {
|
|
135
164
|
Log.e(TAG, "Failed to convert RGB to ${format.name} bytes: ${e.message}", e)
|
|
@@ -200,17 +229,47 @@ object ImageFormatEncoder {
|
|
|
200
229
|
* Create Bitmap from RGB byte array.
|
|
201
230
|
* RGB format: RGBRGBRGB... (3 bytes per pixel, no alpha)
|
|
202
231
|
*/
|
|
203
|
-
private fun createBitmapFromRgb(rgbData: ByteArray, width: Int, height: Int): Bitmap {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
232
|
+
private fun createBitmapFromRgb(rgbData: ByteArray, width: Int, height: Int): Bitmap? {
|
|
233
|
+
return try {
|
|
234
|
+
if (rgbData.isEmpty()) {
|
|
235
|
+
Log.e(TAG, "RGB data is empty")
|
|
236
|
+
return null
|
|
237
|
+
}
|
|
238
|
+
if (width <= 0 || height <= 0) {
|
|
239
|
+
Log.e(TAG, "Invalid dimensions: width=$width, height=$height")
|
|
240
|
+
return null
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
val expectedSize = width * height * 3
|
|
244
|
+
if (rgbData.size < expectedSize) {
|
|
245
|
+
Log.e(TAG, "RGB data too small: expected at least $expectedSize, got ${rgbData.size}")
|
|
246
|
+
return null
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Convert RGB to ARGB for Bitmap (Android uses ARGB_8888)
|
|
250
|
+
val pixels = IntArray(width * height)
|
|
251
|
+
var rgbIdx = 0
|
|
252
|
+
for (i in pixels.indices) {
|
|
253
|
+
if (rgbIdx + 2 >= rgbData.size) {
|
|
254
|
+
Log.e(TAG, "RGB buffer index out of bounds at pixel $i")
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
val r = rgbData[rgbIdx++].toInt() and 0xFF
|
|
258
|
+
val g = rgbData[rgbIdx++].toInt() and 0xFF
|
|
259
|
+
val b = rgbData[rgbIdx++].toInt() and 0xFF
|
|
260
|
+
pixels[i] = 0xFF000000.toInt() or (r shl 16) or (g shl 8) or b // ARGB
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
val bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888)
|
|
264
|
+
if (bitmap == null) {
|
|
265
|
+
Log.e(TAG, "Failed to create bitmap")
|
|
266
|
+
return null
|
|
267
|
+
}
|
|
268
|
+
bitmap
|
|
269
|
+
} catch (e: Exception) {
|
|
270
|
+
Log.e(TAG, "Error creating bitmap from RGB: ${e.message}", e)
|
|
271
|
+
null
|
|
212
272
|
}
|
|
213
|
-
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888)
|
|
214
273
|
}
|
|
215
274
|
|
|
216
275
|
/**
|
|
@@ -221,9 +280,28 @@ object ImageFormatEncoder {
|
|
|
221
280
|
format: ImageFormat,
|
|
222
281
|
quality: Int
|
|
223
282
|
): ByteArray {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
283
|
+
return try {
|
|
284
|
+
if (bitmap.width <= 0 || bitmap.height <= 0) {
|
|
285
|
+
throw IllegalArgumentException("Invalid bitmap dimensions: ${bitmap.width}×${bitmap.height}")
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
val outputStream = ByteArrayOutputStream()
|
|
289
|
+
val compressed = bitmap.compress(format.compressFormat, quality, outputStream)
|
|
290
|
+
|
|
291
|
+
if (!compressed) {
|
|
292
|
+
Log.w(TAG, "Bitmap compression returned false for ${format.name}")
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
val bytes = outputStream.toByteArray()
|
|
296
|
+
if (bytes.isEmpty()) {
|
|
297
|
+
Log.w(TAG, "Compressed image bytes are empty")
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
bytes
|
|
301
|
+
} catch (e: Exception) {
|
|
302
|
+
Log.e(TAG, "Error compressing bitmap: ${e.message}", e)
|
|
303
|
+
ByteArray(0)
|
|
304
|
+
}
|
|
227
305
|
}
|
|
228
306
|
|
|
229
307
|
/**
|
|
@@ -27,28 +27,55 @@ class YuvToFloatArrayConverter {
|
|
|
27
27
|
targetHeight: Int = height,
|
|
28
28
|
outputRgbFloat: FloatArray? = null
|
|
29
29
|
): FloatArray {
|
|
30
|
+
if (nv21.isEmpty()) {
|
|
31
|
+
throw IllegalArgumentException("NV21 array is empty")
|
|
32
|
+
}
|
|
33
|
+
if (width <= 0 || height <= 0) {
|
|
34
|
+
throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
|
|
35
|
+
}
|
|
36
|
+
if (targetWidth <= 0 || targetHeight <= 0) {
|
|
37
|
+
throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
val outputSize = targetWidth * targetHeight * 3
|
|
31
41
|
val output = outputRgbFloat?.takeIf { it.size >= outputSize } ?: FloatArray(outputSize)
|
|
32
42
|
|
|
33
43
|
val ySize = width * height
|
|
34
44
|
val uvPixelStride = 2
|
|
45
|
+
|
|
46
|
+
// Validate NV21 buffer size
|
|
47
|
+
val minExpectedSize = width * height + (width * height) / 2
|
|
48
|
+
if (nv21.size < minExpectedSize) {
|
|
49
|
+
throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
|
|
50
|
+
}
|
|
35
51
|
|
|
36
52
|
var outIdx = 0
|
|
37
53
|
|
|
38
54
|
for (targetY in 0 until targetHeight) {
|
|
39
|
-
// Map target Y to original frame Y (nearest-neighbor)
|
|
40
55
|
val srcY = (targetY * height) / targetHeight
|
|
56
|
+
if (srcY >= height) {
|
|
57
|
+
throw IndexOutOfBoundsException("Source Y index out of bounds: srcY=$srcY, height=$height")
|
|
58
|
+
}
|
|
41
59
|
val srcRowOffset = srcY * width
|
|
42
60
|
|
|
43
61
|
for (targetX in 0 until targetWidth) {
|
|
44
|
-
// Map target X to original frame X (nearest-neighbor)
|
|
45
62
|
val srcX = (targetX * width) / targetWidth
|
|
63
|
+
if (srcX >= width) {
|
|
64
|
+
throw IndexOutOfBoundsException("Source X index out of bounds: srcX=$srcX, width=$width")
|
|
65
|
+
}
|
|
46
66
|
|
|
47
67
|
// Get Y component
|
|
48
|
-
val
|
|
68
|
+
val yIdx = srcRowOffset + srcX
|
|
69
|
+
if (yIdx >= nv21.size) {
|
|
70
|
+
throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
|
|
71
|
+
}
|
|
72
|
+
val yVal = nv21[yIdx].toInt() and 0xFF
|
|
49
73
|
|
|
50
74
|
// Get U and V components (NV21 layout: UV interleaved in second half)
|
|
51
75
|
val uvIndex = ySize + (srcY / 2) * width + (srcX / 2) * uvPixelStride
|
|
76
|
+
if (uvIndex + 1 >= nv21.size) {
|
|
77
|
+
throw IndexOutOfBoundsException("UV index out of bounds: uvIndex=$uvIndex, nv21.size=${nv21.size}")
|
|
78
|
+
}
|
|
52
79
|
val vVal = nv21[uvIndex].toInt() and 0xFF
|
|
53
80
|
val uVal = nv21[uvIndex + 1].toInt() and 0xFF
|
|
54
81
|
|
|
@@ -62,9 +89,13 @@ class YuvToFloatArrayConverter {
|
|
|
62
89
|
val b = (yf + 1.772f * uf).coerceIn(0f, 255f)
|
|
63
90
|
|
|
64
91
|
// Normalize to [0, 1]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
if (outIdx + 2 < output.size) {
|
|
93
|
+
output[outIdx++] = r / 255f
|
|
94
|
+
output[outIdx++] = g / 255f
|
|
95
|
+
output[outIdx++] = b / 255f
|
|
96
|
+
} else {
|
|
97
|
+
throw IndexOutOfBoundsException("Output array index out of bounds: outIdx=$outIdx, output.size=${output.size}")
|
|
98
|
+
}
|
|
68
99
|
}
|
|
69
100
|
}
|
|
70
101
|
|
|
@@ -176,20 +207,48 @@ class YuvToFloatArrayConverter {
|
|
|
176
207
|
targetHeight: Int = height,
|
|
177
208
|
outputGrayscale: IntArray? = null
|
|
178
209
|
): IntArray {
|
|
210
|
+
if (nv21.isEmpty()) {
|
|
211
|
+
throw IllegalArgumentException("NV21 array is empty")
|
|
212
|
+
}
|
|
213
|
+
if (width <= 0 || height <= 0) {
|
|
214
|
+
throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
|
|
215
|
+
}
|
|
216
|
+
if (targetWidth <= 0 || targetHeight <= 0) {
|
|
217
|
+
throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
|
|
218
|
+
}
|
|
219
|
+
|
|
179
220
|
val outputSize = targetWidth * targetHeight
|
|
180
221
|
val output = outputGrayscale?.takeIf { it.size >= outputSize } ?: IntArray(outputSize)
|
|
181
222
|
|
|
182
223
|
val ySize = width * height
|
|
224
|
+
val minExpectedSize = width * height + (width * height) / 2
|
|
225
|
+
if (nv21.size < minExpectedSize) {
|
|
226
|
+
throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
|
|
227
|
+
}
|
|
183
228
|
|
|
184
229
|
var outIdx = 0
|
|
185
230
|
for (targetY in 0 until targetHeight) {
|
|
186
231
|
val srcY = (targetY * height) / targetHeight
|
|
232
|
+
if (srcY >= height) {
|
|
233
|
+
throw IndexOutOfBoundsException("Source Y out of bounds: srcY=$srcY, height=$height")
|
|
234
|
+
}
|
|
187
235
|
val srcRowOffset = srcY * width
|
|
188
236
|
|
|
189
237
|
for (targetX in 0 until targetWidth) {
|
|
190
238
|
val srcX = (targetX * width) / targetWidth
|
|
191
|
-
|
|
192
|
-
|
|
239
|
+
if (srcX >= width) {
|
|
240
|
+
throw IndexOutOfBoundsException("Source X out of bounds: srcX=$srcX, width=$width")
|
|
241
|
+
}
|
|
242
|
+
val yIdx = srcRowOffset + srcX
|
|
243
|
+
if (yIdx >= nv21.size) {
|
|
244
|
+
throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
|
|
245
|
+
}
|
|
246
|
+
val yVal = nv21[yIdx].toInt() and 0xFF
|
|
247
|
+
if (outIdx < output.size) {
|
|
248
|
+
output[outIdx++] = yVal
|
|
249
|
+
} else {
|
|
250
|
+
throw IndexOutOfBoundsException("Output index out of bounds: outIdx=$outIdx, output.size=${output.size}")
|
|
251
|
+
}
|
|
193
252
|
}
|
|
194
253
|
}
|
|
195
254
|
|
|
@@ -217,26 +276,54 @@ class YuvToFloatArrayConverter {
|
|
|
217
276
|
targetHeight: Int = height,
|
|
218
277
|
outputRgbBytes: ByteArray? = null
|
|
219
278
|
): ByteArray {
|
|
279
|
+
if (nv21.isEmpty()) {
|
|
280
|
+
throw IllegalArgumentException("NV21 array is empty")
|
|
281
|
+
}
|
|
282
|
+
if (width <= 0 || height <= 0) {
|
|
283
|
+
throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
|
|
284
|
+
}
|
|
285
|
+
if (targetWidth <= 0 || targetHeight <= 0) {
|
|
286
|
+
throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
|
|
287
|
+
}
|
|
288
|
+
|
|
220
289
|
val outputSize = targetWidth * targetHeight * 3
|
|
221
290
|
val output = outputRgbBytes?.takeIf { it.size >= outputSize } ?: ByteArray(outputSize)
|
|
222
291
|
|
|
223
292
|
val ySize = width * height
|
|
224
293
|
val uvPixelStride = 2
|
|
294
|
+
|
|
295
|
+
val minExpectedSize = width * height + (width * height) / 2
|
|
296
|
+
if (nv21.size < minExpectedSize) {
|
|
297
|
+
throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
|
|
298
|
+
}
|
|
225
299
|
|
|
226
300
|
var outIdx = 0
|
|
227
301
|
|
|
228
302
|
for (targetY in 0 until targetHeight) {
|
|
229
303
|
val srcY = (targetY * height) / targetHeight
|
|
304
|
+
if (srcY >= height) {
|
|
305
|
+
throw IndexOutOfBoundsException("Source Y out of bounds: srcY=$srcY, height=$height")
|
|
306
|
+
}
|
|
230
307
|
val srcRowOffset = srcY * width
|
|
231
308
|
|
|
232
309
|
for (targetX in 0 until targetWidth) {
|
|
233
310
|
val srcX = (targetX * width) / targetWidth
|
|
311
|
+
if (srcX >= width) {
|
|
312
|
+
throw IndexOutOfBoundsException("Source X out of bounds: srcX=$srcX, width=$width")
|
|
313
|
+
}
|
|
234
314
|
|
|
235
315
|
// Get Y component
|
|
236
|
-
val
|
|
316
|
+
val yIdx = srcRowOffset + srcX
|
|
317
|
+
if (yIdx >= nv21.size) {
|
|
318
|
+
throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
|
|
319
|
+
}
|
|
320
|
+
val yVal = nv21[yIdx].toInt() and 0xFF
|
|
237
321
|
|
|
238
322
|
// Get U and V components (NV21 layout: UV interleaved in second half)
|
|
239
323
|
val uvIndex = ySize + (srcY / 2) * width + (srcX / 2) * uvPixelStride
|
|
324
|
+
if (uvIndex + 1 >= nv21.size) {
|
|
325
|
+
throw IndexOutOfBoundsException("UV index out of bounds: uvIndex=$uvIndex, nv21.size=${nv21.size}")
|
|
326
|
+
}
|
|
240
327
|
val vVal = nv21[uvIndex].toInt() and 0xFF
|
|
241
328
|
val uVal = nv21[uvIndex + 1].toInt() and 0xFF
|
|
242
329
|
|
|
@@ -249,9 +336,13 @@ class YuvToFloatArrayConverter {
|
|
|
249
336
|
val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f).toInt().toByte()
|
|
250
337
|
val b = (yf + 1.772f * uf).coerceIn(0f, 255f).toInt().toByte()
|
|
251
338
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
339
|
+
if (outIdx + 2 < output.size) {
|
|
340
|
+
output[outIdx++] = r
|
|
341
|
+
output[outIdx++] = g
|
|
342
|
+
output[outIdx++] = b
|
|
343
|
+
} else {
|
|
344
|
+
throw IndexOutOfBoundsException("Output index out of bounds: outIdx=$outIdx, output.size=${output.size}")
|
|
345
|
+
}
|
|
255
346
|
}
|
|
256
347
|
}
|
|
257
348
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-vision-camera-spoof-detector",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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/jescon-tech/react-native-vision-camera-spoof-detector",
|
|
6
6
|
"repository": {
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"bugs": {
|
|
11
11
|
"url": "https://github.com/jescon-tech/react-native-vision-camera-spoof-detector/issues"
|
|
12
12
|
},
|
|
13
|
-
"license": "
|
|
13
|
+
"license": "MIT",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "PRAFULDAS M M",
|
|
16
|
-
"company": "JESCON TECHNOLOGIES PVT LTD
|
|
16
|
+
"company": "JESCON TECHNOLOGIES PVT LTD",
|
|
17
17
|
"email": "jescontechnologies@gmail.com"
|
|
18
18
|
},
|
|
19
19
|
"main": "index.js",
|