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

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 (201) hide show
  1. package/android/CMakeLists.txt +12 -0
  2. package/android/build.gradle +10 -9
  3. package/android/src/main/cpp/cpp-adapter.cpp +6 -3
  4. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +83 -0
  5. package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorOutput.kt +26 -11
  6. package/package.json +1 -1
  7. package/android/src/main/cpp/CMakeLists.txt +0 -33
  8. package/android/src/main/cpp/FaceAntiSpoofJSI.cpp +0 -142
  9. package/android/src/main/cpp/FastYuvJni.cpp +0 -111
  10. package/android/src/main/cpp/fast_yuv.cpp +0 -342
  11. package/android/src/main/cpp/fast_yuv.h +0 -23
  12. package/android/src/main/cpp/libyuv/.clang-format +0 -6
  13. package/android/src/main/cpp/libyuv/.gn +0 -40
  14. package/android/src/main/cpp/libyuv/.vpython3 +0 -410
  15. package/android/src/main/cpp/libyuv/AUTHORS +0 -7
  16. package/android/src/main/cpp/libyuv/Android.bp +0 -202
  17. package/android/src/main/cpp/libyuv/Android.mk +0 -106
  18. package/android/src/main/cpp/libyuv/BUILD.gn +0 -490
  19. package/android/src/main/cpp/libyuv/CM_linux_packages.cmake +0 -70
  20. package/android/src/main/cpp/libyuv/CMakeLists.txt +0 -259
  21. package/android/src/main/cpp/libyuv/DEPS +0 -935
  22. package/android/src/main/cpp/libyuv/DIR_METADATA +0 -3
  23. package/android/src/main/cpp/libyuv/LICENSE +0 -29
  24. package/android/src/main/cpp/libyuv/OWNERS +0 -11
  25. package/android/src/main/cpp/libyuv/PATENTS +0 -24
  26. package/android/src/main/cpp/libyuv/PRESUBMIT.py +0 -65
  27. package/android/src/main/cpp/libyuv/README.chromium +0 -11
  28. package/android/src/main/cpp/libyuv/README.md +0 -19
  29. package/android/src/main/cpp/libyuv/build_overrides/build.gni +0 -60
  30. package/android/src/main/cpp/libyuv/build_overrides/gtest.gni +0 -19
  31. package/android/src/main/cpp/libyuv/build_overrides/partition_alloc.gni +0 -17
  32. package/android/src/main/cpp/libyuv/codereview.settings +0 -5
  33. package/android/src/main/cpp/libyuv/docs/deprecated_builds.md +0 -409
  34. package/android/src/main/cpp/libyuv/docs/environment_variables.md +0 -64
  35. package/android/src/main/cpp/libyuv/docs/feature_detection.md +0 -108
  36. package/android/src/main/cpp/libyuv/docs/filtering.md +0 -196
  37. package/android/src/main/cpp/libyuv/docs/formats.md +0 -208
  38. package/android/src/main/cpp/libyuv/docs/getting_started.md +0 -296
  39. package/android/src/main/cpp/libyuv/docs/rotation.md +0 -107
  40. package/android/src/main/cpp/libyuv/download_vs_toolchain.py +0 -29
  41. package/android/src/main/cpp/libyuv/include/libyuv/basic_types.h +0 -68
  42. package/android/src/main/cpp/libyuv/include/libyuv/compare.h +0 -111
  43. package/android/src/main/cpp/libyuv/include/libyuv/compare_row.h +0 -112
  44. package/android/src/main/cpp/libyuv/include/libyuv/convert.h +0 -1117
  45. package/android/src/main/cpp/libyuv/include/libyuv/convert_argb.h +0 -2335
  46. package/android/src/main/cpp/libyuv/include/libyuv/convert_from.h +0 -203
  47. package/android/src/main/cpp/libyuv/include/libyuv/convert_from_argb.h +0 -394
  48. package/android/src/main/cpp/libyuv/include/libyuv/cpu_id.h +0 -147
  49. package/android/src/main/cpp/libyuv/include/libyuv/cpu_support.h +0 -99
  50. package/android/src/main/cpp/libyuv/include/libyuv/loongson_intrinsics.h +0 -1949
  51. package/android/src/main/cpp/libyuv/include/libyuv/mjpeg_decoder.h +0 -195
  52. package/android/src/main/cpp/libyuv/include/libyuv/planar_functions.h +0 -1131
  53. package/android/src/main/cpp/libyuv/include/libyuv/rotate.h +0 -296
  54. package/android/src/main/cpp/libyuv/include/libyuv/rotate_argb.h +0 -37
  55. package/android/src/main/cpp/libyuv/include/libyuv/rotate_row.h +0 -265
  56. package/android/src/main/cpp/libyuv/include/libyuv/row.h +0 -6738
  57. package/android/src/main/cpp/libyuv/include/libyuv/row_sve.h +0 -2154
  58. package/android/src/main/cpp/libyuv/include/libyuv/scale.h +0 -336
  59. package/android/src/main/cpp/libyuv/include/libyuv/scale_argb.h +0 -76
  60. package/android/src/main/cpp/libyuv/include/libyuv/scale_rgb.h +0 -42
  61. package/android/src/main/cpp/libyuv/include/libyuv/scale_row.h +0 -1726
  62. package/android/src/main/cpp/libyuv/include/libyuv/scale_uv.h +0 -51
  63. package/android/src/main/cpp/libyuv/include/libyuv/version.h +0 -16
  64. package/android/src/main/cpp/libyuv/include/libyuv/video_common.h +0 -222
  65. package/android/src/main/cpp/libyuv/include/libyuv.h +0 -33
  66. package/android/src/main/cpp/libyuv/infra/config/OWNERS +0 -3
  67. package/android/src/main/cpp/libyuv/infra/config/PRESUBMIT.py +0 -17
  68. package/android/src/main/cpp/libyuv/infra/config/README.md +0 -2
  69. package/android/src/main/cpp/libyuv/infra/config/codereview.settings +0 -6
  70. package/android/src/main/cpp/libyuv/infra/config/commit-queue.cfg +0 -144
  71. package/android/src/main/cpp/libyuv/infra/config/cr-buildbucket.cfg +0 -1185
  72. package/android/src/main/cpp/libyuv/infra/config/luci-logdog.cfg +0 -9
  73. package/android/src/main/cpp/libyuv/infra/config/luci-milo.cfg +0 -246
  74. package/android/src/main/cpp/libyuv/infra/config/luci-scheduler.cfg +0 -385
  75. package/android/src/main/cpp/libyuv/infra/config/main.star +0 -402
  76. package/android/src/main/cpp/libyuv/infra/config/project.cfg +0 -16
  77. package/android/src/main/cpp/libyuv/infra/config/realms.cfg +0 -111
  78. package/android/src/main/cpp/libyuv/libyuv.gni +0 -34
  79. package/android/src/main/cpp/libyuv/libyuv.gyp +0 -149
  80. package/android/src/main/cpp/libyuv/libyuv.gypi +0 -87
  81. package/android/src/main/cpp/libyuv/linux.mk +0 -96
  82. package/android/src/main/cpp/libyuv/public.mk +0 -13
  83. package/android/src/main/cpp/libyuv/pylintrc +0 -49
  84. package/android/src/main/cpp/libyuv/riscv_script/prepare_toolchain_qemu.sh +0 -74
  85. package/android/src/main/cpp/libyuv/riscv_script/riscv-clang.cmake +0 -56
  86. package/android/src/main/cpp/libyuv/riscv_script/run_qemu.sh +0 -15
  87. package/android/src/main/cpp/libyuv/source/compare.cc +0 -435
  88. package/android/src/main/cpp/libyuv/source/compare_common.cc +0 -74
  89. package/android/src/main/cpp/libyuv/source/compare_gcc.cc +0 -362
  90. package/android/src/main/cpp/libyuv/source/compare_neon.cc +0 -96
  91. package/android/src/main/cpp/libyuv/source/compare_neon64.cc +0 -223
  92. package/android/src/main/cpp/libyuv/source/compare_win.cc +0 -241
  93. package/android/src/main/cpp/libyuv/source/convert.cc +0 -4746
  94. package/android/src/main/cpp/libyuv/source/convert_argb.cc +0 -9179
  95. package/android/src/main/cpp/libyuv/source/convert_from.cc +0 -866
  96. package/android/src/main/cpp/libyuv/source/convert_from_argb.cc +0 -3671
  97. package/android/src/main/cpp/libyuv/source/convert_jpeg.cc +0 -602
  98. package/android/src/main/cpp/libyuv/source/convert_to_argb.cc +0 -391
  99. package/android/src/main/cpp/libyuv/source/convert_to_i420.cc +0 -288
  100. package/android/src/main/cpp/libyuv/source/cpu_id.cc +0 -496
  101. package/android/src/main/cpp/libyuv/source/mjpeg_decoder.cc +0 -580
  102. package/android/src/main/cpp/libyuv/source/mjpeg_validate.cc +0 -71
  103. package/android/src/main/cpp/libyuv/source/planar_functions.cc +0 -5663
  104. package/android/src/main/cpp/libyuv/source/rotate.cc +0 -1241
  105. package/android/src/main/cpp/libyuv/source/rotate_any.cc +0 -76
  106. package/android/src/main/cpp/libyuv/source/rotate_argb.cc +0 -259
  107. package/android/src/main/cpp/libyuv/source/rotate_common.cc +0 -208
  108. package/android/src/main/cpp/libyuv/source/rotate_gcc.cc +0 -505
  109. package/android/src/main/cpp/libyuv/source/rotate_lsx.cc +0 -233
  110. package/android/src/main/cpp/libyuv/source/rotate_neon.cc +0 -219
  111. package/android/src/main/cpp/libyuv/source/rotate_neon64.cc +0 -273
  112. package/android/src/main/cpp/libyuv/source/rotate_sme.cc +0 -174
  113. package/android/src/main/cpp/libyuv/source/rotate_win.cc +0 -253
  114. package/android/src/main/cpp/libyuv/source/row_any.cc +0 -2519
  115. package/android/src/main/cpp/libyuv/source/row_common.cc +0 -4461
  116. package/android/src/main/cpp/libyuv/source/row_gcc.cc +0 -9570
  117. package/android/src/main/cpp/libyuv/source/row_lasx.cc +0 -2343
  118. package/android/src/main/cpp/libyuv/source/row_lsx.cc +0 -3030
  119. package/android/src/main/cpp/libyuv/source/row_neon.cc +0 -4026
  120. package/android/src/main/cpp/libyuv/source/row_neon64.cc +0 -5617
  121. package/android/src/main/cpp/libyuv/source/row_rvv.cc +0 -2599
  122. package/android/src/main/cpp/libyuv/source/row_sme.cc +0 -1183
  123. package/android/src/main/cpp/libyuv/source/row_sve.cc +0 -1088
  124. package/android/src/main/cpp/libyuv/source/row_win.cc +0 -6453
  125. package/android/src/main/cpp/libyuv/source/scale.cc +0 -2710
  126. package/android/src/main/cpp/libyuv/source/scale_any.cc +0 -991
  127. package/android/src/main/cpp/libyuv/source/scale_argb.cc +0 -1158
  128. package/android/src/main/cpp/libyuv/source/scale_common.cc +0 -1977
  129. package/android/src/main/cpp/libyuv/source/scale_gcc.cc +0 -2947
  130. package/android/src/main/cpp/libyuv/source/scale_lsx.cc +0 -739
  131. package/android/src/main/cpp/libyuv/source/scale_neon.cc +0 -1449
  132. package/android/src/main/cpp/libyuv/source/scale_neon64.cc +0 -1552
  133. package/android/src/main/cpp/libyuv/source/scale_rgb.cc +0 -82
  134. package/android/src/main/cpp/libyuv/source/scale_rvv.cc +0 -1971
  135. package/android/src/main/cpp/libyuv/source/scale_sme.cc +0 -555
  136. package/android/src/main/cpp/libyuv/source/scale_uv.cc +0 -1159
  137. package/android/src/main/cpp/libyuv/source/scale_win.cc +0 -1392
  138. package/android/src/main/cpp/libyuv/source/test.sh +0 -35
  139. package/android/src/main/cpp/libyuv/source/video_common.cc +0 -62
  140. package/android/src/main/cpp/libyuv/tools_libyuv/OWNERS +0 -4
  141. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/roll_deps.py +0 -931
  142. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/roll_deps_test.py +0 -164
  143. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS +0 -21
  144. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.new +0 -13
  145. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.old +0 -13
  146. package/android/src/main/cpp/libyuv/tools_libyuv/get_landmines.py +0 -38
  147. package/android/src/main/cpp/libyuv/tools_libyuv/msan/OWNERS +0 -3
  148. package/android/src/main/cpp/libyuv/tools_libyuv/msan/blacklist.txt +0 -9
  149. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/OWNERS +0 -3
  150. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/blacklist.txt +0 -15
  151. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/vptr_blacklist.txt +0 -25
  152. package/android/src/main/cpp/libyuv/unit_test/basictypes_test.cc +0 -43
  153. package/android/src/main/cpp/libyuv/unit_test/color_test.cc +0 -848
  154. package/android/src/main/cpp/libyuv/unit_test/compare_test.cc +0 -739
  155. package/android/src/main/cpp/libyuv/unit_test/convert_argb_test.cc +0 -2867
  156. package/android/src/main/cpp/libyuv/unit_test/convert_test.cc +0 -2133
  157. package/android/src/main/cpp/libyuv/unit_test/cpu_test.cc +0 -427
  158. package/android/src/main/cpp/libyuv/unit_test/cpu_thread_test.cc +0 -63
  159. package/android/src/main/cpp/libyuv/unit_test/math_test.cc +0 -160
  160. package/android/src/main/cpp/libyuv/unit_test/planar_test.cc +0 -4731
  161. package/android/src/main/cpp/libyuv/unit_test/rotate_argb_test.cc +0 -334
  162. package/android/src/main/cpp/libyuv/unit_test/rotate_test.cc +0 -962
  163. package/android/src/main/cpp/libyuv/unit_test/scale_argb_test.cc +0 -590
  164. package/android/src/main/cpp/libyuv/unit_test/scale_plane_test.cc +0 -465
  165. package/android/src/main/cpp/libyuv/unit_test/scale_rgb_test.cc +0 -280
  166. package/android/src/main/cpp/libyuv/unit_test/scale_test.cc +0 -1135
  167. package/android/src/main/cpp/libyuv/unit_test/scale_uv_test.cc +0 -249
  168. package/android/src/main/cpp/libyuv/unit_test/testdata/arm_v7.txt +0 -12
  169. package/android/src/main/cpp/libyuv/unit_test/testdata/mips.txt +0 -7
  170. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson2k.txt +0 -5
  171. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson3.txt +0 -10
  172. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson_mmi.txt +0 -7
  173. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64.txt +0 -4
  174. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv.txt +0 -4
  175. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv_zvfh.txt +0 -4
  176. package/android/src/main/cpp/libyuv/unit_test/testdata/tegra3.txt +0 -23
  177. package/android/src/main/cpp/libyuv/unit_test/testdata/test0.jpg +0 -0
  178. package/android/src/main/cpp/libyuv/unit_test/testdata/test1.jpg +0 -0
  179. package/android/src/main/cpp/libyuv/unit_test/testdata/test2.jpg +0 -0
  180. package/android/src/main/cpp/libyuv/unit_test/testdata/test3.jpg +0 -0
  181. package/android/src/main/cpp/libyuv/unit_test/testdata/test4.jpg +0 -0
  182. package/android/src/main/cpp/libyuv/unit_test/unit_test.cc +0 -581
  183. package/android/src/main/cpp/libyuv/unit_test/unit_test.h +0 -232
  184. package/android/src/main/cpp/libyuv/unit_test/video_common_test.cc +0 -112
  185. package/android/src/main/cpp/libyuv/util/Makefile +0 -9
  186. package/android/src/main/cpp/libyuv/util/color.cc +0 -120
  187. package/android/src/main/cpp/libyuv/util/compare.cc +0 -67
  188. package/android/src/main/cpp/libyuv/util/cpuid.c +0 -211
  189. package/android/src/main/cpp/libyuv/util/i444tonv12_eg.cc +0 -28
  190. package/android/src/main/cpp/libyuv/util/psnr.cc +0 -291
  191. package/android/src/main/cpp/libyuv/util/psnr.h +0 -47
  192. package/android/src/main/cpp/libyuv/util/psnr_main.cc +0 -620
  193. package/android/src/main/cpp/libyuv/util/ssim.cc +0 -364
  194. package/android/src/main/cpp/libyuv/util/ssim.h +0 -38
  195. package/android/src/main/cpp/libyuv/util/yuvconstants.c +0 -114
  196. package/android/src/main/cpp/libyuv/util/yuvconvert.cc +0 -367
  197. package/android/src/main/cpp/libyuv/winarm.mk +0 -47
  198. package/android/src/main/java/com/faceantispoof/ByteBufferPool.kt +0 -68
  199. package/android/src/main/java/com/faceantispoof/FastYuv.kt +0 -51
  200. package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +0 -584
  201. package/android/src/main/java/com/faceantispoof/YuvToRgbConverter.kt +0 -73
