react-native-nitro-pose-exercises 1.1.1 → 1.1.3
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/README.md +1 -1
- package/android/CMakeLists.txt +7 -0
- package/android/src/main/cpp/frame_helper.cpp +37 -0
- package/android/src/main/java/com/margelo/nitro/nitroposeexercises/FrameHelper.kt +12 -0
- package/android/src/main/java/com/margelo/nitro/nitroposeexercises/NitroPoseExercises.kt +11 -18
- package/ios/NitroPoseExercises.swift +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ module.exports = {
|
|
|
127
127
|
The iOS podspec needs the Vision and AVFoundation system frameworks:
|
|
128
128
|
|
|
129
129
|
```ruby
|
|
130
|
-
s.frameworks =
|
|
130
|
+
s.frameworks = ["AVFoundation", "Vision"]
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
No CocoaPods dependencies required — Vision is built into iOS.
|
package/android/CMakeLists.txt
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#include <jni.h>
|
|
2
|
+
#include <android/hardware_buffer_jni.h>
|
|
3
|
+
|
|
4
|
+
extern "C"
|
|
5
|
+
JNIEXPORT jobject JNICALL
|
|
6
|
+
Java_com_margelo_nitro_nitroposeexercises_FrameHelper_hardwareBufferToBitmap(
|
|
7
|
+
JNIEnv *env,
|
|
8
|
+
jclass clazz,
|
|
9
|
+
jlong pointer
|
|
10
|
+
) {
|
|
11
|
+
AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
|
|
12
|
+
if (!buffer) return nullptr;
|
|
13
|
+
|
|
14
|
+
// Convert AHardwareBuffer* to Java HardwareBuffer
|
|
15
|
+
jobject hardwareBuffer = AHardwareBuffer_toHardwareBuffer(env, buffer);
|
|
16
|
+
if (!hardwareBuffer) return nullptr;
|
|
17
|
+
|
|
18
|
+
// Call Bitmap.wrapHardwareBuffer(hardwareBuffer, null) to create a hardware-backed Bitmap
|
|
19
|
+
jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
|
|
20
|
+
jmethodID wrapMethod = env->GetStaticMethodID(
|
|
21
|
+
bitmapClass,
|
|
22
|
+
"wrapHardwareBuffer",
|
|
23
|
+
"(Landroid/hardware/HardwareBuffer;Landroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;"
|
|
24
|
+
);
|
|
25
|
+
jobject hwBitmap = env->CallStaticObjectMethod(bitmapClass, wrapMethod, hardwareBuffer, nullptr);
|
|
26
|
+
if (!hwBitmap) return nullptr;
|
|
27
|
+
|
|
28
|
+
// Copy to a software ARGB_8888 Bitmap (ML Kit needs software bitmap)
|
|
29
|
+
jclass configClass = env->FindClass("android/graphics/Bitmap$Config");
|
|
30
|
+
jfieldID argbField = env->GetStaticFieldID(configClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
|
|
31
|
+
jobject argbConfig = env->GetStaticObjectField(configClass, argbField);
|
|
32
|
+
|
|
33
|
+
jmethodID copyMethod = env->GetMethodID(bitmapClass, "copy", "(Landroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;");
|
|
34
|
+
jobject softBitmap = env->CallObjectMethod(hwBitmap, copyMethod, argbConfig, JNI_FALSE);
|
|
35
|
+
|
|
36
|
+
return softBitmap;
|
|
37
|
+
}
|
|
@@ -13,7 +13,6 @@ import com.google.mlkit.vision.pose.defaults.PoseDetectorOptions
|
|
|
13
13
|
import com.margelo.nitro.NitroModules
|
|
14
14
|
import com.margelo.nitro.core.Promise
|
|
15
15
|
import com.margelo.nitro.camera.HybridFrameSpec
|
|
16
|
-
import com.margelo.nitro.camera.NativeFrame
|
|
17
16
|
import kotlin.math.acos
|
|
18
17
|
import kotlin.math.max
|
|
19
18
|
import kotlin.math.min
|
|
@@ -191,25 +190,21 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
|
|
|
191
190
|
// Frame Processing (ML Kit — async with cached results)
|
|
192
191
|
// ═══════════════════════════════════════════════════════════
|
|
193
192
|
|
|
194
|
-
|
|
193
|
+
override fun processFrame(frame: HybridFrameSpec) {
|
|
195
194
|
if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
|
|
196
195
|
if (!isInitialized || poseDetector == null) return
|
|
197
196
|
|
|
198
|
-
// Frame throttle
|
|
199
197
|
frameCount++
|
|
200
198
|
if (frameCount % processEveryNFrames != 0) return
|
|
201
199
|
|
|
202
|
-
// Get the ImageProxy from VisionCamera frame
|
|
203
|
-
val nativeFrame = frame as? NativeFrame ?: return
|
|
204
|
-
val imageProxy = nativeFrame.image ?: return
|
|
205
|
-
|
|
206
200
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
val inputImage = InputImage.
|
|
201
|
+
val nativeBuffer = frame.getNativeBuffer()
|
|
202
|
+
val bitmap = FrameHelper.hardwareBufferToBitmap(nativeBuffer.pointer) ?: return
|
|
203
|
+
|
|
204
|
+
val inputImage = InputImage.fromBitmap(bitmap, 0)
|
|
205
|
+
val imageWidth = bitmap.width.toDouble()
|
|
206
|
+
val imageHeight = bitmap.height.toDouble()
|
|
211
207
|
|
|
212
|
-
// ML Kit is async — fire detection, cache result, use cached landmarks for current frame
|
|
213
208
|
poseDetector!!.process(inputImage)
|
|
214
209
|
.addOnSuccessListener { pose ->
|
|
215
210
|
val poseLandmarks = pose.allPoseLandmarks
|
|
@@ -220,17 +215,12 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
|
|
|
220
215
|
onPoseRegained?.invoke()
|
|
221
216
|
}
|
|
222
217
|
|
|
223
|
-
// Build landmark array mapped to MediaPipe indices (34 slots)
|
|
224
218
|
val landmarkArray = Array(34) { Landmark(x = 0.0, y = 0.0, z = 0.0, visibility = 0.0) }
|
|
225
219
|
|
|
226
|
-
val imageWidth = inputImage.width.toDouble()
|
|
227
|
-
val imageHeight = inputImage.height.toDouble()
|
|
228
|
-
|
|
229
220
|
for (poseLandmark in poseLandmarks) {
|
|
230
221
|
val mediaPipeIndex = mlKitToMediaPipeMap[poseLandmark.landmarkType] ?: continue
|
|
231
222
|
if (mediaPipeIndex >= 34) continue
|
|
232
223
|
|
|
233
|
-
// Normalize coordinates to 0-1 range
|
|
234
224
|
landmarkArray[mediaPipeIndex] = Landmark(
|
|
235
225
|
x = poseLandmark.position.x.toDouble() / imageWidth,
|
|
236
226
|
y = poseLandmark.position.y.toDouble() / imageHeight,
|
|
@@ -251,12 +241,15 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
|
|
|
251
241
|
cachedLandmarks = emptyArray()
|
|
252
242
|
}
|
|
253
243
|
}
|
|
244
|
+
|
|
245
|
+
bitmap.recycle()
|
|
254
246
|
}
|
|
255
247
|
.addOnFailureListener { e ->
|
|
256
248
|
println("[PoseExercise] ML Kit error: ${e.message}")
|
|
249
|
+
bitmap.recycle()
|
|
257
250
|
}
|
|
258
251
|
|
|
259
|
-
// Use cached landmarks
|
|
252
|
+
// Use cached landmarks from previous frame
|
|
260
253
|
val currentLandmarks: Array<Landmark>
|
|
261
254
|
synchronized(landmarkLock) {
|
|
262
255
|
currentLandmarks = cachedLandmarks.copyOf()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ios/
|
|
1
|
+
// ios/NitroPoseExercises.swift
|
|
2
2
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import NitroModules
|
|
@@ -6,7 +6,7 @@ import VisionCamera
|
|
|
6
6
|
import AVFoundation
|
|
7
7
|
import Vision
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class NitroPoseExercises: HybridNitroPoseExercisesSpec {
|
|
10
10
|
|
|
11
11
|
// ─── Vision Framework ───────────────────────────────────────
|
|
12
12
|
private var isInitialized = false
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-pose-exercises",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Real-time on-device exercise tracking for React Native. Rep counting, form validation, and skeleton overlay powered by
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"description": "Real-time on-device exercise tracking for React Native. Rep counting, form validation, and skeleton overlay powered by Apple Vision (iOS) and Google ML Kit (Android) with VisionCamera v5 via Nitro Modules.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
7
7
|
"exports": {
|