@eyepop.ai/eyepop 3.19.0 → 3.20.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 +1 -1
- package/dist/eyepop.index.d.mts +376 -4
- package/dist/eyepop.index.d.ts +376 -4
- package/dist/eyepop.index.js +433 -91
- package/dist/eyepop.index.js.map +1 -1
- package/dist/eyepop.index.mjs +423 -90
- package/dist/eyepop.index.mjs.map +1 -1
- package/dist/eyepop.min.js +1 -1
- package/package.json +3 -1
package/dist/eyepop.index.mjs
CHANGED
|
@@ -9207,6 +9207,67 @@ Content-Disposition: form-data; name="${part.name}"\r
|
|
|
9207
9207
|
});
|
|
9208
9208
|
}
|
|
9209
9209
|
|
|
9210
|
+
// camera.ts
|
|
9211
|
+
var QUATERNION_TOLERANCE = 1e-3;
|
|
9212
|
+
function isFinite_(value) {
|
|
9213
|
+
return value === void 0 || Number.isFinite(value);
|
|
9214
|
+
}
|
|
9215
|
+
function validateCamera(camera) {
|
|
9216
|
+
if (camera.intrinsics !== void 0 && camera.hfovDegrees !== void 0) {
|
|
9217
|
+
throw new Error("camera.intrinsics and camera.hfovDegrees are alternatives, supply one");
|
|
9218
|
+
}
|
|
9219
|
+
if (camera.intrinsics === void 0 && camera.hfovDegrees === void 0) {
|
|
9220
|
+
throw new Error("camera requires either camera.intrinsics or camera.hfovDegrees");
|
|
9221
|
+
}
|
|
9222
|
+
if (camera.hfovDegrees !== void 0) {
|
|
9223
|
+
if (!(camera.hfovDegrees > 0 && camera.hfovDegrees < 180)) {
|
|
9224
|
+
throw new Error("camera.hfovDegrees must be a horizontal field of view in (0, 180) degrees");
|
|
9225
|
+
}
|
|
9226
|
+
} else {
|
|
9227
|
+
const intrinsics = camera.intrinsics;
|
|
9228
|
+
for (const [name, focal] of [
|
|
9229
|
+
["fx", intrinsics.fx],
|
|
9230
|
+
["fy", intrinsics.fy]
|
|
9231
|
+
]) {
|
|
9232
|
+
if (!(focal > 0) || !Number.isFinite(focal)) {
|
|
9233
|
+
throw new Error(`camera.intrinsics.${name} must be a positive, finite focal length`);
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9236
|
+
for (const [name, principal, extent] of [
|
|
9237
|
+
["cx", intrinsics.cx, "width"],
|
|
9238
|
+
["cy", intrinsics.cy, "height"]
|
|
9239
|
+
]) {
|
|
9240
|
+
if (!(principal >= 0 && principal <= 1)) {
|
|
9241
|
+
throw new Error(`camera.intrinsics.${name} must be within [0, 1] of the frame ${extent}`);
|
|
9242
|
+
}
|
|
9243
|
+
}
|
|
9244
|
+
}
|
|
9245
|
+
if (camera.distortion !== void 0) {
|
|
9246
|
+
for (const name of ["k1", "k2", "p1", "p2", "k3"]) {
|
|
9247
|
+
if (!isFinite_(camera.distortion[name])) {
|
|
9248
|
+
throw new Error(`camera.distortion.${name} must be a finite number`);
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
9252
|
+
if (camera.extrinsics !== void 0) {
|
|
9253
|
+
const translation = camera.extrinsics.translation;
|
|
9254
|
+
if (translation !== void 0) {
|
|
9255
|
+
for (const axis of ["x", "y", "z"]) {
|
|
9256
|
+
if (!Number.isFinite(translation[axis])) {
|
|
9257
|
+
throw new Error(`camera.extrinsics.translation.${axis} must be a finite number`);
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
const rotation = camera.extrinsics.rotation;
|
|
9262
|
+
if (rotation !== void 0) {
|
|
9263
|
+
const norm = Math.sqrt(rotation.w ** 2 + rotation.x ** 2 + rotation.y ** 2 + rotation.z ** 2);
|
|
9264
|
+
if (!(Math.abs(norm - 1) <= QUATERNION_TOLERANCE)) {
|
|
9265
|
+
throw new Error("camera.extrinsics.rotation must be a unit quaternion");
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
}
|
|
9270
|
+
|
|
9210
9271
|
// worker/worker_types.ts
|
|
9211
9272
|
var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
|
|
9212
9273
|
PredictionVersion3[PredictionVersion3["V1"] = 1] = "V1";
|
|
@@ -9261,6 +9322,16 @@ var ContourType = /* @__PURE__ */ ((ContourType2) => {
|
|
|
9261
9322
|
ContourType2["RECTANGLE"] = "rectangle";
|
|
9262
9323
|
return ContourType2;
|
|
9263
9324
|
})(ContourType || {});
|
|
9325
|
+
function validatePop(pop) {
|
|
9326
|
+
if (pop.depthMap !== void 0) {
|
|
9327
|
+
if (pop.depthMap.ability === void 0 === (pop.depthMap.abilityUuid === void 0)) {
|
|
9328
|
+
throw new Error("depthMap requires exactly one of ability or abilityUuid");
|
|
9329
|
+
}
|
|
9330
|
+
}
|
|
9331
|
+
if (pop.defaults?.camera !== void 0) {
|
|
9332
|
+
validateCamera(pop.defaults.camera);
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9264
9335
|
|
|
9265
9336
|
// worker/webrtc_whip.ts
|
|
9266
9337
|
import { v4 as uuidv42 } from "uuid";
|
|
@@ -9751,7 +9822,7 @@ var UploadJob = class extends AbstractJob {
|
|
|
9751
9822
|
}
|
|
9752
9823
|
}
|
|
9753
9824
|
const postUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?${queryParams.toString()}`;
|
|
9754
|
-
if (this._params.componentParams || this._params.roi) {
|
|
9825
|
+
if (this._params.componentParams || this._params.roi || this._params.camera) {
|
|
9755
9826
|
const formData = new FormData();
|
|
9756
9827
|
if (this._params.componentParams) {
|
|
9757
9828
|
const blob = new Blob([JSON.stringify(this._params.componentParams)], { type: "application/json" });
|
|
@@ -9765,6 +9836,10 @@ var UploadJob = class extends AbstractJob {
|
|
|
9765
9836
|
const blob = new Blob([JSON.stringify(this._params.fps)], { type: "application/json" });
|
|
9766
9837
|
formData.append("fps", blob);
|
|
9767
9838
|
}
|
|
9839
|
+
if (this._params.camera) {
|
|
9840
|
+
const blob = new Blob([JSON.stringify(this._params.camera)], { type: "application/json" });
|
|
9841
|
+
formData.append("camera", blob);
|
|
9842
|
+
}
|
|
9768
9843
|
const fileBlob = await new Response(await this.uploadBody()).blob();
|
|
9769
9844
|
formData.append("file", fileBlob);
|
|
9770
9845
|
const headers = {
|
|
@@ -9846,7 +9921,7 @@ var UploadJob = class extends AbstractJob {
|
|
|
9846
9921
|
}
|
|
9847
9922
|
}
|
|
9848
9923
|
const postUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?${queryParams.toString()}`;
|
|
9849
|
-
if (this._params.componentParams || this._params.roi) {
|
|
9924
|
+
if (this._params.componentParams || this._params.roi || this._params.camera) {
|
|
9850
9925
|
const formData = new FormData();
|
|
9851
9926
|
if (this._params.componentParams) {
|
|
9852
9927
|
const blob = new Blob([JSON.stringify(this._params.componentParams)], { type: "application/json" });
|
|
@@ -9860,6 +9935,10 @@ var UploadJob = class extends AbstractJob {
|
|
|
9860
9935
|
const blob = new Blob([JSON.stringify(this._params.fps)], { type: "application/json" });
|
|
9861
9936
|
formData.append("fps", blob);
|
|
9862
9937
|
}
|
|
9938
|
+
if (this._params.camera) {
|
|
9939
|
+
const blob = new Blob([JSON.stringify(this._params.camera)], { type: "application/json" });
|
|
9940
|
+
formData.append("camera", blob);
|
|
9941
|
+
}
|
|
9863
9942
|
const fileBlob = await new Response(await this.uploadBody()).blob();
|
|
9864
9943
|
formData.append("file", fileBlob);
|
|
9865
9944
|
const headers = {
|
|
@@ -9916,6 +9995,7 @@ var LoadFromJob = class extends AbstractJob {
|
|
|
9916
9995
|
params: this._params.componentParams,
|
|
9917
9996
|
roi: this._params.roi,
|
|
9918
9997
|
fps: this._params.fps,
|
|
9998
|
+
camera: this._params.camera,
|
|
9919
9999
|
version: this._version
|
|
9920
10000
|
};
|
|
9921
10001
|
if (this._params.motionDetect) {
|
|
@@ -9976,6 +10056,9 @@ var UploadGroupJob = class extends AbstractJob {
|
|
|
9976
10056
|
if (this._params.fps) {
|
|
9977
10057
|
parts.push({ name: "fps", contentType: "application/json", content: JSON.stringify(this._params.fps) });
|
|
9978
10058
|
}
|
|
10059
|
+
if (this._params.camera) {
|
|
10060
|
+
parts.push({ name: "camera", contentType: "application/json", content: JSON.stringify(this._params.camera) });
|
|
10061
|
+
}
|
|
9979
10062
|
for (const source of this._sources) {
|
|
9980
10063
|
parts.push({ name: "file", contentType: source.mimeType, content: source.stream });
|
|
9981
10064
|
}
|
|
@@ -10023,6 +10106,9 @@ var LoadFromGroupJob = class extends AbstractJob {
|
|
|
10023
10106
|
if (this._params.roi) {
|
|
10024
10107
|
body["roi"] = this._params.roi;
|
|
10025
10108
|
}
|
|
10109
|
+
if (this._params.camera) {
|
|
10110
|
+
body["camera"] = this._params.camera;
|
|
10111
|
+
}
|
|
10026
10112
|
if (this._params.motionDetect) {
|
|
10027
10113
|
Object.assign(body, this._params.motionDetect);
|
|
10028
10114
|
}
|
|
@@ -10061,6 +10147,7 @@ var LoadFromAssetUuidJob = class extends AbstractJob {
|
|
|
10061
10147
|
params: this._params.componentParams,
|
|
10062
10148
|
roi: this._params.roi,
|
|
10063
10149
|
fps: this._params.fps,
|
|
10150
|
+
camera: this._params.camera,
|
|
10064
10151
|
version: this._version
|
|
10065
10152
|
};
|
|
10066
10153
|
if (this._params.motionDetect) {
|
|
@@ -10115,6 +10202,7 @@ var LoadMediaStreamJob = class extends AbstractJob {
|
|
|
10115
10202
|
params: this._params.componentParams,
|
|
10116
10203
|
roi: this._params.roi,
|
|
10117
10204
|
fps: this._params.fps,
|
|
10205
|
+
camera: this._params.camera,
|
|
10118
10206
|
version: this._version
|
|
10119
10207
|
};
|
|
10120
10208
|
if (this._params.motionDetect) {
|
|
@@ -10726,6 +10814,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
|
10726
10814
|
return this.options().pop || null;
|
|
10727
10815
|
}
|
|
10728
10816
|
async changePop(pop) {
|
|
10817
|
+
validatePop(pop);
|
|
10729
10818
|
if (this.options().popId == "transient" /* Transient */) {
|
|
10730
10819
|
await this.changeTransientPop(pop);
|
|
10731
10820
|
return;
|
|
@@ -10798,7 +10887,22 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
|
10798
10887
|
return Promise.reject("unknown source type");
|
|
10799
10888
|
}
|
|
10800
10889
|
}
|
|
10890
|
+
/**
|
|
10891
|
+
* Reject a source's calibration before any work is started for it.
|
|
10892
|
+
*
|
|
10893
|
+
* Called by every entry point that takes ProcessParams rather than at the
|
|
10894
|
+
* one boundary they share, because they have none: the group methods build
|
|
10895
|
+
* their jobs directly instead of going through processV2. It runs before
|
|
10896
|
+
* the concurrency limit is acquired, so a request that can never be sent
|
|
10897
|
+
* does not hold a slot.
|
|
10898
|
+
*/
|
|
10899
|
+
validateProcessParams(params) {
|
|
10900
|
+
if (params.camera !== void 0) {
|
|
10901
|
+
validateCamera(params.camera);
|
|
10902
|
+
}
|
|
10903
|
+
}
|
|
10801
10904
|
async processV2(request) {
|
|
10905
|
+
this.validateProcessParams(request);
|
|
10802
10906
|
if (request.source.file !== void 0) {
|
|
10803
10907
|
return this.uploadFile(request.source, request);
|
|
10804
10908
|
} else if (request.source.stream !== void 0) {
|
|
@@ -10819,6 +10923,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
|
10819
10923
|
if (paths.length === 0) {
|
|
10820
10924
|
throw new Error("upload group requires at least one path");
|
|
10821
10925
|
}
|
|
10926
|
+
this.validateProcessParams(params);
|
|
10822
10927
|
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
10823
10928
|
throw new Error("endpoint not connected, use connect()");
|
|
10824
10929
|
}
|
|
@@ -10857,6 +10962,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
|
10857
10962
|
if (mimeTypes !== void 0 && mimeTypes.length !== streams.length) {
|
|
10858
10963
|
throw new Error("mimeTypes must have the same length as streams");
|
|
10859
10964
|
}
|
|
10965
|
+
this.validateProcessParams(params);
|
|
10860
10966
|
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
10861
10967
|
throw new Error("endpoint not connected, use connect()");
|
|
10862
10968
|
}
|
|
@@ -10885,6 +10991,7 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
|
|
|
10885
10991
|
if (urls.length === 0) {
|
|
10886
10992
|
throw new Error("load from group requires at least one url");
|
|
10887
10993
|
}
|
|
10994
|
+
this.validateProcessParams(params);
|
|
10888
10995
|
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
10889
10996
|
throw new Error("endpoint not connected, use connect()");
|
|
10890
10997
|
}
|
|
@@ -11432,57 +11539,8 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
|
11432
11539
|
};
|
|
11433
11540
|
}
|
|
11434
11541
|
|
|
11435
|
-
//
|
|
11436
|
-
|
|
11437
|
-
var resultArray = [];
|
|
11438
|
-
if (strArray.length === 0) {
|
|
11439
|
-
return "";
|
|
11440
|
-
}
|
|
11441
|
-
if (typeof strArray[0] !== "string") {
|
|
11442
|
-
throw new TypeError("Url must be a string. Received " + strArray[0]);
|
|
11443
|
-
}
|
|
11444
|
-
if (strArray[0].match(/^[^/:]+:\/*$/) && strArray.length > 1) {
|
|
11445
|
-
var first = strArray.shift();
|
|
11446
|
-
strArray[0] = first + strArray[0];
|
|
11447
|
-
}
|
|
11448
|
-
if (strArray[0].match(/^file:\/\/\//)) {
|
|
11449
|
-
strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1:///");
|
|
11450
|
-
} else {
|
|
11451
|
-
strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1://");
|
|
11452
|
-
}
|
|
11453
|
-
for (var i = 0; i < strArray.length; i++) {
|
|
11454
|
-
var component = strArray[i];
|
|
11455
|
-
if (typeof component !== "string") {
|
|
11456
|
-
throw new TypeError("Url must be a string. Received " + component);
|
|
11457
|
-
}
|
|
11458
|
-
if (component === "") {
|
|
11459
|
-
continue;
|
|
11460
|
-
}
|
|
11461
|
-
if (i > 0) {
|
|
11462
|
-
component = component.replace(/^[\/]+/, "");
|
|
11463
|
-
}
|
|
11464
|
-
if (i < strArray.length - 1) {
|
|
11465
|
-
component = component.replace(/[\/]+$/, "");
|
|
11466
|
-
} else {
|
|
11467
|
-
component = component.replace(/[\/]+$/, "/");
|
|
11468
|
-
}
|
|
11469
|
-
resultArray.push(component);
|
|
11470
|
-
}
|
|
11471
|
-
var str = resultArray.join("/");
|
|
11472
|
-
str = str.replace(/\/(\?|&|#[^!])/g, "$1");
|
|
11473
|
-
var parts = str.split("?");
|
|
11474
|
-
str = parts.shift() + (parts.length > 0 ? "?" : "") + parts.join("&");
|
|
11475
|
-
return str;
|
|
11476
|
-
}
|
|
11477
|
-
function urlJoin() {
|
|
11478
|
-
var input;
|
|
11479
|
-
if (typeof arguments[0] === "object") {
|
|
11480
|
-
input = arguments[0];
|
|
11481
|
-
} else {
|
|
11482
|
-
input = [].slice.call(arguments);
|
|
11483
|
-
}
|
|
11484
|
-
return normalize(input);
|
|
11485
|
-
}
|
|
11542
|
+
// data/data_endpoint.ts
|
|
11543
|
+
import urljoin from "url-join";
|
|
11486
11544
|
|
|
11487
11545
|
// data/data_types.ts
|
|
11488
11546
|
var DataApiType = /* @__PURE__ */ ((DataApiType2) => {
|
|
@@ -11835,6 +11893,33 @@ var EvaluateDatasetJob = class {
|
|
|
11835
11893
|
// data/data_endpoint.ts
|
|
11836
11894
|
var WS_INITIAL_RECONNECT_DELAY = 1e3;
|
|
11837
11895
|
var WS_MAX_RECONNECT_DELAY = 6e4;
|
|
11896
|
+
function ws_origin(url) {
|
|
11897
|
+
try {
|
|
11898
|
+
return new URL(url).origin;
|
|
11899
|
+
} catch {
|
|
11900
|
+
return url;
|
|
11901
|
+
}
|
|
11902
|
+
}
|
|
11903
|
+
function ws_failure_reason(event, sourceError) {
|
|
11904
|
+
if (sourceError instanceof Error) {
|
|
11905
|
+
return sourceError.message;
|
|
11906
|
+
}
|
|
11907
|
+
if (typeof sourceError === "string") {
|
|
11908
|
+
return sourceError;
|
|
11909
|
+
}
|
|
11910
|
+
return event?.message ?? "";
|
|
11911
|
+
}
|
|
11912
|
+
var EyePopDataWebSocketError = class extends Error {
|
|
11913
|
+
constructor(url, event) {
|
|
11914
|
+
const sourceError = event?.error ?? event;
|
|
11915
|
+
const reason = ws_failure_reason(event, sourceError);
|
|
11916
|
+
super(reason ? `EyePop Dataset event WebSocket connection failed: ${reason}` : "EyePop Dataset event WebSocket connection failed");
|
|
11917
|
+
this.code = "DATA_WEBSOCKET_CONNECTION_FAILED";
|
|
11918
|
+
this.name = "EyePopDataWebSocketError";
|
|
11919
|
+
this.endpoint = ws_origin(url);
|
|
11920
|
+
this.sourceError = sourceError;
|
|
11921
|
+
}
|
|
11922
|
+
};
|
|
11838
11923
|
function omit_nulls(_, value) {
|
|
11839
11924
|
if (value === null) {
|
|
11840
11925
|
return void 0;
|
|
@@ -11848,6 +11933,9 @@ var DataEndpoint = class extends Endpoint {
|
|
|
11848
11933
|
this.vlmApiUrl = null;
|
|
11849
11934
|
this._ws = null;
|
|
11850
11935
|
this._ws_current_reconnect_delay = null;
|
|
11936
|
+
this._ws_reconnect_timeout = null;
|
|
11937
|
+
this._ws_reconnect_enabled = false;
|
|
11938
|
+
this._ws_connecting = null;
|
|
11851
11939
|
this._accountId = options.accountId || null;
|
|
11852
11940
|
this._account_event_handlers = [];
|
|
11853
11941
|
this._dataset_uuid_to_event_handlers = /* @__PURE__ */ new Map();
|
|
@@ -11871,14 +11959,15 @@ var DataEndpoint = class extends Endpoint {
|
|
|
11871
11959
|
};
|
|
11872
11960
|
}
|
|
11873
11961
|
async disconnect(wait = true) {
|
|
11962
|
+
this._ws_reconnect_enabled = false;
|
|
11963
|
+
if (this._ws_reconnect_timeout) {
|
|
11964
|
+
clearTimeout(this._ws_reconnect_timeout);
|
|
11965
|
+
this._ws_reconnect_timeout = null;
|
|
11966
|
+
}
|
|
11874
11967
|
this.datasetApiUrl = null;
|
|
11875
11968
|
this.vlmApiUrl = null;
|
|
11876
11969
|
if (this._ws) {
|
|
11877
|
-
|
|
11878
|
-
this._ws.close();
|
|
11879
|
-
} catch (e) {
|
|
11880
|
-
this._logger.warn("ignored exception", e);
|
|
11881
|
-
}
|
|
11970
|
+
this.close_ws(this._ws, "unsubscribe");
|
|
11882
11971
|
this._ws = null;
|
|
11883
11972
|
}
|
|
11884
11973
|
await super.disconnect(wait);
|
|
@@ -11916,13 +12005,14 @@ var DataEndpoint = class extends Endpoint {
|
|
|
11916
12005
|
const vlmApiUrl = new URL(config.vlm_api_url, this.eyepopUrl());
|
|
11917
12006
|
this.vlmApiUrl = vlmApiUrl.toString();
|
|
11918
12007
|
if (this.options().disableWs) {
|
|
12008
|
+
this._ws_reconnect_enabled = false;
|
|
11919
12009
|
this._logger.info("disabled ws connection");
|
|
11920
12010
|
} else {
|
|
11921
12011
|
await this.reconnect_ws();
|
|
11922
12012
|
}
|
|
11923
12013
|
if (this.options().useCookie) {
|
|
11924
12014
|
const client = this._client;
|
|
11925
|
-
const url =
|
|
12015
|
+
const url = urljoin(this.datasetApiUrl, "security", "check?set_cookie=true");
|
|
11926
12016
|
const headers2 = {
|
|
11927
12017
|
Authorization: await this.authorizationHeader()
|
|
11928
12018
|
};
|
|
@@ -11939,37 +12029,68 @@ var DataEndpoint = class extends Endpoint {
|
|
|
11939
12029
|
return Promise.resolve(this);
|
|
11940
12030
|
}
|
|
11941
12031
|
async reconnect_ws() {
|
|
12032
|
+
if (this._ws_connecting) {
|
|
12033
|
+
return this._ws_connecting;
|
|
12034
|
+
}
|
|
12035
|
+
const connecting = this.open_ws();
|
|
12036
|
+
this._ws_connecting = connecting;
|
|
12037
|
+
try {
|
|
12038
|
+
await connecting;
|
|
12039
|
+
} finally {
|
|
12040
|
+
this._ws_connecting = null;
|
|
12041
|
+
}
|
|
12042
|
+
}
|
|
12043
|
+
async open_ws() {
|
|
11942
12044
|
if (this._ws) {
|
|
11943
|
-
this._ws
|
|
12045
|
+
this.close_ws(this._ws, "unsubscribe");
|
|
11944
12046
|
this._ws = null;
|
|
11945
12047
|
}
|
|
11946
12048
|
const baseUrl = this.datasetApiUrl?.replace("https://", "wss://").replace("http://", "ws://");
|
|
11947
12049
|
if (!baseUrl) {
|
|
11948
12050
|
return;
|
|
11949
12051
|
}
|
|
11950
|
-
const ws_url =
|
|
12052
|
+
const ws_url = urljoin(baseUrl, "events");
|
|
11951
12053
|
this._requestLogger.debug("about to connect ws to: %s", ws_url);
|
|
11952
12054
|
return new Promise((resolve, reject) => {
|
|
12055
|
+
let settled = false;
|
|
12056
|
+
const settle_ok = () => {
|
|
12057
|
+
settled = true;
|
|
12058
|
+
resolve();
|
|
12059
|
+
};
|
|
12060
|
+
const settle_err = (error) => {
|
|
12061
|
+
settled = true;
|
|
12062
|
+
reject(error);
|
|
12063
|
+
};
|
|
11953
12064
|
const ws = createWebSocket(ws_url);
|
|
11954
12065
|
if (!ws) {
|
|
11955
|
-
return
|
|
12066
|
+
return settle_ok();
|
|
11956
12067
|
}
|
|
11957
12068
|
this._ws = ws;
|
|
11958
|
-
ws.onopen =
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
ws
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
12069
|
+
ws.onopen = () => {
|
|
12070
|
+
void this.authorizationHeader().then((auth_header) => {
|
|
12071
|
+
if (this._ws !== ws) {
|
|
12072
|
+
this.close_ws(ws, "unsubscribe");
|
|
12073
|
+
settle_ok();
|
|
12074
|
+
return;
|
|
12075
|
+
}
|
|
12076
|
+
let msg = JSON.stringify({ authorization: auth_header });
|
|
12077
|
+
this._requestLogger.debug("ws send: %s", msg);
|
|
12078
|
+
ws.send(msg);
|
|
12079
|
+
msg = JSON.stringify({ subscribe: { account_uuid: this._accountId } });
|
|
12080
|
+
this._requestLogger.debug("ws send: %s", msg);
|
|
12081
|
+
ws.send(msg);
|
|
12082
|
+
for (const dataset_uuid of this._dataset_uuid_to_event_handlers.keys()) {
|
|
12083
|
+
const msg2 = JSON.stringify({ subscribe: { dataset_uuid } });
|
|
12084
|
+
this._requestLogger.debug("ws send: %s", msg2);
|
|
12085
|
+
ws.send(msg2);
|
|
12086
|
+
}
|
|
12087
|
+
this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
|
|
12088
|
+
this._ws_reconnect_enabled = true;
|
|
12089
|
+
settle_ok();
|
|
12090
|
+
}).catch((error) => {
|
|
12091
|
+
this.close_ws(ws, "authorization failed", 1011);
|
|
12092
|
+
settle_err(error);
|
|
12093
|
+
});
|
|
11973
12094
|
};
|
|
11974
12095
|
ws.onmessage = (event) => {
|
|
11975
12096
|
const data = JSON.parse(event.data);
|
|
@@ -11980,24 +12101,54 @@ var DataEndpoint = class extends Endpoint {
|
|
|
11980
12101
|
ws.onclose = (event) => {
|
|
11981
12102
|
const { code, reason } = event;
|
|
11982
12103
|
this._requestLogger.debug("ws closed: %d %s", code, reason);
|
|
11983
|
-
if (
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
}
|
|
11989
|
-
setTimeout(async () => {
|
|
11990
|
-
await this.reconnect_ws();
|
|
11991
|
-
}, this._ws_current_reconnect_delay);
|
|
12104
|
+
if (!settled) {
|
|
12105
|
+
settle_err(new EyePopDataWebSocketError(ws_url, new Error(`closed before the connection was established: ${code} ${reason}`)));
|
|
12106
|
+
}
|
|
12107
|
+
if (this._ws !== ws) {
|
|
12108
|
+
return;
|
|
11992
12109
|
}
|
|
12110
|
+
this._ws = null;
|
|
12111
|
+
this.schedule_ws_reconnect();
|
|
11993
12112
|
};
|
|
11994
12113
|
ws.onerror = (event) => {
|
|
11995
|
-
|
|
11996
|
-
|
|
12114
|
+
const error = new EyePopDataWebSocketError(ws_url, event);
|
|
12115
|
+
this._logger.error({ err: error }, "dataset event WebSocket connection failed");
|
|
12116
|
+
settle_err(error);
|
|
11997
12117
|
return null;
|
|
11998
12118
|
};
|
|
11999
12119
|
});
|
|
12000
12120
|
}
|
|
12121
|
+
close_ws(ws, reason, code = 1e3) {
|
|
12122
|
+
try {
|
|
12123
|
+
ws.close(code, reason);
|
|
12124
|
+
} catch (e) {
|
|
12125
|
+
this._logger.warn({ err: e }, "ignored exception closing the dataset event WebSocket");
|
|
12126
|
+
}
|
|
12127
|
+
}
|
|
12128
|
+
schedule_ws_reconnect() {
|
|
12129
|
+
if (!this._ws_reconnect_enabled || this._ws_reconnect_timeout) {
|
|
12130
|
+
return;
|
|
12131
|
+
}
|
|
12132
|
+
if (!this._ws_current_reconnect_delay) {
|
|
12133
|
+
this._ws_current_reconnect_delay = WS_INITIAL_RECONNECT_DELAY;
|
|
12134
|
+
} else if (this._ws_current_reconnect_delay < WS_MAX_RECONNECT_DELAY) {
|
|
12135
|
+
this._ws_current_reconnect_delay *= 1.5;
|
|
12136
|
+
}
|
|
12137
|
+
this._ws_reconnect_timeout = setTimeout(() => {
|
|
12138
|
+
if (!this._ws_reconnect_enabled) {
|
|
12139
|
+
this._ws_reconnect_timeout = null;
|
|
12140
|
+
return;
|
|
12141
|
+
}
|
|
12142
|
+
void this.reconnect_ws().catch((error) => {
|
|
12143
|
+
this._requestLogger.debug("dataset event WebSocket reconnect failed: %s", error);
|
|
12144
|
+
}).finally(() => {
|
|
12145
|
+
this._ws_reconnect_timeout = null;
|
|
12146
|
+
if (!this._ws) {
|
|
12147
|
+
this.schedule_ws_reconnect();
|
|
12148
|
+
}
|
|
12149
|
+
});
|
|
12150
|
+
}, this._ws_current_reconnect_delay);
|
|
12151
|
+
}
|
|
12001
12152
|
options() {
|
|
12002
12153
|
return this._options;
|
|
12003
12154
|
}
|
|
@@ -12022,7 +12173,7 @@ var DataEndpoint = class extends Endpoint {
|
|
|
12022
12173
|
body: options.body
|
|
12023
12174
|
};
|
|
12024
12175
|
this._requestLogger.debug("Request - " + ri.method + " " + path2 + " BODY: " + (ri.body ? ri.body : ""));
|
|
12025
|
-
return await client.fetch(
|
|
12176
|
+
return await client.fetch(urljoin(baseUrl ?? "", path2), ri);
|
|
12026
12177
|
});
|
|
12027
12178
|
if (response.status === 204) {
|
|
12028
12179
|
return null;
|
|
@@ -12760,6 +12911,176 @@ function decodeDepthMap(depth) {
|
|
|
12760
12911
|
return new DepthMap(depth.width, depth.height, values);
|
|
12761
12912
|
}
|
|
12762
12913
|
|
|
12914
|
+
// point_cloud.ts
|
|
12915
|
+
var VALUES_PER_POINT = 3;
|
|
12916
|
+
var isLittleEndianPlatform2 = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
|
|
12917
|
+
function base64ToBytes2(base64) {
|
|
12918
|
+
if (typeof Buffer !== "undefined") {
|
|
12919
|
+
const buffer = Buffer.from(base64, "base64");
|
|
12920
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
12921
|
+
}
|
|
12922
|
+
const binary = atob(base64);
|
|
12923
|
+
const bytes = new Uint8Array(binary.length);
|
|
12924
|
+
for (let i = 0; i < binary.length; i++) {
|
|
12925
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12926
|
+
}
|
|
12927
|
+
return bytes;
|
|
12928
|
+
}
|
|
12929
|
+
var PointCloud = class {
|
|
12930
|
+
constructor(width, height, points, box) {
|
|
12931
|
+
this.width = width;
|
|
12932
|
+
this.height = height;
|
|
12933
|
+
this.points = points;
|
|
12934
|
+
this.box = box;
|
|
12935
|
+
}
|
|
12936
|
+
/**
|
|
12937
|
+
* The world point for pixel (i, j), or undefined where the worker placed none.
|
|
12938
|
+
*
|
|
12939
|
+
* (i, j) indexes the cloud's own grid - the mask bitmap, or the depth map
|
|
12940
|
+
* for a scene cloud: i is the column, j is the row.
|
|
12941
|
+
*/
|
|
12942
|
+
at(i, j) {
|
|
12943
|
+
if (!(i >= 0 && i < this.width && j >= 0 && j < this.height)) {
|
|
12944
|
+
throw new Error(`(${i}, ${j}) is outside a ${this.width}x${this.height} cloud`);
|
|
12945
|
+
}
|
|
12946
|
+
const offset = (j * this.width + i) * VALUES_PER_POINT;
|
|
12947
|
+
const x = this.points[offset];
|
|
12948
|
+
const y = this.points[offset + 1];
|
|
12949
|
+
const z = this.points[offset + 2];
|
|
12950
|
+
if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
|
|
12951
|
+
return void 0;
|
|
12952
|
+
}
|
|
12953
|
+
return { x, y, z };
|
|
12954
|
+
}
|
|
12955
|
+
/**
|
|
12956
|
+
* The world point for a source frame coordinate inside the cloud's box.
|
|
12957
|
+
*
|
|
12958
|
+
* Needs a cloud built with cloudOfObject(), or with cloudOfDepth() and the
|
|
12959
|
+
* frame size: the grid covers a box, and nothing else knows where it sits.
|
|
12960
|
+
*/
|
|
12961
|
+
atSource(x, y) {
|
|
12962
|
+
if (this.box === void 0) {
|
|
12963
|
+
throw new Error("atSource needs a box; build with cloudOfObject or cloudOfDepth");
|
|
12964
|
+
}
|
|
12965
|
+
if (!(this.box.width > 0) || !(this.box.height > 0)) {
|
|
12966
|
+
throw new Error(`invalid bounding box extent: ${this.box.width}x${this.box.height}`);
|
|
12967
|
+
}
|
|
12968
|
+
const i = Math.min(Math.max(Math.floor((x - this.box.x) / this.box.width * this.width), 0), this.width - 1);
|
|
12969
|
+
const j = Math.min(Math.max(Math.floor((y - this.box.y) / this.box.height * this.height), 0), this.height - 1);
|
|
12970
|
+
return this.at(i, j);
|
|
12971
|
+
}
|
|
12972
|
+
isPlaced(i, j) {
|
|
12973
|
+
return this.at(i, j) !== void 0;
|
|
12974
|
+
}
|
|
12975
|
+
/**
|
|
12976
|
+
* Just the points the worker placed.
|
|
12977
|
+
*
|
|
12978
|
+
* The shape a scatter plot or an export wants: the grid with its NaN holes
|
|
12979
|
+
* dropped, and no mask to apply first.
|
|
12980
|
+
*/
|
|
12981
|
+
get placedPoints() {
|
|
12982
|
+
const placed = [];
|
|
12983
|
+
for (let offset = 0; offset < this.points.length; offset += VALUES_PER_POINT) {
|
|
12984
|
+
const x = this.points[offset];
|
|
12985
|
+
const y = this.points[offset + 1];
|
|
12986
|
+
const z = this.points[offset + 2];
|
|
12987
|
+
if (!Number.isNaN(x) && !Number.isNaN(y) && !Number.isNaN(z)) {
|
|
12988
|
+
placed.push({ x, y, z });
|
|
12989
|
+
}
|
|
12990
|
+
}
|
|
12991
|
+
return placed;
|
|
12992
|
+
}
|
|
12993
|
+
/**
|
|
12994
|
+
* Per axis (min, max) in metres over the placed points, or undefined if none were.
|
|
12995
|
+
*
|
|
12996
|
+
* The counterpart to DepthMap's finiteMin/finiteMax, which are one axis
|
|
12997
|
+
* because a depth map has one value per pixel.
|
|
12998
|
+
*/
|
|
12999
|
+
get bounds() {
|
|
13000
|
+
let found = false;
|
|
13001
|
+
const min = { x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY, z: Number.POSITIVE_INFINITY };
|
|
13002
|
+
const max = { x: Number.NEGATIVE_INFINITY, y: Number.NEGATIVE_INFINITY, z: Number.NEGATIVE_INFINITY };
|
|
13003
|
+
for (let offset = 0; offset < this.points.length; offset += VALUES_PER_POINT) {
|
|
13004
|
+
const x = this.points[offset];
|
|
13005
|
+
const y = this.points[offset + 1];
|
|
13006
|
+
const z = this.points[offset + 2];
|
|
13007
|
+
if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
|
|
13008
|
+
continue;
|
|
13009
|
+
}
|
|
13010
|
+
found = true;
|
|
13011
|
+
min.x = Math.min(min.x, x);
|
|
13012
|
+
min.y = Math.min(min.y, y);
|
|
13013
|
+
min.z = Math.min(min.z, z);
|
|
13014
|
+
max.x = Math.max(max.x, x);
|
|
13015
|
+
max.y = Math.max(max.y, y);
|
|
13016
|
+
max.z = Math.max(max.z, z);
|
|
13017
|
+
}
|
|
13018
|
+
return found ? { min, max } : void 0;
|
|
13019
|
+
}
|
|
13020
|
+
};
|
|
13021
|
+
function decode(width, height, world, box) {
|
|
13022
|
+
if (!Number.isSafeInteger(width) || width <= 0 || !Number.isSafeInteger(height) || height <= 0) {
|
|
13023
|
+
throw new Error(`invalid point cloud dimensions: ${width}x${height}`);
|
|
13024
|
+
}
|
|
13025
|
+
const bytes = base64ToBytes2(world);
|
|
13026
|
+
const count = width * height * VALUES_PER_POINT;
|
|
13027
|
+
if (bytes.byteLength !== count * 4) {
|
|
13028
|
+
throw new Error(`'world' holds ${bytes.byteLength} bytes, expected ${count * 4} for ${width}x${height} xyz float32`);
|
|
13029
|
+
}
|
|
13030
|
+
let points;
|
|
13031
|
+
if (isLittleEndianPlatform2) {
|
|
13032
|
+
const aligned = new Uint8Array(bytes);
|
|
13033
|
+
points = new Float32Array(aligned.buffer, 0, count);
|
|
13034
|
+
} else {
|
|
13035
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
13036
|
+
points = new Float32Array(count);
|
|
13037
|
+
for (let i = 0; i < count; i++) {
|
|
13038
|
+
points[i] = view.getFloat32(i * 4, true);
|
|
13039
|
+
}
|
|
13040
|
+
}
|
|
13041
|
+
return new PointCloud(width, height, points, box);
|
|
13042
|
+
}
|
|
13043
|
+
function decodePointCloud(mask, box) {
|
|
13044
|
+
if (mask === void 0 || mask.world === void 0) {
|
|
13045
|
+
return void 0;
|
|
13046
|
+
}
|
|
13047
|
+
return decode(mask.width, mask.height, mask.world, box);
|
|
13048
|
+
}
|
|
13049
|
+
function cloudOfObject(obj) {
|
|
13050
|
+
if (obj === void 0 || obj.mask === void 0) {
|
|
13051
|
+
return void 0;
|
|
13052
|
+
}
|
|
13053
|
+
return decodePointCloud(obj.mask, { x: obj.x, y: obj.y, width: obj.width, height: obj.height });
|
|
13054
|
+
}
|
|
13055
|
+
function cloudOfDepth(depth, sourceWidth, sourceHeight) {
|
|
13056
|
+
if (depth === void 0 || depth.world === void 0) {
|
|
13057
|
+
return void 0;
|
|
13058
|
+
}
|
|
13059
|
+
const box = sourceWidth && sourceHeight ? { x: 0, y: 0, width: sourceWidth, height: sourceHeight } : void 0;
|
|
13060
|
+
return decode(depth.width, depth.height, depth.world, box);
|
|
13061
|
+
}
|
|
13062
|
+
function cloudsOfPrediction(prediction) {
|
|
13063
|
+
const clouds = [];
|
|
13064
|
+
if (prediction === void 0) {
|
|
13065
|
+
return clouds;
|
|
13066
|
+
}
|
|
13067
|
+
const walk = (objects) => {
|
|
13068
|
+
for (const obj of objects ?? []) {
|
|
13069
|
+
const cloud = cloudOfObject(obj);
|
|
13070
|
+
if (cloud !== void 0) {
|
|
13071
|
+
clouds.push(cloud);
|
|
13072
|
+
}
|
|
13073
|
+
walk(obj.objects);
|
|
13074
|
+
}
|
|
13075
|
+
};
|
|
13076
|
+
walk(prediction.objects);
|
|
13077
|
+
const scene = cloudOfDepth(prediction.depth, prediction.source_width, prediction.source_height);
|
|
13078
|
+
if (scene !== void 0) {
|
|
13079
|
+
clouds.push(scene);
|
|
13080
|
+
}
|
|
13081
|
+
return clouds;
|
|
13082
|
+
}
|
|
13083
|
+
|
|
12763
13084
|
// worker/webrtc_whep.ts
|
|
12764
13085
|
var WebrtcWhep = class extends WebrtcBase {
|
|
12765
13086
|
constructor(ingressId, getSession, client, requestLogger) {
|
|
@@ -12864,6 +13185,9 @@ var EyePop;
|
|
|
12864
13185
|
} : void 0;
|
|
12865
13186
|
EyePop2.endpoint = workerEndpoint;
|
|
12866
13187
|
function workerEndpoint(opts = {}) {
|
|
13188
|
+
if (opts.pop !== void 0) {
|
|
13189
|
+
validatePop(opts.pop);
|
|
13190
|
+
}
|
|
12867
13191
|
if (opts.isLocalMode === void 0) {
|
|
12868
13192
|
opts.isLocalMode = stringToBooleanSafe(readEnv("EYEPOP_LOCAL_MODE"));
|
|
12869
13193
|
}
|
|
@@ -12999,6 +13323,7 @@ export {
|
|
|
12999
13323
|
EvaluationStatus,
|
|
13000
13324
|
ExportedBy,
|
|
13001
13325
|
EyePop,
|
|
13326
|
+
EyePopDataWebSocketError,
|
|
13002
13327
|
EyepopLineDecoder,
|
|
13003
13328
|
ForwardOperatorType,
|
|
13004
13329
|
InferenceType,
|
|
@@ -13014,8 +13339,10 @@ export {
|
|
|
13014
13339
|
ModelTrainingStage,
|
|
13015
13340
|
ModelType,
|
|
13016
13341
|
MotionModel,
|
|
13342
|
+
PointCloud,
|
|
13017
13343
|
PopComponentType,
|
|
13018
13344
|
PredictionVersion,
|
|
13345
|
+
QUATERNION_TOLERANCE,
|
|
13019
13346
|
Semaphore,
|
|
13020
13347
|
Stream,
|
|
13021
13348
|
TaskMode,
|
|
@@ -13034,13 +13361,19 @@ export {
|
|
|
13034
13361
|
WorkflowPhase,
|
|
13035
13362
|
accessTokenCredential,
|
|
13036
13363
|
apiKeyCredential,
|
|
13364
|
+
cloudOfDepth,
|
|
13365
|
+
cloudOfObject,
|
|
13366
|
+
cloudsOfPrediction,
|
|
13037
13367
|
decodeDepthMap,
|
|
13368
|
+
decodePointCloud,
|
|
13038
13369
|
index_default as default,
|
|
13039
13370
|
encodeMultipartFormData,
|
|
13040
13371
|
getBaseUrl,
|
|
13041
13372
|
randomMultipartBoundary,
|
|
13042
13373
|
readableStreamFromString,
|
|
13043
|
-
resolveAuth
|
|
13374
|
+
resolveAuth,
|
|
13375
|
+
validateCamera,
|
|
13376
|
+
validatePop
|
|
13044
13377
|
};
|
|
13045
13378
|
/*! Bundled license information:
|
|
13046
13379
|
|