react-native-vision-camera-spoof-detector 1.0.8 → 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.
Files changed (114) hide show
  1. package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{codemodel-v2-501c3f4eb817726556cc.json → codemodel-v2-83ca4b3bde3a78057c1f.json} +2 -2
  2. package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{index-2025-11-25T16-55-22-0685.json → index-2025-11-27T05-30-44-0002.json} +2 -2
  3. package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv-Debug-63fdb78450b0b16b8484.json → target-yuv-Debug-a4349d98b162d7b162c2.json} +4 -4
  4. package/android/.cxx/Debug/2a1it684/arm64-v8a/.cmake/api/v1/reply/{target-yuv_shared-Debug-5c8b5c6f50bc8769a5bd.json → target-yuv_shared-Debug-f13b6c904c5f79494e23.json} +4 -4
  5. package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_deps +0 -0
  6. package/android/.cxx/Debug/2a1it684/arm64-v8a/.ninja_log +125 -125
  7. package/android/.cxx/Debug/2a1it684/arm64-v8a/CMakeFiles/CMakeOutput.log +22 -22
  8. package/android/.cxx/Debug/2a1it684/arm64-v8a/configure_fingerprint.bin +15 -15
  9. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{codemodel-v2-053850119b681f93cecc.json → codemodel-v2-52ac1d8b40b69596389b.json} +2 -2
  10. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{index-2025-11-25T16-21-43-0410.json → index-2025-11-27T04-19-25-0344.json} +2 -2
  11. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{target-yuv-Debug-a12ad49ff79080871bce.json → target-yuv-Debug-97f9e822dbd84a186d44.json} +2 -2
  12. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.cmake/api/v1/reply/{target-yuv_shared-Debug-2b00e64621a56b8dd632.json → target-yuv_shared-Debug-3730d20e27c3481c06b2.json} +2 -2
  13. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_deps +0 -0
  14. package/android/.cxx/Debug/2a1it684/armeabi-v7a/.ninja_log +119 -176
  15. package/android/.cxx/Debug/2a1it684/armeabi-v7a/CMakeFiles/CMakeOutput.log +20 -20
  16. package/android/.cxx/Debug/2a1it684/armeabi-v7a/configure_fingerprint.bin +15 -15
  17. package/android/.cxx/Debug/2a1it684/x86/.ninja_deps +0 -0
  18. package/android/.cxx/Debug/2a1it684/x86/.ninja_log +111 -164
  19. package/android/.cxx/Debug/2a1it684/x86/CMakeFiles/CMakeOutput.log +20 -20
  20. package/android/.cxx/Debug/2a1it684/x86/configure_fingerprint.bin +15 -15
  21. package/android/.cxx/Debug/2a1it684/x86_64/.ninja_deps +0 -0
  22. package/android/.cxx/Debug/2a1it684/x86_64/.ninja_log +109 -109
  23. package/android/.cxx/Debug/2a1it684/x86_64/CMakeFiles/CMakeOutput.log +20 -20
  24. package/android/.cxx/Debug/2a1it684/x86_64/configure_fingerprint.bin +15 -15
  25. package/android/build/.transforms/4ebf3dd0dca4f83fe1776360e21ded81/transformed/classes/classes_dex/classes.dex +0 -0
  26. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/AntiSpoofingResult.dex +0 -0
  27. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.dex +0 -0
  28. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.dex +0 -0
  29. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.dex +0 -0
  30. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/FaceAntiSpoofingAdvanced.dex +0 -0
  31. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/ImageFormatEncoder$ImageFormat.dex +0 -0
  32. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/ImageFormatEncoder.dex +0 -0
  33. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/YuvToFloatArrayConverter$Companion.dex +0 -0
  34. package/android/build/.transforms/c0a07cd9442d741ad3cef3760159211f/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/faceantispoof/YuvToFloatArrayConverter.dex +0 -0
  35. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  36. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/build_stdout_targets.txt +53 -53
  37. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_947_timing.txt +9 -0
  38. package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/build_stdout_targets.txt +46 -46
  39. package/android/build/intermediates/cxx/Debug/2a1it684/logs/armeabi-v7a/{generate_cxx_metadata_1042_timing.txt → generate_cxx_metadata_944_timing.txt} +1 -1
  40. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/build_stdout_targets.txt +40 -40
  41. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/build_stdout_targets.txt +43 -43
  42. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_944_timing.txt +9 -0
  43. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  44. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/AntiSpoofingResult.class +0 -0
  45. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.class +0 -0
  46. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.class +0 -0
  47. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
  48. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
  49. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/ImageFormatEncoder$ImageFormat.class +0 -0
  50. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/ImageFormatEncoder.class +0 -0
  51. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/YuvToFloatArrayConverter$Companion.class +0 -0
  52. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
  53. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  97. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/AntiSpoofingResult.class +0 -0
  98. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$Companion.class +0 -0
  99. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$EnvironmentAssessment.class +0 -0
  100. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced$SpoofDetectionResult.class +0 -0
  101. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/FaceAntiSpoofingAdvanced.class +0 -0
  102. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/ImageFormatEncoder$ImageFormat.class +0 -0
  103. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/ImageFormatEncoder.class +0 -0
  104. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/YuvToFloatArrayConverter$Companion.class +0 -0
  105. package/android/build/tmp/kotlin-classes/debug/com/faceantispoof/YuvToFloatArrayConverter.class +0 -0
  106. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +72 -19
  107. package/android/src/main/java/com/faceantispoof/ImageFormatEncoder.kt +92 -6
  108. package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +203 -2
  109. package/package.json +1 -1
  110. package/android/build/intermediates/cxx/Debug/2a1it684/logs/arm64-v8a/generate_cxx_metadata_1041_timing.txt +0 -9
  111. package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86_64/generate_cxx_metadata_1042_timing.txt +0 -9
  112. /package/android/.cxx/Debug/2a1it684/x86/.cmake/api/v1/reply/{index-2025-11-25T16-21-51-0349.json → index-2025-11-27T04-19-33-0139.json} +0 -0
  113. /package/android/.cxx/Debug/2a1it684/x86_64/.cmake/api/v1/reply/{index-2025-11-25T16-55-39-0849.json → index-2025-11-27T05-31-26-0486.json} +0 -0
  114. /package/android/build/intermediates/cxx/Debug/2a1it684/logs/x86/{generate_cxx_metadata_1042_timing.txt → generate_cxx_metadata_944_timing.txt} +0 -0
