react-native-yolo 0.0.2 → 0.0.4
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/LICENSE +20 -20
- package/README.md +51 -29
- package/Yolo.podspec +31 -31
- package/android/CMakeLists.txt +35 -32
- package/android/build.gradle +154 -147
- package/android/fix-prefab.gradle +50 -50
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifest.xml +3 -2
- package/android/src/main/cpp/cpp-adapter.cpp +8 -8
- package/android/src/main/java/com/yolo/HybridYolo.kt +65 -27
- package/android/src/main/java/com/yolo/YoloPackage.kt +20 -29
- package/android/src/main/java/com/yolo/loader/YoloModelLoader.kt +130 -0
- package/android/src/main/java/com/yolo/utils/BitmapOrientationFixer.kt +56 -0
- package/android/src/main/java/com/yolo/utils/ContextProvider.kt +14 -0
- package/android/src/main/java/com/yolo/utils/FrameJpegConverter.kt +27 -0
- package/android/src/main/java/com/yolo/utils/FrameValidator.kt +29 -0
- package/android/src/main/java/com/yolo/utils/Nv21JpegEncoder.kt +34 -0
- package/android/src/main/java/com/yolo/utils/Yuv420ToNv21Converter.kt +62 -0
- package/ios/Bridge.h +8 -8
- package/ios/HybridYolo.swift +14 -21
- package/lib/commonjs/index.js +10 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +11 -3
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/yolo.nitro.d.ts +8 -8
- package/lib/typescript/src/specs/yolo.nitro.d.ts.map +1 -1
- package/nitro.json +29 -29
- package/nitrogen/generated/android/Yolo+autolinking.cmake +0 -2
- package/nitrogen/generated/android/YoloOnLoad.cpp +0 -2
- package/nitrogen/generated/android/c++/JHybridYoloSpec.cpp +21 -12
- package/nitrogen/generated/android/c++/JHybridYoloSpec.hpp +4 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/yolo/HybridYoloSpec.kt +13 -7
- package/nitrogen/generated/ios/Yolo-Swift-Cxx-Bridge.cpp +11 -0
- package/nitrogen/generated/ios/Yolo-Swift-Cxx-Bridge.hpp +47 -0
- package/nitrogen/generated/ios/Yolo-Swift-Cxx-Umbrella.hpp +8 -0
- package/nitrogen/generated/ios/c++/HybridYoloSpecSwift.hpp +28 -9
- package/nitrogen/generated/ios/swift/HybridYoloSpec.swift +6 -3
- package/nitrogen/generated/ios/swift/HybridYoloSpec_cxx.swift +35 -25
- package/nitrogen/generated/shared/c++/HybridYoloSpec.cpp +3 -2
- package/nitrogen/generated/shared/c++/HybridYoloSpec.hpp +9 -5
- package/package.json +127 -122
- package/src/index.ts +12 -14
- package/src/specs/yolo.nitro.ts +8 -13
- package/nitrogen/generated/android/c++/views/JHybridYoloStateUpdater.cpp +0 -56
- package/nitrogen/generated/android/c++/views/JHybridYoloStateUpdater.hpp +0 -49
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/yolo/views/HybridYoloManager.kt +0 -80
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/yolo/views/HybridYoloStateUpdater.kt +0 -23
- package/nitrogen/generated/ios/c++/views/HybridYoloComponent.mm +0 -122
- package/nitrogen/generated/shared/c++/views/HybridYoloComponent.cpp +0 -83
- package/nitrogen/generated/shared/c++/views/HybridYoloComponent.hpp +0 -110
- package/nitrogen/generated/shared/json/YoloConfig.json +0 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Yolo_kotlinVersion=2.1.20
|
|
2
|
-
Yolo_minSdkVersion=23
|
|
3
|
-
Yolo_targetSdkVersion=35
|
|
4
|
-
Yolo_compileSdkVersion=34
|
|
5
|
-
Yolo_ndkVersion=27.1.12297006
|
|
1
|
+
Yolo_kotlinVersion=2.1.20
|
|
2
|
+
Yolo_minSdkVersion=23
|
|
3
|
+
Yolo_targetSdkVersion=35
|
|
4
|
+
Yolo_compileSdkVersion=34
|
|
5
|
+
Yolo_ndkVersion=27.1.12297006
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
-
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
|
3
|
+
</manifest>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#include <jni.h>
|
|
2
|
-
#include <fbjni/fbjni.h>
|
|
3
|
-
#include "YoloOnLoad.hpp"
|
|
4
|
-
|
|
5
|
-
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
6
|
-
return facebook::jni::initialize(vm, []() {
|
|
7
|
-
margelo::nitro::yolo::registerAllNatives();
|
|
8
|
-
});
|
|
1
|
+
#include <jni.h>
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
3
|
+
#include "YoloOnLoad.hpp"
|
|
4
|
+
|
|
5
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
6
|
+
return facebook::jni::initialize(vm, []() {
|
|
7
|
+
margelo::nitro::yolo::registerAllNatives();
|
|
8
|
+
});
|
|
9
9
|
}
|
|
@@ -1,27 +1,65 @@
|
|
|
1
|
-
package com.yolo
|
|
2
|
-
|
|
3
|
-
import android.
|
|
4
|
-
import android.
|
|
5
|
-
import
|
|
6
|
-
import com.
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
package com.yolo
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.margelo.nitro.NitroModules
|
|
6
|
+
import com.margelo.nitro.yolo.HybridYoloSpec
|
|
7
|
+
import java.io.File
|
|
8
|
+
import java.io.RandomAccessFile
|
|
9
|
+
import java.net.URL
|
|
10
|
+
import java.nio.MappedByteBuffer
|
|
11
|
+
import java.nio.channels.FileChannel
|
|
12
|
+
import org.tensorflow.lite.Interpreter
|
|
13
|
+
import org.tensorflow.lite.support.common.FileUtil
|
|
14
|
+
import yolo.com.loader.YoloModelLoader
|
|
15
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
16
|
+
|
|
17
|
+
import android.util.Base64
|
|
18
|
+
import com.yolo.utils.FrameJpegConverter
|
|
19
|
+
import com.yolo.utils.FrameValidator
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HybridYolo : HybridYoloSpec() {
|
|
25
|
+
private var interpreter: Interpreter? = null
|
|
26
|
+
private val modelLoader = YoloModelLoader()
|
|
27
|
+
|
|
28
|
+
override fun sum(num1: Double, num2: Double): Double {
|
|
29
|
+
return num1 + num2
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override fun loadModel(modelPath: String) {
|
|
33
|
+
val context =
|
|
34
|
+
NitroModules.applicationContext ?: throw IllegalStateException("Context is null")
|
|
35
|
+
|
|
36
|
+
Log.d("YOLO_TAG", "Trying to load: $modelPath")
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
val modelBuffer = modelLoader.load(modelPath)
|
|
40
|
+
|
|
41
|
+
interpreter?.close()
|
|
42
|
+
interpreter = Interpreter(modelBuffer)
|
|
43
|
+
|
|
44
|
+
Log.d("YOLO_TAG", "✅ Model loaded successfully!")
|
|
45
|
+
} catch (e: Exception) {
|
|
46
|
+
Log.e("YOLO_TAG", "❌ Failed to load model: ${e.message}", e)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
override fun frameToBase64(frame: HybridFrameSpec): String {
|
|
50
|
+
return try {
|
|
51
|
+
if (!FrameValidator.isValidYuv(frame)) return ""
|
|
52
|
+
|
|
53
|
+
val jpegBytes = FrameJpegConverter.toJpegBytes(
|
|
54
|
+
frame = frame,
|
|
55
|
+
quality = 80
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
Base64.encodeToString(jpegBytes, Base64.NO_WRAP)
|
|
59
|
+
} catch (e: Exception) {
|
|
60
|
+
Log.e("YOLO_TAG", "❌ frameToBase64 failed", e)
|
|
61
|
+
""
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
package com.yolo;
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.NativeModule;
|
|
4
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
|
-
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
6
|
-
import com.facebook.react.BaseReactPackage;
|
|
7
|
-
import com.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
override fun
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
companion object {
|
|
24
|
-
init {
|
|
25
|
-
YoloOnLoad.initializeNative()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
1
|
+
package com.yolo;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.NativeModule;
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
5
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
6
|
+
import com.facebook.react.BaseReactPackage;
|
|
7
|
+
import com.margelo.nitro.yolo.YoloOnLoad;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
public class YoloPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = null
|
|
12
|
+
|
|
13
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { emptyMap() }
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
init {
|
|
17
|
+
YoloOnLoad.initializeNative();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
package yolo.com.loader
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.yolo.utils.ContextProvider
|
|
6
|
+
import java.io.File
|
|
7
|
+
import java.io.RandomAccessFile
|
|
8
|
+
import java.net.URL
|
|
9
|
+
import java.nio.MappedByteBuffer
|
|
10
|
+
import java.nio.channels.FileChannel
|
|
11
|
+
import org.tensorflow.lite.support.common.FileUtil
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A utility class for loading YOLO models from various sources, including URLs, file URIs, absolute paths, and APK assets.
|
|
17
|
+
* This class provides methods to load a model into a MappedByteBuffer, which can be used with TensorFlow Lite's Interpreter for inference.
|
|
18
|
+
* It also includes methods to copy raw resources to the cache directory and map files for efficient reading
|
|
19
|
+
*/
|
|
20
|
+
class YoloModelLoader {
|
|
21
|
+
companion object {
|
|
22
|
+
private const val TAG = "YOLO_TAG_LOADER"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Loads a YOLO model from the specified path. The path can be a URL, a file URI, an absolute
|
|
27
|
+
* path, or an asset name.
|
|
28
|
+
* @param modelPath The path to the YOLO model.
|
|
29
|
+
* @return A MappedByteBuffer containing the model data.
|
|
30
|
+
* @throws IllegalArgumentException if the model cannot be loaded from the specified path.
|
|
31
|
+
*/
|
|
32
|
+
fun load(modelPath: String): MappedByteBuffer {
|
|
33
|
+
val context = ContextProvider.context
|
|
34
|
+
|
|
35
|
+
return when {
|
|
36
|
+
modelPath.startsWith("http://") || modelPath.startsWith("https://") -> {
|
|
37
|
+
Log.d(TAG, "Loading model from URL")
|
|
38
|
+
val cachedFile = downloadToCache(modelPath)
|
|
39
|
+
mapFile(cachedFile)
|
|
40
|
+
}
|
|
41
|
+
modelPath.startsWith("file://") -> {
|
|
42
|
+
Log.d(TAG, "Loading model from file URI")
|
|
43
|
+
val file = File(Uri.parse(modelPath).path!!)
|
|
44
|
+
mapFile(file)
|
|
45
|
+
}
|
|
46
|
+
modelPath.startsWith("/") -> {
|
|
47
|
+
Log.d(TAG, "Loading model from absolute path")
|
|
48
|
+
mapFile(File(modelPath))
|
|
49
|
+
}
|
|
50
|
+
modelPath.startsWith("assets_") -> {
|
|
51
|
+
Log.d(TAG, "Loading model from RN raw resource")
|
|
52
|
+
val file = copyRawResourceToCache(modelPath)
|
|
53
|
+
mapFile(file)
|
|
54
|
+
}
|
|
55
|
+
else -> {
|
|
56
|
+
Log.d(TAG, "Loading model from APK assets")
|
|
57
|
+
FileUtil.loadMappedFile(context, modelPath)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Copies a raw resource to the cache directory and returns the corresponding File object.
|
|
64
|
+
* @param resourceName The name of the raw resource (without the file extension).
|
|
65
|
+
* @return The File object pointing to the copied resource in the cache directory.
|
|
66
|
+
* @throws IllegalArgumentException if the raw resource is not found.
|
|
67
|
+
*/
|
|
68
|
+
private fun copyRawResourceToCache(resourceName: String): File {
|
|
69
|
+
val context = ContextProvider.context
|
|
70
|
+
|
|
71
|
+
val resId = context.resources.getIdentifier(resourceName, "raw", context.packageName)
|
|
72
|
+
|
|
73
|
+
if (resId == 0) {
|
|
74
|
+
throw IllegalArgumentException("Raw resource not found: $resourceName")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
val file = File(context.cacheDir, "$resourceName.tflite")
|
|
78
|
+
|
|
79
|
+
context.resources.openRawResource(resId).use { input ->
|
|
80
|
+
file.outputStream().use { output -> input.copyTo(output) }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Log.d(TAG, "Copied raw resource to: ${file.absolutePath}")
|
|
84
|
+
Log.d(TAG, "Copied raw resource size: ${file.length()} bytes")
|
|
85
|
+
|
|
86
|
+
return file
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Maps a file to a MappedByteBuffer for efficient reading.
|
|
91
|
+
* @param file The file to be mapped.
|
|
92
|
+
* @return A MappedByteBuffer containing the file data.
|
|
93
|
+
* @throws IllegalArgumentException if the file does not exist or is empty.
|
|
94
|
+
*/
|
|
95
|
+
private fun mapFile(file: File): MappedByteBuffer {
|
|
96
|
+
if (!file.exists()) {
|
|
97
|
+
throw IllegalArgumentException("Model file does not exist: ${file.absolutePath}")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (file.length() <= 0) {
|
|
101
|
+
throw IllegalArgumentException("Model file is empty: ${file.absolutePath}")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
val randomAccessFile = RandomAccessFile(file, "r")
|
|
105
|
+
val fileChannel = randomAccessFile.channel
|
|
106
|
+
|
|
107
|
+
return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size())
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Downloads a file from a URL to the cache directory and returns the corresponding File object.
|
|
112
|
+
* @param urlString The URL of the file to download.
|
|
113
|
+
* @return The File object pointing to the downloaded file in the cache directory.
|
|
114
|
+
* @throws IllegalArgumentException if the file cannot be downloaded.
|
|
115
|
+
*/
|
|
116
|
+
private fun downloadToCache(urlString: String): File {
|
|
117
|
+
val context = ContextProvider.context
|
|
118
|
+
|
|
119
|
+
val file = File(context.cacheDir, "yolo_model.tflite")
|
|
120
|
+
|
|
121
|
+
URL(urlString).openStream().use { input ->
|
|
122
|
+
file.outputStream().use { output -> input.copyTo(output) }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Log.d(TAG, "Downloaded model to: ${file.absolutePath}")
|
|
126
|
+
Log.d(TAG, "Downloaded model size: ${file.length()} bytes")
|
|
127
|
+
|
|
128
|
+
return file
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import android.graphics.Matrix
|
|
6
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
7
|
+
import java.io.ByteArrayOutputStream
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
object BitmapOrientationFixer {
|
|
12
|
+
fun fix(
|
|
13
|
+
jpegBytes: ByteArray,
|
|
14
|
+
frame: HybridFrameSpec,
|
|
15
|
+
quality: Int
|
|
16
|
+
): ByteArray {
|
|
17
|
+
val bitmap = BitmapFactory.decodeByteArray(
|
|
18
|
+
jpegBytes,
|
|
19
|
+
0,
|
|
20
|
+
jpegBytes.size
|
|
21
|
+
) ?: return jpegBytes
|
|
22
|
+
|
|
23
|
+
val rotationDegrees = 90f //TODO: Get the actual rotation degrees from the frame metadata if available
|
|
24
|
+
|
|
25
|
+
val matrix = Matrix().apply {
|
|
26
|
+
postRotate(rotationDegrees)
|
|
27
|
+
|
|
28
|
+
if (frame.isMirrored) {
|
|
29
|
+
postScale(-1f, 1f)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
val rotatedBitmap = Bitmap.createBitmap(
|
|
34
|
+
bitmap,
|
|
35
|
+
0,
|
|
36
|
+
0,
|
|
37
|
+
bitmap.width,
|
|
38
|
+
bitmap.height,
|
|
39
|
+
matrix,
|
|
40
|
+
true
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
val output = ByteArrayOutputStream()
|
|
44
|
+
|
|
45
|
+
rotatedBitmap.compress(
|
|
46
|
+
Bitmap.CompressFormat.JPEG,
|
|
47
|
+
quality,
|
|
48
|
+
output
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
bitmap.recycle()
|
|
52
|
+
rotatedBitmap.recycle()
|
|
53
|
+
|
|
54
|
+
return output.toByteArray()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.margelo.nitro.NitroModules
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Provides a way to access the application context from anywhere in the app.
|
|
8
|
+
* This is useful for classes that do not have a direct reference to a Context object.
|
|
9
|
+
*/
|
|
10
|
+
object ContextProvider {
|
|
11
|
+
val context: Context
|
|
12
|
+
get() = NitroModules.applicationContext
|
|
13
|
+
?: throw IllegalStateException("Context is null")
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
5
|
+
import kotlin.math.roundToInt
|
|
6
|
+
|
|
7
|
+
object FrameJpegConverter {
|
|
8
|
+
private const val TAG = "YOLO_TAG_FrameJpegConverter"
|
|
9
|
+
fun toJpegBytes(frame : HybridFrameSpec, quality: Int = 80): ByteArray {
|
|
10
|
+
val width = frame.width.roundToInt()
|
|
11
|
+
val height = frame.height.roundToInt()
|
|
12
|
+
|
|
13
|
+
val nv21 = Yuv420ToNv21Converter.convert(frame, width, height)
|
|
14
|
+
|
|
15
|
+
val jpegBytes = Nv21JpegEncoder.encode(
|
|
16
|
+
nv21 = nv21,
|
|
17
|
+
width = width,
|
|
18
|
+
height = height,
|
|
19
|
+
quality = quality
|
|
20
|
+
)
|
|
21
|
+
return BitmapOrientationFixer.fix(
|
|
22
|
+
jpegBytes = jpegBytes,
|
|
23
|
+
frame = frame,
|
|
24
|
+
quality = quality
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
object FrameValidator {
|
|
8
|
+
private const val TAG = "YOLO_TAG_FrameValidator"
|
|
9
|
+
fun isValidYuv(frame: HybridFrameSpec): Boolean {
|
|
10
|
+
if (!frame.isValid) {
|
|
11
|
+
Log.e(TAG, "❌ Frame is not valid")
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
val planes = frame.getPlanes()
|
|
16
|
+
if (planes.size < 3) {
|
|
17
|
+
Log.e(TAG, "❌ Expected 3 YUV planes, got ${planes.size}")
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
planes.forEachIndexed { index, plane ->
|
|
22
|
+
if (!plane.isValid) {
|
|
23
|
+
Log.e(TAG, "❌ Plane $index is not valid")
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import android.graphics.ImageFormat
|
|
4
|
+
import android.graphics.Rect
|
|
5
|
+
import android.graphics.YuvImage
|
|
6
|
+
import java.io.ByteArrayOutputStream
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
object Nv21JpegEncoder {
|
|
10
|
+
fun encode(
|
|
11
|
+
nv21: ByteArray,
|
|
12
|
+
width: Int,
|
|
13
|
+
height: Int,
|
|
14
|
+
quality: Int
|
|
15
|
+
): ByteArray {
|
|
16
|
+
val yuvImage = YuvImage(
|
|
17
|
+
nv21,
|
|
18
|
+
ImageFormat.NV21,
|
|
19
|
+
width,
|
|
20
|
+
height,
|
|
21
|
+
null
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
val output = ByteArrayOutputStream()
|
|
25
|
+
|
|
26
|
+
yuvImage.compressToJpeg(
|
|
27
|
+
Rect(0, 0, width, height),
|
|
28
|
+
quality,
|
|
29
|
+
output
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return output.toByteArray()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package com.yolo.utils
|
|
2
|
+
|
|
3
|
+
import com.margelo.nitro.camera.HybridFrameSpec
|
|
4
|
+
import kotlin.math.roundToInt
|
|
5
|
+
import android.util.Log
|
|
6
|
+
|
|
7
|
+
object Yuv420ToNv21Converter {
|
|
8
|
+
fun convert(frame: HybridFrameSpec, width: Int, height: Int): ByteArray {
|
|
9
|
+
|
|
10
|
+
val planes = frame.getPlanes()
|
|
11
|
+
|
|
12
|
+
val yPlane = planes[0]
|
|
13
|
+
val uPlane = planes[1]
|
|
14
|
+
val vPlane = planes[2]
|
|
15
|
+
|
|
16
|
+
val yBytes = yPlane.getPixelBuffer().toByteArray()
|
|
17
|
+
val uBytes = uPlane.getPixelBuffer().toByteArray()
|
|
18
|
+
val vBytes = vPlane.getPixelBuffer().toByteArray()
|
|
19
|
+
|
|
20
|
+
val yRowStride = yPlane.bytesPerRow.roundToInt()
|
|
21
|
+
val uRowStride = uPlane.bytesPerRow.roundToInt()
|
|
22
|
+
val vRowStride = vPlane.bytesPerRow.roundToInt()
|
|
23
|
+
|
|
24
|
+
val ySize = width * height
|
|
25
|
+
val uvSize = width * height / 2
|
|
26
|
+
val nv21 = ByteArray(ySize + uvSize)
|
|
27
|
+
|
|
28
|
+
var dst = 0
|
|
29
|
+
|
|
30
|
+
for (row in 0 until height) {
|
|
31
|
+
val src = row * yRowStride
|
|
32
|
+
if (src + width > yBytes.size) break
|
|
33
|
+
|
|
34
|
+
System.arraycopy(yBytes, src, nv21, dst, width)
|
|
35
|
+
dst += width
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
val chromaWidth = width / 2
|
|
39
|
+
val chromaHeight = height / 2
|
|
40
|
+
|
|
41
|
+
var uvDst = ySize
|
|
42
|
+
|
|
43
|
+
for (row in 0 until chromaHeight) {
|
|
44
|
+
for (col in 0 until chromaWidth) {
|
|
45
|
+
val uIndex = row * uRowStride + col * 2
|
|
46
|
+
val vIndex = row * vRowStride + col * 2
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
uIndex >= uBytes.size ||
|
|
50
|
+
vIndex >= vBytes.size ||
|
|
51
|
+
uvDst + 1 >= nv21.size
|
|
52
|
+
) {
|
|
53
|
+
return nv21
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
nv21[uvDst++] = vBytes[vIndex]
|
|
57
|
+
nv21[uvDst++] = uBytes[uIndex]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return nv21
|
|
61
|
+
}
|
|
62
|
+
}
|
package/ios/Bridge.h
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Bridge.h
|
|
3
|
-
// yolo
|
|
4
|
-
//
|
|
5
|
-
// Created by Khaoula-Ghalimi on
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
1
|
+
//
|
|
2
|
+
// Bridge.h
|
|
3
|
+
// yolo
|
|
4
|
+
//
|
|
5
|
+
// Created by Khaoula-Ghalimi on 22/06/2026
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
package/ios/HybridYolo.swift
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
//
|
|
2
|
-
// HybridYolo.swift
|
|
3
|
-
// Pods
|
|
4
|
-
//
|
|
5
|
-
// Created by Khaoula-Ghalimi on
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import Foundation
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Props
|
|
16
|
-
var isRed: Bool = false {
|
|
17
|
-
didSet {
|
|
18
|
-
view.backgroundColor = isRed ? .red : .black
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
1
|
+
//
|
|
2
|
+
// HybridYolo.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Khaoula-Ghalimi on 22/06/2026.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
class HybridYolo: HybridYoloSpec {
|
|
11
|
+
func sum(num1: Double, num2: Double) throws -> Double {
|
|
12
|
+
return num1 + num2
|
|
13
|
+
}
|
|
14
|
+
}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -5,7 +5,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.Yolo = void 0;
|
|
7
7
|
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
const Yolo = exports.Yolo =
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
const NativeYolo = _reactNativeNitroModules.NitroModules.createHybridObject('Yolo');
|
|
10
|
+
const Yolo = exports.Yolo = Object.assign(NativeYolo, {
|
|
11
|
+
loadModelTest(modelAssetId) {
|
|
12
|
+
const {
|
|
13
|
+
uri
|
|
14
|
+
} = _reactNative.Image.resolveAssetSource(modelAssetId);
|
|
15
|
+
return NativeYolo.loadModel(uri);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
11
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNativeNitroModules","require","
|
|
1
|
+
{"version":3,"names":["_reactNativeNitroModules","require","_reactNative","NativeYolo","NitroModules","createHybridObject","Yolo","exports","Object","assign","loadModelTest","modelAssetId","uri","Image","resolveAssetSource","loadModel"],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAEA,IAAAC,YAAA,GAAAD,OAAA;AAEA,MAAME,UAAU,GAAGC,qCAAY,CAACC,kBAAkB,CAAW,MAAM,CAAC;AAE7D,MAAMC,IAAI,GAAAC,OAAA,CAAAD,IAAA,GAAGE,MAAM,CAACC,MAAM,CAACN,UAAU,EAAE;EAC5CO,aAAaA,CAACC,YAAoB,EAAC;IACjC,MAAM;MAAEC;IAAI,CAAC,GAAGC,kBAAK,CAACC,kBAAkB,CAACH,YAAY,CAAC;IACtD,OAAOR,UAAU,CAACY,SAAS,CAACH,GAAG,CAAC;EAClC;AACF,CAAC,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
3
|
+
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
|
+
import { Image } from 'react-native';
|
|
5
|
+
const NativeYolo = NitroModules.createHybridObject('Yolo');
|
|
6
|
+
export const Yolo = Object.assign(NativeYolo, {
|
|
7
|
+
loadModelTest(modelAssetId) {
|
|
8
|
+
const {
|
|
9
|
+
uri
|
|
10
|
+
} = Image.resolveAssetSource(modelAssetId);
|
|
11
|
+
return NativeYolo.loadModel(uri);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
6
14
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["NitroModules","Image","NativeYolo","createHybridObject","Yolo","Object","assign","loadModelTest","modelAssetId","uri","resolveAssetSource","loadModel"],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAEzD,SAASC,KAAK,QAAQ,cAAc;AAEpC,MAAMC,UAAU,GAAGF,YAAY,CAACG,kBAAkB,CAAW,MAAM,CAAC;AAEpE,OAAO,MAAMC,IAAI,GAAGC,MAAM,CAACC,MAAM,CAACJ,UAAU,EAAE;EAC5CK,aAAaA,CAACC,YAAoB,EAAC;IACjC,MAAM;MAAEC;IAAI,CAAC,GAAGR,KAAK,CAACS,kBAAkB,CAACF,YAAY,CAAC;IACtD,OAAON,UAAU,CAACS,SAAS,CAACF,GAAG,CAAC;EAClC;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { Yolo as YoloSpec } from './specs/yolo.nitro';
|
|
2
|
+
export declare const Yolo: YoloSpec & {
|
|
3
|
+
loadModelTest(modelAssetId: number): void;
|
|
4
|
+
};
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAK1D,eAAO,MAAM,IAAI;gCACa,MAAM;CAIlC,CAAA"}
|