@@ -1,584 +0,0 @@
1
- package com.faceantispoof
2
-
3
- import java.nio.ByteBuffer
4
- import kotlin.math.abs
5
- import kotlin.math.pow
6
-
7
- /**
8
- * Converts YUV NV21 data directly to normalized RGB float arrays without creating Bitmaps.
9
- * Supports high-quality bilinear interpolation for resizing to minimize blur.
10
- * Includes motion/stability detection and frame quality assessment.
11
- */
12
- class YuvToFloatArrayConverter {
13
-
14
- // Motion detection and frame stability
15
- private var previousGrayscale: IntArray? = null
16
- private var frameMotionScore = 0f
17
- private var isFrameStable = false
18
- private var consecutiveStableFrames = 0
19
- private val STABILITY_THRESHOLD = 0.05f // Lower = more stable frames required
20
- private val STABLE_FRAMES_REQUIRED = 3
21
-
22
- companion object {
23
- // Interpolation quality modes
24
- const val INTERPOLATION_NEAREST = 0
25
- const val INTERPOLATION_BILINEAR = 1
26
- const val INTERPOLATION_BICUBIC = 2
27
-
28
- // Default to bilinear for better quality
29
- private val DEFAULT_INTERPOLATION = INTERPOLATION_BILINEAR
30
- }
31
-
32
- /**
33
- * Convert NV21 YUV data directly to a normalized RGB float array.
34
- * Uses high-quality bilinear interpolation to preserve image sharpness.
35
- * Output array is [height][width][3] where RGB channels are normalized to [0, 1].
36
- * @param nv21 NV21 encoded YUV data
37
- * @param width Original frame width
38
- * @param height Original frame height
39
- * @param targetWidth Target output width (can be different for resizing)
40
- * @param targetHeight Target output height (can be different for resizing)
41
- * @param outputRgbFloat Pre-allocated float array to store output, or null to allocate new
42
- * @return Float array of shape [targetHeight * targetWidth * 3] with RGB values normalized to [0, 1]
43
- */
44
- fun nv21ToRgbFloatArray(
45
- nv21: ByteArray,
46
- width: Int,
47
- height: Int,
48
- targetWidth: Int = width,
49
- targetHeight: Int = height,
50
- outputRgbFloat: FloatArray? = null,
51
- interpolationMode: Int = DEFAULT_INTERPOLATION
52
- ): FloatArray {
53
- if (nv21.isEmpty()) {
54
- throw IllegalArgumentException("NV21 array is empty")
55
- }
56
- if (width <= 0 || height <= 0) {
57
- throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
58
- }
59
- if (targetWidth <= 0 || targetHeight <= 0) {
60
- throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
61
- }
62
-
63
- val outputSize = targetWidth * targetHeight * 3
64
- val output = outputRgbFloat?.takeIf { it.size >= outputSize } ?: FloatArray(outputSize)
65
-
66
- val ySize = width * height
67
- val uvPixelStride = 2
68
-
69
- // Validate NV21 buffer size
70
- val minExpectedSize = width * height + (width * height) / 2
71
- if (nv21.size < minExpectedSize) {
72
- throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
73
- }
74
-
75
- // Use bilinear interpolation for high-quality resizing
76
- if (interpolationMode == INTERPOLATION_BILINEAR && (width != targetWidth || height != targetHeight)) {
77
- return nv21ToRgbFloatArrayBilinear(nv21, width, height, targetWidth, targetHeight, output)
78
- }
79
-
80
- // Fallback to nearest neighbor
81
- var outIdx = 0
82
-
83
- for (targetY in 0 until targetHeight) {
84
- val srcY = (targetY * height) / targetHeight
85
- if (srcY >= height) {
86
- throw IndexOutOfBoundsException("Source Y index out of bounds: srcY=$srcY, height=$height")
87
- }
88
- val srcRowOffset = srcY * width
89
-
90
- for (targetX in 0 until targetWidth) {
91
- val srcX = (targetX * width) / targetWidth
92
- if (srcX >= width) {
93
- throw IndexOutOfBoundsException("Source X index out of bounds: srcX=$srcX, width=$width")
94
- }
95
-
96
- // Get Y component
97
- val yIdx = srcRowOffset + srcX
98
- if (yIdx >= nv21.size) {
99
- throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
100
- }
101
- val yVal = nv21[yIdx].toInt() and 0xFF
102
-
103
- // Get U and V components (NV21 layout: UV interleaved in second half)
104
- val uvIndex = ySize + (srcY / 2) * width + (srcX / 2) * uvPixelStride
105
- if (uvIndex + 1 >= nv21.size) {
106
- throw IndexOutOfBoundsException("UV index out of bounds: uvIndex=$uvIndex, nv21.size=${nv21.size}")
107
- }
108
- val vVal = nv21[uvIndex].toInt() and 0xFF
109
- val uVal = nv21[uvIndex + 1].toInt() and 0xFF
110
-
111
- // YUV to RGB conversion
112
- val yf = yVal.toFloat()
113
- val uf = uVal - 128f
114
- val vf = vVal - 128f
115
-
116
- val r = (yf + 1.402f * vf).coerceIn(0f, 255f)
117
- val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f)
118
- val b = (yf + 1.772f * uf).coerceIn(0f, 255f)
119
-
120
- // Normalize to [0, 1]
121
- if (outIdx + 2 < output.size) {
122
- output[outIdx++] = r / 255f
123
- output[outIdx++] = g / 255f
124
- output[outIdx++] = b / 255f
125
- } else {
126
- throw IndexOutOfBoundsException("Output array index out of bounds: outIdx=$outIdx, output.size=${output.size}")
127
- }
128
- }
129
- }
130
-
131
- return output
132
- }
133
-
134
- /**
135
- * High-quality bilinear interpolation for resizing YUV to RGB.
136
- * Significantly better quality than nearest-neighbor, reduces blur artifacts.
137
- */
138
- private fun nv21ToRgbFloatArrayBilinear(
139
- nv21: ByteArray,
140
- width: Int,
141
- height: Int,
142
- targetWidth: Int,
143
- targetHeight: Int,
144
- output: FloatArray
145
- ): FloatArray {
146
- val ySize = width * height
147
- val uvPixelStride = 2
148
- val scaleX = width.toFloat() / targetWidth
149
- val scaleY = height.toFloat() / targetHeight
150
-
151
- var outIdx = 0
152
-
153
- for (targetY in 0 until targetHeight) {
154
- val srcY = targetY * scaleY
155
- val y1 = srcY.toInt()
156
- val y2 = (y1 + 1).coerceAtMost(height - 1)
157
- val fracY = srcY - y1
158
-
159
- for (targetX in 0 until targetWidth) {
160
- val srcX = targetX * scaleX
161
- val x1 = srcX.toInt()
162
- val x2 = (x1 + 1).coerceAtMost(width - 1)
163
- val fracX = srcX - x1
164
-
165
- // Sample 4 neighboring pixels in YUV space and interpolate
166
- val (r, g, b) = bilinearInterpolateYuv(
167
- nv21, width, height, ySize, uvPixelStride,
168
- x1, y1, x2, y2, fracX, fracY
169
- )
170
-
171
- if (outIdx + 2 < output.size) {
172
- output[outIdx++] = r / 255f
173
- output[outIdx++] = g / 255f
174
- output[outIdx++] = b / 255f
175
- }
176
- }
177
- }
178
-
179
- return output
180
- }
181
-
182
- /**
183
- * Bilinear interpolation for YUV pixel sampling.
184
- * Returns interpolated RGB values.
185
- */
186
- private fun bilinearInterpolateYuv(
187
- nv21: ByteArray,
188
- width: Int,
189
- height: Int,
190
- ySize: Int,
191
- uvPixelStride: Int,
192
- x1: Int,
193
- y1: Int,
194
- x2: Int,
195
- y2: Int,
196
- fracX: Float,
197
- fracY: Float
198
- ): Triple<Float, Float, Float> {
199
- // Sample all 4 corners
200
- val samples = arrayOf(
201
- yuvToRgb(nv21, width, ySize, uvPixelStride, x1, y1),
202
- yuvToRgb(nv21, width, ySize, uvPixelStride, x2, y1),
203
- yuvToRgb(nv21, width, ySize, uvPixelStride, x1, y2),
204
- yuvToRgb(nv21, width, ySize, uvPixelStride, x2, y2)
205
- )
206
-
207
- // Bilinear interpolation
208
- val top = lerp(samples[0], samples[1], fracX)
209
- val bottom = lerp(samples[2], samples[3], fracX)
210
- return lerp(top, bottom, fracY)
211
- }
212
-
213
- /**
214
- * Linear interpolation helper.
215
- */
216
- private fun lerp(a: Triple<Float, Float, Float>, b: Triple<Float, Float, Float>, t: Float): Triple<Float, Float, Float> {
217
- return Triple(
218
- a.first * (1 - t) + b.first * t,
219
- a.second * (1 - t) + b.second * t,
220
- a.third * (1 - t) + b.third * t
221
- )
222
- }
223
-
224
- /**
225
- * Convert a single YUV pixel to RGB with optional gamma correction.
226
- * Applies sRGB gamma curve to match hardware ISP output.
227
- */
228
- private fun yuvToRgb(
229
- nv21: ByteArray,
230
- width: Int,
231
- ySize: Int,
232
- uvPixelStride: Int,
233
- x: Int,
234
- y: Int
235
- ): Triple<Float, Float, Float> {
236
- val yIdx = y * width + x
237
- val yVal = nv21[yIdx].toInt() and 0xFF
238
-
239
- val uvIndex = ySize + (y / 2) * width + (x / 2) * uvPixelStride
240
- val vVal = nv21[uvIndex].toInt() and 0xFF
241
- val uVal = nv21[uvIndex + 1].toInt() and 0xFF
242
-
243
- val yf = yVal.toFloat()
244
- val uf = uVal - 128f
245
- val vf = vVal - 128f
246
-
247
- val r = (yf + 1.402f * vf).coerceIn(0f, 255f)
248
- val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f)
249
- val b = (yf + 1.772f * uf).coerceIn(0f, 255f)
250
-
251
- // FIXED: Apply sRGB gamma correction to match hardware ISP
252
- // This ensures that colors match hardware-captured JPEGs
253
- val rLinear = r / 255f
254
- val gLinear = g / 255f
255
- val bLinear = b / 255f
256
-
257
- val rCorrected = applyGammaCorrection(rLinear)
258
- val gCorrected = applyGammaCorrection(gLinear)
259
- val bCorrected = applyGammaCorrection(bLinear)
260
-
261
- return Triple(rCorrected * 255f, gCorrected * 255f, bCorrected * 255f)
262
- }
263
-
264
- /**
265
- * Convert NV21 directly to a flat RGB float buffer (single channel interleaving).
266
- * @param nv21 NV21 encoded YUV data
267
- * @param width Original frame width
268
- * @param height Original frame height
269
- * @param targetWidth Target output width
270
- * @param targetHeight Target output height
271
- * @return ByteBuffer containing RGB floats in native byte order
272
- */
273
- fun nv21ToRgbByteBuffer(
274
- nv21: ByteArray,
275
- width: Int,
276
- height: Int,
277
- targetWidth: Int = width,
278
- targetHeight: Int = height
279
- ): ByteBuffer {
280
- val outputSize = targetWidth * targetHeight * 3 * 4 // 3 floats per pixel, 4 bytes per float
281
- val output = ByteBuffer.allocateDirect(outputSize).apply {
282
- order(java.nio.ByteOrder.nativeOrder())
283
- }
284
-
285
- val ySize = width * height
286
- val uvPixelStride = 2
287
-
288
- for (targetY in 0 until targetHeight) {
289
- val srcY = (targetY * height) / targetHeight
290
- val srcRowOffset = srcY * width
291
-
292
- for (targetX in 0 until targetWidth) {
293
- val srcX = (targetX * width) / targetWidth
294
-
295
- val yVal = nv21[srcRowOffset + srcX].toInt() and 0xFF
296
-
297
- val uvIndex = ySize + (srcY / 2) * width + (srcX / 2) * uvPixelStride
298
- val vVal = nv21[uvIndex].toInt() and 0xFF
299
- val uVal = nv21[uvIndex + 1].toInt() and 0xFF
300
-
301
- val yf = yVal.toFloat()
302
- val uf = uVal - 128f
303
- val vf = vVal - 128f
304
-
305
- val r = (yf + 1.402f * vf).coerceIn(0f, 255f) / 255f
306
- val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f) / 255f
307
- val b = (yf + 1.772f * uf).coerceIn(0f, 255f) / 255f
308
-
309
- output.putFloat(r)
310
- output.putFloat(g)
311
- output.putFloat(b)
312
- }
313
- }
314
-
315
- output.rewind()
316
- return output
317
- }
318
-
319
- /**
320
- * Convert NV21 to grayscale intensity values stored as floats [0, 1].
321
- * Useful for Laplacian edge detection.
322
- * @param nv21 NV21 encoded YUV data
323
- * @param width Original frame width
324
- * @param height Original frame height
325
- * @param targetWidth Target output width
326
- * @param targetHeight Target output height
327
- * @return Float array of size [targetHeight * targetWidth] with normalized grayscale values
328
- */
329
- fun nv21ToGrayscaleFloatArray(
330
- nv21: ByteArray,
331
- width: Int,
332
- height: Int,
333
- targetWidth: Int = width,
334
- targetHeight: Int = height,
335
- outputGrayscale: FloatArray? = null
336
- ): FloatArray {
337
- val outputSize = targetWidth * targetHeight
338
- val output = outputGrayscale?.takeIf { it.size >= outputSize } ?: FloatArray(outputSize)
339
-
340
- val ySize = width * height
341
-
342
- var outIdx = 0
343
- for (targetY in 0 until targetHeight) {
344
- val srcY = (targetY * height) / targetHeight
345
- val srcRowOffset = srcY * width
346
-
347
- for (targetX in 0 until targetWidth) {
348
- val srcX = (targetX * width) / targetWidth
349
- val yVal = nv21[srcRowOffset + srcX].toInt() and 0xFF
350
- output[outIdx++] = yVal / 255f
351
- }
352
- }
353
-
354
- return output
355
- }
356
-
357
- /**
358
- * Convert NV21 to integer grayscale values [0, 255].
359
- * More efficient for operations that don't need float precision.
360
- */
361
- fun nv21ToGrayscaleIntArray(
362
- nv21: ByteArray,
363
- width: Int,
364
- height: Int,
365
- targetWidth: Int = width,
366
- targetHeight: Int = height,
367
- outputGrayscale: IntArray? = null
368
- ): IntArray {
369
- if (nv21.isEmpty()) {
370
- throw IllegalArgumentException("NV21 array is empty")
371
- }
372
- if (width <= 0 || height <= 0) {
373
- throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
374
- }
375
- if (targetWidth <= 0 || targetHeight <= 0) {
376
- throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
377
- }
378
-
379
- val outputSize = targetWidth * targetHeight
380
- val output = outputGrayscale?.takeIf { it.size >= outputSize } ?: IntArray(outputSize)
381
-
382
- val ySize = width * height
383
- val minExpectedSize = width * height + (width * height) / 2
384
- if (nv21.size < minExpectedSize) {
385
- throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
386
- }
387
-
388
- var outIdx = 0
389
- for (targetY in 0 until targetHeight) {
390
- val srcY = (targetY * height) / targetHeight
391
- if (srcY >= height) {
392
- throw IndexOutOfBoundsException("Source Y out of bounds: srcY=$srcY, height=$height")
393
- }
394
- val srcRowOffset = srcY * width
395
-
396
- for (targetX in 0 until targetWidth) {
397
- val srcX = (targetX * width) / targetWidth
398
- if (srcX >= width) {
399
- throw IndexOutOfBoundsException("Source X out of bounds: srcX=$srcX, width=$width")
400
- }
401
- val yIdx = srcRowOffset + srcX
402
- if (yIdx >= nv21.size) {
403
- throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
404
- }
405
- val yVal = nv21[yIdx].toInt() and 0xFF
406
- if (outIdx < output.size) {
407
- output[outIdx++] = yVal
408
- } else {
409
- throw IndexOutOfBoundsException("Output index out of bounds: outIdx=$outIdx, output.size=${output.size}")
410
- }
411
- }
412
- }
413
-
414
- return output
415
- }
416
-
417
- /**
418
- * Convert NV21 directly to RGB byte array without float conversion.
419
- * Uses high-quality bilinear interpolation for resizing.
420
- * Optimized for JPEG encoding (where RGB bytes are needed directly).
421
- * Output format: RGBRGBRGB... (no alpha channel)
422
- *
423
- * @param nv21 NV21 encoded YUV data
424
- * @param width Original frame width
425
- * @param height Original frame height
426
- * @param targetWidth Target output width (for resizing)
427
- * @param targetHeight Target output height (for resizing)
428
- * @param outputRgbBytes Pre-allocated byte array, or null to allocate new
429
- * @return Byte array of size [targetHeight * targetWidth * 3] with RGB bytes
430
- */
431
- fun nv21ToRgbByteArray(
432
- nv21: ByteArray,
433
- width: Int,
434
- height: Int,
435
- targetWidth: Int = width,
436
- targetHeight: Int = height,
437
- outputRgbBytes: ByteArray? = null
438
- ): ByteArray {
439
- if (nv21.isEmpty()) {
440
- throw IllegalArgumentException("NV21 array is empty")
441
- }
442
- if (width <= 0 || height <= 0) {
443
- throw IllegalArgumentException("Invalid frame dimensions: width=$width, height=$height")
444
- }
445
- if (targetWidth <= 0 || targetHeight <= 0) {
446
- throw IllegalArgumentException("Invalid target dimensions: targetWidth=$targetWidth, targetHeight=$targetHeight")
447
- }
448
-
449
- val outputSize = targetWidth * targetHeight * 3
450
- val output = outputRgbBytes?.takeIf { it.size >= outputSize } ?: ByteArray(outputSize)
451
-
452
- val ySize = width * height
453
- val uvPixelStride = 2
454
-
455
- val minExpectedSize = width * height + (width * height) / 2
456
- if (nv21.size < minExpectedSize) {
457
- throw IllegalArgumentException("NV21 buffer too small: expected at least $minExpectedSize, got ${nv21.size}")
458
- }
459
-
460
- // Use bilinear interpolation for better quality
461
- if (width != targetWidth || height != targetHeight) {
462
- return nv21ToRgbByteArrayBilinear(nv21, width, height, targetWidth, targetHeight, output)
463
- }
464
-
465
- // Direct copy with nearest neighbor
466
- var outIdx = 0
467
-
468
- for (targetY in 0 until targetHeight) {
469
- val srcY = (targetY * height) / targetHeight
470
- if (srcY >= height) {
471
- throw IndexOutOfBoundsException("Source Y out of bounds: srcY=$srcY, height=$height")
472
- }
473
- val srcRowOffset = srcY * width
474
-
475
- for (targetX in 0 until targetWidth) {
476
- val srcX = (targetX * width) / targetWidth
477
- if (srcX >= width) {
478
- throw IndexOutOfBoundsException("Source X out of bounds: srcX=$srcX, width=$width")
479
- }
480
-
481
- // Get Y component
482
- val yIdx = srcRowOffset + srcX
483
- if (yIdx >= nv21.size) {
484
- throw IndexOutOfBoundsException("Y index out of bounds: yIdx=$yIdx, nv21.size=${nv21.size}")
485
- }
486
- val yVal = nv21[yIdx].toInt() and 0xFF
487
-
488
- // Get U and V components (NV21 layout: UV interleaved in second half)
489
- val uvIndex = ySize + (srcY / 2) * width + (srcX / 2) * uvPixelStride
490
- if (uvIndex + 1 >= nv21.size) {
491
- throw IndexOutOfBoundsException("UV index out of bounds: uvIndex=$uvIndex, nv21.size=${nv21.size}")
492
- }
493
- val vVal = nv21[uvIndex].toInt() and 0xFF
494
- val uVal = nv21[uvIndex + 1].toInt() and 0xFF
495
-
496
- // YUV to RGB conversion
497
- val yf = yVal.toFloat()
498
- val uf = uVal - 128f
499
- val vf = vVal - 128f
500
-
501
- val r = (yf + 1.402f * vf).coerceIn(0f, 255f).toInt().toByte()
502
- val g = (yf - 0.34414f * uf - 0.71414f * vf).coerceIn(0f, 255f).toInt().toByte()
503
- val b = (yf + 1.772f * uf).coerceIn(0f, 255f).toInt().toByte()
504
-
505
- if (outIdx + 2 < output.size) {
506
- output[outIdx++] = r
507
- output[outIdx++] = g
508
- output[outIdx++] = b
509
- } else {
510
- throw IndexOutOfBoundsException("Output index out of bounds: outIdx=$outIdx, output.size=${output.size}")
511
- }
512
- }
513
- }
514
-
515
- return output
516
- }
517
-
518
- /**
519
- * High-quality bilinear interpolation for RGB byte array resizing.
520
- */
521
- private fun nv21ToRgbByteArrayBilinear(
522
- nv21: ByteArray,
523
- width: Int,
524
- height: Int,
525
- targetWidth: Int,
526
- targetHeight: Int,
527
- output: ByteArray
528
- ): ByteArray {
529
- val ySize = width * height
530
- val uvPixelStride = 2
531
- val scaleX = width.toFloat() / targetWidth
532
- val scaleY = height.toFloat() / targetHeight
533
-
534
- var outIdx = 0
535
-
536
- for (targetY in 0 until targetHeight) {
537
- val srcY = targetY * scaleY
538
- val y1 = srcY.toInt()
539
- val y2 = (y1 + 1).coerceAtMost(height - 1)
540
- val fracY = srcY - y1
541
-
542
- for (targetX in 0 until targetWidth) {
543
- val srcX = targetX * scaleX
544
- val x1 = srcX.toInt()
545
- val x2 = (x1 + 1).coerceAtMost(width - 1)
546
- val fracX = srcX - x1
547
-
548
-
549
- val (r, g, b) = bilinearInterpolateYuv(
550
- nv21, width, height, ySize, uvPixelStride,
551
- x1, y1, x2, y2, fracX, fracY
552
- )
553
-
554
- if (outIdx + 2 < output.size) {
555
- output[outIdx++] = r.toInt().toByte()
556
- output[outIdx++] = g.toInt().toByte()
557
- output[outIdx++] = b.toInt().toByte()
558
- }
559
- }
560
- }
561
-
562
- return output
563
- }
564
-
565
- /**
566
- * Apply sRGB gamma correction to match hardware camera ISP output.
567
- * This is CRITICAL for face recognition: hardware cameras apply gamma correction,
568
- * but raw YUV conversion doesn't. This function brings the frame processor output
569
- * in line with native camera JPEGs.
570
- *
571
- * @param linear Linear RGB value in range [0, 1]
572
- * @return Gamma-corrected RGB value in range [0, 1]
573
- */
574
- private fun applyGammaCorrection(linear: Float): Float {
575
- return if (linear <= 0.0031308f) {
576
- // Linear segment for very dark values
577
- linear * 12.92f
578
- } else {
579
- // Power law segment for normal values
580
- // sRGB formula: value^(1/2.4)
581
- 1.055f * Math.pow(linear.toDouble(), 1.0 / 2.4).toFloat() - 0.055f
582
- }
583
- }
584
- }
@@ -1,73 +0,0 @@
1
- package com.faceantispoof
2
-
3
- import android.content.Context
4
- import android.graphics.Bitmap
5
- import android.media.Image
6
- import android.renderscript.Allocation
7
- import android.renderscript.Element
8
- import android.renderscript.RenderScript
9
- import android.renderscript.ScriptIntrinsicYuvToRGB
10
- import android.util.Log
11
-
12
- /**
13
- * DEPRECATED: Use YuvToFloatArrayConverter instead for Bitmap-free processing.
14
- *
15
- * Lightweight YUV -> RGB converter using RenderScript intrinsics.
16
- * Based on CameraX sample implementation; faster than compressing to JPEG.
17
- * This class creates Bitmaps which should be avoided in performance-critical code.
18
- */
19
- @Deprecated("Use YuvToFloatArrayConverter for direct ByteBuffer/FloatArray processing without Bitmaps", ReplaceWith("YuvToFloatArrayConverter"))
20
- class YuvToRgbConverter(context: Context) {
21
- private val rs: RenderScript = RenderScript.create(context.applicationContext)
22
- private val scriptYuvToRgb: ScriptIntrinsicYuvToRGB = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
23
- private var yuvBuffer: ByteArray = ByteArray(0)
24
- private var yuvAlloc: Allocation? = null
25
- private var rgbaAlloc: Allocation? = null
26
-
27
- init {
28
- Log.w("YuvToRgbConverter", "YuvToRgbConverter is deprecated. Use YuvToFloatArrayConverter instead.")
29
- }
30
-
31
- fun yuvToRgb(image: Image, output: Bitmap) {
32
- val planes = image.planes
33
- val yBuffer = planes[0].buffer
34
- val uBuffer = planes[1].buffer
35
- val vBuffer = planes[2].buffer
36
-
37
- val ySize = yBuffer.remaining()
38
- val uSize = uBuffer.remaining()
39
- val vSize = vBuffer.remaining()
40
- val total = ySize + uSize + vSize
41
-
42
- if (yuvBuffer.size != total) {
43
- yuvBuffer = ByteArray(total)
44
- }
45
-
46
- yBuffer.get(yuvBuffer, 0, ySize)
47
- vBuffer.get(yuvBuffer, ySize, vSize)
48
- uBuffer.get(yuvBuffer, ySize + vSize, uSize)
49
-
50
- if (yuvAlloc == null || yuvAlloc!!.bytesSize < yuvBuffer.size) {
51
- try { yuvAlloc?.destroy() } catch (ignored: Throwable) {}
52
- yuvAlloc = Allocation.createSized(rs, Element.U8(rs), yuvBuffer.size)
53
- }
54
-
55
- yuvAlloc!!.copyFrom(yuvBuffer)
56
- scriptYuvToRgb.setInput(yuvAlloc)
57
-
58
- // Create or reuse rgba allocation tied to the output bitmap
59
- if (rgbaAlloc == null || rgbaAlloc!!.type.x != output.width || rgbaAlloc!!.type.y != output.height) {
60
- try { rgbaAlloc?.destroy() } catch (ignored: Throwable) {}
61
- rgbaAlloc = Allocation.createFromBitmap(rs, output)
62
- }
63
-
64
- scriptYuvToRgb.forEach(rgbaAlloc)
65
- rgbaAlloc!!.copyTo(output)
66
- }
67
-
68
- fun destroy() {
69
- try { yuvAlloc?.destroy() } catch (ignored: Throwable) {}
70
- try { rgbaAlloc?.destroy() } catch (ignored: Throwable) {}
71
- try { rs.destroy() } catch (ignored: Throwable) {}
72
- }
73
- }