@@ -34,10 +34,10 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
34
34
  const val ACCELERATOR_GPU = "GPU"
35
35
  const val ACCELERATOR_NNAPI = "NNAPI"
36
36
 
37
- // Image format configuration for face recognition systems
38
- // Supported: JPEG (compressed), PNG (lossless), BMP (uncompressed)
37
+ // ENHANCED IMAGE FORMAT CONFIGURATION FOR FACE RECOGNITION
38
+ // Optimized for maximum quality for face detection APIs
39
39
  const val DEFAULT_IMAGE_FORMAT = "JPEG" // Options: "JPEG", "PNG", "BMP"
40
- const val DEFAULT_JPEG_QUALITY = 85 // 0-100, higher = better quality but larger
40
+ const val DEFAULT_JPEG_QUALITY = 92 // Increased from 85 to 92 for better quality
41
41
  const val DEFAULT_OUTPUT_RESOLUTION = 640 // 640×640 for face recognition
42
42
 
43
43
  // Advanced spoof detection constants
@@ -49,6 +49,11 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
49
49
  private const val TEXTURE_COMPLEXITY_THRESHOLD = 800
50
50
  private const val COLOR_SATURATION_THRESHOLD = 0.4f
51
51
  private const val DEPTH_VARIATION_THRESHOLD = 0.1f
52
+
53
+ // STABILITY ENHANCEMENT CONSTANTS
54
+ private const val BLUR_DETECTION_THRESHOLD = 100 // Laplacian score threshold
55
+ private const val MIN_STABLE_FRAMES = 3 // Require stable frames before generating base64
56
+ private const val MOTION_THRESHOLD = 0.1f // Limit motion/instability
52
57
  }
53
58
 
54
59
  private var interpreter: Interpreter? = null
@@ -79,6 +84,12 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
79
84
  private var frameCount = 0
80
85
  private var adaptiveThreshold = BASE_THRESHOLD
81
86
  private var environmentAssessment = EnvironmentAssessment()
87
+
88
+ // STABILITY AND QUALITY TRACKING
89
+ private var consecutiveQualityFrames = 0 // Track consecutive high-quality frames
90
+ private var lastStableFrameTime = System.currentTimeMillis()
91
+ private var lastProcessedBlurScore = 0
92
+ private var frameStabilityScore = 0f
82
93
 
