@talmolab/sleap-io.js 0.1.0 → 0.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 +15 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +77 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -69,6 +69,21 @@ The demo in `demo/` loads the built package from `dist/`. Build first, then serv
|
|
|
69
69
|
npm run build
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Release & Publishing
|
|
73
|
+
|
|
74
|
+
This package uses npm Trusted Publishing. The first publish must be done manually to unlock the npm package settings:
|
|
75
|
+
|
|
76
|
+
1. First-time publish (one-time):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm login
|
|
80
|
+
npm publish --access public
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
2. Enable Trusted Publisher at `https://www.npmjs.com/package/@talmolab/sleap-io.js/access`.
|
|
84
|
+
|
|
85
|
+
After that, GitHub Releases trigger the publish workflow automatically.
|
|
86
|
+
|
|
72
87
|
## Links
|
|
73
88
|
|
|
74
89
|
- Python sleap-io: https://github.com/talmolab/sleap-io
|
package/dist/index.d.ts
CHANGED
|
@@ -433,4 +433,7 @@ declare function labelsFromNumpy(data: number[][][][], options: {
|
|
|
433
433
|
returnConfidence?: boolean;
|
|
434
434
|
}): Labels;
|
|
435
435
|
|
|
436
|
-
|
|
436
|
+
declare function decodeYamlSkeleton(yamlData: string): Skeleton | Skeleton[];
|
|
437
|
+
declare function encodeYamlSkeleton(skeletons: Skeleton | Skeleton[]): string;
|
|
438
|
+
|
|
439
|
+
export { Camera, CameraGroup, Edge, FrameGroup, Instance, InstanceGroup, LabeledFrame, Labels, type LabelsDict, LabelsSet, Mp4BoxVideoBackend, Node, type NodeOrIndex, type Point, type PointsArray, PredictedInstance, type PredictedPoint, type PredictedPointsArray, RecordingSession, Skeleton, SuggestionFrame, Symmetry, Track, Video, type VideoBackend, type VideoFrame, decodeYamlSkeleton, encodeYamlSkeleton, fromDict, fromNumpy, labelsFromNumpy, loadSlp, loadVideo, makeCameraFromDict, pointsEmpty, pointsFromArray, pointsFromDict, predictedPointsEmpty, predictedPointsFromArray, predictedPointsFromDict, rodriguesTransformation, saveSlp, toDict, toNumpy };
|
package/dist/index.js
CHANGED
|
@@ -2578,6 +2578,81 @@ async function loadVideo(filename, options) {
|
|
|
2578
2578
|
const backend = await createVideoBackend(filename, { dataset: options?.dataset });
|
|
2579
2579
|
return new Video({ filename, backend, openBackend: options?.openBackend ?? true });
|
|
2580
2580
|
}
|
|
2581
|
+
|
|
2582
|
+
// src/codecs/skeleton-yaml.ts
|
|
2583
|
+
import YAML from "yaml";
|
|
2584
|
+
function getNodeName(entry) {
|
|
2585
|
+
if (typeof entry === "string") return entry;
|
|
2586
|
+
if (entry && typeof entry.name === "string") return entry.name;
|
|
2587
|
+
throw new Error("Invalid node entry in skeleton YAML.");
|
|
2588
|
+
}
|
|
2589
|
+
function resolveName(value) {
|
|
2590
|
+
if (typeof value === "string") return value;
|
|
2591
|
+
if (value && typeof value.name === "string") return value.name;
|
|
2592
|
+
throw new Error("Invalid name reference in skeleton YAML.");
|
|
2593
|
+
}
|
|
2594
|
+
function decodeSkeleton(data, fallbackName) {
|
|
2595
|
+
if (!data?.nodes) throw new Error("Skeleton YAML missing nodes.");
|
|
2596
|
+
const nodes = data.nodes.map((entry) => new Node(getNodeName(entry)));
|
|
2597
|
+
const edges = (data.edges ?? []).map((edge) => {
|
|
2598
|
+
if (Array.isArray(edge)) {
|
|
2599
|
+
const [source2, destination] = edge;
|
|
2600
|
+
return new Edge(nodes[Number(source2)], nodes[Number(destination)]);
|
|
2601
|
+
}
|
|
2602
|
+
const sourceName = resolveName(edge.source);
|
|
2603
|
+
const destName = resolveName(edge.destination);
|
|
2604
|
+
const source = nodes.find((node) => node.name === sourceName);
|
|
2605
|
+
const dest = nodes.find((node) => node.name === destName);
|
|
2606
|
+
if (!source || !dest) throw new Error("Edge references unknown node.");
|
|
2607
|
+
return new Edge(source, dest);
|
|
2608
|
+
});
|
|
2609
|
+
const symmetries = (data.symmetries ?? []).map((symmetry) => {
|
|
2610
|
+
if (!Array.isArray(symmetry) || symmetry.length !== 2) {
|
|
2611
|
+
throw new Error("Symmetry must contain exactly 2 nodes.");
|
|
2612
|
+
}
|
|
2613
|
+
const [left, right] = symmetry;
|
|
2614
|
+
const leftName = resolveName(left);
|
|
2615
|
+
const rightName = resolveName(right);
|
|
2616
|
+
const leftNode = nodes.find((node) => node.name === leftName);
|
|
2617
|
+
const rightNode = nodes.find((node) => node.name === rightName);
|
|
2618
|
+
if (!leftNode || !rightNode) throw new Error("Symmetry references unknown node.");
|
|
2619
|
+
return new Symmetry([leftNode, rightNode]);
|
|
2620
|
+
});
|
|
2621
|
+
return new Skeleton({
|
|
2622
|
+
name: data.name ?? fallbackName,
|
|
2623
|
+
nodes,
|
|
2624
|
+
edges,
|
|
2625
|
+
symmetries
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
function decodeYamlSkeleton(yamlData) {
|
|
2629
|
+
const parsed = YAML.parse(yamlData);
|
|
2630
|
+
if (!parsed) throw new Error("Empty skeleton YAML.");
|
|
2631
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "nodes")) {
|
|
2632
|
+
return decodeSkeleton(parsed);
|
|
2633
|
+
}
|
|
2634
|
+
return Object.entries(parsed).map(
|
|
2635
|
+
([name, skeletonData]) => decodeSkeleton(skeletonData, name)
|
|
2636
|
+
);
|
|
2637
|
+
}
|
|
2638
|
+
function encodeYamlSkeleton(skeletons) {
|
|
2639
|
+
const list = Array.isArray(skeletons) ? skeletons : [skeletons];
|
|
2640
|
+
const payload = {};
|
|
2641
|
+
list.forEach((skeleton, index) => {
|
|
2642
|
+
const name = skeleton.name ?? `Skeleton-${index}`;
|
|
2643
|
+
const nodes = skeleton.nodes.map((node) => ({ name: node.name }));
|
|
2644
|
+
const edges = skeleton.edges.map((edge) => ({
|
|
2645
|
+
source: { name: edge.source.name },
|
|
2646
|
+
destination: { name: edge.destination.name }
|
|
2647
|
+
}));
|
|
2648
|
+
const symmetries = skeleton.symmetries.map((symmetry) => {
|
|
2649
|
+
const pair = Array.from(symmetry.nodes);
|
|
2650
|
+
return [{ name: pair[0].name }, { name: pair[1].name }];
|
|
2651
|
+
});
|
|
2652
|
+
payload[name] = { nodes, edges, symmetries };
|
|
2653
|
+
});
|
|
2654
|
+
return YAML.stringify(payload);
|
|
2655
|
+
}
|
|
2581
2656
|
export {
|
|
2582
2657
|
Camera,
|
|
2583
2658
|
CameraGroup,
|
|
@@ -2597,6 +2672,8 @@ export {
|
|
|
2597
2672
|
Symmetry,
|
|
2598
2673
|
Track,
|
|
2599
2674
|
Video,
|
|
2675
|
+
decodeYamlSkeleton,
|
|
2676
|
+
encodeYamlSkeleton,
|
|
2600
2677
|
fromDict,
|
|
2601
2678
|
fromNumpy,
|
|
2602
2679
|
labelsFromNumpy,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talmolab/sleap-io.js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"main": "./dist/index.js",
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
14
|
"files": ["dist"],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/talmolab/sleap-io.js.git"
|
|
18
|
+
},
|
|
15
19
|
"scripts": {
|
|
16
20
|
"build": "tsup src/index.ts --format esm --dts",
|
|
17
21
|
"test": "vitest",
|