react-native-nitro-pose-exercises 1.0.5 → 1.0.6
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.
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
package com.margelo.nitro.nitroposeexercises
|
|
2
2
|
|
|
3
3
|
import android.graphics.Bitmap
|
|
4
|
-
import android.graphics.Matrix
|
|
5
4
|
import androidx.annotation.Keep
|
|
6
5
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
7
|
-
import com.google.mediapipe.framework.image.
|
|
6
|
+
import com.google.mediapipe.framework.image.MediaImageBuilder
|
|
8
7
|
import com.google.mediapipe.tasks.core.BaseOptions
|
|
9
8
|
import com.google.mediapipe.tasks.vision.core.RunningMode
|
|
10
9
|
import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarker
|
|
@@ -12,7 +11,7 @@ import com.google.mediapipe.tasks.vision.poselandmarker.PoseLandmarkerOptions
|
|
|
12
11
|
import com.margelo.nitro.NitroModules
|
|
13
12
|
import com.margelo.nitro.core.Promise
|
|
14
13
|
import com.margelo.nitro.camera.HybridFrameSpec
|
|
15
|
-
import com.margelo.nitro.camera
|
|
14
|
+
import com.margelo.nitro.camera.`public`.NativeFrame
|
|
16
15
|
import kotlin.math.acos
|
|
17
16
|
import kotlin.math.max
|
|
18
17
|
import kotlin.math.min
|
|
@@ -156,59 +155,66 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
|
|
|
156
155
|
// Frame Processing
|
|
157
156
|
// ═══════════════════════════════════════════════════════════
|
|
158
157
|
|
|
159
|
-
|
|
158
|
+
override fun processFrame(frame: HybridFrameSpec) {
|
|
160
159
|
if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
|
|
161
160
|
if (!isInitialized || poseLandmarker == null) return
|
|
162
161
|
|
|
163
|
-
// Cast to NativeFrame to get the ImageProxy
|
|
164
|
-
val nativeFrame = frame as? NativeFrame ?: return
|
|
165
|
-
val imageProxy = nativeFrame.image ?: return
|
|
166
|
-
|
|
167
162
|
frameCount++
|
|
168
163
|
if (frameCount % processEveryNFrames != 0) return
|
|
169
164
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
val bitmap = imageProxyToBitmap(imageProxy)
|
|
173
|
-
val mpImage = BitmapImageBuilder(bitmap).build()
|
|
174
|
-
|
|
175
|
-
val result = poseLandmarker!!.detect(mpImage)
|
|
176
|
-
|
|
177
|
-
if (result.landmarks().isNotEmpty()) {
|
|
178
|
-
val poseLandmarks = result.landmarks()[0]
|
|
179
|
-
|
|
180
|
-
if (poseWasLost) {
|
|
181
|
-
poseWasLost = false
|
|
182
|
-
onPoseRegained?.invoke()
|
|
183
|
-
}
|
|
165
|
+
val nativeFrame = frame as? NativeFrame ?: return
|
|
166
|
+
val imageProxy = nativeFrame.image ?: return
|
|
184
167
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
192
|
-
}.toTypedArray()
|
|
193
|
-
|
|
194
|
-
if (_status == SessionStatus.ACTIVE) {
|
|
195
|
-
processExerciseLogic()
|
|
196
|
-
}
|
|
168
|
+
try {
|
|
169
|
+
val bitmapBuffer = Bitmap.createBitmap(
|
|
170
|
+
imageProxy.width,
|
|
171
|
+
imageProxy.height,
|
|
172
|
+
Bitmap.Config.ARGB_8888
|
|
173
|
+
)
|
|
197
174
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
175
|
+
// Use MediaPipe's BitmapImageBuilder
|
|
176
|
+
imageProxy.use {
|
|
177
|
+
val mpImage = com.google.mediapipe.framework.image.MediaImageBuilder(
|
|
178
|
+
imageProxy.image!!
|
|
179
|
+
).build()
|
|
180
|
+
|
|
181
|
+
val result = poseLandmarker!!.detect(mpImage)
|
|
182
|
+
|
|
183
|
+
if (result.landmarks().isNotEmpty()) {
|
|
184
|
+
val poseLandmarks = result.landmarks()[0]
|
|
185
|
+
|
|
186
|
+
if (poseWasLost) {
|
|
187
|
+
poseWasLost = false
|
|
188
|
+
onPoseRegained?.invoke()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_landmarks = poseLandmarks.map { lm ->
|
|
192
|
+
Landmark(
|
|
193
|
+
x = lm.x().toDouble(),
|
|
194
|
+
y = lm.y().toDouble(),
|
|
195
|
+
z = lm.z().toDouble(),
|
|
196
|
+
visibility = (lm.visibility().orElse(0f)).toDouble()
|
|
197
|
+
)
|
|
198
|
+
}.toTypedArray()
|
|
199
|
+
|
|
200
|
+
if (_status == SessionStatus.ACTIVE) {
|
|
201
|
+
processExerciseLogic()
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
if (!poseWasLost) {
|
|
205
|
+
poseWasLost = true
|
|
206
|
+
onPoseLost?.invoke()
|
|
207
|
+
}
|
|
208
|
+
_landmarks = emptyArray()
|
|
209
|
+
}
|
|
202
210
|
}
|
|
203
|
-
_landmarks = emptyArray()
|
|
204
|
-
}
|
|
205
211
|
|
|
206
|
-
|
|
212
|
+
bitmapBuffer.recycle()
|
|
207
213
|
|
|
208
214
|
} catch (e: Exception) {
|
|
209
|
-
|
|
215
|
+
// MediaPipe detection failed — skip this frame
|
|
210
216
|
}
|
|
211
|
-
|
|
217
|
+
}
|
|
212
218
|
|
|
213
219
|
// ═══════════════════════════════════════════════════════════
|
|
214
220
|
// ImageProxy to Bitmap conversion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-pose-exercises",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Real-time on-device exercise tracking for React Native. Rep counting, form validation, and skeleton overlay powered by MediaPipe Pose Landmarker and VisionCamera v5 via Nitro Modules.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|