@jax-data-science/sci-image-visualizer 0.2.19 → 0.3.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.
Files changed (35) hide show
  1. package/esm2022/index.mjs +2 -9
  2. package/esm2022/lib/contracts/instance-segmenter.contract.mjs +10 -10
  3. package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +10 -9
  4. package/esm2022/lib/contracts/toolbar-tool.contract.mjs +64 -0
  5. package/esm2022/lib/toolbar/model-info.mjs +32 -0
  6. package/esm2022/lib/toolbar/toolbar.component.mjs +82 -55
  7. package/esm2022/lib/visualizer.component.mjs +131 -138
  8. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-iPNL2JyC.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs} +2 -2
  9. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-iPNL2JyC.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map} +1 -1
  10. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +710 -1565
  11. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  12. package/index.d.ts +1 -6
  13. package/lib/contracts/instance-segmenter.contract.d.ts +7 -3
  14. package/lib/contracts/semantic-segmenter.contract.d.ts +7 -2
  15. package/lib/contracts/toolbar-tool.contract.d.ts +190 -0
  16. package/lib/toolbar/model-info.d.ts +17 -0
  17. package/lib/toolbar/toolbar.component.d.ts +53 -30
  18. package/lib/visualizer.component.d.ts +49 -58
  19. package/package.json +2 -4
  20. package/src/lib/assets/cell-detection.svg +37 -0
  21. package/src/lib/assets/cells.svg +1 -1
  22. package/src/lib/assets/sam-box-prompt.svg +61 -0
  23. package/src/lib/assets/sam-point-prompt.svg +91 -0
  24. package/esm2022/lib/toolbar/segmentation/retinal-layer-tool.service.mjs +0 -197
  25. package/esm2022/lib/toolbar/segmentation/retinal-model-registry.mjs +0 -119
  26. package/esm2022/lib/toolbar/segmentation/retinal-segmenter.service.mjs +0 -172
  27. package/esm2022/lib/toolbar/segmentation/yolo-detect-tool.service.mjs +0 -208
  28. package/esm2022/lib/toolbar/segmentation/yolo-model-registry.mjs +0 -129
  29. package/esm2022/lib/toolbar/segmentation/yolo-segmenter.service.mjs +0 -172
  30. package/lib/toolbar/segmentation/retinal-layer-tool.service.d.ts +0 -49
  31. package/lib/toolbar/segmentation/retinal-model-registry.d.ts +0 -71
  32. package/lib/toolbar/segmentation/retinal-segmenter.service.d.ts +0 -60
  33. package/lib/toolbar/segmentation/yolo-detect-tool.service.d.ts +0 -51
  34. package/lib/toolbar/segmentation/yolo-model-registry.d.ts +0 -59
  35. package/lib/toolbar/segmentation/yolo-segmenter.service.d.ts +0 -50
