@talmolab/sleap-io.js 0.1.3 → 0.1.4
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/dist/index.d.ts +109 -1
- package/dist/index.js +380 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -241,10 +241,118 @@ declare class Mp4BoxVideoBackend implements VideoBackend {
|
|
|
241
241
|
private addToCache;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Streaming HDF5 file access via Web Worker.
|
|
246
|
+
*
|
|
247
|
+
* This module provides a high-level API for accessing remote HDF5 files
|
|
248
|
+
* using HTTP range requests for efficient streaming. The actual HDF5
|
|
249
|
+
* operations run in a Web Worker where synchronous XHR is allowed.
|
|
250
|
+
*
|
|
251
|
+
* @module
|
|
252
|
+
*/
|
|
253
|
+
/**
|
|
254
|
+
* Options for opening a streaming HDF5 file.
|
|
255
|
+
*/
|
|
256
|
+
interface StreamingH5Options {
|
|
257
|
+
/** URL to h5wasm IIFE bundle. Defaults to CDN. */
|
|
258
|
+
h5wasmUrl?: string;
|
|
259
|
+
/** Filename hint for the HDF5 file. */
|
|
260
|
+
filenameHint?: string;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* A streaming HDF5 file handle that uses a Web Worker for range request access.
|
|
264
|
+
*
|
|
265
|
+
* This class provides an API similar to h5wasm.File but operates via message
|
|
266
|
+
* passing to a worker where createLazyFile enables HTTP range requests.
|
|
267
|
+
*/
|
|
268
|
+
declare class StreamingH5File {
|
|
269
|
+
private worker;
|
|
270
|
+
private messageId;
|
|
271
|
+
private pendingMessages;
|
|
272
|
+
private _keys;
|
|
273
|
+
private _isOpen;
|
|
274
|
+
constructor();
|
|
275
|
+
private handleMessage;
|
|
276
|
+
private handleError;
|
|
277
|
+
private send;
|
|
278
|
+
/**
|
|
279
|
+
* Initialize the h5wasm module in the worker.
|
|
280
|
+
*/
|
|
281
|
+
init(options?: StreamingH5Options): Promise<void>;
|
|
282
|
+
/**
|
|
283
|
+
* Open a remote HDF5 file for streaming access.
|
|
284
|
+
*
|
|
285
|
+
* @param url - URL to the HDF5 file (must support HTTP range requests)
|
|
286
|
+
* @param options - Optional settings
|
|
287
|
+
*/
|
|
288
|
+
open(url: string, options?: StreamingH5Options): Promise<void>;
|
|
289
|
+
/**
|
|
290
|
+
* Whether a file is currently open.
|
|
291
|
+
*/
|
|
292
|
+
get isOpen(): boolean;
|
|
293
|
+
/**
|
|
294
|
+
* Get the root-level keys in the file.
|
|
295
|
+
*/
|
|
296
|
+
keys(): string[];
|
|
297
|
+
/**
|
|
298
|
+
* Get the keys (children) at a given path.
|
|
299
|
+
*/
|
|
300
|
+
getKeys(path: string): Promise<string[]>;
|
|
301
|
+
/**
|
|
302
|
+
* Get an attribute value.
|
|
303
|
+
*/
|
|
304
|
+
getAttr(path: string, name: string): Promise<unknown>;
|
|
305
|
+
/**
|
|
306
|
+
* Get all attributes at a path.
|
|
307
|
+
*/
|
|
308
|
+
getAttrs(path: string): Promise<Record<string, unknown>>;
|
|
309
|
+
/**
|
|
310
|
+
* Get dataset metadata (shape, dtype) without reading values.
|
|
311
|
+
*/
|
|
312
|
+
getDatasetMeta(path: string): Promise<{
|
|
313
|
+
shape: number[];
|
|
314
|
+
dtype: string;
|
|
315
|
+
}>;
|
|
316
|
+
/**
|
|
317
|
+
* Read a dataset's value.
|
|
318
|
+
*
|
|
319
|
+
* @param path - Path to the dataset
|
|
320
|
+
* @param slice - Optional slice specification (array of [start, end] pairs)
|
|
321
|
+
*/
|
|
322
|
+
getDatasetValue(path: string, slice?: Array<[number, number] | []>): Promise<{
|
|
323
|
+
value: unknown;
|
|
324
|
+
shape: number[];
|
|
325
|
+
dtype: string;
|
|
326
|
+
}>;
|
|
327
|
+
/**
|
|
328
|
+
* Close the file and terminate the worker.
|
|
329
|
+
*/
|
|
330
|
+
close(): Promise<void>;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Check if streaming via Web Worker is supported in the current environment.
|
|
334
|
+
*/
|
|
335
|
+
declare function isStreamingSupported(): boolean;
|
|
336
|
+
/**
|
|
337
|
+
* Open a remote HDF5 file with streaming support.
|
|
338
|
+
*
|
|
339
|
+
* @param url - URL to the HDF5 file
|
|
340
|
+
* @param options - Optional settings
|
|
341
|
+
* @returns A StreamingH5File instance
|
|
342
|
+
*/
|
|
343
|
+
declare function openStreamingH5(url: string, options?: StreamingH5Options): Promise<StreamingH5File>;
|
|
344
|
+
|
|
244
345
|
type SlpSource = string | ArrayBuffer | Uint8Array | File | FileSystemFileHandle;
|
|
245
346
|
type StreamMode = "auto" | "range" | "download";
|
|
246
347
|
type OpenH5Options = {
|
|
348
|
+
/**
|
|
349
|
+
* Streaming mode for remote files:
|
|
350
|
+
* - "auto": Try range requests, fall back to download
|
|
351
|
+
* - "range": Use HTTP range requests (requires Worker support in browser)
|
|
352
|
+
* - "download": Always download the entire file
|
|
353
|
+
*/
|
|
247
354
|
stream?: StreamMode;
|
|
355
|
+
/** Filename hint for the HDF5 file */
|
|
248
356
|
filenameHint?: string;
|
|
249
357
|
};
|
|
250
358
|
|
|
@@ -550,4 +658,4 @@ declare function checkFfmpeg(): Promise<boolean>;
|
|
|
550
658
|
*/
|
|
551
659
|
declare function renderVideo(source: Labels | LabeledFrame[], outputPath: string, options?: VideoOptions): Promise<void>;
|
|
552
660
|
|
|
553
|
-
export { Camera, CameraGroup, type ColorScheme, type ColorSpec, FrameGroup, Instance, InstanceContext, InstanceGroup, LabeledFrame, Labels, type LabelsDict, LabelsSet, MARKER_FUNCTIONS, type MarkerShape, Mp4BoxVideoBackend, NAMED_COLORS, PALETTES, type PaletteName, PredictedInstance, type RGB, type RGBA, RecordingSession, RenderContext, type RenderOptions, Skeleton, SuggestionFrame, Track, Video, type VideoBackend, type VideoFrame, type VideoOptions, checkFfmpeg, decodeYamlSkeleton, determineColorScheme, drawCircle, drawCross, drawDiamond, drawSquare, drawTriangle, encodeYamlSkeleton, fromDict, fromNumpy, getMarkerFunction, getPalette, labelsFromNumpy, loadSlp, loadVideo, makeCameraFromDict, renderImage, renderVideo, resolveColor, rgbToCSS, rodriguesTransformation, saveImage, saveSlp, toDataURL, toDict, toJPEG, toNumpy, toPNG };
|
|
661
|
+
export { Camera, CameraGroup, type ColorScheme, type ColorSpec, FrameGroup, Instance, InstanceContext, InstanceGroup, LabeledFrame, Labels, type LabelsDict, LabelsSet, MARKER_FUNCTIONS, type MarkerShape, Mp4BoxVideoBackend, NAMED_COLORS, PALETTES, type PaletteName, PredictedInstance, type RGB, type RGBA, RecordingSession, RenderContext, type RenderOptions, Skeleton, StreamingH5File, SuggestionFrame, Track, Video, type VideoBackend, type VideoFrame, type VideoOptions, checkFfmpeg, decodeYamlSkeleton, determineColorScheme, drawCircle, drawCross, drawDiamond, drawSquare, drawTriangle, encodeYamlSkeleton, fromDict, fromNumpy, getMarkerFunction, getPalette, isStreamingSupported, labelsFromNumpy, loadSlp, loadVideo, makeCameraFromDict, openStreamingH5, renderImage, renderVideo, resolveColor, rgbToCSS, rodriguesTransformation, saveImage, saveSlp, toDataURL, toDict, toJPEG, toNumpy, toPNG };
|
package/dist/index.js
CHANGED
|
@@ -1203,6 +1203,383 @@ var Mp4BoxVideoBackend = class {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
};
|
|
1205
1205
|
|
|
1206
|
+
// src/codecs/slp/h5-worker.ts
|
|
1207
|
+
var H5_WORKER_CODE = `
|
|
1208
|
+
// h5wasm streaming worker
|
|
1209
|
+
// Uses createLazyFile for HTTP range request streaming
|
|
1210
|
+
|
|
1211
|
+
let h5wasmModule = null;
|
|
1212
|
+
let FS = null;
|
|
1213
|
+
let currentFile = null;
|
|
1214
|
+
let mountPath = null;
|
|
1215
|
+
|
|
1216
|
+
self.onmessage = async function(e) {
|
|
1217
|
+
const { type, payload, id } = e.data;
|
|
1218
|
+
|
|
1219
|
+
try {
|
|
1220
|
+
switch (type) {
|
|
1221
|
+
case 'init':
|
|
1222
|
+
await initH5Wasm(payload?.h5wasmUrl);
|
|
1223
|
+
respond(id, { success: true });
|
|
1224
|
+
break;
|
|
1225
|
+
|
|
1226
|
+
case 'openUrl':
|
|
1227
|
+
const result = await openRemoteFile(payload.url, payload.filename);
|
|
1228
|
+
respond(id, result);
|
|
1229
|
+
break;
|
|
1230
|
+
|
|
1231
|
+
case 'getKeys':
|
|
1232
|
+
const keys = getKeys(payload.path);
|
|
1233
|
+
respond(id, { success: true, keys });
|
|
1234
|
+
break;
|
|
1235
|
+
|
|
1236
|
+
case 'getAttr':
|
|
1237
|
+
const attr = getAttr(payload.path, payload.name);
|
|
1238
|
+
respond(id, { success: true, value: attr });
|
|
1239
|
+
break;
|
|
1240
|
+
|
|
1241
|
+
case 'getAttrs':
|
|
1242
|
+
const attrs = getAttrs(payload.path);
|
|
1243
|
+
respond(id, { success: true, attrs });
|
|
1244
|
+
break;
|
|
1245
|
+
|
|
1246
|
+
case 'getDatasetMeta':
|
|
1247
|
+
const meta = getDatasetMeta(payload.path);
|
|
1248
|
+
respond(id, { success: true, meta });
|
|
1249
|
+
break;
|
|
1250
|
+
|
|
1251
|
+
case 'getDatasetValue':
|
|
1252
|
+
const data = getDatasetValue(payload.path, payload.slice);
|
|
1253
|
+
respond(id, { success: true, data }, data.transferables);
|
|
1254
|
+
break;
|
|
1255
|
+
|
|
1256
|
+
case 'close':
|
|
1257
|
+
closeFile();
|
|
1258
|
+
respond(id, { success: true });
|
|
1259
|
+
break;
|
|
1260
|
+
|
|
1261
|
+
default:
|
|
1262
|
+
respond(id, { success: false, error: 'Unknown message type: ' + type });
|
|
1263
|
+
}
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
respond(id, { success: false, error: error.message || String(error) });
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
function respond(id, data, transferables) {
|
|
1270
|
+
if (transferables) {
|
|
1271
|
+
self.postMessage({ id, ...data }, transferables);
|
|
1272
|
+
} else {
|
|
1273
|
+
self.postMessage({ id, ...data });
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
async function initH5Wasm(h5wasmUrl) {
|
|
1278
|
+
if (h5wasmModule) return;
|
|
1279
|
+
|
|
1280
|
+
// Default to CDN if no URL provided
|
|
1281
|
+
const url = h5wasmUrl || 'https://cdn.jsdelivr.net/npm/h5wasm@0.8.8/dist/iife/h5wasm.js';
|
|
1282
|
+
|
|
1283
|
+
// Import h5wasm IIFE
|
|
1284
|
+
importScripts(url);
|
|
1285
|
+
|
|
1286
|
+
// Wait for module to be ready
|
|
1287
|
+
const Module = await h5wasm.ready;
|
|
1288
|
+
h5wasmModule = h5wasm;
|
|
1289
|
+
FS = Module.FS;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
async function openRemoteFile(url, filename = 'data.h5') {
|
|
1293
|
+
if (!h5wasmModule) {
|
|
1294
|
+
throw new Error('h5wasm not initialized');
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// Close any existing file
|
|
1298
|
+
closeFile();
|
|
1299
|
+
|
|
1300
|
+
// Create mount point
|
|
1301
|
+
mountPath = '/remote-' + Date.now();
|
|
1302
|
+
FS.mkdir(mountPath);
|
|
1303
|
+
|
|
1304
|
+
// Create lazy file - this enables range request streaming!
|
|
1305
|
+
FS.createLazyFile(mountPath, filename, url, true, false);
|
|
1306
|
+
|
|
1307
|
+
// Open with h5wasm
|
|
1308
|
+
const filePath = mountPath + '/' + filename;
|
|
1309
|
+
currentFile = new h5wasm.File(filePath, 'r');
|
|
1310
|
+
|
|
1311
|
+
return {
|
|
1312
|
+
success: true,
|
|
1313
|
+
path: currentFile.path,
|
|
1314
|
+
filename: currentFile.filename,
|
|
1315
|
+
keys: currentFile.keys()
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function getKeys(path) {
|
|
1320
|
+
if (!currentFile) throw new Error('No file open');
|
|
1321
|
+
const item = path === '/' || !path ? currentFile : currentFile.get(path);
|
|
1322
|
+
if (!item) throw new Error('Path not found: ' + path);
|
|
1323
|
+
return item.keys ? item.keys() : [];
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
function getAttr(path, name) {
|
|
1327
|
+
if (!currentFile) throw new Error('No file open');
|
|
1328
|
+
const item = path === '/' || !path ? currentFile : currentFile.get(path);
|
|
1329
|
+
if (!item) throw new Error('Path not found: ' + path);
|
|
1330
|
+
const attrs = item.attrs;
|
|
1331
|
+
return attrs?.[name] || null;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
function getAttrs(path) {
|
|
1335
|
+
if (!currentFile) throw new Error('No file open');
|
|
1336
|
+
const item = path === '/' || !path ? currentFile : currentFile.get(path);
|
|
1337
|
+
if (!item) throw new Error('Path not found: ' + path);
|
|
1338
|
+
return item.attrs || {};
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
function getDatasetMeta(path) {
|
|
1342
|
+
if (!currentFile) throw new Error('No file open');
|
|
1343
|
+
const dataset = currentFile.get(path);
|
|
1344
|
+
if (!dataset) throw new Error('Dataset not found: ' + path);
|
|
1345
|
+
return {
|
|
1346
|
+
shape: dataset.shape,
|
|
1347
|
+
dtype: dataset.dtype,
|
|
1348
|
+
metadata: dataset.metadata
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function getDatasetValue(path, slice) {
|
|
1353
|
+
if (!currentFile) throw new Error('No file open');
|
|
1354
|
+
const dataset = currentFile.get(path);
|
|
1355
|
+
if (!dataset) throw new Error('Dataset not found: ' + path);
|
|
1356
|
+
|
|
1357
|
+
// Get value or slice
|
|
1358
|
+
let value;
|
|
1359
|
+
if (slice && Array.isArray(slice)) {
|
|
1360
|
+
value = dataset.slice(slice);
|
|
1361
|
+
} else {
|
|
1362
|
+
value = dataset.value;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// Prepare for transfer
|
|
1366
|
+
const transferables = [];
|
|
1367
|
+
let transferValue = value;
|
|
1368
|
+
|
|
1369
|
+
if (ArrayBuffer.isView(value)) {
|
|
1370
|
+
// TypedArray - transfer the underlying buffer
|
|
1371
|
+
transferValue = {
|
|
1372
|
+
type: 'typedarray',
|
|
1373
|
+
dtype: value.constructor.name,
|
|
1374
|
+
buffer: value.buffer,
|
|
1375
|
+
byteOffset: value.byteOffset,
|
|
1376
|
+
length: value.length
|
|
1377
|
+
};
|
|
1378
|
+
transferables.push(value.buffer);
|
|
1379
|
+
} else if (value instanceof ArrayBuffer) {
|
|
1380
|
+
transferValue = { type: 'arraybuffer', buffer: value };
|
|
1381
|
+
transferables.push(value);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
return {
|
|
1385
|
+
value: transferValue,
|
|
1386
|
+
shape: dataset.shape,
|
|
1387
|
+
dtype: dataset.dtype,
|
|
1388
|
+
transferables
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
function closeFile() {
|
|
1393
|
+
if (currentFile) {
|
|
1394
|
+
try { currentFile.close(); } catch (e) {}
|
|
1395
|
+
currentFile = null;
|
|
1396
|
+
}
|
|
1397
|
+
if (mountPath && FS) {
|
|
1398
|
+
try { FS.rmdir(mountPath); } catch (e) {}
|
|
1399
|
+
mountPath = null;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
`;
|
|
1403
|
+
function createH5Worker() {
|
|
1404
|
+
const blob = new Blob([H5_WORKER_CODE], { type: "application/javascript" });
|
|
1405
|
+
const url = URL.createObjectURL(blob);
|
|
1406
|
+
const worker = new Worker(url);
|
|
1407
|
+
worker.addEventListener(
|
|
1408
|
+
"error",
|
|
1409
|
+
() => {
|
|
1410
|
+
URL.revokeObjectURL(url);
|
|
1411
|
+
},
|
|
1412
|
+
{ once: true }
|
|
1413
|
+
);
|
|
1414
|
+
return worker;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/codecs/slp/h5-streaming.ts
|
|
1418
|
+
function reconstructValue(data) {
|
|
1419
|
+
if (data && typeof data === "object" && "type" in data) {
|
|
1420
|
+
const typed = data;
|
|
1421
|
+
if (typed.type === "typedarray" && typed.buffer) {
|
|
1422
|
+
const TypedArrayConstructor = getTypedArrayConstructor(typed.dtype || "Uint8Array");
|
|
1423
|
+
return new TypedArrayConstructor(typed.buffer, typed.byteOffset || 0, typed.length);
|
|
1424
|
+
}
|
|
1425
|
+
if (typed.type === "arraybuffer" && typed.buffer) {
|
|
1426
|
+
return typed.buffer;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return data;
|
|
1430
|
+
}
|
|
1431
|
+
function getTypedArrayConstructor(name) {
|
|
1432
|
+
const constructors = {
|
|
1433
|
+
Int8Array,
|
|
1434
|
+
Uint8Array,
|
|
1435
|
+
Uint8ClampedArray,
|
|
1436
|
+
Int16Array,
|
|
1437
|
+
Uint16Array,
|
|
1438
|
+
Int32Array,
|
|
1439
|
+
Uint32Array,
|
|
1440
|
+
Float32Array,
|
|
1441
|
+
Float64Array,
|
|
1442
|
+
BigInt64Array,
|
|
1443
|
+
BigUint64Array
|
|
1444
|
+
};
|
|
1445
|
+
return constructors[name] || Uint8Array;
|
|
1446
|
+
}
|
|
1447
|
+
var StreamingH5File = class {
|
|
1448
|
+
worker;
|
|
1449
|
+
messageId = 0;
|
|
1450
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
1451
|
+
_keys = [];
|
|
1452
|
+
_isOpen = false;
|
|
1453
|
+
constructor() {
|
|
1454
|
+
this.worker = createH5Worker();
|
|
1455
|
+
this.worker.onmessage = this.handleMessage.bind(this);
|
|
1456
|
+
this.worker.onerror = this.handleError.bind(this);
|
|
1457
|
+
}
|
|
1458
|
+
handleMessage(e) {
|
|
1459
|
+
const { id, ...data } = e.data;
|
|
1460
|
+
const pending = this.pendingMessages.get(id);
|
|
1461
|
+
if (pending) {
|
|
1462
|
+
this.pendingMessages.delete(id);
|
|
1463
|
+
if (data.success) {
|
|
1464
|
+
pending.resolve(e.data);
|
|
1465
|
+
} else {
|
|
1466
|
+
pending.reject(new Error(data.error || "Worker operation failed"));
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
handleError(e) {
|
|
1471
|
+
console.error("[StreamingH5File] Worker error:", e.message);
|
|
1472
|
+
for (const [id, pending] of this.pendingMessages) {
|
|
1473
|
+
pending.reject(new Error(`Worker error: ${e.message}`));
|
|
1474
|
+
this.pendingMessages.delete(id);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
send(type, payload) {
|
|
1478
|
+
return new Promise((resolve, reject) => {
|
|
1479
|
+
const id = ++this.messageId;
|
|
1480
|
+
this.pendingMessages.set(id, { resolve, reject });
|
|
1481
|
+
this.worker.postMessage({ type, payload, id });
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Initialize the h5wasm module in the worker.
|
|
1486
|
+
*/
|
|
1487
|
+
async init(options) {
|
|
1488
|
+
await this.send("init", { h5wasmUrl: options?.h5wasmUrl });
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Open a remote HDF5 file for streaming access.
|
|
1492
|
+
*
|
|
1493
|
+
* @param url - URL to the HDF5 file (must support HTTP range requests)
|
|
1494
|
+
* @param options - Optional settings
|
|
1495
|
+
*/
|
|
1496
|
+
async open(url, options) {
|
|
1497
|
+
await this.init(options);
|
|
1498
|
+
const filename = options?.filenameHint || url.split("/").pop()?.split("?")[0] || "data.h5";
|
|
1499
|
+
const result = await this.send("openUrl", { url, filename });
|
|
1500
|
+
this._keys = result.keys || [];
|
|
1501
|
+
this._isOpen = true;
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Whether a file is currently open.
|
|
1505
|
+
*/
|
|
1506
|
+
get isOpen() {
|
|
1507
|
+
return this._isOpen;
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Get the root-level keys in the file.
|
|
1511
|
+
*/
|
|
1512
|
+
keys() {
|
|
1513
|
+
return this._keys;
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Get the keys (children) at a given path.
|
|
1517
|
+
*/
|
|
1518
|
+
async getKeys(path) {
|
|
1519
|
+
const result = await this.send("getKeys", { path });
|
|
1520
|
+
return result.keys || [];
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Get an attribute value.
|
|
1524
|
+
*/
|
|
1525
|
+
async getAttr(path, name) {
|
|
1526
|
+
const result = await this.send("getAttr", { path, name });
|
|
1527
|
+
return result.value?.value ?? result.value;
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Get all attributes at a path.
|
|
1531
|
+
*/
|
|
1532
|
+
async getAttrs(path) {
|
|
1533
|
+
const result = await this.send("getAttrs", { path });
|
|
1534
|
+
return result.attrs || {};
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Get dataset metadata (shape, dtype) without reading values.
|
|
1538
|
+
*/
|
|
1539
|
+
async getDatasetMeta(path) {
|
|
1540
|
+
const result = await this.send("getDatasetMeta", { path });
|
|
1541
|
+
const meta = result.meta;
|
|
1542
|
+
return meta;
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Read a dataset's value.
|
|
1546
|
+
*
|
|
1547
|
+
* @param path - Path to the dataset
|
|
1548
|
+
* @param slice - Optional slice specification (array of [start, end] pairs)
|
|
1549
|
+
*/
|
|
1550
|
+
async getDatasetValue(path, slice) {
|
|
1551
|
+
const result = await this.send("getDatasetValue", { path, slice });
|
|
1552
|
+
const data = result.data;
|
|
1553
|
+
return {
|
|
1554
|
+
value: reconstructValue(data.value),
|
|
1555
|
+
shape: data.shape,
|
|
1556
|
+
dtype: data.dtype
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Close the file and terminate the worker.
|
|
1561
|
+
*/
|
|
1562
|
+
async close() {
|
|
1563
|
+
if (this._isOpen) {
|
|
1564
|
+
await this.send("close");
|
|
1565
|
+
this._isOpen = false;
|
|
1566
|
+
}
|
|
1567
|
+
this.worker.terminate();
|
|
1568
|
+
this._keys = [];
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
function isStreamingSupported() {
|
|
1572
|
+
return typeof Worker !== "undefined" && typeof Blob !== "undefined" && typeof URL !== "undefined";
|
|
1573
|
+
}
|
|
1574
|
+
async function openStreamingH5(url, options) {
|
|
1575
|
+
if (!isStreamingSupported()) {
|
|
1576
|
+
throw new Error("Streaming HDF5 requires Web Worker support");
|
|
1577
|
+
}
|
|
1578
|
+
const file = new StreamingH5File();
|
|
1579
|
+
await file.open(url, options);
|
|
1580
|
+
return file;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1206
1583
|
// src/codecs/slp/h5.ts
|
|
1207
1584
|
var isNode = typeof process !== "undefined" && !!process.versions?.node;
|
|
1208
1585
|
var modulePromise = null;
|
|
@@ -3017,6 +3394,7 @@ export {
|
|
|
3017
3394
|
RecordingSession,
|
|
3018
3395
|
RenderContext,
|
|
3019
3396
|
Skeleton,
|
|
3397
|
+
StreamingH5File,
|
|
3020
3398
|
SuggestionFrame,
|
|
3021
3399
|
Symmetry,
|
|
3022
3400
|
Track,
|
|
@@ -3034,10 +3412,12 @@ export {
|
|
|
3034
3412
|
fromNumpy,
|
|
3035
3413
|
getMarkerFunction,
|
|
3036
3414
|
getPalette,
|
|
3415
|
+
isStreamingSupported,
|
|
3037
3416
|
labelsFromNumpy,
|
|
3038
3417
|
loadSlp,
|
|
3039
3418
|
loadVideo,
|
|
3040
3419
|
makeCameraFromDict,
|
|
3420
|
+
openStreamingH5,
|
|
3041
3421
|
pointsEmpty,
|
|
3042
3422
|
pointsFromArray,
|
|
3043
3423
|
pointsFromDict,
|