react-native-nitro-pose-exercises 1.1.1 → 1.1.2

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 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 = 'Vision', 'AVFoundation'
130
+ s.frameworks = ["AVFoundation", "Vision"]
131
131
  ```
132
132
 
133
133
  No CocoaPods dependencies required — Vision is built into iOS.
@@ -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,7 +190,7 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
191
190
  // Frame Processing (ML Kit — async with cached results)
192
191
  // ═══════════════════════════════════════════════════════════
193
192
 
194
- override fun processFrame(frame: HybridFrameSpec) {
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
 
@@ -199,17 +198,26 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
199
198
  frameCount++
200
199
  if (frameCount % processEveryNFrames != 0) return
201
200
 
202
- // Get the ImageProxy from VisionCamera frame
203
- val nativeFrame = frame as? NativeFrame ?: return
204
- val imageProxy = nativeFrame.image ?: return
205
-
206
201
  try {
207
- // ML Kit takes InputImage directly from ImageProxy — no color conversion needed
208
- @androidx.camera.core.ExperimentalGetImage
209
- val mediaImage: Image = imageProxy.image ?: return
210
- val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
202
+ // Get raw buffer from VisionCamera frame
203
+ val nativeBuffer = frame.getNativeBuffer()
204
+ val width = frame.getWidth()
205
+ val height = frame.getHeight()
206
+ val bytesPerRow = frame.getBytesPerRow()
207
+
208
+ // Create Bitmap from the native buffer
209
+ val buffer = java.nio.ByteBuffer.allocateDirect(height * bytesPerRow)
210
+ val pointer = nativeBuffer.pointer
211
+ // Copy from native pointer to ByteBuffer
212
+ NativeBufferHelper.copyToByteBuffer(pointer, buffer, height * bytesPerRow)
213
+
214
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
215
+ buffer.rewind()
216
+ bitmap.copyPixelsFromBuffer(buffer)
211
217
 
212
- // ML Kit is async — fire detection, cache result, use cached landmarks for current frame
218
+ val inputImage = InputImage.fromBitmap(bitmap, 0)
219
+
220
+ // ML Kit is async — fire detection, cache result
213
221
  poseDetector!!.process(inputImage)
214
222
  .addOnSuccessListener { pose ->
215
223
  val poseLandmarks = pose.allPoseLandmarks
@@ -220,17 +228,15 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
220
228
  onPoseRegained?.invoke()
221
229
  }
222
230
 
223
- // Build landmark array mapped to MediaPipe indices (34 slots)
224
231
  val landmarkArray = Array(34) { Landmark(x = 0.0, y = 0.0, z = 0.0, visibility = 0.0) }
225
232
 
226
- val imageWidth = inputImage.width.toDouble()
227
- val imageHeight = inputImage.height.toDouble()
233
+ val imageWidth = width.toDouble()
234
+ val imageHeight = height.toDouble()
228
235
 
229
236
  for (poseLandmark in poseLandmarks) {
230
237
  val mediaPipeIndex = mlKitToMediaPipeMap[poseLandmark.landmarkType] ?: continue
231
238
  if (mediaPipeIndex >= 34) continue
232
239
 
233
- // Normalize coordinates to 0-1 range
234
240
  landmarkArray[mediaPipeIndex] = Landmark(
235
241
  x = poseLandmark.position.x.toDouble() / imageWidth,
236
242
  y = poseLandmark.position.y.toDouble() / imageHeight,
@@ -251,12 +257,15 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
251
257
  cachedLandmarks = emptyArray()
252
258
  }
253
259
  }
260
+
261
+ bitmap.recycle()
254
262
  }
255
263
  .addOnFailureListener { e ->
256
264
  println("[PoseExercise] ML Kit error: ${e.message}")
265
+ bitmap.recycle()
257
266
  }
258
267
 
259
- // Use cached landmarks for exercise logic (from previous frame's detection)
268
+ // Use cached landmarks for exercise logic
260
269
  val currentLandmarks: Array<Landmark>
261
270
  synchronized(landmarkLock) {
262
271
  currentLandmarks = cachedLandmarks.copyOf()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-nitro-pose-exercises",
3
- "version": "1.1.1",
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.",
3
+ "version": "1.1.2",
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": {