react-native-vision-camera-spoof-detector 1.1.23 → 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 (199) 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/package.json +1 -1
  5. package/android/src/main/cpp/CMakeLists.txt +0 -33
  6. package/android/src/main/cpp/FaceAntiSpoofJSI.cpp +0 -142
  7. package/android/src/main/cpp/FastYuvJni.cpp +0 -111
  8. package/android/src/main/cpp/fast_yuv.cpp +0 -342
  9. package/android/src/main/cpp/fast_yuv.h +0 -23
  10. package/android/src/main/cpp/libyuv/.clang-format +0 -6
  11. package/android/src/main/cpp/libyuv/.gn +0 -40
  12. package/android/src/main/cpp/libyuv/.vpython3 +0 -410
  13. package/android/src/main/cpp/libyuv/AUTHORS +0 -7
  14. package/android/src/main/cpp/libyuv/Android.bp +0 -202
  15. package/android/src/main/cpp/libyuv/Android.mk +0 -106
  16. package/android/src/main/cpp/libyuv/BUILD.gn +0 -490
  17. package/android/src/main/cpp/libyuv/CM_linux_packages.cmake +0 -70
  18. package/android/src/main/cpp/libyuv/CMakeLists.txt +0 -259
  19. package/android/src/main/cpp/libyuv/DEPS +0 -935
  20. package/android/src/main/cpp/libyuv/DIR_METADATA +0 -3
  21. package/android/src/main/cpp/libyuv/LICENSE +0 -29
  22. package/android/src/main/cpp/libyuv/OWNERS +0 -11
  23. package/android/src/main/cpp/libyuv/PATENTS +0 -24
  24. package/android/src/main/cpp/libyuv/PRESUBMIT.py +0 -65
  25. package/android/src/main/cpp/libyuv/README.chromium +0 -11
  26. package/android/src/main/cpp/libyuv/README.md +0 -19
  27. package/android/src/main/cpp/libyuv/build_overrides/build.gni +0 -60
  28. package/android/src/main/cpp/libyuv/build_overrides/gtest.gni +0 -19
  29. package/android/src/main/cpp/libyuv/build_overrides/partition_alloc.gni +0 -17
  30. package/android/src/main/cpp/libyuv/codereview.settings +0 -5
  31. package/android/src/main/cpp/libyuv/docs/deprecated_builds.md +0 -409
  32. package/android/src/main/cpp/libyuv/docs/environment_variables.md +0 -64
  33. package/android/src/main/cpp/libyuv/docs/feature_detection.md +0 -108
  34. package/android/src/main/cpp/libyuv/docs/filtering.md +0 -196
  35. package/android/src/main/cpp/libyuv/docs/formats.md +0 -208
  36. package/android/src/main/cpp/libyuv/docs/getting_started.md +0 -296
  37. package/android/src/main/cpp/libyuv/docs/rotation.md +0 -107
  38. package/android/src/main/cpp/libyuv/download_vs_toolchain.py +0 -29
  39. package/android/src/main/cpp/libyuv/include/libyuv/basic_types.h +0 -68
  40. package/android/src/main/cpp/libyuv/include/libyuv/compare.h +0 -111
  41. package/android/src/main/cpp/libyuv/include/libyuv/compare_row.h +0 -112
  42. package/android/src/main/cpp/libyuv/include/libyuv/convert.h +0 -1117
  43. package/android/src/main/cpp/libyuv/include/libyuv/convert_argb.h +0 -2335
  44. package/android/src/main/cpp/libyuv/include/libyuv/convert_from.h +0 -203
  45. package/android/src/main/cpp/libyuv/include/libyuv/convert_from_argb.h +0 -394
  46. package/android/src/main/cpp/libyuv/include/libyuv/cpu_id.h +0 -147
  47. package/android/src/main/cpp/libyuv/include/libyuv/cpu_support.h +0 -99
  48. package/android/src/main/cpp/libyuv/include/libyuv/loongson_intrinsics.h +0 -1949
  49. package/android/src/main/cpp/libyuv/include/libyuv/mjpeg_decoder.h +0 -195
  50. package/android/src/main/cpp/libyuv/include/libyuv/planar_functions.h +0 -1131
  51. package/android/src/main/cpp/libyuv/include/libyuv/rotate.h +0 -296
  52. package/android/src/main/cpp/libyuv/include/libyuv/rotate_argb.h +0 -37
  53. package/android/src/main/cpp/libyuv/include/libyuv/rotate_row.h +0 -265
  54. package/android/src/main/cpp/libyuv/include/libyuv/row.h +0 -6738
  55. package/android/src/main/cpp/libyuv/include/libyuv/row_sve.h +0 -2154
  56. package/android/src/main/cpp/libyuv/include/libyuv/scale.h +0 -336
  57. package/android/src/main/cpp/libyuv/include/libyuv/scale_argb.h +0 -76
  58. package/android/src/main/cpp/libyuv/include/libyuv/scale_rgb.h +0 -42
  59. package/android/src/main/cpp/libyuv/include/libyuv/scale_row.h +0 -1726
  60. package/android/src/main/cpp/libyuv/include/libyuv/scale_uv.h +0 -51
  61. package/android/src/main/cpp/libyuv/include/libyuv/version.h +0 -16
  62. package/android/src/main/cpp/libyuv/include/libyuv/video_common.h +0 -222
  63. package/android/src/main/cpp/libyuv/include/libyuv.h +0 -33
  64. package/android/src/main/cpp/libyuv/infra/config/OWNERS +0 -3
  65. package/android/src/main/cpp/libyuv/infra/config/PRESUBMIT.py +0 -17
  66. package/android/src/main/cpp/libyuv/infra/config/README.md +0 -2
  67. package/android/src/main/cpp/libyuv/infra/config/codereview.settings +0 -6
  68. package/android/src/main/cpp/libyuv/infra/config/commit-queue.cfg +0 -144
  69. package/android/src/main/cpp/libyuv/infra/config/cr-buildbucket.cfg +0 -1185
  70. package/android/src/main/cpp/libyuv/infra/config/luci-logdog.cfg +0 -9
  71. package/android/src/main/cpp/libyuv/infra/config/luci-milo.cfg +0 -246
  72. package/android/src/main/cpp/libyuv/infra/config/luci-scheduler.cfg +0 -385
  73. package/android/src/main/cpp/libyuv/infra/config/main.star +0 -402
  74. package/android/src/main/cpp/libyuv/infra/config/project.cfg +0 -16
  75. package/android/src/main/cpp/libyuv/infra/config/realms.cfg +0 -111
  76. package/android/src/main/cpp/libyuv/libyuv.gni +0 -34
  77. package/android/src/main/cpp/libyuv/libyuv.gyp +0 -149
  78. package/android/src/main/cpp/libyuv/libyuv.gypi +0 -87
  79. package/android/src/main/cpp/libyuv/linux.mk +0 -96
  80. package/android/src/main/cpp/libyuv/public.mk +0 -13
  81. package/android/src/main/cpp/libyuv/pylintrc +0 -49
  82. package/android/src/main/cpp/libyuv/riscv_script/prepare_toolchain_qemu.sh +0 -74
  83. package/android/src/main/cpp/libyuv/riscv_script/riscv-clang.cmake +0 -56
  84. package/android/src/main/cpp/libyuv/riscv_script/run_qemu.sh +0 -15
  85. package/android/src/main/cpp/libyuv/source/compare.cc +0 -435
  86. package/android/src/main/cpp/libyuv/source/compare_common.cc +0 -74
  87. package/android/src/main/cpp/libyuv/source/compare_gcc.cc +0 -362
  88. package/android/src/main/cpp/libyuv/source/compare_neon.cc +0 -96
  89. package/android/src/main/cpp/libyuv/source/compare_neon64.cc +0 -223
  90. package/android/src/main/cpp/libyuv/source/compare_win.cc +0 -241
  91. package/android/src/main/cpp/libyuv/source/convert.cc +0 -4746
  92. package/android/src/main/cpp/libyuv/source/convert_argb.cc +0 -9179
  93. package/android/src/main/cpp/libyuv/source/convert_from.cc +0 -866
  94. package/android/src/main/cpp/libyuv/source/convert_from_argb.cc +0 -3671
  95. package/android/src/main/cpp/libyuv/source/convert_jpeg.cc +0 -602
  96. package/android/src/main/cpp/libyuv/source/convert_to_argb.cc +0 -391
  97. package/android/src/main/cpp/libyuv/source/convert_to_i420.cc +0 -288
  98. package/android/src/main/cpp/libyuv/source/cpu_id.cc +0 -496
  99. package/android/src/main/cpp/libyuv/source/mjpeg_decoder.cc +0 -580
  100. package/android/src/main/cpp/libyuv/source/mjpeg_validate.cc +0 -71
  101. package/android/src/main/cpp/libyuv/source/planar_functions.cc +0 -5663
  102. package/android/src/main/cpp/libyuv/source/rotate.cc +0 -1241
  103. package/android/src/main/cpp/libyuv/source/rotate_any.cc +0 -76
  104. package/android/src/main/cpp/libyuv/source/rotate_argb.cc +0 -259
  105. package/android/src/main/cpp/libyuv/source/rotate_common.cc +0 -208
  106. package/android/src/main/cpp/libyuv/source/rotate_gcc.cc +0 -505
  107. package/android/src/main/cpp/libyuv/source/rotate_lsx.cc +0 -233
  108. package/android/src/main/cpp/libyuv/source/rotate_neon.cc +0 -219
  109. package/android/src/main/cpp/libyuv/source/rotate_neon64.cc +0 -273
  110. package/android/src/main/cpp/libyuv/source/rotate_sme.cc +0 -174
  111. package/android/src/main/cpp/libyuv/source/rotate_win.cc +0 -253
  112. package/android/src/main/cpp/libyuv/source/row_any.cc +0 -2519
  113. package/android/src/main/cpp/libyuv/source/row_common.cc +0 -4461
  114. package/android/src/main/cpp/libyuv/source/row_gcc.cc +0 -9570
  115. package/android/src/main/cpp/libyuv/source/row_lasx.cc +0 -2343
  116. package/android/src/main/cpp/libyuv/source/row_lsx.cc +0 -3030
  117. package/android/src/main/cpp/libyuv/source/row_neon.cc +0 -4026
  118. package/android/src/main/cpp/libyuv/source/row_neon64.cc +0 -5617
  119. package/android/src/main/cpp/libyuv/source/row_rvv.cc +0 -2599
  120. package/android/src/main/cpp/libyuv/source/row_sme.cc +0 -1183
  121. package/android/src/main/cpp/libyuv/source/row_sve.cc +0 -1088
  122. package/android/src/main/cpp/libyuv/source/row_win.cc +0 -6453
  123. package/android/src/main/cpp/libyuv/source/scale.cc +0 -2710
  124. package/android/src/main/cpp/libyuv/source/scale_any.cc +0 -991
  125. package/android/src/main/cpp/libyuv/source/scale_argb.cc +0 -1158
  126. package/android/src/main/cpp/libyuv/source/scale_common.cc +0 -1977
  127. package/android/src/main/cpp/libyuv/source/scale_gcc.cc +0 -2947
  128. package/android/src/main/cpp/libyuv/source/scale_lsx.cc +0 -739
  129. package/android/src/main/cpp/libyuv/source/scale_neon.cc +0 -1449
  130. package/android/src/main/cpp/libyuv/source/scale_neon64.cc +0 -1552
  131. package/android/src/main/cpp/libyuv/source/scale_rgb.cc +0 -82
  132. package/android/src/main/cpp/libyuv/source/scale_rvv.cc +0 -1971
  133. package/android/src/main/cpp/libyuv/source/scale_sme.cc +0 -555
  134. package/android/src/main/cpp/libyuv/source/scale_uv.cc +0 -1159
  135. package/android/src/main/cpp/libyuv/source/scale_win.cc +0 -1392
  136. package/android/src/main/cpp/libyuv/source/test.sh +0 -35
  137. package/android/src/main/cpp/libyuv/source/video_common.cc +0 -62
  138. package/android/src/main/cpp/libyuv/tools_libyuv/OWNERS +0 -4
  139. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/roll_deps.py +0 -931
  140. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/roll_deps_test.py +0 -164
  141. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS +0 -21
  142. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.new +0 -13
  143. package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.old +0 -13
  144. package/android/src/main/cpp/libyuv/tools_libyuv/get_landmines.py +0 -38
  145. package/android/src/main/cpp/libyuv/tools_libyuv/msan/OWNERS +0 -3
  146. package/android/src/main/cpp/libyuv/tools_libyuv/msan/blacklist.txt +0 -9
  147. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/OWNERS +0 -3
  148. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/blacklist.txt +0 -15
  149. package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/vptr_blacklist.txt +0 -25
  150. package/android/src/main/cpp/libyuv/unit_test/basictypes_test.cc +0 -43
  151. package/android/src/main/cpp/libyuv/unit_test/color_test.cc +0 -848
  152. package/android/src/main/cpp/libyuv/unit_test/compare_test.cc +0 -739
  153. package/android/src/main/cpp/libyuv/unit_test/convert_argb_test.cc +0 -2867
  154. package/android/src/main/cpp/libyuv/unit_test/convert_test.cc +0 -2133
  155. package/android/src/main/cpp/libyuv/unit_test/cpu_test.cc +0 -427
  156. package/android/src/main/cpp/libyuv/unit_test/cpu_thread_test.cc +0 -63
  157. package/android/src/main/cpp/libyuv/unit_test/math_test.cc +0 -160
  158. package/android/src/main/cpp/libyuv/unit_test/planar_test.cc +0 -4731
  159. package/android/src/main/cpp/libyuv/unit_test/rotate_argb_test.cc +0 -334
  160. package/android/src/main/cpp/libyuv/unit_test/rotate_test.cc +0 -962
  161. package/android/src/main/cpp/libyuv/unit_test/scale_argb_test.cc +0 -590
  162. package/android/src/main/cpp/libyuv/unit_test/scale_plane_test.cc +0 -465
  163. package/android/src/main/cpp/libyuv/unit_test/scale_rgb_test.cc +0 -280
  164. package/android/src/main/cpp/libyuv/unit_test/scale_test.cc +0 -1135
  165. package/android/src/main/cpp/libyuv/unit_test/scale_uv_test.cc +0 -249
  166. package/android/src/main/cpp/libyuv/unit_test/testdata/arm_v7.txt +0 -12
  167. package/android/src/main/cpp/libyuv/unit_test/testdata/mips.txt +0 -7
  168. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson2k.txt +0 -5
  169. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson3.txt +0 -10
  170. package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson_mmi.txt +0 -7
  171. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64.txt +0 -4
  172. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv.txt +0 -4
  173. package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv_zvfh.txt +0 -4
  174. package/android/src/main/cpp/libyuv/unit_test/testdata/tegra3.txt +0 -23
  175. package/android/src/main/cpp/libyuv/unit_test/testdata/test0.jpg +0 -0
  176. package/android/src/main/cpp/libyuv/unit_test/testdata/test1.jpg +0 -0
  177. package/android/src/main/cpp/libyuv/unit_test/testdata/test2.jpg +0 -0
  178. package/android/src/main/cpp/libyuv/unit_test/testdata/test3.jpg +0 -0
  179. package/android/src/main/cpp/libyuv/unit_test/testdata/test4.jpg +0 -0
  180. package/android/src/main/cpp/libyuv/unit_test/unit_test.cc +0 -581
  181. package/android/src/main/cpp/libyuv/unit_test/unit_test.h +0 -232
  182. package/android/src/main/cpp/libyuv/unit_test/video_common_test.cc +0 -112
  183. package/android/src/main/cpp/libyuv/util/Makefile +0 -9
  184. package/android/src/main/cpp/libyuv/util/color.cc +0 -120
  185. package/android/src/main/cpp/libyuv/util/compare.cc +0 -67
  186. package/android/src/main/cpp/libyuv/util/cpuid.c +0 -211
  187. package/android/src/main/cpp/libyuv/util/i444tonv12_eg.cc +0 -28
  188. package/android/src/main/cpp/libyuv/util/psnr.cc +0 -291
  189. package/android/src/main/cpp/libyuv/util/psnr.h +0 -47
  190. package/android/src/main/cpp/libyuv/util/psnr_main.cc +0 -620
  191. package/android/src/main/cpp/libyuv/util/ssim.cc +0 -364
  192. package/android/src/main/cpp/libyuv/util/ssim.h +0 -38
  193. package/android/src/main/cpp/libyuv/util/yuvconstants.c +0 -114
  194. package/android/src/main/cpp/libyuv/util/yuvconvert.cc +0 -367
  195. package/android/src/main/cpp/libyuv/winarm.mk +0 -47
  196. package/android/src/main/java/com/faceantispoof/ByteBufferPool.kt +0 -68
  197. package/android/src/main/java/com/faceantispoof/FastYuv.kt +0 -51
  198. package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +0 -584
  199. 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
- }