react-native-vision-camera-spoof-detector 1.1.23 → 1.1.25

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