@pose-tracker/react-native-pose-estimation-light 0.1.0 → 0.2.0
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 +10 -2
- package/lib/backends/webview/WebViewPoseBackend.d.ts +1 -1
- package/lib/backends/webview/onlineRuntime.d.ts +14 -4
- package/lib/backends/webview/onlineRuntime.js +22 -4
- package/lib/backends/webview/poseHtml.d.ts +12 -4
- package/lib/backends/webview/poseHtml.js +15 -6
- package/lib/backends/webview/poseRuntimeSource.js +2 -2
- package/lib/camera/WebViewPoseView.d.ts +12 -0
- package/lib/camera/WebViewPoseView.js +29 -0
- package/lib/client.d.ts +2 -2
- package/lib/client.js +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +4 -2
- package/lib/models/poseModels.d.ts +8 -6
- package/lib/models/poseModels.js +9 -8
- package/lib/sdkVersion.d.ts +1 -1
- package/lib/sdkVersion.js +1 -1
- package/lib/types/events.d.ts +1 -1
- package/lib/types/features.d.ts +2 -2
- package/lib/types/features.js +8 -9
- package/package.json +1 -1
- package/src/backends/webview/WebViewPoseBackend.ts +1 -1
- package/src/backends/webview/onlineRuntime.ts +36 -5
- package/src/backends/webview/poseHtml.ts +23 -6
- package/src/camera/WebViewPoseView.tsx +43 -0
- package/src/client.ts +3 -3
- package/src/index.ts +3 -0
- package/src/models/poseModels.ts +14 -11
- package/src/sdkVersion.ts +1 -1
- package/src/types/events.ts +1 -0
- package/src/types/features.ts +8 -9
|
@@ -178,6 +178,8 @@ function WebViewPoseView(props) {
|
|
|
178
178
|
// injected via __PT_SET_SKELETON (avoids remounting on uuid fetch).
|
|
179
179
|
skeletonDef: props.skeletonDef ?? null,
|
|
180
180
|
capturePriority: client.getQualityState().capturePriority,
|
|
181
|
+
sourceType: props.source ?? 'camera',
|
|
182
|
+
sourceUrl: props.sourceUri,
|
|
181
183
|
});
|
|
182
184
|
}, [
|
|
183
185
|
facingMode,
|
|
@@ -188,6 +190,8 @@ function WebViewPoseView(props) {
|
|
|
188
190
|
coldStart,
|
|
189
191
|
loadingText,
|
|
190
192
|
props.skeletonDef,
|
|
193
|
+
props.source,
|
|
194
|
+
props.sourceUri,
|
|
191
195
|
]);
|
|
192
196
|
// Apply custom skeleton (uuid fetch or prop) without remounting the page.
|
|
193
197
|
(0, react_1.useEffect)(() => {
|
|
@@ -210,6 +214,31 @@ function WebViewPoseView(props) {
|
|
|
210
214
|
const payload = JSON.stringify(loadingText);
|
|
211
215
|
webRef.current?.injectJavaScript?.(`window.__PT_SET_LOADING_TEXT && window.__PT_SET_LOADING_TEXT(${payload}); true;`);
|
|
212
216
|
}, [isWebViewBackend, html, loadingText]);
|
|
217
|
+
// Live source switches after first HTML boot (CFG already has initial source).
|
|
218
|
+
const lastSourceHtmlRef = (0, react_1.useRef)(null);
|
|
219
|
+
(0, react_1.useEffect)(() => {
|
|
220
|
+
if (!isWebViewBackend || !html)
|
|
221
|
+
return;
|
|
222
|
+
if (lastSourceHtmlRef.current !== html) {
|
|
223
|
+
lastSourceHtmlRef.current = html;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const type = props.source ?? 'camera';
|
|
227
|
+
const payload = JSON.stringify({
|
|
228
|
+
type,
|
|
229
|
+
url: props.sourceUri ?? null,
|
|
230
|
+
base64: props.sourceBase64 ?? null,
|
|
231
|
+
mime: props.sourceMime ?? null,
|
|
232
|
+
});
|
|
233
|
+
webRef.current?.injectJavaScript?.(`window.__PT_SET_SOURCE && window.__PT_SET_SOURCE(${payload}); true;`);
|
|
234
|
+
}, [
|
|
235
|
+
isWebViewBackend,
|
|
236
|
+
html,
|
|
237
|
+
props.source,
|
|
238
|
+
props.sourceUri,
|
|
239
|
+
props.sourceBase64,
|
|
240
|
+
props.sourceMime,
|
|
241
|
+
]);
|
|
213
242
|
(0, react_1.useEffect)(() => {
|
|
214
243
|
if (!(backend instanceof WebViewPoseBackend_1.WebViewPoseBackend)) {
|
|
215
244
|
return;
|
package/lib/client.d.ts
CHANGED
|
@@ -60,8 +60,8 @@ export interface PoseTrackerClientOptions extends ConfigureOptions {
|
|
|
60
60
|
usageTracker?: UsageTracker;
|
|
61
61
|
/**
|
|
62
62
|
* Docs API `model` query parity (`movenet` default, `blazepose`, …).
|
|
63
|
-
* BlazePose
|
|
64
|
-
*
|
|
63
|
+
* BlazePose loads via CDN pose-detection in the WebView (heavier than
|
|
64
|
+
* MoveNet). Ignored when {@link modelUrl} is set.
|
|
65
65
|
*/
|
|
66
66
|
model?: PoseModelAlias;
|
|
67
67
|
/**
|
package/lib/client.js
CHANGED
|
@@ -398,7 +398,7 @@ class PoseTrackerClient {
|
|
|
398
398
|
tfjsVersion: this.options.tfjsVersion,
|
|
399
399
|
});
|
|
400
400
|
this.options.onDiagnostic?.(`[posetracker-light] pose-runtime online version=${parts.version} ` +
|
|
401
|
-
`modelId=${parts.modelId} modelUrl=${parts.modelUrl}`);
|
|
401
|
+
`modelId=${parts.modelId} modelKind=${parts.modelKind} modelUrl=${parts.modelUrl}`);
|
|
402
402
|
return parts;
|
|
403
403
|
}
|
|
404
404
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -19,10 +19,10 @@ export { UsageTracker, TrackError } from './api/track';
|
|
|
19
19
|
export type { TrackRequest, TrackResponse, UsageTrackerOptions } from './api/track';
|
|
20
20
|
export { RuntimeCache } from './runtime/RuntimeCache';
|
|
21
21
|
export type { PoseRuntimeParts, RuntimeCacheOptions, RuntimeCacheProgress, } from './runtime/RuntimeCache';
|
|
22
|
-
export { getOnlineRuntimeParts, getOnlineRuntimeVersion, defaultOnlineRuntimeUrls, ONLINE_TFJS_VERSION, } from './backends/webview/onlineRuntime';
|
|
22
|
+
export { getOnlineRuntimeParts, getOnlineRuntimeVersion, defaultOnlineRuntimeUrls, defaultPoseDetectionCdnUrl, ONLINE_TFJS_VERSION, ONLINE_POSE_DETECTION_VERSION, } from './backends/webview/onlineRuntime';
|
|
23
23
|
export type { GetOnlineRuntimeOptions, OnlineRuntimeParts, OnlineRuntimeUrls, } from './backends/webview/onlineRuntime';
|
|
24
24
|
export { DEFAULT_MOVENET_LIGHTNING_URL, originFromModelUrl, resolvePoseModel, } from './models/poseModels';
|
|
25
|
-
export type { PoseModelAlias, ResolvePoseModelOptions, ResolvedPoseModel, } from './models/poseModels';
|
|
25
|
+
export type { PoseModelAlias, PoseModelKind, ResolvePoseModelOptions, ResolvedPoseModel, } from './models/poseModels';
|
|
26
26
|
export { PoseTrackerClient } from './client';
|
|
27
27
|
export type { PoseTrackerClientOptions, PreferredBackend, StartExerciseOptions } from './client';
|
|
28
28
|
export type { ColdStartMode, PreloadOptions } from './types/preload';
|
package/lib/index.js
CHANGED
|
@@ -28,8 +28,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
28
28
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.
|
|
32
|
-
exports.resolveFeatures = exports.shouldShowWatermark = exports.isPaidPlan = exports.freeBlockedFeatures = exports.featureNotSupportedMessage = exports.COMBINED_REFERENCE_EXERCISE_MESSAGE = exports.INVALID_TOKEN_MESSAGE = exports.FREE_PLAN_FEATURES_MESSAGE = exports.DEFAULT_FEATURES = exports.RuntimeGuard = exports.scoreDeviceCapability = exports.profileFromScore = exports.isMaliRenderer = exports.ENABLE_MALI_HARD_CAP = exports.CAPTURE_CONSTRAINT_MODE = exports.ANDROID_SOFT_CAP_PROFILE = exports.ANDROID_PREPROCESS_PATH = exports.ANDROID_PERF_DEBUG = exports.ANDROID_MIN_TARGET_FPS = exports.ANDROID_INFER_FRAME_SKIP = exports.profileFromWarmupMedianMs = exports.nextLowerQualityProfile = exports.minMedianMsForMinTarget = exports.lowerQualityProfile = exports.isQualityProfileId = exports.getQualityProfile = exports.getMinTargetFps = exports.getIdealFpsRange = exports.getCriticalFpsThreshold = exports.estimatedFpsFromMedianMs = exports.currentQualityPlatform = exports.TARGET_MEDIAN_MS = exports.TARGET_FPS = exports.QUALITY_PROFILES = exports.QUALITY_LADDER = exports.MIN_TARGET_FPS_IOS = exports.MIN_TARGET_FPS_DEFAULT = exports.MIN_TARGET_FPS_ANDROID = exports.QUALITY_SETTLE_MS = exports.LOW_FPS_STREAK_BEFORE_DOWNGRADE = exports.IDEAL_FPS_RANGE_IOS = exports.IDEAL_FPS_RANGE_ANDROID = exports.CRITICAL_FPS_THRESHOLD = exports.AdaptiveQualityController = exports.toClassicNativeMessage = void 0;
|
|
31
|
+
exports.logAccelerationReport = exports.defaultDiagnosticLogger = exports.createNativeFileStore = exports.createMemoryFileStore = exports.createBlobUtilFileStore = exports.createRnfsFileStore = exports.createExpoFileStore = exports.EngineLoader = exports.isExpoGo = exports.PoseCameraView = exports.VISION_JOINT_TO_SDK = exports.poseFromVisionPluginResult = exports.VISION_BODY_POSE_PLUGIN_NAME = exports.isVisionPoseCameraAvailable = exports.isVisionBackendAvailable = exports.VisionUnavailableError = exports.VisionPoseBackend = exports.POSETRACKER_LOGO_DATA_URL = exports.POSE_HTML_BUILD = exports.DEFAULT_LOADING_TEXT = exports.buildPoseHtml = exports.WebViewPoseView = exports.isWebViewPoseBackend = exports.WebViewPoseBackend = exports.usePoseTracker = exports.PoseTrackerProvider = exports.SkeletonFetchError = exports.fetchSkeletonDefinition = exports.DEFAULT_SKELETON_DEFINITION = exports.PoseTrackerClient = exports.resolvePoseModel = exports.originFromModelUrl = exports.DEFAULT_MOVENET_LIGHTNING_URL = exports.ONLINE_POSE_DETECTION_VERSION = exports.ONLINE_TFJS_VERSION = exports.defaultPoseDetectionCdnUrl = exports.defaultOnlineRuntimeUrls = exports.getOnlineRuntimeVersion = exports.getOnlineRuntimeParts = exports.RuntimeCache = exports.TrackError = exports.UsageTracker = exports.resolveExerciseName = exports.findExerciseByIdOrAlias = exports.EXERCISE_ALIASES = exports.SDK_VERSION = exports.SDK_NAME = exports.DEFAULT_BASE_URL = exports.ConfigureError = exports.configure = void 0;
|
|
32
|
+
exports.resolveFeatures = exports.shouldShowWatermark = exports.isPaidPlan = exports.freeBlockedFeatures = exports.featureNotSupportedMessage = exports.COMBINED_REFERENCE_EXERCISE_MESSAGE = exports.INVALID_TOKEN_MESSAGE = exports.FREE_PLAN_FEATURES_MESSAGE = exports.DEFAULT_FEATURES = exports.RuntimeGuard = exports.scoreDeviceCapability = exports.profileFromScore = exports.isMaliRenderer = exports.ENABLE_MALI_HARD_CAP = exports.CAPTURE_CONSTRAINT_MODE = exports.ANDROID_SOFT_CAP_PROFILE = exports.ANDROID_PREPROCESS_PATH = exports.ANDROID_PERF_DEBUG = exports.ANDROID_MIN_TARGET_FPS = exports.ANDROID_INFER_FRAME_SKIP = exports.profileFromWarmupMedianMs = exports.nextLowerQualityProfile = exports.minMedianMsForMinTarget = exports.lowerQualityProfile = exports.isQualityProfileId = exports.getQualityProfile = exports.getMinTargetFps = exports.getIdealFpsRange = exports.getCriticalFpsThreshold = exports.estimatedFpsFromMedianMs = exports.currentQualityPlatform = exports.TARGET_MEDIAN_MS = exports.TARGET_FPS = exports.QUALITY_PROFILES = exports.QUALITY_LADDER = exports.MIN_TARGET_FPS_IOS = exports.MIN_TARGET_FPS_DEFAULT = exports.MIN_TARGET_FPS_ANDROID = exports.QUALITY_SETTLE_MS = exports.LOW_FPS_STREAK_BEFORE_DOWNGRADE = exports.IDEAL_FPS_RANGE_IOS = exports.IDEAL_FPS_RANGE_ANDROID = exports.CRITICAL_FPS_THRESHOLD = exports.AdaptiveQualityController = exports.toClassicNativeMessage = exports.logPlatformBanner = exports.logFrameStats = void 0;
|
|
33
33
|
// Handshake + usage tracking
|
|
34
34
|
var configure_1 = require("./api/configure");
|
|
35
35
|
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return configure_1.configure; } });
|
|
@@ -51,7 +51,9 @@ var onlineRuntime_1 = require("./backends/webview/onlineRuntime");
|
|
|
51
51
|
Object.defineProperty(exports, "getOnlineRuntimeParts", { enumerable: true, get: function () { return onlineRuntime_1.getOnlineRuntimeParts; } });
|
|
52
52
|
Object.defineProperty(exports, "getOnlineRuntimeVersion", { enumerable: true, get: function () { return onlineRuntime_1.getOnlineRuntimeVersion; } });
|
|
53
53
|
Object.defineProperty(exports, "defaultOnlineRuntimeUrls", { enumerable: true, get: function () { return onlineRuntime_1.defaultOnlineRuntimeUrls; } });
|
|
54
|
+
Object.defineProperty(exports, "defaultPoseDetectionCdnUrl", { enumerable: true, get: function () { return onlineRuntime_1.defaultPoseDetectionCdnUrl; } });
|
|
54
55
|
Object.defineProperty(exports, "ONLINE_TFJS_VERSION", { enumerable: true, get: function () { return onlineRuntime_1.ONLINE_TFJS_VERSION; } });
|
|
56
|
+
Object.defineProperty(exports, "ONLINE_POSE_DETECTION_VERSION", { enumerable: true, get: function () { return onlineRuntime_1.ONLINE_POSE_DETECTION_VERSION; } });
|
|
55
57
|
var poseModels_1 = require("./models/poseModels");
|
|
56
58
|
Object.defineProperty(exports, "DEFAULT_MOVENET_LIGHTNING_URL", { enumerable: true, get: function () { return poseModels_1.DEFAULT_MOVENET_LIGHTNING_URL; } });
|
|
57
59
|
Object.defineProperty(exports, "originFromModelUrl", { enumerable: true, get: function () { return poseModels_1.originFromModelUrl; } });
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* MoveNet SinglePose Lightning hosted at app.posetracker.com (same URL as
|
|
6
6
|
* `usePoseDetection` / tracking WebView `modelUrl`).
|
|
7
7
|
*
|
|
8
|
-
* BlazePose
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* BlazePose loads via CDN `@tensorflow-models/pose-detection` inside the
|
|
9
|
+
* WebView (no graph `modelUrl`). Pass a TF.js `modelUrl` for any custom
|
|
10
|
+
* graph model.
|
|
11
11
|
*/
|
|
12
12
|
/** Production MoveNet SinglePose Lightning topology (Front default). */
|
|
13
13
|
export declare const DEFAULT_MOVENET_LIGHTNING_URL = "https://app.posetracker.com/scripts/tmp_model_to_remove.json";
|
|
@@ -22,16 +22,18 @@ export interface ResolvePoseModelOptions {
|
|
|
22
22
|
/** Explicit TF.js graph-model topology URL (weights resolve as siblings). */
|
|
23
23
|
modelUrl?: string;
|
|
24
24
|
}
|
|
25
|
+
export type PoseModelKind = 'movenet-graph' | 'blazepose' | 'custom-graph' | 'unsupported';
|
|
25
26
|
export interface ResolvedPoseModel {
|
|
26
27
|
/** Stable id reported in diagnostics / track params. */
|
|
27
28
|
modelId: string;
|
|
28
|
-
|
|
29
|
+
kind: PoseModelKind;
|
|
30
|
+
/** TF.js `loadGraphModel` URL when kind is movenet-graph or custom-graph. */
|
|
29
31
|
modelUrl: string | null;
|
|
30
|
-
/** Human-readable reason when
|
|
32
|
+
/** Human-readable reason when kind is unsupported. */
|
|
31
33
|
unsupportedReason?: string;
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
|
-
* Resolve which
|
|
36
|
+
* Resolve which online pose model the light WebView should run.
|
|
35
37
|
* Default: MoveNet SinglePose Lightning (product URL).
|
|
36
38
|
*/
|
|
37
39
|
export declare function resolvePoseModel(options?: ResolvePoseModelOptions): ResolvedPoseModel;
|
package/lib/models/poseModels.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* MoveNet SinglePose Lightning hosted at app.posetracker.com (same URL as
|
|
7
7
|
* `usePoseDetection` / tracking WebView `modelUrl`).
|
|
8
8
|
*
|
|
9
|
-
* BlazePose
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* BlazePose loads via CDN `@tensorflow-models/pose-detection` inside the
|
|
10
|
+
* WebView (no graph `modelUrl`). Pass a TF.js `modelUrl` for any custom
|
|
11
|
+
* graph model.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.DEFAULT_MOVENET_LIGHTNING_URL = void 0;
|
|
@@ -17,7 +17,7 @@ exports.originFromModelUrl = originFromModelUrl;
|
|
|
17
17
|
/** Production MoveNet SinglePose Lightning topology (Front default). */
|
|
18
18
|
exports.DEFAULT_MOVENET_LIGHTNING_URL = 'https://app.posetracker.com/scripts/tmp_model_to_remove.json';
|
|
19
19
|
/**
|
|
20
|
-
* Resolve which
|
|
20
|
+
* Resolve which online pose model the light WebView should run.
|
|
21
21
|
* Default: MoveNet SinglePose Lightning (product URL).
|
|
22
22
|
*/
|
|
23
23
|
function resolvePoseModel(options = {}) {
|
|
@@ -28,27 +28,28 @@ function resolvePoseModel(options = {}) {
|
|
|
28
28
|
const alias = typeof options.model === 'string' && options.model.trim().length > 0
|
|
29
29
|
? options.model.trim()
|
|
30
30
|
: 'custom';
|
|
31
|
-
return { modelId: alias, modelUrl: explicit };
|
|
31
|
+
return { modelId: alias, kind: 'custom-graph', modelUrl: explicit };
|
|
32
32
|
}
|
|
33
33
|
const key = (options.model ?? 'movenet').trim().toLowerCase();
|
|
34
34
|
if (key === 'movenet' || key === 'movenet-singlepose-lightning' || key === 'lightning') {
|
|
35
35
|
return {
|
|
36
36
|
modelId: 'movenet-singlepose-lightning',
|
|
37
|
+
kind: 'movenet-graph',
|
|
37
38
|
modelUrl: exports.DEFAULT_MOVENET_LIGHTNING_URL,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
if (key === 'blazepose') {
|
|
41
42
|
return {
|
|
42
43
|
modelId: 'blazepose',
|
|
44
|
+
kind: 'blazepose',
|
|
43
45
|
modelUrl: null,
|
|
44
|
-
unsupportedReason: 'BlazePose is not wired in the light RN WebView yet (needs MediaPipe). ' +
|
|
45
|
-
'Use model="movenet" or pass a TF.js graph modelUrl.',
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
return {
|
|
49
49
|
modelId: key,
|
|
50
|
+
kind: 'unsupported',
|
|
50
51
|
modelUrl: null,
|
|
51
|
-
unsupportedReason: `Unknown model "${options.model}". Use "movenet" (default) or pass modelUrl.`,
|
|
52
|
+
unsupportedReason: `Unknown model "${options.model}". Use "movenet" (default), "blazepose", or pass modelUrl.`,
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
/** Origin used as WebView `baseUrl` so model fetches are same-origin when possible. */
|
package/lib/sdkVersion.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** AUTO-GENERATED by scripts/sync-sdk-version.mjs — do not edit. */
|
|
2
|
-
export declare const SDK_VERSION = "0.
|
|
2
|
+
export declare const SDK_VERSION = "0.2.0";
|
package/lib/sdkVersion.js
CHANGED
package/lib/types/events.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export type PoseTrackerStatus = 'idle' | 'configuring' | 'downloading' | 'warmin
|
|
|
23
23
|
* restarting the camera pipeline, via `configure()` or a preload retry.
|
|
24
24
|
*/
|
|
25
25
|
export type PoseTrackerMode = 'keypoints-only' | 'full-engine';
|
|
26
|
-
export type InitializationStep = 'configuring' | 'downloading' | 'warming' | 'accessing_webcam' | 'loading_pose_model' | 'ready';
|
|
26
|
+
export type InitializationStep = 'configuring' | 'downloading' | 'warming' | 'accessing_webcam' | 'loading_media' | 'loading_pose_model' | 'ready';
|
|
27
27
|
export interface InitializationEvent {
|
|
28
28
|
type: 'initialization';
|
|
29
29
|
/**
|
package/lib/types/features.d.ts
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* - `keypoints` DURING an exercise session requires a paid plan too
|
|
12
12
|
* (free may stream keypoints only in pose-only mode, i.e. no exercise).
|
|
13
13
|
* - `blazepose` / `poseEngine` / `mediapipeModel` / `poseBackend` /
|
|
14
|
-
* `runInWorker` are NOT
|
|
15
|
-
*
|
|
14
|
+
* `runInWorker` are NOT feature flags here — select BlazePose with
|
|
15
|
+
* `options.model = 'blazepose'` instead of `{ features: { blazepose: true } }`.
|
|
16
16
|
*/
|
|
17
17
|
import type { MinGrade } from './events';
|
|
18
18
|
export interface PoseTrackerFeatures {
|
package/lib/types/features.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* - `keypoints` DURING an exercise session requires a paid plan too
|
|
13
13
|
* (free may stream keypoints only in pose-only mode, i.e. no exercise).
|
|
14
14
|
* - `blazepose` / `poseEngine` / `mediapipeModel` / `poseBackend` /
|
|
15
|
-
* `runInWorker` are NOT
|
|
16
|
-
*
|
|
15
|
+
* `runInWorker` are NOT feature flags here — select BlazePose with
|
|
16
|
+
* `options.model = 'blazepose'` instead of `{ features: { blazepose: true } }`.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.COMBINED_REFERENCE_EXERCISE_MESSAGE = exports.INVALID_TOKEN_MESSAGE = exports.FREE_PLAN_FEATURES_MESSAGE = exports.DEFAULT_FEATURES = void 0;
|
|
@@ -38,12 +38,12 @@ const KNOWN_FEATURE_KEYS = new Set([
|
|
|
38
38
|
'minGrade',
|
|
39
39
|
]);
|
|
40
40
|
/**
|
|
41
|
-
* WebView params that
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* WebView params that are NOT feature flags in this SDK.
|
|
42
|
+
* Note: `options.model = 'blazepose'` is supported on light; passing
|
|
43
|
+
* `{ blazepose: true }` inside `features` is rejected (WebView query parity).
|
|
44
44
|
*/
|
|
45
45
|
const UNSUPPORTED_FEATURE_HINTS = {
|
|
46
|
-
blazepose: 'BlazePose',
|
|
46
|
+
blazepose: 'BlazePose as a features flag (use options.model = "blazepose" instead)',
|
|
47
47
|
poseEngine: 'pose engine selection (MediaPipe/PoseLandmarker)',
|
|
48
48
|
mediapipeModel: 'MediaPipe model selection',
|
|
49
49
|
poseBackend: 'pose backend selection',
|
|
@@ -62,9 +62,8 @@ exports.COMBINED_REFERENCE_EXERCISE_MESSAGE = 'You cannot combine reference & ex
|
|
|
62
62
|
/** SDK-specific: the requested WebView feature does not exist in this SDK. */
|
|
63
63
|
function featureNotSupportedMessage(key) {
|
|
64
64
|
const hint = UNSUPPORTED_FEATURE_HINTS[key] ?? `'${key}'`;
|
|
65
|
-
return (`The '${key}' option (${hint}) is not available
|
|
66
|
-
'
|
|
67
|
-
'Remove the option, or use the PoseTracker WebView product if you need it. ' +
|
|
65
|
+
return (`The '${key}' option (${hint}) is not available as a features flag. ` +
|
|
66
|
+
'Remove the option, or use options.model / modelUrl for model selection. ' +
|
|
68
67
|
'(visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)');
|
|
69
68
|
}
|
|
70
69
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pose-tracker/react-native-pose-estimation-light",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Human pose estimation SDK for React Native (light / online) — MoveNet Lightning via CDN + model URL each session, no bundled weights. iOS & Android (Expo Go). Optional API-key exercise engine.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -86,7 +86,7 @@ export type WebViewPoseMessage =
|
|
|
86
86
|
type: 'initialization';
|
|
87
87
|
message: string;
|
|
88
88
|
ready?: boolean;
|
|
89
|
-
step?: 'accessing_webcam' | 'loading_pose_model' | 'ready';
|
|
89
|
+
step?: 'accessing_webcam' | 'loading_media' | 'loading_pose_model' | 'ready';
|
|
90
90
|
}
|
|
91
91
|
| { type: 'warning'; message: string; code?: string }
|
|
92
92
|
| { type: 'diag'; message: string }
|
|
@@ -5,22 +5,29 @@
|
|
|
5
5
|
* TF.js + WASM backends load from a CDN; MoveNet (or a custom graph model)
|
|
6
6
|
* loads from a URL on every WebView boot — same idea as the historical
|
|
7
7
|
* PoseTracker Front tracking page.
|
|
8
|
+
*
|
|
9
|
+
* BlazePose: appends `@tensorflow-models/pose-detection` CDN after TF.js
|
|
10
|
+
* (no graph `modelUrl`); the page runtime calls `createDetector`.
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
13
|
import { POSE_RUNTIME_JS, POSE_RUNTIME_VERSION } from './poseRuntimeSource';
|
|
11
14
|
import {
|
|
12
15
|
resolvePoseModel,
|
|
13
16
|
type PoseModelAlias,
|
|
17
|
+
type PoseModelKind,
|
|
14
18
|
type ResolvedPoseModel,
|
|
15
19
|
} from '../../models/poseModels';
|
|
16
20
|
|
|
17
21
|
/** TF.js version pinned for CDN scripts (matches offline SDK build). */
|
|
18
22
|
export const ONLINE_TFJS_VERSION = '4.22.0';
|
|
19
23
|
|
|
24
|
+
/** pose-detection UMD pin (BlazePose). */
|
|
25
|
+
export const ONLINE_POSE_DETECTION_VERSION = '2.1.3';
|
|
26
|
+
|
|
20
27
|
const JSDELIVR = 'https://cdn.jsdelivr.net/npm';
|
|
21
28
|
|
|
22
29
|
export interface OnlineRuntimeUrls {
|
|
23
|
-
/** Ordered <script src> list (core → converter → webgl → wasm). */
|
|
30
|
+
/** Ordered <script src> list (core → converter → webgl → wasm [→ pose-detection]). */
|
|
24
31
|
tfjsScriptUrls: string[];
|
|
25
32
|
/** Directory passed to `tf.wasm.setWasmPaths`. */
|
|
26
33
|
tfjsWasmPath: string;
|
|
@@ -30,7 +37,9 @@ export interface OnlineRuntimeParts {
|
|
|
30
37
|
version: string;
|
|
31
38
|
delivery: 'online';
|
|
32
39
|
modelId: string;
|
|
33
|
-
|
|
40
|
+
/** Graph kinds only; null for BlazePose. */
|
|
41
|
+
modelUrl: string | null;
|
|
42
|
+
modelKind: Exclude<PoseModelKind, 'unsupported'>;
|
|
34
43
|
tfjsScriptUrls: string[];
|
|
35
44
|
tfjsWasmPath: string;
|
|
36
45
|
/** Thin page runtime shipped in the package (~50 KB). */
|
|
@@ -47,6 +56,8 @@ export interface GetOnlineRuntimeOptions {
|
|
|
47
56
|
tfjsCdnBase?: string;
|
|
48
57
|
/** Override TF.js version segment in CDN URLs. */
|
|
49
58
|
tfjsVersion?: string;
|
|
59
|
+
/** Override pose-detection version (BlazePose CDN). */
|
|
60
|
+
poseDetectionVersion?: string;
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
export function defaultOnlineRuntimeUrls(
|
|
@@ -65,25 +76,45 @@ export function defaultOnlineRuntimeUrls(
|
|
|
65
76
|
};
|
|
66
77
|
}
|
|
67
78
|
|
|
79
|
+
export function defaultPoseDetectionCdnUrl(
|
|
80
|
+
cdnBase: string = JSDELIVR,
|
|
81
|
+
version: string = ONLINE_POSE_DETECTION_VERSION,
|
|
82
|
+
): string {
|
|
83
|
+
const base = cdnBase.replace(/\/$/, '');
|
|
84
|
+
return `${base}/@tensorflow-models/pose-detection@${version}/dist/pose-detection.min.js`;
|
|
85
|
+
}
|
|
86
|
+
|
|
68
87
|
/**
|
|
69
88
|
* Build the online runtime descriptor used by {@link buildPoseHtml}.
|
|
70
|
-
* Throws when the selected model cannot be loaded
|
|
89
|
+
* Throws when the selected model cannot be loaded.
|
|
71
90
|
*/
|
|
72
91
|
export function getOnlineRuntimeParts(options: GetOnlineRuntimeOptions = {}): OnlineRuntimeParts {
|
|
73
92
|
const resolved: ResolvedPoseModel = resolvePoseModel({
|
|
74
93
|
model: options.model,
|
|
75
94
|
modelUrl: options.modelUrl,
|
|
76
95
|
});
|
|
77
|
-
if (
|
|
96
|
+
if (resolved.kind === 'unsupported') {
|
|
78
97
|
throw new Error(resolved.unsupportedReason ?? `Model "${resolved.modelId}" is unavailable.`);
|
|
79
98
|
}
|
|
99
|
+
if (resolved.kind !== 'blazepose' && !resolved.modelUrl) {
|
|
100
|
+
throw new Error(resolved.unsupportedReason ?? `Model "${resolved.modelId}" is unavailable.`);
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
const urls = defaultOnlineRuntimeUrls(options.tfjsCdnBase, options.tfjsVersion);
|
|
104
|
+
const scriptUrls = [...urls.tfjsScriptUrls];
|
|
105
|
+
if (resolved.kind === 'blazepose') {
|
|
106
|
+
scriptUrls.push(
|
|
107
|
+
defaultPoseDetectionCdnUrl(options.tfjsCdnBase, options.poseDetectionVersion),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
81
111
|
return {
|
|
82
112
|
version: POSE_RUNTIME_VERSION,
|
|
83
113
|
delivery: 'online',
|
|
84
114
|
modelId: resolved.modelId,
|
|
85
115
|
modelUrl: resolved.modelUrl,
|
|
86
|
-
|
|
116
|
+
modelKind: resolved.kind,
|
|
117
|
+
tfjsScriptUrls: scriptUrls,
|
|
87
118
|
tfjsWasmPath: urls.tfjsWasmPath,
|
|
88
119
|
runtimeJs: POSE_RUNTIME_JS,
|
|
89
120
|
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* WebView HTML assembler (light / online).
|
|
3
3
|
*
|
|
4
4
|
* Loads TF.js from CDN script tags and MoveNet (or a custom graph model)
|
|
5
|
-
* from {@link OnlineRuntimeParts.modelUrl}.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* from {@link OnlineRuntimeParts.modelUrl}. For BlazePose, also loads
|
|
6
|
+
* `@tensorflow-models/pose-detection` (no graph URL). The thin page runtime
|
|
7
|
+
* is still inlined from the package. Same camera / adaptive quality /
|
|
8
|
+
* postMessage events as the offline SDK.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import { Platform } from 'react-native';
|
|
@@ -21,7 +22,7 @@ import { POSETRACKER_LOGO_DATA_URL } from './brandAssets';
|
|
|
21
22
|
import type { OnlineRuntimeParts } from './onlineRuntime';
|
|
22
23
|
|
|
23
24
|
/** Bumped on every assembler-path change — appears in WebView diag logs. */
|
|
24
|
-
export const POSE_HTML_BUILD = '
|
|
25
|
+
export const POSE_HTML_BUILD = '20260812-onlineLight-mediaSources';
|
|
25
26
|
|
|
26
27
|
/** Default boot overlay copy (WebView `loading_message` parity). */
|
|
27
28
|
export const DEFAULT_LOADING_TEXT = 'AI Loading';
|
|
@@ -76,16 +77,23 @@ export interface PoseHtmlOptions {
|
|
|
76
77
|
showWatermark?: boolean;
|
|
77
78
|
/** Show the technical `#hud` overlay (FPS / backend). Default false. */
|
|
78
79
|
debugHud?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Input source mode. Default `camera`.
|
|
82
|
+
* For `video` / `image`, pass a URI the WebView can load (`sourceUrl`).
|
|
83
|
+
*/
|
|
84
|
+
sourceType?: 'camera' | 'video' | 'image';
|
|
85
|
+
/** file://, content://, https://, or data: URL for video/image modes. */
|
|
86
|
+
sourceUrl?: string;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
const PAGE_CSS = `
|
|
82
90
|
html, body { margin:0; padding:0; width:100%; height:100%; background:#000; overflow:hidden; }
|
|
83
91
|
#wrap { position:relative; width:100%; height:100%; background:#000; }
|
|
84
|
-
video, canvas {
|
|
92
|
+
video, img#still, canvas {
|
|
85
93
|
position:absolute; inset:0; width:100%; height:100%;
|
|
86
94
|
object-fit:cover; opacity:0; transition:opacity 180ms ease-out;
|
|
87
95
|
}
|
|
88
|
-
video { z-index:1; background:#000; }
|
|
96
|
+
video, img#still { z-index:1; background:#000; }
|
|
89
97
|
canvas { z-index:2; pointer-events:none; }
|
|
90
98
|
#boot {
|
|
91
99
|
position:absolute; inset:0; z-index:4;
|
|
@@ -204,11 +212,17 @@ export function buildPoseHtml(parts: OnlineRuntimeParts, options?: PoseHtmlOptio
|
|
|
204
212
|
loadingText,
|
|
205
213
|
showWatermark: watermarkOn,
|
|
206
214
|
debugHud,
|
|
215
|
+
sourceType:
|
|
216
|
+
options?.sourceType === 'video' || options?.sourceType === 'image'
|
|
217
|
+
? options.sourceType
|
|
218
|
+
: 'camera',
|
|
219
|
+
sourceUrl: typeof options?.sourceUrl === 'string' ? options.sourceUrl : null,
|
|
207
220
|
captureConstraintMode: CAPTURE_CONSTRAINT_MODE,
|
|
208
221
|
capturePriority,
|
|
209
222
|
platform: Platform.OS,
|
|
210
223
|
modelUrl: parts.modelUrl,
|
|
211
224
|
modelId: parts.modelId,
|
|
225
|
+
modelKind: parts.modelKind,
|
|
212
226
|
tfjsWasmPath: parts.tfjsWasmPath,
|
|
213
227
|
inferFrameSkip:
|
|
214
228
|
isAndroid && ANDROID_INFER_FRAME_SKIP > 0 ? ANDROID_INFER_FRAME_SKIP : 0,
|
|
@@ -228,6 +242,7 @@ export function buildPoseHtml(parts: OnlineRuntimeParts, options?: PoseHtmlOptio
|
|
|
228
242
|
'<style>', PAGE_CSS, '</style></head><body>',
|
|
229
243
|
'<div id="wrap">',
|
|
230
244
|
'<video id="video" playsinline muted autoplay></video>',
|
|
245
|
+
'<img id="still" alt="" draggable="false" />',
|
|
231
246
|
'<canvas id="overlay"></canvas>',
|
|
232
247
|
'<div id="wm">',
|
|
233
248
|
'<div class="wm-powered">powered by</div>',
|
|
@@ -247,6 +262,8 @@ export function buildPoseHtml(parts: OnlineRuntimeParts, options?: PoseHtmlOptio
|
|
|
247
262
|
'<script>window.__PT_BUILD=', JSON.stringify(`${POSE_HTML_BUILD}/${parts.version}`),
|
|
248
263
|
';window.__PT_CONFIG=', config,
|
|
249
264
|
';window.__PT_MODEL_URL=', JSON.stringify(parts.modelUrl),
|
|
265
|
+
';window.__PT_MODEL_ID=', JSON.stringify(parts.modelId),
|
|
266
|
+
';window.__PT_MODEL_KIND=', JSON.stringify(parts.modelKind),
|
|
250
267
|
';window.__PT_WASM_PATH=', JSON.stringify(parts.tfjsWasmPath),
|
|
251
268
|
';window.__PT_MODEL_JSON=null',
|
|
252
269
|
';window.__PT_WEIGHTS_B64=null',
|
|
@@ -120,6 +120,18 @@ export interface WebViewPoseViewProps {
|
|
|
120
120
|
* Fetched via `GET /api/sdk/skeleton`. Ignored if `skeletonDef` is set.
|
|
121
121
|
*/
|
|
122
122
|
skeletonUuid?: string | null;
|
|
123
|
+
/**
|
|
124
|
+
* Input source. Default `camera` (getUserMedia).
|
|
125
|
+
* For `video` / `image`, the host app picks a file and passes `sourceUri`
|
|
126
|
+
* (file://, content://, https, data URL) or `sourceBase64`.
|
|
127
|
+
*/
|
|
128
|
+
source?: 'camera' | 'video' | 'image';
|
|
129
|
+
/** URI injected into the WebView for video/image modes. */
|
|
130
|
+
sourceUri?: string;
|
|
131
|
+
/** Optional base64 payload (without data: prefix); paired with sourceMime. */
|
|
132
|
+
sourceBase64?: string;
|
|
133
|
+
/** MIME for sourceBase64 (default image/jpeg or video/mp4). */
|
|
134
|
+
sourceMime?: string;
|
|
123
135
|
onPose?: (pose: Pose, stats: { inferenceTimeMs: number; timestampMs: number }) => void;
|
|
124
136
|
children?: React.ReactNode;
|
|
125
137
|
}
|
|
@@ -266,6 +278,8 @@ export function WebViewPoseView(props: WebViewPoseViewProps): React.ReactElement
|
|
|
266
278
|
// injected via __PT_SET_SKELETON (avoids remounting on uuid fetch).
|
|
267
279
|
skeletonDef: props.skeletonDef ?? null,
|
|
268
280
|
capturePriority: client.getQualityState().capturePriority,
|
|
281
|
+
sourceType: props.source ?? 'camera',
|
|
282
|
+
sourceUrl: props.sourceUri,
|
|
269
283
|
});
|
|
270
284
|
}, [
|
|
271
285
|
facingMode,
|
|
@@ -276,6 +290,8 @@ export function WebViewPoseView(props: WebViewPoseViewProps): React.ReactElement
|
|
|
276
290
|
coldStart,
|
|
277
291
|
loadingText,
|
|
278
292
|
props.skeletonDef,
|
|
293
|
+
props.source,
|
|
294
|
+
props.sourceUri,
|
|
279
295
|
]);
|
|
280
296
|
|
|
281
297
|
// Apply custom skeleton (uuid fetch or prop) without remounting the page.
|
|
@@ -307,6 +323,33 @@ export function WebViewPoseView(props: WebViewPoseViewProps): React.ReactElement
|
|
|
307
323
|
);
|
|
308
324
|
}, [isWebViewBackend, html, loadingText]);
|
|
309
325
|
|
|
326
|
+
// Live source switches after first HTML boot (CFG already has initial source).
|
|
327
|
+
const lastSourceHtmlRef = useRef<string | null>(null);
|
|
328
|
+
useEffect(() => {
|
|
329
|
+
if (!isWebViewBackend || !html) return;
|
|
330
|
+
if (lastSourceHtmlRef.current !== html) {
|
|
331
|
+
lastSourceHtmlRef.current = html;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const type = props.source ?? 'camera';
|
|
335
|
+
const payload = JSON.stringify({
|
|
336
|
+
type,
|
|
337
|
+
url: props.sourceUri ?? null,
|
|
338
|
+
base64: props.sourceBase64 ?? null,
|
|
339
|
+
mime: props.sourceMime ?? null,
|
|
340
|
+
});
|
|
341
|
+
webRef.current?.injectJavaScript?.(
|
|
342
|
+
`window.__PT_SET_SOURCE && window.__PT_SET_SOURCE(${payload}); true;`,
|
|
343
|
+
);
|
|
344
|
+
}, [
|
|
345
|
+
isWebViewBackend,
|
|
346
|
+
html,
|
|
347
|
+
props.source,
|
|
348
|
+
props.sourceUri,
|
|
349
|
+
props.sourceBase64,
|
|
350
|
+
props.sourceMime,
|
|
351
|
+
]);
|
|
352
|
+
|
|
310
353
|
useEffect(() => {
|
|
311
354
|
if (!(backend instanceof WebViewPoseBackend)) {
|
|
312
355
|
return;
|
package/src/client.ts
CHANGED
|
@@ -122,8 +122,8 @@ export interface PoseTrackerClientOptions extends ConfigureOptions {
|
|
|
122
122
|
usageTracker?: UsageTracker;
|
|
123
123
|
/**
|
|
124
124
|
* Docs API `model` query parity (`movenet` default, `blazepose`, …).
|
|
125
|
-
* BlazePose
|
|
126
|
-
*
|
|
125
|
+
* BlazePose loads via CDN pose-detection in the WebView (heavier than
|
|
126
|
+
* MoveNet). Ignored when {@link modelUrl} is set.
|
|
127
127
|
*/
|
|
128
128
|
model?: PoseModelAlias;
|
|
129
129
|
/**
|
|
@@ -579,7 +579,7 @@ export class PoseTrackerClient {
|
|
|
579
579
|
});
|
|
580
580
|
this.options.onDiagnostic?.(
|
|
581
581
|
`[posetracker-light] pose-runtime online version=${parts.version} ` +
|
|
582
|
-
`modelId=${parts.modelId} modelUrl=${parts.modelUrl}`,
|
|
582
|
+
`modelId=${parts.modelId} modelKind=${parts.modelKind} modelUrl=${parts.modelUrl}`,
|
|
583
583
|
);
|
|
584
584
|
return parts;
|
|
585
585
|
}
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,9 @@ export {
|
|
|
35
35
|
getOnlineRuntimeParts,
|
|
36
36
|
getOnlineRuntimeVersion,
|
|
37
37
|
defaultOnlineRuntimeUrls,
|
|
38
|
+
defaultPoseDetectionCdnUrl,
|
|
38
39
|
ONLINE_TFJS_VERSION,
|
|
40
|
+
ONLINE_POSE_DETECTION_VERSION,
|
|
39
41
|
} from './backends/webview/onlineRuntime';
|
|
40
42
|
export type {
|
|
41
43
|
GetOnlineRuntimeOptions,
|
|
@@ -49,6 +51,7 @@ export {
|
|
|
49
51
|
} from './models/poseModels';
|
|
50
52
|
export type {
|
|
51
53
|
PoseModelAlias,
|
|
54
|
+
PoseModelKind,
|
|
52
55
|
ResolvePoseModelOptions,
|
|
53
56
|
ResolvedPoseModel,
|
|
54
57
|
} from './models/poseModels';
|