@@ -1,172 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
- import { getOrtWasmBase } from './ort-runtime-config';
3
- import { getDefaultYoloModelId, getYoloModel } from './yolo-model-registry';
4
- import * as i0 from "@angular/core";
5
- /**
6
- * Default in-library {@link IInstanceSegmenter}, backed by yolo-segdetect-js
7
- * (YOLOv8-seg on WebGPU, falling back to WASM).
8
- *
9
- * `yolo-segdetect-js` is a regular dependency but is **lazy-imported** on first
10
- * use, so apps that never run YOLO pay nothing for it — or for onnxruntime-web —
11
- * in their initial bundle. This mirrors {@link CellposeSegmenterService}.
12
- *
13
- * The one structural difference from the Cellpose service: there are four YOLO
14
- * checkpoints, not one, so instances are cached **per model id** rather than as
15
- * a single singleton. Each entry owns its own worker and ORT session, so a host
16
- * that switches models pays a second load but keeps the first one warm. Nothing
17
- * evicts automatically — these are ~144 MB each, so a host that cycles through
18
- * all four should call {@link dispose} for the ones it is done with.
19
- */
20
- export class YoloSegmenterService {
21
- constructor() {
22
- this.instances = new Map();
23
- this.loading = new Map();
24
- }
25
- async segmentInstances(image, opts = {}, progress) {
26
- const modelId = opts.modelId ?? getDefaultYoloModelId();
27
- const def = this.requireModel(modelId);
28
- let announcedDownload = false;
29
- const seg = await this.getModel(modelId, (loaded, total) => {
30
- if (total)
31
- progress?.onProgress?.(loaded / total);
32
- progress?.onBytes?.(loaded, total);
33
- if (loaded > 0 && !announcedDownload) {
34
- announcedDownload = true;
35
- progress?.onStatus?.(`Downloading ${def.label} model…`);
36
- }
37
- }, (status) => progress?.onStatus?.(status));
38
- progress?.onStatus?.('Running inference…');
39
- const result = await seg.segment({ data: image.data, width: image.width, height: image.height }, {
40
- confThreshold: opts.confidence ?? def.defaults.confidence,
41
- iouThreshold: opts.iouThreshold ?? def.defaults.iouThreshold,
42
- threshold: opts.mergeThreshold ?? def.defaults.mergeThreshold,
43
- overlapX: opts.overlapX ?? def.defaults.overlapX,
44
- overlapY: opts.overlapY ?? def.defaults.overlapY,
45
- ...(opts.maskThreshold !== undefined ? { maskThreshold: opts.maskThreshold } : {}),
46
- ...(opts.maxDetections !== undefined ? { maxDetections: opts.maxDetections } : {}),
47
- ...(opts.classFilter ? { classFilter: opts.classFilter } : {}),
48
- ...(opts.withMasks !== undefined ? { withMasks: opts.withMasks } : {}),
49
- ...(opts.signal ? { signal: opts.signal } : {}),
50
- // Only worth tracing when there are masks to trace.
51
- tracePolygons: opts.withMasks !== false,
52
- traceOptions: {
53
- ...(opts.simplifyTolerance !== undefined ? { simplifyTolerance: opts.simplifyTolerance } : {}),
54
- ...(opts.minArea !== undefined ? { minArea: opts.minArea } : {}),
55
- },
56
- // The worker narrates its own phases — shader warm-up, per tile, merge,
57
- // tracing — because only it knows which one it is in. Deriving a second
58
- // status from tile counts here would just fight it.
59
- onStatus: (s) => progress?.onStatus?.(s),
60
- });
61
- // A detection without an outline is still a detection.
62
- //
63
- // These used to be dropped, which quietly emptied the entire result in
64
- // detection mode: no masks means no polygons, so every object was
65
- // discarded and the caller saw nothing at all. The server does not work
66
- // that way either — it always writes a box feature and only *adds*
67
- // segmentation outlines when asked. Callers get the box and decide what to
68
- // do with it.
69
- const detections = result.detections.map((d) => ({
70
- polygons: (d.polygons ?? []).map((p) => ({
71
- exterior: p.exterior.map(([x, y]) => [x, y]),
72
- holes: p.holes.map((h) => h.map(([x, y]) => [x, y])),
73
- })),
74
- box: [d.box[0], d.box[1], d.box[2], d.box[3]],
75
- score: d.score,
76
- classId: d.classId,
77
- className: d.className ?? `class${d.classId}`,
78
- }));
79
- return {
80
- detections,
81
- width: result.width,
82
- height: result.height,
83
- classNames: result.classNames,
84
- };
85
- }
86
- /**
87
- * The shared segmenter for a model id, created on first use and reused
88
- * thereafter (deduped across concurrent callers). Exposed so a host's
89
- * processing pipeline can drive richer `segment()` options on the same loaded
90
- * session instead of loading the model twice.
91
- */
92
- getModel(modelId = getDefaultYoloModelId(), onProgress, onStatus) {
93
- const existing = this.instances.get(modelId);
94
- if (existing)
95
- return Promise.resolve(existing);
96
- const inFlight = this.loading.get(modelId);
97
- if (inFlight)
98
- return inFlight;
99
- const def = this.requireModel(modelId);
100
- const load = (async () => {
101
- // Lazy: keep yolo-segdetect-js + its ORT runtime out of the initial bundle.
102
- const { YoloSegmenter, configureOrt } = await import('yolo-segdetect-js');
103
- configureOrt({ wasmPaths: getOrtWasmBase() });
104
- const seg = await YoloSegmenter.fromPretrained(def.modelUrl, {
105
- preload: true,
106
- ...(def.metaUrl ? { metaUrl: def.metaUrl } : {}),
107
- onProgress: ({ loaded, total }) => onProgress?.(loaded, total),
108
- onStatus: (s) => onStatus?.(s),
109
- });
110
- this.instances.set(modelId, seg);
111
- return seg;
112
- })();
113
- this.loading.set(modelId, load);
114
- // A failed load must not poison the cache — the next caller should retry
115
- // rather than inherit the rejected promise forever.
116
- load.catch(() => this.loading.delete(modelId));
117
- return load;
118
- }
119
- /** Whether a model is already loaded (warm). */
120
- isLoaded(modelId = getDefaultYoloModelId()) {
121
- return this.instances.has(modelId);
122
- }
123
- /**
124
- * Release one model, or all of them. Safe to call on a model never loaded.
125
- *
126
- * In-flight loads are torn down too, not just warm ones. Simply dropping the
127
- * pending promise and returning would let the load finish afterwards,
128
- * re-populate {@link instances}, and strand a live worker and ORT session that
129
- * nothing owns — disposal would have reported success while leaking. So a load
130
- * that is still running is awaited and then released.
131
- */
132
- async dispose(modelId) {
133
- const ids = modelId ? [modelId] : [...new Set([...this.instances.keys(), ...this.loading.keys()])];
134
- await Promise.all(ids.map((id) => this.disposeOne(id)));
135
- }
136
- async disposeOne(id) {
137
- const pending = this.loading.get(id);
138
- const loaded = this.instances.get(id);
139
- this.instances.delete(id);
140
- this.loading.delete(id);
141
- if (loaded)
142
- await loaded.dispose();
143
- if (!pending)
144
- return;
145
- // A pending load assigns instances[id] before its promise resolves, so by
146
- // the time this await returns the entry is back. Clear it only if it still
147
- // points at what *this* load produced — a newer load started in the
148
- // meantime owns its own entry and must not be evicted.
149
- const seg = await pending.catch(() => null);
150
- if (!seg || seg === loaded)
151
- return;
152
- if (this.instances.get(id) === seg)
153
- this.instances.delete(id);
154
- await seg.dispose();
155
- }
156
- requireModel(modelId) {
157
- const def = getYoloModel(modelId);
158
- if (!def)
159
- throw new Error(`Unknown YOLO model id "${modelId}".`);
160
- if (!def.modelUrl.trim()) {
161
- throw new Error(`YOLO model "${modelId}" is not configured — its modelUrl is empty.`);
162
- }
163
- return def;
164
- }
165
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: YoloSegmenterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
166
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: YoloSegmenterService, providedIn: 'root' }); }
167
- }
168
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: YoloSegmenterService, decorators: [{
169
- type: Injectable,
170
- args: [{ providedIn: 'root' }]
171
- }] });
172
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieW9sby1zZWdtZW50ZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvdG9vbGJhci9zZWdtZW50YXRpb24veW9sby1zZWdtZW50ZXIuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsWUFBWSxFQUFxQixNQUFNLHVCQUF1QixDQUFDOztBQVcvRjs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUVILE1BQU0sT0FBTyxvQkFBb0I7SUFEakM7UUFFbUIsY0FBUyxHQUFHLElBQUksR0FBRyxFQUF5QixDQUFDO1FBQzdDLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBa0MsQ0FBQztLQW1LdEU7SUFqS0MsS0FBSyxDQUFDLGdCQUFnQixDQUNwQixLQUFpRSxFQUNqRSxPQUErQixFQUFFLEVBQ2pDLFFBQWtDO1FBRWxDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLElBQUkscUJBQXFCLEVBQUUsQ0FBQztRQUN4RCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRXZDLElBQUksaUJBQWlCLEdBQUcsS0FBSyxDQUFDO1FBQzlCLE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FDN0IsT0FBTyxFQUNQLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFO1lBQ2hCLElBQUksS0FBSztnQkFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxDQUFDO1lBQ2xELFFBQVEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDbkMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxJQUFJ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QztRQUNELE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQzs4R0FwS1Usb0JBQW9CO2tIQUFwQixvQkFBb0IsY0FEUCxNQUFNOzsyRkFDbkIsb0JBQW9CO2tCQURoQyxVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IGdldE9ydFdhc21CYXNlIH0gZnJvbSAnLi9vcnQtcnVudGltZS1jb25maWcnO1xuaW1wb3J0IHsgZ2V0RGVmYXVsdFlvbG9Nb2RlbElkLCBnZXRZb2xvTW9kZWwsIHR5cGUgWW9sb01vZGVsRGVmIH0gZnJvbSAnLi95b2xvLW1vZGVsLXJlZ2lzdHJ5JztcblxuaW1wb3J0IHR5cGUge1xuICBJSW5zdGFuY2VTZWdtZW50ZXIsXG4gIEluc3RhbmNlRGV0ZWN0aW9uLFxuICBJbnN0YW5jZVNlZ21lbnRhdGlvbixcbiAgSW5zdGFuY2VTZWdtZW50T3B0aW9ucyxcbiAgSW5zdGFuY2VTZWdtZW50UHJvZ3Jlc3MsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9pbnN0YW5jZS1zZWdtZW50ZXIuY29udHJhY3QnO1xuaW1wb3J0IHR5cGUgeyBZb2xvU2VnbWVudGVyIH0gZnJvbSAneW9sby1zZWdkZXRlY3QtanMnO1xuXG4vKipcbiAqIERlZmF1bHQgaW4tbGlicmFyeSB7QGxpbmsgSUluc3RhbmNlU2VnbWVudGVyfSwgYmFja2VkIGJ5IHlvbG8tc2VnZGV0ZWN0LWpzXG4gKiAoWU9MT3Y4LXNlZyBvbiBXZWJHUFUsIGZhbGxpbmcgYmFjayB0byBXQVNNKS5cbiAqXG4gKiBgeW9sby1zZWdkZXRlY3QtanNgIGlzIGEgcmVndWxhciBkZXBlbmRlbmN5IGJ1dCBpcyAqKmxhenktaW1wb3J0ZWQqKiBvbiBmaXJzdFxuICogdXNlLCBzbyBhcHBzIHRoYXQgbmV2ZXIgcnVuIFlPTE8gcGF5IG5vdGhpbmcgZm9yIGl0IOKAlCBvciBmb3Igb25ueHJ1bnRpbWUtd2ViIOKAlFxuICogaW4gdGhlaXIgaW5pdGlhbCBidW5kbGUuIFRoaXMgbWlycm9ycyB7QGxpbmsgQ2VsbHBvc2VTZWdtZW50ZXJTZXJ2aWNlfS5cbiAqXG4gKiBUaGUgb25lIHN0cnVjdHVyYWwgZGlmZmVyZW5jZSBmcm9tIHRoZSBDZWxscG9zZSBzZXJ2aWNlOiB0aGVyZSBhcmUgZm91ciBZT0xPXG4gKiBjaGVja3BvaW50cywgbm90IG9uZSwgc28gaW5zdGFuY2VzIGFyZSBjYWNoZWQgKipwZXIgbW9kZWwgaWQqKiByYXRoZXIgdGhhbiBhc1xuICogYSBzaW5nbGUgc2luZ2xldG9uLiBFYWNoIGVudHJ5IG93bnMgaXRzIG93biB3b3JrZXIgYW5kIE9SVCBzZXNzaW9uLCBzbyBhIGhvc3RcbiAqIHRoYXQgc3dpdGNoZXMgbW9kZWxzIHBheXMgYSBzZWNvbmQgbG9hZCBidXQga2VlcHMgdGhlIGZpcnN0IG9uZSB3YXJtLiBOb3RoaW5nXG4gKiBldmljdHMgYXV0b21hdGljYWxseSDigJQgdGhlc2UgYXJlIH4xNDQgTUIgZWFjaCwgc28gYSBob3N0IHRoYXQgY3ljbGVzIHRocm91Z2hcbiAqIGFsbCBmb3VyIHNob3VsZCBjYWxsIHtAbGluayBkaXNwb3NlfSBmb3IgdGhlIG9uZXMgaXQgaXMgZG9uZSB3aXRoLlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIFlvbG9TZWdtZW50ZXJTZXJ2aWNlIGltcGxlbWVudHMgSUluc3RhbmNlU2VnbWVudGVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBpbnN0YW5jZXMgPSBuZXcgTWFwPHN0cmluZywgWW9sb1NlZ21lbnRlcj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2FkaW5nID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8WW9sb1NlZ21lbnRlcj4+KCk7XG5cbiAgYXN5bmMgc2VnbWVudEluc3RhbmNlcyhcbiAgICBpbWFnZTogeyBkYXRhOiBVaW50OENsYW1wZWRBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSxcbiAgICBvcHRzOiBJbnN0YW5jZVNlZ21lbnRPcHRpb25zID0ge30sXG4gICAgcHJvZ3Jlc3M/OiBJbnN0YW5jZVNlZ21lbnRQcm9ncmVzcyxcbiAgKTogUHJvbWlzZTxJbnN0YW5jZVNlZ21lbnRhdGlvbj4ge1xuICAgIGNvbnN0IG1vZGVsSWQgPSBvcHRzLm1vZGVsSWQgPz8gZ2V0RGVmYXVsdFlvbG9Nb2RlbElkKCk7XG4gICAgY29uc3QgZGVmID0gdGhpcy5yZXF1aXJlTW9kZWwobW9kZWxJZCk7XG5cbiAgICBsZXQgYW5ub3VuY2VkRG93bmxvYWQgPSBmYWxzZTtcbiAgICBjb25zdCBzZWcgPSBhd2FpdCB0aGlzLmdldE1vZGVsKFxuICAgICAgbW9kZWxJZCxcbiAgICAgIChsb2FkZWQsIHRvdGFsKSA9PiB7XG4gICAgICAgIGlmICh0b3RhbCkgcHJvZ3Jlc3M/Lm9uUHJvZ3Jlc3M/Lihsb2FkZWQgLyB0b3RhbCk7XG4gICAgICAgIHByb2dyZXNzPy5vbkJ5dGVzPy4obG9hZGVkLCB0b3RhbCk7XG4gICAgICAgIGlmIChsb2FkZWQgPiAwICYmICFhbm5vdW5jZWREb3dubG9hZCkge1xuICAgICAgICAgIGFubm91bmNlZERvd25sb2FkID0gdHJ1ZTtcbiAgICAgICAgICBwcm9ncmVzcz8ub25TdGF0dXM/LihgRG93bmxvYWRpbmcgJHtkZWYubGFiZWx9IG1vZGVs4oCmYCk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICAoc3RhdHVzKSA9PiBwcm9ncmVzcz8ub25TdGF0dXM/LihzdGF0dXMpLFxuICAgICk7XG5cbiAgICBwcm9ncmVzcz8ub25TdGF0dXM/LignUnVubmluZyBpbmZlcmVuY2XigKYnKTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZWcuc2VnbWVudChcbiAgICAgIHsgZGF0YTogaW1hZ2UuZGF0YSwgd2lkdGg6IGltYWdlLndpZHRoLCBoZWlnaHQ6IGltYWdlLmhlaWdodCB9LFxuICAgICAge1xuICAgICAgICBjb25mVGhyZXNob2xkOiBvcHRzLmNvbmZpZGVuY2UgPz8gZGVmLmRlZmF1bHRzLmNvbmZpZGVuY2UsXG4gICAgICAgIGlvdVRocmVzaG9sZDogb3B0cy5pb3VUaHJlc2hvbGQgPz8gZGVmLmRlZmF1bHRzLmlvdVRocmVzaG9sZCxcbiAgICAgICAgdGhyZXNob2xkOiBvcHRzLm1lcmdlVGhyZXNob2xkID8/IGRlZi5kZWZhdWx0cy5tZXJnZVRocmVzaG9sZCxcbiAgICAgICAgb3ZlcmxhcFg6IG9wdHMub3ZlcmxhcFggPz8gZGVmLmRlZmF1bHRzLm92ZXJsYXBYLFxuICAgICAgICBvdmVybGFwWTogb3B0cy5vdmVybGFwWSA/PyBkZWYuZGVmYXVsdHMub3ZlcmxhcFksXG4gICAgICAgIC4uLihvcHRzLm1hc2tUaHJlc2hvbGQgIT09IHVuZGVmaW5lZCA/IHsgbWFza1RocmVzaG9sZDogb3B0cy5tYXNrVGhyZXNob2xkIH0gOiB7fSksXG4gICAgICAgIC4uLihvcHRzLm1heERldGVjdGlvbnMgIT09IHVuZGVmaW5lZCA/IHsgbWF4RGV0ZWN0aW9uczogb3B0cy5tYXhEZXRlY3Rpb25zIH0gOiB7fSksXG4gICAgICAgIC4uLihvcHRzLmNsYXNzRmlsdGVyID8geyBjbGFzc0ZpbHRlcjogb3B0cy5jbGFzc0ZpbHRlciB9IDoge30pLFxuICAgICAgICAuLi4ob3B0cy53aXRoTWFza3MgIT09IHVuZGVmaW5lZCA/IHsgd2l0aE1hc2tzOiBvcHRzLndpdGhNYXNrcyB9IDoge30pLFxuICAgICAgICAuLi4ob3B0cy5zaWduYWwgPyB7IHNpZ25hbDogb3B0cy5zaWduYWwgfSA6IHt9KSxcbiAgICAgICAgLy8gT25seSB3b3J0aCB0cmFjaW5nIHdoZW4gdGhlcmUgYXJlIG1hc2tzIHRvIHRyYWNlLlxuICAgICAgICB0cmFjZVBvbHlnb25zOiBvcHRzLndpdGhNYXNrcyAhPT0gZmFsc2UsXG4gICAgICAgIHRyYWNlT3B0aW9uczoge1xuICAgICAgICAgIC4uLihvcHRzLnNpbXBsaWZ5VG9sZXJhbmNlICE9PSB1bmRlZmluZWQgPyB7IHNpbXBsaWZ5VG9sZXJhbmNlOiBvcHRzLnNpbXBsaWZ5VG9sZXJhbmNlIH0gOiB7fSksXG4gICAgICAgICAgLi4uKG9wdHMubWluQXJlYSAhPT0gdW5kZWZpbmVkID8geyBtaW5BcmVhOiBvcHRzLm1pbkFyZWEgfSA6IHt9KSxcbiAgICAgICAgfSxcbiAgICAgICAgLy8gVGhlIHdvcmtlciBuYXJyYXRlcyBpdHMgb3duIHBoYXNlcyDigJQgc2hhZGVyIHdhcm0tdXAsIHBlciB0aWxlLCBtZXJnZSxcbiAgICAgICAgLy8gdHJhY2luZyDigJQgYmVjYXVzZSBvbmx5IGl0IGtub3dzIHdoaWNoIG9uZSBpdCBpcyBpbi4gRGVyaXZpbmcgYSBzZWNvbmRcbiAgICAgICAgLy8gc3RhdHVzIGZyb20gdGlsZSBjb3VudHMgaGVyZSB3b3VsZCBqdXN0IGZpZ2h0IGl0LlxuICAgICAgICBvblN0YXR1czogKHMpID0+IHByb2dyZXNzPy5vblN0YXR1cz8uKHMpLFxuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gQSBkZXRlY3Rpb24gd2l0aG91dCBhbiBvdXRsaW5lIGlzIHN0aWxsIGEgZGV0ZWN0aW9uLlxuICAgIC8vXG4gICAgLy8gVGhlc2UgdXNlZCB0byBiZSBkcm9wcGVkLCB3aGljaCBxdWlldGx5IGVtcHRpZWQgdGhlIGVudGlyZSByZXN1bHQgaW5cbiAgICAvLyBkZXRlY3Rpb24gbW9kZTogbm8gbWFza3MgbWVhbnMgbm8gcG9seWdvbnMsIHNvIGV2ZXJ5IG9iamVjdCB3YXNcbiAgICAvLyBkaXNjYXJkZWQgYW5kIHRoZSBjYWxsZXIgc2F3IG5vdGhpbmcgYXQgYWxsLiBUaGUgc2VydmVyIGRvZXMgbm90IHdvcmtcbiAgICAvLyB0aGF0IHdheSBlaXRoZXIg4oCUIGl0IGFsd2F5cyB3cml0ZXMgYSBib3ggZmVhdHVyZSBhbmQgb25seSAqYWRkcypcbiAgICAvLyBzZWdtZW50YXRpb24gb3V0bGluZXMgd2hlbiBhc2tlZC4gQ2FsbGVycyBnZXQgdGhlIGJveCBhbmQgZGVjaWRlIHdoYXQgdG9cbiAgICAvLyBkbyB3aXRoIGl0LlxuICAgIGNvbnN0IGRldGVjdGlvbnM6IEluc3RhbmNlRGV0ZWN0aW9uW10gPSByZXN1bHQuZGV0ZWN0aW9ucy5tYXAoKGQpID0+ICh7XG4gICAgICBwb2x5Z29uczogKGQucG9seWdvbnMgPz8gW10pLm1hcCgocCkgPT4gKHtcbiAgICAgICAgZXh0ZXJpb3I6IHAuZXh0ZXJpb3IubWFwKChbeCwgeV0pOiBbbnVtYmVyLCBudW1iZXJdID0+IFt4LCB5XSksXG4gICAgICAgIGhvbGVzOiBwLmhvbGVzLm1hcCgoaCkgPT4gaC5tYXAoKFt4LCB5XSk6IFtudW1iZXIsIG51bWJlcl0gPT4gW3gsIHldKSksXG4gICAgICB9KSksXG4gICAgICBib3g6IFtkLmJveFswXSwgZC5ib3hbMV0sIGQuYm94WzJdLCBkLmJveFszXV0gYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgICBzY29yZTogZC5zY29yZSxcbiAgICAgIGNsYXNzSWQ6IGQuY2xhc3NJZCxcbiAgICAgIGNsYXNzTmFtZTogZC5jbGFzc05hbWUgPz8gYGNsYXNzJHtkLmNsYXNzSWR9YCxcbiAgICB9KSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGV0ZWN0aW9ucyxcbiAgICAgIHdpZHRoOiByZXN1bHQud2lkdGgsXG4gICAgICBoZWlnaHQ6IHJlc3VsdC5oZWlnaHQsXG4gICAgICBjbGFzc05hbWVzOiByZXN1bHQuY2xhc3NOYW1lcyxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBzaGFyZWQgc2VnbWVudGVyIGZvciBhIG1vZGVsIGlkLCBjcmVhdGVkIG9uIGZpcnN0IHVzZSBhbmQgcmV1c2VkXG4gICAqIHRoZXJlYWZ0ZXIgKGRlZHVwZWQgYWNyb3NzIGNvbmN1cnJlbnQgY2FsbGVycykuIEV4cG9zZWQgc28gYSBob3N0J3NcbiAgICogcHJvY2Vzc2luZyBwaXBlbGluZSBjYW4gZHJpdmUgcmljaGVyIGBzZWdtZW50KClgIG9wdGlvbnMgb24gdGhlIHNhbWUgbG9hZGVkXG4gICAqIHNlc3Npb24gaW5zdGVhZCBvZiBsb2FkaW5nIHRoZSBtb2RlbCB0d2ljZS5cbiAgICovXG4gIGdldE1vZGVsKFxuICAgIG1vZGVsSWQ6IHN0cmluZyA9IGdldERlZmF1bHRZb2xvTW9kZWxJZCgpLFxuICAgIG9uUHJvZ3Jlc3M/OiAobG9hZGVkOiBudW1iZXIsIHRvdGFsOiBudW1iZXIgfCBudWxsKSA9PiB2b2lkLFxuICAgIG9uU3RhdHVzPzogKHN0YXR1czogc3RyaW5nKSA9PiB2b2lkLFxuICApOiBQcm9taXNlPFlvbG9TZWdtZW50ZXI+IHtcbiAgICBjb25zdCBleGlzdGluZyA9IHRoaXMuaW5zdGFuY2VzLmdldChtb2RlbElkKTtcbiAgICBpZiAoZXhpc3RpbmcpIHJldHVybiBQcm9taXNlLnJlc29sdmUoZXhpc3RpbmcpO1xuXG4gICAgY29uc3QgaW5GbGlnaHQgPSB0aGlzLmxvYWRpbmcuZ2V0KG1vZGVsSWQpO1xuICAgIGlmIChpbkZsaWdodCkgcmV0dXJuIGluRmxpZ2h0O1xuXG4gICAgY29uc3QgZGVmID0gdGhpcy5yZXF1aXJlTW9kZWwobW9kZWxJZCk7XG4gICAgY29uc3QgbG9hZCA9IChhc3luYyAoKSA9PiB7XG4gICAgICAvLyBMYXp5OiBrZWVwIHlvbG8tc2VnZGV0ZWN0LWpzICsgaXRzIE9SVCBydW50aW1lIG91dCBvZiB0aGUgaW5pdGlhbCBidW5kbGUuXG4gICAgICBjb25zdCB7IFlvbG9TZWdtZW50ZXIsIGNvbmZpZ3VyZU9ydCB9ID0gYXdhaXQgaW1wb3J0KCd5b2xvLXNlZ2RldGVjdC1qcycpO1xuICAgICAgY29uZmlndXJlT3J0KHsgd2FzbVBhdGhzOiBnZXRPcnRXYXNtQmFzZSgpIH0pO1xuICAgICAgY29uc3Qgc2VnID0gYXdhaXQgWW9sb1NlZ21lbnRlci5mcm9tUHJldHJhaW5lZChkZWYubW9kZWxVcmwsIHtcbiAgICAgICAgcHJlbG9hZDogdHJ1ZSxcbiAgICAgICAgLi4uKGRlZi5tZXRhVXJsID8geyBtZXRhVXJsOiBkZWYubWV0YVVybCB9IDoge30pLFxuICAgICAgICBvblByb2dyZXNzOiAoeyBsb2FkZWQsIHRvdGFsIH0pID0+IG9uUHJvZ3Jlc3M/Lihsb2FkZWQsIHRvdGFsKSxcbiAgICAgICAgb25TdGF0dXM6IChzKSA9PiBvblN0YXR1cz8uKHMpLFxuICAgICAgfSk7XG4gICAgICB0aGlzLmluc3RhbmNlcy5zZXQobW9kZWxJZCwgc2VnKTtcbiAgICAgIHJldHVybiBzZWc7XG4gICAgfSkoKTtcblxuICAgIHRoaXMubG9hZGluZy5zZXQobW9kZWxJZCwgbG9hZCk7XG4gICAgLy8gQSBmYWlsZWQgbG9hZCBtdXN0IG5vdCBwb2lzb24gdGhlIGNhY2hlIOKAlCB0aGUgbmV4dCBjYWxsZXIgc2hvdWxkIHJldHJ5XG4gICAgLy8gcmF0aGVyIHRoYW4gaW5oZXJpdCB0aGUgcmVqZWN0ZWQgcHJvbWlzZSBmb3JldmVyLlxuICAgIGxvYWQuY2F0Y2goKCkgPT4gdGhpcy5sb2FkaW5nLmRlbGV0ZShtb2RlbElkKSk7XG4gICAgcmV0dXJuIGxvYWQ7XG4gIH1cblxuICAvKiogV2hldGhlciBhIG1vZGVsIGlzIGFscmVhZHkgbG9hZGVkICh3YXJtKS4gKi9cbiAgaXNMb2FkZWQobW9kZWxJZDogc3RyaW5nID0gZ2V0RGVmYXVsdFlvbG9Nb2RlbElkKCkpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pbnN0YW5jZXMuaGFzKG1vZGVsSWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2Ugb25lIG1vZGVsLCBvciBhbGwgb2YgdGhlbS4gU2FmZSB0byBjYWxsIG9uIGEgbW9kZWwgbmV2ZXIgbG9hZGVkLlxuICAgKlxuICAgKiBJbi1mbGlnaHQgbG9hZHMgYXJlIHRvcm4gZG93biB0b28sIG5vdCBqdXN0IHdhcm0gb25lcy4gU2ltcGx5IGRyb3BwaW5nIHRoZVxuICAgKiBwZW5kaW5nIHByb21pc2UgYW5kIHJldHVybmluZyB3b3VsZCBsZXQgdGhlIGxvYWQgZmluaXNoIGFmdGVyd2FyZHMsXG4gICAqIHJlLXBvcHVsYXRlIHtAbGluayBpbnN0YW5jZXN9LCBhbmQgc3RyYW5kIGEgbGl2ZSB3b3JrZXIgYW5kIE9SVCBzZXNzaW9uIHRoYXRcbiAgICogbm90aGluZyBvd25zIOKAlCBkaXNwb3NhbCB3b3VsZCBoYXZlIHJlcG9ydGVkIHN1Y2Nlc3Mgd2hpbGUgbGVha2luZy4gU28gYSBsb2FkXG4gICAqIHRoYXQgaXMgc3RpbGwgcnVubmluZyBpcyBhd2FpdGVkIGFuZCB0aGVuIHJlbGVhc2VkLlxuICAgKi9cbiAgYXN5bmMgZGlzcG9zZShtb2RlbElkPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgaWRzID0gbW9kZWxJZCA/IFttb2RlbElkXSA6IFsuLi5uZXcgU2V0KFsuLi50aGlzLmluc3RhbmNlcy5rZXlzKCksIC4uLnRoaXMubG9hZGluZy5rZXlzKCldKV07XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoaWRzLm1hcCgoaWQpID0+IHRoaXMuZGlzcG9zZU9uZShpZCkpKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZGlzcG9zZU9uZShpZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcGVuZGluZyA9IHRoaXMubG9hZGluZy5nZXQoaWQpO1xuICAgIGNvbnN0IGxvYWRlZCA9IHRoaXMuaW5zdGFuY2VzLmdldChpZCk7XG4gICAgdGhpcy5pbnN0YW5jZXMuZGVsZXRlKGlkKTtcbiAgICB0aGlzLmxvYWRpbmcuZGVsZXRlKGlkKTtcblxuICAgIGlmIChsb2FkZWQpIGF3YWl0IGxvYWRlZC5kaXNwb3NlKCk7XG4gICAgaWYgKCFwZW5kaW5nKSByZXR1cm47XG5cbiAgICAvLyBBIHBlbmRpbmcgbG9hZCBhc3NpZ25zIGluc3RhbmNlc1tpZF0gYmVmb3JlIGl0cyBwcm9taXNlIHJlc29sdmVzLCBzbyBieVxuICAgIC8vIHRoZSB0aW1lIHRoaXMgYXdhaXQgcmV0dXJucyB0aGUgZW50cnkgaXMgYmFjay4gQ2xlYXIgaXQgb25seSBpZiBpdCBzdGlsbFxuICAgIC8vIHBvaW50cyBhdCB3aGF0ICp0aGlzKiBsb2FkIHByb2R1Y2VkIOKAlCBhIG5ld2VyIGxvYWQgc3RhcnRlZCBpbiB0aGVcbiAgICAvLyBtZWFudGltZSBvd25zIGl0cyBvd24gZW50cnkgYW5kIG11c3Qgbm90IGJlIGV2aWN0ZWQuXG4gICAgY29uc3Qgc2VnID0gYXdhaXQgcGVuZGluZy5jYXRjaCgoKSA9PiBudWxsKTtcbiAgICBpZiAoIXNlZyB8fCBzZWcgPT09IGxvYWRlZCkgcmV0dXJuO1xuICAgIGlmICh0aGlzLmluc3RhbmNlcy5nZXQoaWQpID09PSBzZWcpIHRoaXMuaW5zdGFuY2VzLmRlbGV0ZShpZCk7XG4gICAgYXdhaXQgc2VnLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVxdWlyZU1vZGVsKG1vZGVsSWQ6IHN0cmluZyk6IFlvbG9Nb2RlbERlZiB7XG4gICAgY29uc3QgZGVmID0gZ2V0WW9sb01vZGVsKG1vZGVsSWQpO1xuICAgIGlmICghZGVmKSB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gWU9MTyBtb2RlbCBpZCBcIiR7bW9kZWxJZH1cIi5gKTtcbiAgICBpZiAoIWRlZi5tb2RlbFVybC50cmltKCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgWU9MTyBtb2RlbCBcIiR7bW9kZWxJZH1cIiBpcyBub3QgY29uZmlndXJlZCDigJQgaXRzIG1vZGVsVXJsIGlzIGVtcHR5LmApO1xuICAgIH1cbiAgICByZXR1cm4gZGVmO1xuICB9XG59XG4iXX0=
@@ -1,49 +0,0 @@
1
- import { BehaviorSubject } from 'rxjs';
2
- import { type TileAccessPort } from '../../contracts/ports/tile-access.port';
3
- import type { IVisualizer } from '../../contracts/visualizer.contract';
4
- import type { ISemanticSegmenter, SemanticSegmentOptions } from '../../contracts/semantic-segmenter.contract';
5
- import * as i0 from "@angular/core";
6
- /** Marks regions this tool produced, so a re-run can replace only its own. */
7
- export declare const RETINAL_REGION_SOURCE = "retinal-layer";
8
- /**
9
- * Retinal-layer segmentation over the current view.
10
- *
11
- * Sibling of {@link YoloDetectToolService}, and structured the same way: run on
12
- * whatever is on screen, map the results onto full-image coordinates through
13
- * the viewer's source rect, and commit them as regions. The difference is what
14
- * comes back — layers that tile the field rather than discrete objects, so
15
- * every region is one connected component of one layer and there is no
16
- * per-object confidence to filter on.
17
- *
18
- * Exposes the same `status$` / `busy$` / `progress$` surface as the cellpose and
19
- * YOLO tools so the host's shared segmentation toast drives it unchanged.
20
- */
21
- export declare class RetinalLayerToolService {
22
- /** Server-side crop, so segmentation can run at the model's scale rather
23
- * than the viewer's. Optional: without it the tool falls back to the
24
- * displayed pixels, which is correct but scale-dependent. */
25
- private tileAccess;
26
- readonly status$: BehaviorSubject<string>;
27
- readonly busy$: BehaviorSubject<boolean>;
28
- readonly progress$: BehaviorSubject<number>;
29
- constructor(
30
- /** Server-side crop, so segmentation can run at the model's scale rather
31
- * than the viewer's. Optional: without it the tool falls back to the
32
- * displayed pixels, which is correct but scale-dependent. */
33
- tileAccess?: TileAccessPort | null);
34
- /**
35
- * Segment the current view and commit the layers as regions.
36
- *
37
- * @returns the number of regions added.
38
- */
39
- segmentInView(viz: IVisualizer, segmenter: ISemanticSegmenter, opts?: SemanticSegmentOptions): Promise<number>;
40
- /**
41
- * Re-crop `rect` at `1/target` of full resolution. Returns null on any
42
- * failure, so the caller falls back to the displayed pixels — a coarser crop
43
- * beats a failed run.
44
- */
45
- private fetchCropAtScale;
46
- private makeRegion;
47
- static ɵfac: i0.ɵɵFactoryDeclaration<RetinalLayerToolService, [{ optional: true; }]>;
48
- static ɵprov: i0.ɵɵInjectableDeclaration<RetinalLayerToolService>;
49
- }
@@ -1,71 +0,0 @@
1
- /**
2
- * Registry of retinal-layer semantic-segmentation models.
3
- *
4
- * Mirrors {@link YOLO_MODELS}: public HF-hosted ONNX by default, repointable at
5
- * private hosting via {@link setRetinalModelUrls}.
6
- *
7
- * ONLY ONE CHECKPOINT IS ENABLED, AND THAT IS DELIBERATE
8
- * The server offers four. Measured against the ground-truth masks shipped
9
- * alongside them, only VNet reproduces them:
10
- *
11
- * | checkpoint | mIoU |
12
- * |---|---|
13
- * | **VNet 2D** | **0.9061** |
14
- * | ResUNet-a 2D (base) | 0.3065 |
15
- * | ResUNet-a 2D 20x | 0.2869 |
16
- * | ResUNet-a 2D 40x | 0.2718 |
17
- *
18
- * The ResUNet-a numbers are not quantization damage — their ONNX exports match
19
- * their own Keras originals exactly. They disagree with the *masks*, folding
20
- * ground-truth class 1 into their class 2 and class 3 into background, and no
21
- * relabelling rescues them (the best of all 24 class permutations reaches only
22
- * 0.36). Either those masks belong to a different model generation or the class
23
- * definitions moved — an open question for whoever trained them.
24
- *
25
- * So their weights stay unpublished (their Hub repos are private) and their
26
- * entries here carry an empty `modelUrl`, which {@link isRetinalModelReady}
27
- * reports as not-ready. They are listed rather than deleted so a host shows
28
- * them as unavailable-in-browser instead of pretending the server's choice does
29
- * not exist — and so enabling one later is a URL, not a code change.
30
- *
31
- * Export tooling: the sibling `browser-onnx-tools` project.
32
- */
33
- export interface RetinalModelDef {
34
- id: string;
35
- label: string;
36
- /** URL of the `.onnx` weights. **Empty disables the model.** */
37
- modelUrl: string;
38
- /**
39
- * URL of the `model.json` sidecar. Omit to use `model.json` alongside the
40
- * weights, which is how every published repo is laid out.
41
- */
42
- metaUrl?: string;
43
- /** Patch size the checkpoint was trained at, for callers that size reads to it. */
44
- patchSize: number;
45
- /** Download size of the `fp16w` weights, MB — these are large enough to warn about. */
46
- sizeMb: number;
47
- /** Measured mIoU against the bucket's ground-truth masks. */
48
- miou: number;
49
- /** Set when the model is knowingly disabled; shown to the user. */
50
- unavailableReason?: string;
51
- }
52
- export declare const RETINAL_MODELS: RetinalModelDef[];
53
- export declare const DEFAULT_RETINAL_MODEL_ID = "vnet-2d-retinal";
54
- /** Look up a model definition by id. Returns undefined for an unknown id. */
55
- export declare function getRetinalModel(id: string): RetinalModelDef | undefined;
56
- /** Whether a model has usable weights configured. */
57
- export declare function isRetinalModelReady(id: string): boolean;
58
- /** Only the models a browser can actually run. */
59
- export declare function readyRetinalModels(): RetinalModelDef[];
60
- /**
61
- * Repoint a model at different hosting (a GCS bucket, a same-origin proxy), or
62
- * enable one of the disabled entries. Call once at app init, before first use.
63
- * An empty `modelUrl` disables the model.
64
- */
65
- export declare function setRetinalModelUrls(id: string, urls: {
66
- modelUrl?: string;
67
- metaUrl?: string;
68
- }): void;
69
- /** Change which model runs when a caller does not name one. */
70
- export declare function setDefaultRetinalModel(id: string): void;
71
- export declare function getDefaultRetinalModelId(): string;
@@ -1,60 +0,0 @@
1
- import type { ISemanticSegmenter, SemanticSegmentation, SemanticSegmentOptions, SemanticSegmentProgress } from '../../contracts/semantic-segmenter.contract';
2
- import type { RetinalSegmenter } from 'jax-ai-js';
3
- import * as i0 from "@angular/core";
4
- /**
5
- * Default in-library {@link ISemanticSegmenter}, backed by jax-ai-js.
6
- *
7
- * `jax-ai-js` is a regular dependency but is **lazy-imported** on first use, so
8
- * apps that never segment retinal layers pay nothing for it — or for
9
- * onnxruntime-web — in their initial bundle. Mirrors
10
- * {@link YoloSegmenterService}, including its per-model-id cache.
11
- *
12
- * WEBGPU ONLY, AND A HOST MUST BE READY FOR THAT
13
- * Unlike the YOLO and Cellpose services, there is no WASM fallback to degrade
14
- * to: ORT-Web's WASM EP dies with `std::bad_alloc` on these graphs at 512², at
15
- * every precision. jax-ai-js throws `WebGpuRequiredError` rather than trying,
16
- * and it is re-thrown here unchanged so a host can show a real
17
- * unsupported-browser message instead of a generic failure.
18
- */
19
- export declare class RetinalSegmenterService implements ISemanticSegmenter {
20
- private readonly instances;
21
- private readonly loading;
22
- segmentSemantic(image: {
23
- data: Uint8ClampedArray;
24
- width: number;
25
- height: number;
26
- }, opts?: SemanticSegmentOptions, progress?: SemanticSegmentProgress): Promise<SemanticSegmentation>;
27
- /**
28
- * The shared segmenter for a model id, created on first use and reused
29
- * thereafter (deduped across concurrent callers). Exposed so a host's
30
- * processing pipeline can run on the same loaded session rather than paying
31
- * the ~590 MB load twice.
32
- */
33
- getModel(modelId?: string, onProgress?: (loaded: number, total: number | null) => void, onStatus?: (status: string) => void,
34
- /**
35
- * Abort the load. This model is ~590 MB, so a cancel has to reach the
36
- * *download* — aborting only the inference that follows leaves the transfer
37
- * running and the UI stuck in "Cancelling" until it completes.
38
- *
39
- * Deliberately not applied to a load already in flight: that promise may be
40
- * shared with another caller (the toolbar tool and the pipeline both use
41
- * this service), and one caller's cancel must not tear the model out from
42
- * under the other. A second caller joining an in-flight load simply waits.
43
- */
44
- signal?: AbortSignal): Promise<RetinalSegmenter>;
45
- /** Whether a model is already loaded (warm). */
46
- isLoaded(modelId?: string): boolean;
47
- /**
48
- * Release one model, or all of them. Safe to call on a model never loaded.
49
- *
50
- * In-flight loads are torn down too: dropping a pending promise and returning
51
- * would let the load finish afterwards, re-populate {@link instances}, and
52
- * strand a live ORT session that nothing owns — disposal would report success
53
- * while leaking. At 590 MB that leak is not survivable.
54
- */
55
- dispose(modelId?: string): Promise<void>;
56
- private disposeOne;
57
- private requireModel;
58
- static ɵfac: i0.ɵɵFactoryDeclaration<RetinalSegmenterService, never>;
59
- static ɵprov: i0.ɵɵInjectableDeclaration<RetinalSegmenterService>;
60
- }
@@ -1,51 +0,0 @@
1
- import { BehaviorSubject } from 'rxjs';
2
- import { type TileAccessPort } from '../../contracts/ports/tile-access.port';
3
- import type { IVisualizer } from '../../contracts/visualizer.contract';
4
- import type { IInstanceSegmenter, InstanceSegmentOptions } from '../../contracts/instance-segmenter.contract';
5
- import * as i0 from "@angular/core";
6
- /**
7
- * YOLO object detection over the current view.
8
- *
9
- * The complement to {@link CellSegmentToolService}: cellpose auto-segments cells
10
- * inside a box you draw, whereas a detector is meant to find objects across a
11
- * field. So this takes no prompt — it runs on whatever is on screen and turns
12
- * every detection into a region.
13
- *
14
- * Detections come back in the displayed image's pixels; they are mapped onto
15
- * the full-resolution image through the viewer's own source rect, so the regions
16
- * land where the objects actually are rather than at view-local coordinates.
17
- *
18
- * Exposes the same `status$` / `busy$` / `progress$` surface as the cellpose
19
- * tool so the host's shared segmentation toast drives it unchanged.
20
- */
21
- /** Marks regions this tool produced, so a re-run can replace only its own. */
22
- export declare const YOLO_REGION_SOURCE = "yolo";
23
- export declare class YoloDetectToolService {
24
- /** Server-side crop, so detection can run at the model's scale rather than
25
- * the viewer's. Optional: without it the tool falls back to the displayed
26
- * pixels, which is correct but scale-dependent. */
27
- private tileAccess;
28
- readonly status$: BehaviorSubject<string>;
29
- readonly busy$: BehaviorSubject<boolean>;
30
- readonly progress$: BehaviorSubject<number>;
31
- constructor(
32
- /** Server-side crop, so detection can run at the model's scale rather than
33
- * the viewer's. Optional: without it the tool falls back to the displayed
34
- * pixels, which is correct but scale-dependent. */
35
- tileAccess?: TileAccessPort | null);
36
- /**
37
- * Detect in the current view and append the results as regions.
38
- *
39
- * @returns the number of regions added.
40
- */
41
- detectInView(viz: IVisualizer, segmenter: IInstanceSegmenter, opts?: InstanceSegmentOptions): Promise<number>;
42
- /**
43
- * Re-crop `rect` at `1/target` of full resolution. Returns null on any
44
- * failure, so the caller falls back to the displayed pixels — a coarser crop
45
- * beats a failed run.
46
- */
47
- private fetchCropAtScale;
48
- private makeRegion;
49
- static ɵfac: i0.ɵɵFactoryDeclaration<YoloDetectToolService, [{ optional: true; }]>;
50
- static ɵprov: i0.ɵɵInjectableDeclaration<YoloDetectToolService>;
51
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Registry of YOLOv8-seg instance-segmentation models.
3
- *
4
- * Mirrors {@link SAM_MODELS}: public HF-hosted ONNX by default so the tool works
5
- * out of the box in any host, repointable at private hosting via
6
- * {@link setYoloModelUrls}.
7
- *
8
- * Each entry carries the per-model *defaults* the server-side worker uses for
9
- * that checkpoint. These are properties of how the model was trained (object
10
- * scale, expected crowding), not UI preferences — the embryo models want heavy
11
- * 60% tile overlap and loose merge thresholds because their objects are large
12
- * and touch, while the eye models were trained on sparse, downsampled fields.
13
- * Keeping them here means a host that just picks a model gets sane behaviour
14
- * without having to know any of that.
15
- *
16
- * Weights point at the `model.fp16w.onnx` variant deliberately. The `.fp16`
17
- * variant accumulates in half precision on WebGPU and, at the x-scale these
18
- * models use, produces materially wrong boxes; `fp16w` stores weights in fp16
19
- * and computes in fp32, so it is half the download at identical accuracy.
20
- *
21
- * Export tooling: the sibling `browser-onnx-tools` project.
22
- */
23
- export interface YoloModelDef {
24
- id: string;
25
- label: string;
26
- /** URL of the `.onnx` weights. Empty disables the model. */
27
- modelUrl: string;
28
- /**
29
- * URL of the `model.json` sidecar. Omit to use `model.json` alongside the
30
- * weights, which is how every published repo is laid out.
31
- */
32
- metaUrl?: string;
33
- /** Per-model defaults, from the server worker's model table. */
34
- defaults: {
35
- confidence: number;
36
- iouThreshold: number;
37
- mergeThreshold: number;
38
- overlapX: number;
39
- overlapY: number;
40
- };
41
- }
42
- export declare const YOLO_MODELS: YoloModelDef[];
43
- export declare const DEFAULT_YOLO_MODEL_ID = "yolov8x-seg-opticnerve";
44
- /** Look up a model definition by id. Returns undefined for an unknown id. */
45
- export declare function getYoloModel(id: string): YoloModelDef | undefined;
46
- /** Whether a model has a usable weights URL configured. */
47
- export declare function isYoloModelReady(id: string): boolean;
48
- /**
49
- * Repoint a model at different hosting (e.g. a GCS bucket or a same-origin
50
- * proxy). Call once at app init, before the first run. An empty `modelUrl`
51
- * disables the model.
52
- */
53
- export declare function setYoloModelUrls(id: string, urls: {
54
- modelUrl?: string;
55
- metaUrl?: string;
56
- }): void;
57
- /** Change which model runs when a caller does not name one. */
58
- export declare function setDefaultYoloModel(id: string): void;
59
- export declare function getDefaultYoloModelId(): string;
@@ -1,50 +0,0 @@
1
- import type { IInstanceSegmenter, InstanceSegmentation, InstanceSegmentOptions, InstanceSegmentProgress } from '../../contracts/instance-segmenter.contract';
2
- import type { YoloSegmenter } from 'yolo-segdetect-js';
3
- import * as i0 from "@angular/core";
4
- /**
5
- * Default in-library {@link IInstanceSegmenter}, backed by yolo-segdetect-js
6
- * (YOLOv8-seg on WebGPU, falling back to WASM).
7
- *
8
- * `yolo-segdetect-js` is a regular dependency but is **lazy-imported** on first
9
- * use, so apps that never run YOLO pay nothing for it — or for onnxruntime-web —
10
- * in their initial bundle. This mirrors {@link CellposeSegmenterService}.
11
- *
12
- * The one structural difference from the Cellpose service: there are four YOLO
13
- * checkpoints, not one, so instances are cached **per model id** rather than as
14
- * a single singleton. Each entry owns its own worker and ORT session, so a host
15
- * that switches models pays a second load but keeps the first one warm. Nothing
16
- * evicts automatically — these are ~144 MB each, so a host that cycles through
17
- * all four should call {@link dispose} for the ones it is done with.
18
- */
19
- export declare class YoloSegmenterService implements IInstanceSegmenter {
20
- private readonly instances;
21
- private readonly loading;
22
- segmentInstances(image: {
23
- data: Uint8ClampedArray;
24
- width: number;
25
- height: number;
26
- }, opts?: InstanceSegmentOptions, progress?: InstanceSegmentProgress): Promise<InstanceSegmentation>;
27
- /**
28
- * The shared segmenter for a model id, created on first use and reused
29
- * thereafter (deduped across concurrent callers). Exposed so a host's
30
- * processing pipeline can drive richer `segment()` options on the same loaded
31
- * session instead of loading the model twice.
32
- */
33
- getModel(modelId?: string, onProgress?: (loaded: number, total: number | null) => void, onStatus?: (status: string) => void): Promise<YoloSegmenter>;
34
- /** Whether a model is already loaded (warm). */
35
- isLoaded(modelId?: string): boolean;
36
- /**
37
- * Release one model, or all of them. Safe to call on a model never loaded.
38
- *
39
- * In-flight loads are torn down too, not just warm ones. Simply dropping the
40
- * pending promise and returning would let the load finish afterwards,
41
- * re-populate {@link instances}, and strand a live worker and ORT session that
42
- * nothing owns — disposal would have reported success while leaking. So a load
43
- * that is still running is awaited and then released.
44
- */
45
- dispose(modelId?: string): Promise<void>;
46
- private disposeOne;
47
- private requireModel;
48
- static ɵfac: i0.ɵɵFactoryDeclaration<YoloSegmenterService, never>;
49
- static ɵprov: i0.ɵɵInjectableDeclaration<YoloSegmenterService>;
50
- }