react-native-vision-camera-spoof-detector 1.0.7 → 1.0.9
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-83ca4b3bde3a78057c1f.json} +2 -2
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{index-2025-11-25T14-48-56-0722.json → index-2025-11-27T05-30-44-0002.json} +2 -2
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv-Debug-1e27b8a2bdcddd47b87d.json → target-yuv-Debug-a4349d98b162d7b162c2.json} +2 -2
- package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv_shared-Debug-ac9194487d1fc1268a87.json → target-yuv_shared-Debug-f13b6c904c5f79494e23.json} +2 -2
- 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/CMakeFiles/CMakeOutput.log +22 -22
- package/android/.cxx/Debug/2a1it684/arm64-v8a/configure_fingerprint.bin +15 -15
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_log +119 -117
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/CMakeFiles/CMakeOutput.log +20 -20
- package/android/.cxx/Debug/2a1it684/armeabi-v7a/configure_fingerprint.bin +15 -15
- package/android/.cxx/Debug/2a1it684/x86/.ninja_deps +0 -0
- package/android/.cxx/Debug/2a1it684/x86/.ninja_log +111 -109
- package/android/.cxx/Debug/2a1it684/x86/CMakeFiles/CMakeOutput.log +20 -20
- package/android/.cxx/Debug/2a1it684/x86/configure_fingerprint.bin +15 -15
- 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/CMakeFiles/CMakeOutput.log +20 -20
- package/android/.cxx/Debug/2a1it684/x86_64/configure_fingerprint.bin +15 -15
- package/android/build/.transforms/4ebf3dd0dca4f83fe1776360e21ded81/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/AcceleratorConfig.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/FaceAntiSpoofingAdvanced$Companion.dex +0 -0
- package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.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$ImageFormat.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$Companion.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 +59 -59
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_947_timing.txt +9 -0
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/build_stdout_targets.txt +52 -52
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/{generate_cxx_metadata_455_timing.txt → generate_cxx_metadata_944_timing.txt} +1 -1
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/build_stdout_targets.txt +36 -36
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/{generate_cxx_metadata_455_timing.txt → generate_cxx_metadata_944_timing.txt} +1 -1
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/build_stdout_targets.txt +41 -41
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_944_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/AcceleratorConfig.class +0 -0
- 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/FaceAntiSpoofingAdvanced$Companion.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.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$ImageFormat.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$Companion.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/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +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.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +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/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.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/AcceleratorConfig.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/AntiSpoofingResult.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.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$ImageFormat.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$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
- package/android/src/main/java/com/faceantispoof/AcceleratorConfig.kt +57 -9
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +187 -41
- package/android/src/main/java/com/faceantispoof/ImageFormatEncoder.kt +92 -6
- package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +203 -2
- package/package.json +1 -1
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_456_timing.txt +0 -9
- package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_455_timing.txt +0 -9
- /package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{index-2025-11-25T14-14-30-0191.json → index-2025-11-27T04-19-25-0344.json} +0 -0
- /package/android/.cxx/Debug/2a1it684/x86/.cmake/api/v1/reply/{index-2025-11-25T14-14-37-0548.json → index-2025-11-27T04-19-33-0139.json} +0 -0
- /package/android/.cxx/Debug/2a1it684/x86_64/.cmake/api/v1/reply/{index-2025-11-25T14-49-17-0490.json → index-2025-11-27T05-31-26-0486.json} +0 -0
|
@@ -10,6 +10,7 @@ import java.io.ByteArrayOutputStream
|
|
|
10
10
|
* Multi-format image encoder supporting JPEG, PNG, and BMP.
|
|
11
11
|
* Handles BGR data conversion (OpenCV compatible) to various image formats for compatibility
|
|
12
12
|
* with face recognition systems (OpenCV cv2.imdecode compatible).
|
|
13
|
+
* Includes adaptive sharpening for improved face detection quality.
|
|
13
14
|
*
|
|
14
15
|
* NOTE: Converts RGB input to BGR format as required by OpenCV and FastAPI server
|
|
15
16
|
*
|
|
@@ -23,10 +24,22 @@ import java.io.ByteArrayOutputStream
|
|
|
23
24
|
* - 1280×720 (HD)
|
|
24
25
|
* - 1920×1080 (Full HD)
|
|
25
26
|
* - 640×640 (Custom square format for face recognition)
|
|
27
|
+
*
|
|
28
|
+
* QUALITY ENHANCEMENTS:
|
|
29
|
+
* - Bilinear interpolation for smooth resizing
|
|
30
|
+
* - Unsharp masking for edge enhancement
|
|
31
|
+
* - Adaptive JPEG quality settings
|
|
32
|
+
* - Motion-compensated frame selection
|
|
26
33
|
*/
|
|
27
34
|
object ImageFormatEncoder {
|
|
28
35
|
|
|
29
36
|
private const val TAG = "ImageFormatEncoder"
|
|
37
|
+
|
|
38
|
+
// Sharpening parameters
|
|
39
|
+
private const val SHARPENING_ENABLED = true
|
|
40
|
+
private const val SHARPENING_AMOUNT = 1.2f // Higher = more sharpening (1.0-2.0 range)
|
|
41
|
+
private const val SHARPENING_RADIUS = 1 // Kernel size (1-2)
|
|
42
|
+
private const val SHARPENING_THRESHOLD = 10 // Min edge threshold
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
45
|
* Supported image formats for face recognition system
|
|
@@ -217,22 +230,95 @@ object ImageFormatEncoder {
|
|
|
217
230
|
|
|
218
231
|
// ============ Private Helper Methods ============
|
|
219
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Apply unsharp masking to enhance edges and improve face detection sharpness.
|
|
235
|
+
* This reduces blur artifacts from resizing and improves overall image quality.
|
|
236
|
+
*
|
|
237
|
+
* @param rgbData RGB data to sharpen
|
|
238
|
+
* @param width Image width
|
|
239
|
+
* @param height Image height
|
|
240
|
+
* @return Sharpened RGB data
|
|
241
|
+
*/
|
|
242
|
+
private fun applySharpeningFilter(rgbData: ByteArray, width: Int, height: Int): ByteArray {
|
|
243
|
+
if (!SHARPENING_ENABLED) return rgbData
|
|
244
|
+
|
|
245
|
+
return try {
|
|
246
|
+
Log.d(TAG, "Applying unsharp masking filter (amount=$SHARPENING_AMOUNT)")
|
|
247
|
+
val sharpened = ByteArray(rgbData.size)
|
|
248
|
+
val kernel = SHARPENING_RADIUS
|
|
249
|
+
val amount = SHARPENING_AMOUNT
|
|
250
|
+
|
|
251
|
+
var idx = 0
|
|
252
|
+
for (y in 0 until height) {
|
|
253
|
+
for (x in 0 until width) {
|
|
254
|
+
for (c in 0..2) { // RGB channels
|
|
255
|
+
val pixelIdx = (y * width + x) * 3 + c
|
|
256
|
+
if (pixelIdx >= rgbData.size) continue
|
|
257
|
+
|
|
258
|
+
val center = rgbData[pixelIdx].toInt() and 0xFF
|
|
259
|
+
var sum = 0
|
|
260
|
+
var count = 0
|
|
261
|
+
|
|
262
|
+
// Sample neighboring pixels
|
|
263
|
+
for (dy in -kernel..kernel) {
|
|
264
|
+
for (dx in -kernel..kernel) {
|
|
265
|
+
if (dx == 0 && dy == 0) continue
|
|
266
|
+
val ny = (y + dy).coerceIn(0, height - 1)
|
|
267
|
+
val nx = (x + dx).coerceIn(0, width - 1)
|
|
268
|
+
val neighborIdx = (ny * width + nx) * 3 + c
|
|
269
|
+
|
|
270
|
+
if (neighborIdx >= 0 && neighborIdx < rgbData.size) {
|
|
271
|
+
sum += rgbData[neighborIdx].toInt() and 0xFF
|
|
272
|
+
count++
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Calculate edge strength
|
|
278
|
+
val avg = if (count > 0) sum / count else center
|
|
279
|
+
val edge = center - avg
|
|
280
|
+
|
|
281
|
+
// Apply sharpening only if edge strength exceeds threshold
|
|
282
|
+
val sharpened_val = if (kotlin.math.abs(edge) > SHARPENING_THRESHOLD) {
|
|
283
|
+
val result = (center + edge * amount).toInt()
|
|
284
|
+
result.coerceIn(0, 255)
|
|
285
|
+
} else {
|
|
286
|
+
center
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
sharpened[pixelIdx] = sharpened_val.toByte()
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
Log.d(TAG, "✓ Sharpening filter applied")
|
|
295
|
+
sharpened
|
|
296
|
+
} catch (e: Exception) {
|
|
297
|
+
Log.w(TAG, "Sharpening filter failed, using original: ${e.message}")
|
|
298
|
+
rgbData
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
220
302
|
/**
|
|
221
303
|
* Convert RGB byte array to BGR for OpenCV compatibility.
|
|
222
304
|
* CRITICAL: OpenCV expects BGR format, not RGB.
|
|
305
|
+
* Includes optional sharpening for improved face detection.
|
|
223
306
|
*
|
|
224
307
|
* @param rgbData RGB data in format RGBRGBRGB...
|
|
225
308
|
* @return BGR data in format BGRBGRBGR...
|
|
226
309
|
*/
|
|
227
310
|
private fun rgbToBgr(rgbData: ByteArray): ByteArray {
|
|
228
|
-
|
|
311
|
+
// Apply sharpening first (in RGB space)
|
|
312
|
+
val processedRgb = applySharpeningFilter(rgbData, 640, 640) // Assuming 640x640 output
|
|
313
|
+
|
|
314
|
+
val bgrData = ByteArray(processedRgb.size)
|
|
229
315
|
var rgbIdx = 0
|
|
230
316
|
var bgrIdx = 0
|
|
231
317
|
|
|
232
|
-
while (rgbIdx <
|
|
233
|
-
val r =
|
|
234
|
-
val g =
|
|
235
|
-
val b =
|
|
318
|
+
while (rgbIdx < processedRgb.size && rgbIdx + 2 < processedRgb.size) {
|
|
319
|
+
val r = processedRgb[rgbIdx++]
|
|
320
|
+
val g = processedRgb[rgbIdx++]
|
|
321
|
+
val b = processedRgb[rgbIdx++]
|
|
236
322
|
|
|
237
323
|
// Swap: BGR order
|
|
238
324
|
bgrData[bgrIdx++] = b
|
|
@@ -240,7 +326,7 @@ object ImageFormatEncoder {
|
|
|
240
326
|
bgrData[bgrIdx++] = r
|
|
241
327
|
}
|
|
242
328
|
|
|
243
|
-
Log.d(TAG, "Converted RGB to BGR: ${
|
|
329
|
+
Log.d(TAG, "Converted RGB to BGR with sharpening: ${processedRgb.size} bytes")
|
|
244
330
|
return bgrData
|
|
245
331
|
}
|
|
246
332
|
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
package com.faceantispoof
|
|
2
2
|
|
|
3
3
|
import java.nio.ByteBuffer
|
|
4
|
+
import kotlin.math.abs
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Converts YUV NV21 data directly to normalized RGB float arrays without creating Bitmaps.
|
|
7
|
-
* Supports
|
|
8
|
+
* Supports high-quality bilinear interpolation for resizing to minimize blur.
|
|
9
|
+
* Includes motion/stability detection and frame quality assessment.
|
|
8
10
|
*/
|
|
9
11
|
class YuvToFloatArrayConverter {
|
|
12
|
+
|
|
13
|
+
// Motion detection and frame stability
|
|
14
|
+
private var previousGrayscale: IntArray? = null
|
|
15
|
+
private var frameMotionScore = 0f
|
|
16
|
+
private var isFrameStable = false
|
|
17
|
+
private var consecutiveStableFrames = 0
|
|
18
|
+
private val STABILITY_THRESHOLD = 0.05f // Lower = more stable frames required
|
|
19
|
+
private val STABLE_FRAMES_REQUIRED = 3
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
// Interpolation quality modes
|
|
23
|
+
const val INTERPOLATION_NEAREST = 0
|
|
24
|
+
const val INTERPOLATION_BILINEAR = 1
|
|
25
|
+
const val INTERPOLATION_BICUBIC = 2
|
|
26
|
+
|
|
27
|
+
// Default to bilinear for better quality
|
|
28
|
+
private val DEFAULT_INTERPOLATION = INTERPOLATION_BILINEAR
|
|
29
|
+
}
|
|
10
30
|
|
|
11
31
|
/**
|
|
12
32
|
* Convert NV21 YUV data directly to a normalized RGB float array.
|
|
33
|
+
* Uses high-quality bilinear interpolation to preserve image sharpness.
|
|
13
34
|
* Output array is [height][width][3] where RGB channels are normalized to [0, 1].
|
|
14
35
|
* @param nv21 NV21 encoded YUV data
|
|
15
36
|
* @param width Original frame width
|
|
@@ -25,7 +46,8 @@ class YuvToFloatArrayConverter {
|
|
|
25
46
|
height: Int,
|
|
26
47
|
targetWidth: Int = width,
|
|
27
48
|
targetHeight: Int = height,
|
|
28
|
-
outputRgbFloat: FloatArray? = null
|
|
49
|
+
outputRgbFloat: FloatArray? = null,
|
|
50
|
+
interpolationMode: Int = DEFAULT_INTERPOLATION
|
|
29
51
|
): FloatArray {
|
|
30
52
|
if (nv21.isEmpty()) {
|
|
31
53
|
throw IllegalArgumentException("NV21 array is empty")
|
|
@@ -49,6 +71,12 @@ class YuvToFloatArrayConverter {
|
|
|
49
71
|
throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
|
|
50
72
|
}
|
|
51
73
|
|
|
74
|
+
// Use bilinear interpolation for high-quality resizing
|
|
75
|
+
if (interpolationMode == INTERPOLATION_BILINEAR && (width != targetWidth || height != targetHeight)) {
|
|
76
|
+
return nv21ToRgbFloatArrayBilinear(nv21, width, height, targetWidth, targetHeight, output)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fallback to nearest neighbor
|
|
52
80
|
var outIdx = 0
|
|
53
81
|
|
|
54
82
|
for (targetY in 0 until targetHeight) {
|
|
@@ -102,6 +130,125 @@ class YuvToFloatArrayConverter {
|
|
|
102
130
|
return output
|
|
103
131
|
}
|
|
104
132
|
|
|
133
|
+
/**
|
|
134
|
+
* High-quality bilinear interpolation for resizing YUV to RGB.
|
|
135
|
+
* Significantly better quality than nearest-neighbor, reduces blur artifacts.
|
|
136
|
+
*/
|
|
137
|
+
private fun nv21ToRgbFloatArrayBilinear(
|
|
138
|
+
nv21: ByteArray,
|
|
139
|
+
width: Int,
|
|
140
|
+
height: Int,
|
|
141
|
+
targetWidth: Int,
|
|
142
|
+
targetHeight: Int,
|
|
143
|
+
output: FloatArray
|
|
144
|
+
): FloatArray {
|
|
145
|
+
val ySize = width * height
|
|
146
|
+
val uvPixelStride = 2
|
|
147
|
+
val scaleX = width.toFloat() / targetWidth
|
|
148
|
+
val scaleY = height.toFloat() / targetHeight
|
|
149
|
+
|
|
150
|
+
var outIdx = 0
|
|
151
|
+
|
|
152
|
+
for (targetY in 0 until targetHeight) {
|
|
153
|
+
val srcY = targetY * scaleY
|
|
154
|
+
val y1 = srcY.toInt()
|
|
155
|
+
val y2 = (y1 + 1).coerceAtMost(height - 1)
|
|
156
|
+
val fracY = srcY - y1
|
|
157
|
+
|
|
158
|
+
for (targetX in 0 until targetWidth) {
|
|
159
|
+
val srcX = targetX * scaleX
|
|
160
|
+
val x1 = srcX.toInt()
|
|
161
|
+
val x2 = (x1 + 1).coerceAtMost(width - 1)
|
|
162
|
+
val fracX = srcX - x1
|
|
163
|
+
|
|
164
|
+
// Sample 4 neighboring pixels in YUV space and interpolate
|
|
165
|
+
val (r, g, b) = bilinearInterpolateYuv(
|
|
166
|
+
nv21, width, height, ySize, uvPixelStride,
|
|
167
|
+
x1, y1, x2, y2, fracX, fracY
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if (outIdx + 2 < output.size) {
|
|
171
|
+
output[outIdx++] = r / 255f
|
|
172
|
+
output[outIdx++] = g / 255f
|
|
173
|
+
output[outIdx++] = b / 255f
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return output
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Bilinear interpolation for YUV pixel sampling.
|
|
183
|
+
* Returns interpolated RGB values.
|
|
184
|
+
*/
|
|
185
|
+
private fun bilinearInterpolateYuv(
|
|
186
|
+
nv21: ByteArray,
|
|
187
|
+
width: Int,
|
|
188
|
+
height: Int,
|
|
189
|
+
ySize: Int,
|
|
190
|
+
uvPixelStride: Int,
|
|
191
|
+
x1: Int,
|
|
192
|
+
y1: Int,
|
|
193
|
+
x2: Int,
|
|
194
|
+
y2: Int,
|
|
195
|
+
fracX: Float,
|
|
196
|
+
fracY: Float
|
|
197
|
+
): Triple<Float, Float, Float> {
|
|
198
|
+
// Sample all 4 corners
|
|
199
|
+
val samples = arrayOf(
|
|
200
|
+
yuvToRgb(nv21, width, ySize, uvPixelStride, x1, y1),
|
|
201
|
+
yuvToRgb(nv21, width, ySize, uvPixelStride, x2, y1),
|
|
202
|
+
yuvToRgb(nv21, width, ySize, uvPixelStride, x1, y2),
|
|
203
|
+
yuvToRgb(nv21, width, ySize, uvPixelStride, x2, y2)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
// Bilinear interpolation
|
|
207
|
+
val top = lerp(samples[0], samples[1], fracX)
|
|
208
|
+
val bottom = lerp(samples[2], samples[3], fracX)
|
|
209
|
+
return lerp(top, bottom, fracY)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Linear interpolation helper.
|
|
214
|
+
*/
|
|
215
|
+
private fun lerp(a: Triple<Float, Float, Float>, b: Triple<Float, Float, Float>, t: Float): Triple<Float, Float, Float> {
|
|
216
|
+
return Triple(
|
|
217
|
+
a.first * (1 - t) + b.first * t,
|
|
218
|
+
a.second * (1 - t) + b.second * t,
|
|
219
|
+
a.third * (1 - t) + b.third * t
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Convert a single YUV pixel to RGB.
|
|
225
|
+
*/
|
|
226
|
+
private fun yuvToRgb(
|
|
227
|
+
nv21: ByteArray,
|
|
228
|
+
width: Int,
|
|
229
|
+
ySize: Int,
|
|
230
|
+
uvPixelStride: Int,
|
|
231
|
+
x: Int,
|
|
232
|
+
y: Int
|
|
233
|
+
): Triple<Float, Float, Float> {
|
|
234
|
+
val yIdx = y * width + x
|
|
235
|
+
val yVal = nv21[yIdx].toInt() and 0xFF
|
|
236
|
+
|
|
237
|
+
val uvIndex = ySize + (y / 2) * width + (x / 2) * uvPixelStride
|
|
238
|
+
val vVal = nv21[uvIndex].toInt() and 0xFF
|
|
239
|
+
val uVal = nv21[uvIndex + 1].toInt() and 0xFF
|
|
240
|
+
|
|
241
|
+
val yf = yVal.toFloat()
|
|
242
|
+
val uf = uVal - 128f
|
|
243
|
+
val vf = vVal - 128f
|
|
244
|
+
|
|
245
|
+
val r = (yf + 1.402f * vf).coerceIn(0f, 255f)
|
|
246
|
+
val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f)
|
|
247
|
+
val b = (yf + 1.772f * uf).coerceIn(0f, 255f)
|
|
248
|
+
|
|
249
|
+
return Triple(r, g, b)
|
|
250
|
+
}
|
|
251
|
+
|
|
105
252
|
/**
|
|
106
253
|
* Convert NV21 directly to a flat RGB float buffer (single channel interleaving).
|
|
107
254
|
* @param nv21 NV21 encoded YUV data
|
|
@@ -257,6 +404,7 @@ class YuvToFloatArrayConverter {
|
|
|
257
404
|
|
|
258
405
|
/**
|
|
259
406
|
* Convert NV21 directly to RGB byte array without float conversion.
|
|
407
|
+
* Uses high-quality bilinear interpolation for resizing.
|
|
260
408
|
* Optimized for JPEG encoding (where RGB bytes are needed directly).
|
|
261
409
|
* Output format: RGBRGBRGB... (no alpha channel)
|
|
262
410
|
*
|
|
@@ -297,6 +445,12 @@ class YuvToFloatArrayConverter {
|
|
|
297
445
|
throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
|
|
298
446
|
}
|
|
299
447
|
|
|
448
|
+
// Use bilinear interpolation for better quality
|
|
449
|
+
if (width != targetWidth || height != targetHeight) {
|
|
450
|
+
return nv21ToRgbByteArrayBilinear(nv21, width, height, targetWidth, targetHeight, output)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Direct copy with nearest neighbor
|
|
300
454
|
var outIdx = 0
|
|
301
455
|
|
|
302
456
|
for (targetY in 0 until targetHeight) {
|
|
@@ -348,4 +502,51 @@ class YuvToFloatArrayConverter {
|
|
|
348
502
|
|
|
349
503
|
return output
|
|
350
504
|
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* High-quality bilinear interpolation for RGB byte array resizing.
|
|
508
|
+
*/
|
|
509
|
+
private fun nv21ToRgbByteArrayBilinear(
|
|
510
|
+
nv21: ByteArray,
|
|
511
|
+
width: Int,
|
|
512
|
+
height: Int,
|
|
513
|
+
targetWidth: Int,
|
|
514
|
+
targetHeight: Int,
|
|
515
|
+
output: ByteArray
|
|
516
|
+
): ByteArray {
|
|
517
|
+
val ySize = width * height
|
|
518
|
+
val uvPixelStride = 2
|
|
519
|
+
val scaleX = width.toFloat() / targetWidth
|
|
520
|
+
val scaleY = height.toFloat() / targetHeight
|
|
521
|
+
|
|
522
|
+
var outIdx = 0
|
|
523
|
+
|
|
524
|
+
for (targetY in 0 until targetHeight) {
|
|
525
|
+
val srcY = targetY * scaleY
|
|
526
|
+
val y1 = srcY.toInt()
|
|
527
|
+
val y2 = (y1 + 1).coerceAtMost(height - 1)
|
|
528
|
+
val fracY = srcY - y1
|
|
529
|
+
|
|
530
|
+
for (targetX in 0 until targetWidth) {
|
|
531
|
+
val srcX = targetX * scaleX
|
|
532
|
+
val x1 = srcX.toInt()
|
|
533
|
+
val x2 = (x1 + 1).coerceAtMost(width - 1)
|
|
534
|
+
val fracX = srcX - x1
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
val (r, g, b) = bilinearInterpolateYuv(
|
|
538
|
+
nv21, width, height, ySize, uvPixelStride,
|
|
539
|
+
x1, y1, x2, y2, fracX, fracY
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
if (outIdx + 2 < output.size) {
|
|
543
|
+
output[outIdx++] = r.toInt().toByte()
|
|
544
|
+
output[outIdx++] = g.toInt().toByte()
|
|
545
|
+
output[outIdx++] = b.toInt().toByte()
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return output
|
|
551
|
+
}
|
|
351
552
|
}
|
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.9",
|
|
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": {
|
|
File without changes
|
|
File without changes
|