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.
- package/android/CMakeLists.txt +12 -0
- package/android/build.gradle +10 -9
- package/android/src/main/cpp/cpp-adapter.cpp +6 -3
- package/android/src/main/java/com/faceantispoof/FaceAntiSpoofingAdvanced.kt +83 -0
- package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorOutput.kt +26 -11
- package/package.json +1 -1
- package/android/src/main/cpp/CMakeLists.txt +0 -33
- package/android/src/main/cpp/FaceAntiSpoofJSI.cpp +0 -142
- package/android/src/main/cpp/FastYuvJni.cpp +0 -111
- package/android/src/main/cpp/fast_yuv.cpp +0 -342
- package/android/src/main/cpp/fast_yuv.h +0 -23
- package/android/src/main/cpp/libyuv/.clang-format +0 -6
- package/android/src/main/cpp/libyuv/.gn +0 -40
- package/android/src/main/cpp/libyuv/.vpython3 +0 -410
- package/android/src/main/cpp/libyuv/AUTHORS +0 -7
- package/android/src/main/cpp/libyuv/Android.bp +0 -202
- package/android/src/main/cpp/libyuv/Android.mk +0 -106
- package/android/src/main/cpp/libyuv/BUILD.gn +0 -490
- package/android/src/main/cpp/libyuv/CM_linux_packages.cmake +0 -70
- package/android/src/main/cpp/libyuv/CMakeLists.txt +0 -259
- package/android/src/main/cpp/libyuv/DEPS +0 -935
- package/android/src/main/cpp/libyuv/DIR_METADATA +0 -3
- package/android/src/main/cpp/libyuv/LICENSE +0 -29
- package/android/src/main/cpp/libyuv/OWNERS +0 -11
- package/android/src/main/cpp/libyuv/PATENTS +0 -24
- package/android/src/main/cpp/libyuv/PRESUBMIT.py +0 -65
- package/android/src/main/cpp/libyuv/README.chromium +0 -11
- package/android/src/main/cpp/libyuv/README.md +0 -19
- package/android/src/main/cpp/libyuv/build_overrides/build.gni +0 -60
- package/android/src/main/cpp/libyuv/build_overrides/gtest.gni +0 -19
- package/android/src/main/cpp/libyuv/build_overrides/partition_alloc.gni +0 -17
- package/android/src/main/cpp/libyuv/codereview.settings +0 -5
- package/android/src/main/cpp/libyuv/docs/deprecated_builds.md +0 -409
- package/android/src/main/cpp/libyuv/docs/environment_variables.md +0 -64
- package/android/src/main/cpp/libyuv/docs/feature_detection.md +0 -108
- package/android/src/main/cpp/libyuv/docs/filtering.md +0 -196
- package/android/src/main/cpp/libyuv/docs/formats.md +0 -208
- package/android/src/main/cpp/libyuv/docs/getting_started.md +0 -296
- package/android/src/main/cpp/libyuv/docs/rotation.md +0 -107
- package/android/src/main/cpp/libyuv/download_vs_toolchain.py +0 -29
- package/android/src/main/cpp/libyuv/include/libyuv/basic_types.h +0 -68
- package/android/src/main/cpp/libyuv/include/libyuv/compare.h +0 -111
- package/android/src/main/cpp/libyuv/include/libyuv/compare_row.h +0 -112
- package/android/src/main/cpp/libyuv/include/libyuv/convert.h +0 -1117
- package/android/src/main/cpp/libyuv/include/libyuv/convert_argb.h +0 -2335
- package/android/src/main/cpp/libyuv/include/libyuv/convert_from.h +0 -203
- package/android/src/main/cpp/libyuv/include/libyuv/convert_from_argb.h +0 -394
- package/android/src/main/cpp/libyuv/include/libyuv/cpu_id.h +0 -147
- package/android/src/main/cpp/libyuv/include/libyuv/cpu_support.h +0 -99
- package/android/src/main/cpp/libyuv/include/libyuv/loongson_intrinsics.h +0 -1949
- package/android/src/main/cpp/libyuv/include/libyuv/mjpeg_decoder.h +0 -195
- package/android/src/main/cpp/libyuv/include/libyuv/planar_functions.h +0 -1131
- package/android/src/main/cpp/libyuv/include/libyuv/rotate.h +0 -296
- package/android/src/main/cpp/libyuv/include/libyuv/rotate_argb.h +0 -37
- package/android/src/main/cpp/libyuv/include/libyuv/rotate_row.h +0 -265
- package/android/src/main/cpp/libyuv/include/libyuv/row.h +0 -6738
- package/android/src/main/cpp/libyuv/include/libyuv/row_sve.h +0 -2154
- package/android/src/main/cpp/libyuv/include/libyuv/scale.h +0 -336
- package/android/src/main/cpp/libyuv/include/libyuv/scale_argb.h +0 -76
- package/android/src/main/cpp/libyuv/include/libyuv/scale_rgb.h +0 -42
- package/android/src/main/cpp/libyuv/include/libyuv/scale_row.h +0 -1726
- package/android/src/main/cpp/libyuv/include/libyuv/scale_uv.h +0 -51
- package/android/src/main/cpp/libyuv/include/libyuv/version.h +0 -16
- package/android/src/main/cpp/libyuv/include/libyuv/video_common.h +0 -222
- package/android/src/main/cpp/libyuv/include/libyuv.h +0 -33
- package/android/src/main/cpp/libyuv/infra/config/OWNERS +0 -3
- package/android/src/main/cpp/libyuv/infra/config/PRESUBMIT.py +0 -17
- package/android/src/main/cpp/libyuv/infra/config/README.md +0 -2
- package/android/src/main/cpp/libyuv/infra/config/codereview.settings +0 -6
- package/android/src/main/cpp/libyuv/infra/config/commit-queue.cfg +0 -144
- package/android/src/main/cpp/libyuv/infra/config/cr-buildbucket.cfg +0 -1185
- package/android/src/main/cpp/libyuv/infra/config/luci-logdog.cfg +0 -9
- package/android/src/main/cpp/libyuv/infra/config/luci-milo.cfg +0 -246
- package/android/src/main/cpp/libyuv/infra/config/luci-scheduler.cfg +0 -385
- package/android/src/main/cpp/libyuv/infra/config/main.star +0 -402
- package/android/src/main/cpp/libyuv/infra/config/project.cfg +0 -16
- package/android/src/main/cpp/libyuv/infra/config/realms.cfg +0 -111
- package/android/src/main/cpp/libyuv/libyuv.gni +0 -34
- package/android/src/main/cpp/libyuv/libyuv.gyp +0 -149
- package/android/src/main/cpp/libyuv/libyuv.gypi +0 -87
- package/android/src/main/cpp/libyuv/linux.mk +0 -96
- package/android/src/main/cpp/libyuv/public.mk +0 -13
- package/android/src/main/cpp/libyuv/pylintrc +0 -49
- package/android/src/main/cpp/libyuv/riscv_script/prepare_toolchain_qemu.sh +0 -74
- package/android/src/main/cpp/libyuv/riscv_script/riscv-clang.cmake +0 -56
- package/android/src/main/cpp/libyuv/riscv_script/run_qemu.sh +0 -15
- package/android/src/main/cpp/libyuv/source/compare.cc +0 -435
- package/android/src/main/cpp/libyuv/source/compare_common.cc +0 -74
- package/android/src/main/cpp/libyuv/source/compare_gcc.cc +0 -362
- package/android/src/main/cpp/libyuv/source/compare_neon.cc +0 -96
- package/android/src/main/cpp/libyuv/source/compare_neon64.cc +0 -223
- package/android/src/main/cpp/libyuv/source/compare_win.cc +0 -241
- package/android/src/main/cpp/libyuv/source/convert.cc +0 -4746
- package/android/src/main/cpp/libyuv/source/convert_argb.cc +0 -9179
- package/android/src/main/cpp/libyuv/source/convert_from.cc +0 -866
- package/android/src/main/cpp/libyuv/source/convert_from_argb.cc +0 -3671
- package/android/src/main/cpp/libyuv/source/convert_jpeg.cc +0 -602
- package/android/src/main/cpp/libyuv/source/convert_to_argb.cc +0 -391
- package/android/src/main/cpp/libyuv/source/convert_to_i420.cc +0 -288
- package/android/src/main/cpp/libyuv/source/cpu_id.cc +0 -496
- package/android/src/main/cpp/libyuv/source/mjpeg_decoder.cc +0 -580
- package/android/src/main/cpp/libyuv/source/mjpeg_validate.cc +0 -71
- package/android/src/main/cpp/libyuv/source/planar_functions.cc +0 -5663
- package/android/src/main/cpp/libyuv/source/rotate.cc +0 -1241
- package/android/src/main/cpp/libyuv/source/rotate_any.cc +0 -76
- package/android/src/main/cpp/libyuv/source/rotate_argb.cc +0 -259
- package/android/src/main/cpp/libyuv/source/rotate_common.cc +0 -208
- package/android/src/main/cpp/libyuv/source/rotate_gcc.cc +0 -505
- package/android/src/main/cpp/libyuv/source/rotate_lsx.cc +0 -233
- package/android/src/main/cpp/libyuv/source/rotate_neon.cc +0 -219
- package/android/src/main/cpp/libyuv/source/rotate_neon64.cc +0 -273
- package/android/src/main/cpp/libyuv/source/rotate_sme.cc +0 -174
- package/android/src/main/cpp/libyuv/source/rotate_win.cc +0 -253
- package/android/src/main/cpp/libyuv/source/row_any.cc +0 -2519
- package/android/src/main/cpp/libyuv/source/row_common.cc +0 -4461
- package/android/src/main/cpp/libyuv/source/row_gcc.cc +0 -9570
- package/android/src/main/cpp/libyuv/source/row_lasx.cc +0 -2343
- package/android/src/main/cpp/libyuv/source/row_lsx.cc +0 -3030
- package/android/src/main/cpp/libyuv/source/row_neon.cc +0 -4026
- package/android/src/main/cpp/libyuv/source/row_neon64.cc +0 -5617
- package/android/src/main/cpp/libyuv/source/row_rvv.cc +0 -2599
- package/android/src/main/cpp/libyuv/source/row_sme.cc +0 -1183
- package/android/src/main/cpp/libyuv/source/row_sve.cc +0 -1088
- package/android/src/main/cpp/libyuv/source/row_win.cc +0 -6453
- package/android/src/main/cpp/libyuv/source/scale.cc +0 -2710
- package/android/src/main/cpp/libyuv/source/scale_any.cc +0 -991
- package/android/src/main/cpp/libyuv/source/scale_argb.cc +0 -1158
- package/android/src/main/cpp/libyuv/source/scale_common.cc +0 -1977
- package/android/src/main/cpp/libyuv/source/scale_gcc.cc +0 -2947
- package/android/src/main/cpp/libyuv/source/scale_lsx.cc +0 -739
- package/android/src/main/cpp/libyuv/source/scale_neon.cc +0 -1449
- package/android/src/main/cpp/libyuv/source/scale_neon64.cc +0 -1552
- package/android/src/main/cpp/libyuv/source/scale_rgb.cc +0 -82
- package/android/src/main/cpp/libyuv/source/scale_rvv.cc +0 -1971
- package/android/src/main/cpp/libyuv/source/scale_sme.cc +0 -555
- package/android/src/main/cpp/libyuv/source/scale_uv.cc +0 -1159
- package/android/src/main/cpp/libyuv/source/scale_win.cc +0 -1392
- package/android/src/main/cpp/libyuv/source/test.sh +0 -35
- package/android/src/main/cpp/libyuv/source/video_common.cc +0 -62
- package/android/src/main/cpp/libyuv/tools_libyuv/OWNERS +0 -4
- package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/roll_deps.py +0 -931
- package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/roll_deps_test.py +0 -164
- package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS +0 -21
- package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.new +0 -13
- package/android/src/main/cpp/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.old +0 -13
- package/android/src/main/cpp/libyuv/tools_libyuv/get_landmines.py +0 -38
- package/android/src/main/cpp/libyuv/tools_libyuv/msan/OWNERS +0 -3
- package/android/src/main/cpp/libyuv/tools_libyuv/msan/blacklist.txt +0 -9
- package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/OWNERS +0 -3
- package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/blacklist.txt +0 -15
- package/android/src/main/cpp/libyuv/tools_libyuv/ubsan/vptr_blacklist.txt +0 -25
- package/android/src/main/cpp/libyuv/unit_test/basictypes_test.cc +0 -43
- package/android/src/main/cpp/libyuv/unit_test/color_test.cc +0 -848
- package/android/src/main/cpp/libyuv/unit_test/compare_test.cc +0 -739
- package/android/src/main/cpp/libyuv/unit_test/convert_argb_test.cc +0 -2867
- package/android/src/main/cpp/libyuv/unit_test/convert_test.cc +0 -2133
- package/android/src/main/cpp/libyuv/unit_test/cpu_test.cc +0 -427
- package/android/src/main/cpp/libyuv/unit_test/cpu_thread_test.cc +0 -63
- package/android/src/main/cpp/libyuv/unit_test/math_test.cc +0 -160
- package/android/src/main/cpp/libyuv/unit_test/planar_test.cc +0 -4731
- package/android/src/main/cpp/libyuv/unit_test/rotate_argb_test.cc +0 -334
- package/android/src/main/cpp/libyuv/unit_test/rotate_test.cc +0 -962
- package/android/src/main/cpp/libyuv/unit_test/scale_argb_test.cc +0 -590
- package/android/src/main/cpp/libyuv/unit_test/scale_plane_test.cc +0 -465
- package/android/src/main/cpp/libyuv/unit_test/scale_rgb_test.cc +0 -280
- package/android/src/main/cpp/libyuv/unit_test/scale_test.cc +0 -1135
- package/android/src/main/cpp/libyuv/unit_test/scale_uv_test.cc +0 -249
- package/android/src/main/cpp/libyuv/unit_test/testdata/arm_v7.txt +0 -12
- package/android/src/main/cpp/libyuv/unit_test/testdata/mips.txt +0 -7
- package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson2k.txt +0 -5
- package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson3.txt +0 -10
- package/android/src/main/cpp/libyuv/unit_test/testdata/mips_loongson_mmi.txt +0 -7
- package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64.txt +0 -4
- package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv.txt +0 -4
- package/android/src/main/cpp/libyuv/unit_test/testdata/riscv64_rvv_zvfh.txt +0 -4
- package/android/src/main/cpp/libyuv/unit_test/testdata/tegra3.txt +0 -23
- package/android/src/main/cpp/libyuv/unit_test/testdata/test0.jpg +0 -0
- package/android/src/main/cpp/libyuv/unit_test/testdata/test1.jpg +0 -0
- package/android/src/main/cpp/libyuv/unit_test/testdata/test2.jpg +0 -0
- package/android/src/main/cpp/libyuv/unit_test/testdata/test3.jpg +0 -0
- package/android/src/main/cpp/libyuv/unit_test/testdata/test4.jpg +0 -0
- package/android/src/main/cpp/libyuv/unit_test/unit_test.cc +0 -581
- package/android/src/main/cpp/libyuv/unit_test/unit_test.h +0 -232
- package/android/src/main/cpp/libyuv/unit_test/video_common_test.cc +0 -112
- package/android/src/main/cpp/libyuv/util/Makefile +0 -9
- package/android/src/main/cpp/libyuv/util/color.cc +0 -120
- package/android/src/main/cpp/libyuv/util/compare.cc +0 -67
- package/android/src/main/cpp/libyuv/util/cpuid.c +0 -211
- package/android/src/main/cpp/libyuv/util/i444tonv12_eg.cc +0 -28
- package/android/src/main/cpp/libyuv/util/psnr.cc +0 -291
- package/android/src/main/cpp/libyuv/util/psnr.h +0 -47
- package/android/src/main/cpp/libyuv/util/psnr_main.cc +0 -620
- package/android/src/main/cpp/libyuv/util/ssim.cc +0 -364
- package/android/src/main/cpp/libyuv/util/ssim.h +0 -38
- package/android/src/main/cpp/libyuv/util/yuvconstants.c +0 -114
- package/android/src/main/cpp/libyuv/util/yuvconvert.cc +0 -367
- package/android/src/main/cpp/libyuv/winarm.mk +0 -47
- package/android/src/main/java/com/faceantispoof/ByteBufferPool.kt +0 -68
- package/android/src/main/java/com/faceantispoof/FastYuv.kt +0 -51
- package/android/src/main/java/com/faceantispoof/YuvToFloatArrayConverter.kt +0 -584
- 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
|
-
}
|