83
94
  data class EnvironmentAssessment(
84
95
  var avgBrightness: Float = 0f,
@@ -285,21 +296,47 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
285
296
  var recognitionImageBase64: String? = null
286
297
  val DEBUG_FORCE_BASE64 = false // Set to true to force Base64 generation for testing
287
298
 
288
- if (isLive || DEBUG_FORCE_BASE64) {
289
- Log.d(TAG, "🔍 isLive=$isLive (DEBUG_FORCE_BASE64=$DEBUG_FORCE_BASE64) - generating ${outputImageResolution}×${outputImageResolution} $outputImageFormat Base64...")
299
+ // STABILITY CHECK: Only generate base64 for high-quality, stable frames
300
+ val isQualityFrame = laplacianScore > BLUR_DETECTION_THRESHOLD &&
301
+ temporalConsistency < MOTION_THRESHOLD &&
302
+ textureScore > TEXTURE_COMPLEXITY_THRESHOLD / 2
303
+
304
+ if (isQualityFrame) {
305
+ consecutiveQualityFrames++
306
+ frameStabilityScore = (laplacianScore.toFloat() / 1000f).coerceIn(0f, 1f)
307
+ } else {
308
+ consecutiveQualityFrames = 0
309
+ frameStabilityScore = 0f
310
+ }
311
+
312
+ // Only generate base64 after collecting stable frames
313
+ val shouldGenerateBase64 = (isLive && (consecutiveQualityFrames >= MIN_STABLE_FRAMES || DEBUG_FORCE_BASE64)) ||
314
+ DEBUG_FORCE_BASE64
315
+
316
+ if (shouldGenerateBase64) {
317
+ Log.d(TAG, "🔍 Quality Frame Detected - Stability Score: $frameStabilityScore (${consecutiveQualityFrames}/$MIN_STABLE_FRAMES frames)")
318
+ Log.d(TAG, " Blur Score: $laplacianScore (threshold: $BLUR_DETECTION_THRESHOLD)")
319
+ Log.d(TAG, " Motion: $temporalConsistency (threshold: $MOTION_THRESHOLD)")
320
+ Log.d(TAG, " Texture: $textureScore (min: ${TEXTURE_COMPLEXITY_THRESHOLD / 2})")
321
+
290
322
  try {
291
323
  recognitionImageBase64 = generateRecognitionImageBase64(nv21, width, height)
292
324
  if (recognitionImageBase64 != null) {
293
- Log.d(TAG, " Generated 640x640 JPEG Base64 for recognition API (size: ${recognitionImageBase64.length} bytes)")
325
+ Log.d(TAG, " Generated 640x640 JPEG Base64 for recognition API (size: ${recognitionImageBase64.length} bytes)")
294
326
  } else {
295
- Log.w(TAG, " generate640x640JpegBase64 returned null")
327
+ Log.w(TAG, "⚠️ generateRecognitionImageBase64 returned null")
296
328
  }
297
329
  } catch (e: Exception) {
298
- Log.w(TAG, " Failed to generate 640x640 JPEG Base64: ${e.message}", e)
330
+ Log.w(TAG, " Failed to generate Base64: ${e.message}", e)
299
331
  // Continue without the image - it's not critical
300
332
  }
301
333
  } else {
302
- Log.d(TAG, " isLive is FALSE - skipping Base64 generation")
334
+ if (!isLive) {
335
+ Log.d(TAG, "🚫 Not generating Base64 - Spoof detected (${spoofDetection.type})")
336
+ } else {
337
+ Log.d(TAG, "🚫 Waiting for stable frames... (${consecutiveQualityFrames}/$MIN_STABLE_FRAMES)")
338
+ Log.d(TAG, " Quality Score: $frameStabilityScore | Blur: $laplacianScore | Motion: $temporalConsistency")
339
+ }
303
340
  }
304
341
 
305
342
  AntiSpoofingResult(
@@ -397,6 +434,13 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
397
434
 
398
435
  /**
399
436
  * Generate image as Base64 string for face recognition API.
437
+ *
438
+ * QUALITY ENHANCEMENTS:
439
+ * - Uses bilinear interpolation for smooth resizing (no blur artifacts)
440
+ * - Applies unsharp masking for edge enhancement
441
+ * - Uses quality 92 JPEG encoding (up from 85) for better face detection
442
+ * - Validates frame stability before generation
443
+ *
400
444
  * Supports JPEG, PNG, and BMP formats with configurable resolution.
401
445
  * Compatible with OpenCV cv2.imdecode() for face recognition systems.
402
446
  *
@@ -407,9 +451,10 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
407
451
  */
408
452
  private fun generateRecognitionImageBase64(nv21: ByteArray, width: Int, height: Int): String? {
409
453
  return try {
410
- Log.d(TAG, "Starting ${outputImageResolution}×${outputImageResolution} $outputImageFormat generation. Input: ${width}x${height}, NV21 size: ${nv21.size}")
454
+ Log.d(TAG, "Starting HIGH-QUALITY ${outputImageResolution}×${outputImageResolution} $outputImageFormat generation...")
455
+ Log.d(TAG, "Input: ${width}x${height}, JPEG Quality: $outputImageQuality (optimized for face detection)")
411
456
 
412
- // Convert NV21 to RGB byte array directly (configurable resolution)
457
+ // Convert NV21 to RGB byte array using HIGH-QUALITY BILINEAR interpolation
413
458
  val rgbBytes = ByteArray(outputImageResolution * outputImageResolution * 3)
414
459
  Log.d(TAG, "Allocated RGB buffer: ${rgbBytes.size} bytes")
415
460
 
@@ -420,10 +465,13 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
420
465
  rgbBytes
421
466
  )
422
467
  val convertTime = System.currentTimeMillis() - startConvert
423
- Log.d(TAG, "YUV to RGB conversion completed in ${convertTime}ms. RGB data: ${convertedRgb.size} bytes")
468
+ Log.d(TAG, "YUVRGB conversion (bilinear): ${convertTime}ms | ${convertedRgb.size} bytes")
424
469
 
425
- // Encode RGB to target format (JPEG, PNG, or BMP) and Base64
426
- // NOTE: ImageFormatEncoder.rgbToBase64() automatically converts RGB→BGR for OpenCV compatibility
470
+ // Encode RGB to target format with sharpening
471
+ // NOTE: ImageFormatEncoder.rgbToBase64() automatically:
472
+ // - Applies unsharp masking for edge enhancement
473
+ // - Converts RGB→BGR for OpenCV compatibility
474
+ // - Uses optimized JPEG quality
427
475
  val startEncode = System.currentTimeMillis()
428
476
  val format = when (outputImageFormat) {
429
477
  "PNG" -> ImageFormatEncoder.ImageFormat.PNG
@@ -436,17 +484,22 @@ class FaceAntiSpoofingAdvanced(private val assetManager: AssetManager) {
436
484
  outputImageResolution,
437
485
  outputImageResolution,
438
486
  format,
439
- outputImageQuality
487
+ outputImageQuality // Using quality 92 for better face detection
440
488
  )
441
489
  val encodeTime = System.currentTimeMillis() - startEncode
442
490
 
443
491
  if (base64Result != null) {
444
- Log.d(TAG, "$outputImageFormat encoding completed in ${encodeTime}ms. Base64 size: ${base64Result.length} bytes")
445
- Log.d(TAG, "✅ Generated ${outputImageResolution}×${outputImageResolution} $outputImageFormat Base64 for recognition API in BGR format (total: ${convertTime + encodeTime}ms)")
492
+ Log.d(TAG, "$outputImageFormat encoding (with sharpening): ${encodeTime}ms")
493
+ Log.d(TAG, "✅ HIGH-QUALITY Base64 Generated:")
494
+ Log.d(TAG, " Resolution: ${outputImageResolution}×${outputImageResolution}")
495
+ Log.d(TAG, " Format: $outputImageFormat (BGR for OpenCV)")
496
+ Log.d(TAG, " Quality: $outputImageQuality (enhanced for face recognition)")
497
+ Log.d(TAG, " Size: ${base64Result.length} chars (est. ${base64Result.length * 0.75 / 1024}KB)")
498
+ Log.d(TAG, " Total Time: ${convertTime + encodeTime}ms")
446
499
  val estSize = estimateImageSize()
447
- Log.d(TAG, "Format: $outputImageFormat (BGR), Quality: $outputImageQuality, Estimated: ${estSize / 1024}KB")
500
+ Log.d(TAG, " Estimated: ${estSize / 1024}KB")
448
501
  } else {
449
- Log.w(TAG, "⚠️ ImageFormatEncoder returned null for BGR conversion")
502
+ Log.w(TAG, "⚠️ ImageFormatEncoder returned null")
450
503
  }
451
504
 
452
505
  base64Result
@@ -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
- val bgrData = ByteArray(rgbData.size)
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 < rgbData.size && rgbIdx + 2 < rgbData.size) {
233
- val r = rgbData[rgbIdx++]
234
- val g = rgbData[rgbIdx++]
235
- val b = rgbData[rgbIdx++]
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: ${rgbData.size} bytes")
329
+ Log.d(TAG, "Converted RGB to BGR with sharpening: ${processedRgb.size} bytes")
244
330
  return bgrData
245
331
  }
246
332