@jax-data-science/sci-image-visualizer 0.2.16 → 0.2.17
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/esm2022/index.mjs +6 -1
- package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +11 -0
- package/esm2022/lib/toolbar/segmentation/retinal-layer-tool.service.mjs +197 -0
- package/esm2022/lib/toolbar/segmentation/retinal-model-registry.mjs +119 -0
- package/esm2022/lib/toolbar/segmentation/retinal-segmenter.service.mjs +157 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +31 -3
- package/esm2022/lib/visualizer.component.mjs +86 -22
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +579 -18
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/index.d.ts +4 -0
- package/lib/contracts/semantic-segmenter.contract.d.ts +105 -0
- package/lib/toolbar/segmentation/retinal-layer-tool.service.d.ts +49 -0
- package/lib/toolbar/segmentation/retinal-model-registry.d.ts +71 -0
- package/lib/toolbar/segmentation/retinal-segmenter.service.d.ts +49 -0
- package/lib/toolbar/toolbar.component.d.ts +15 -1
- package/lib/visualizer.component.d.ts +38 -2
- package/package.json +2 -1
package/index.d.ts
CHANGED
|
@@ -25,11 +25,15 @@ export { CellposeSegmenterService } from './lib/toolbar/segmentation/cellpose-se
|
|
|
25
25
|
export * from './lib/contracts/instance-segmenter.contract';
|
|
26
26
|
export { YoloSegmenterService } from './lib/toolbar/segmentation/yolo-segmenter.service';
|
|
27
27
|
export { YoloDetectToolService, YOLO_REGION_SOURCE } from './lib/toolbar/segmentation/yolo-detect-tool.service';
|
|
28
|
+
export * from './lib/contracts/semantic-segmenter.contract';
|
|
29
|
+
export { RetinalSegmenterService } from './lib/toolbar/segmentation/retinal-segmenter.service';
|
|
30
|
+
export { RetinalLayerToolService, RETINAL_REGION_SOURCE, } from './lib/toolbar/segmentation/retinal-layer-tool.service';
|
|
28
31
|
export { ProcessingImage } from './lib/processing/processing-image';
|
|
29
32
|
export { cropImage, CropImageOptions } from './lib/processing/image-crop';
|
|
30
33
|
export { ImageConverterService } from './lib/processing/image-converter.service';
|
|
31
34
|
export { SAM_MODELS, DEFAULT_SAM_MODEL_ID, getSamModel, isSamModelReady, setSamModelUrls, setDefaultSamModel, getDefaultSamModelId, } from './lib/toolbar/segmentation/sam-model-registry';
|
|
32
35
|
export { YOLO_MODELS, DEFAULT_YOLO_MODEL_ID, getYoloModel, isYoloModelReady, setYoloModelUrls, setDefaultYoloModel, getDefaultYoloModelId, YoloModelDef, } from './lib/toolbar/segmentation/yolo-model-registry';
|
|
36
|
+
export { RETINAL_MODELS, DEFAULT_RETINAL_MODEL_ID, getRetinalModel, isRetinalModelReady, readyRetinalModels, setRetinalModelUrls, setDefaultRetinalModel, getDefaultRetinalModelId, RetinalModelDef, } from './lib/toolbar/segmentation/retinal-model-registry';
|
|
33
37
|
export { setOrtWasmBase, getOrtWasmBase } from './lib/toolbar/segmentation/ort-runtime-config';
|
|
34
38
|
export * from './lib/models/region';
|
|
35
39
|
export * from './lib/models/class-preset';
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Port for a **semantic** segmenter — one that assigns every pixel a class from
|
|
4
|
+
* a fixed set, with no notion of separate objects.
|
|
5
|
+
*
|
|
6
|
+
* The third sibling of {@link ICellSegmenter} and {@link IInstanceSegmenter},
|
|
7
|
+
* and genuinely a different shape from both. Cellpose separates touching
|
|
8
|
+
* instances of one class; YOLO returns overlapping classified objects; this
|
|
9
|
+
* returns *layers* — regions that tile the image, never overlap, and where two
|
|
10
|
+
* disconnected patches of the same class are the same finding rather than two
|
|
11
|
+
* objects. There is no confidence per object and no NMS, because there are no
|
|
12
|
+
* objects.
|
|
13
|
+
*
|
|
14
|
+
* Results are delivered as polygon rings in **image pixels**, so a caller can
|
|
15
|
+
* build {@link Region}s directly — the same currency the other two ports use.
|
|
16
|
+
*/
|
|
17
|
+
/** A closed ring; the last point is NOT a repeat of the first. */
|
|
18
|
+
export type SemanticRing = Array<[number, number]>;
|
|
19
|
+
/** One connected component of one class, with any interior voids. */
|
|
20
|
+
export interface SemanticRegion {
|
|
21
|
+
exterior: SemanticRing;
|
|
22
|
+
holes: SemanticRing[];
|
|
23
|
+
classId: number;
|
|
24
|
+
className: string;
|
|
25
|
+
/** Area in pixels, holes already subtracted. */
|
|
26
|
+
area: number;
|
|
27
|
+
}
|
|
28
|
+
export interface SemanticSegmentation {
|
|
29
|
+
regions: SemanticRegion[];
|
|
30
|
+
/** Total pixels assigned to each class, indexed by class id. */
|
|
31
|
+
classAreas: number[];
|
|
32
|
+
/** Class names indexed by class id, from the model's own metadata. */
|
|
33
|
+
classNames: string[];
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
/**
|
|
37
|
+
* Fraction of pixels that met no class's threshold.
|
|
38
|
+
*
|
|
39
|
+
* Worth surfacing rather than discarding: these models are thresholded per
|
|
40
|
+
* class rather than argmaxed, so a high value means the model was unsure
|
|
41
|
+
* everywhere — which is the signature of wrong preprocessing (wrong input
|
|
42
|
+
* scale, wrong greyscale) long before the polygons look obviously wrong.
|
|
43
|
+
*/
|
|
44
|
+
unassignedFraction: number;
|
|
45
|
+
}
|
|
46
|
+
export interface SemanticSegmentOptions {
|
|
47
|
+
/** Which registered model to run. Defaults to the registry's default. */
|
|
48
|
+
modelId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Scale to run at, as a divisor of full resolution — the same meaning the
|
|
51
|
+
* server gives it.
|
|
52
|
+
*
|
|
53
|
+
* Matters as much here as for a detector: these checkpoints were trained on
|
|
54
|
+
* 40x or 20x patches, and a layer imaged at the wrong scale does not look
|
|
55
|
+
* like the layer the model learned.
|
|
56
|
+
*/
|
|
57
|
+
downsamplingFactor?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Minimum probability for a pixel to be assigned to a class, default 0.5.
|
|
60
|
+
*
|
|
61
|
+
* Not a plain argmax. The reference worker thresholds each class channel
|
|
62
|
+
* independently at 0.5; because the model's last layer is a softmax at most
|
|
63
|
+
* one class can clear that, so this is argmax *plus a confidence floor* —
|
|
64
|
+
* pixels where the winner is weak are left unassigned rather than forced into
|
|
65
|
+
* the best of a bad set. Lowering it toward 0.25 approaches a true argmax and
|
|
66
|
+
* visibly fattens every layer.
|
|
67
|
+
*/
|
|
68
|
+
classThreshold?: number;
|
|
69
|
+
/** Keep only these class ids; omit for everything except background. */
|
|
70
|
+
classFilter?: number[];
|
|
71
|
+
/** Douglas-Peucker tolerance in pixels for outline simplification; 0 disables. */
|
|
72
|
+
simplifyTolerance?: number;
|
|
73
|
+
/** Drop regions whose area is below this, in square pixels. */
|
|
74
|
+
minArea?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Cooperative cancellation, checked at patch boundaries — so it takes effect
|
|
77
|
+
* within roughly one patch rather than instantly.
|
|
78
|
+
*/
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
}
|
|
81
|
+
/** Progress/status callbacks (all optional). Mirrors {@link InstanceSegmentProgress}. */
|
|
82
|
+
export interface SemanticSegmentProgress {
|
|
83
|
+
/** Model-download progress, 0..1 (first run only). */
|
|
84
|
+
onProgress?: (fraction: number) => void;
|
|
85
|
+
/**
|
|
86
|
+
* Raw download byte counts, for hosts that render a size rather than a bare
|
|
87
|
+
* percentage. `total` is null when the response carried no Content-Length.
|
|
88
|
+
*/
|
|
89
|
+
onBytes?: (loaded: number, total: number | null) => void;
|
|
90
|
+
/** Human-readable phase, e.g. 'Segmenting patch 3/8…'. */
|
|
91
|
+
onStatus?: (status: string) => void;
|
|
92
|
+
}
|
|
93
|
+
export interface ISemanticSegmenter {
|
|
94
|
+
/** Assign every pixel a class and return the resulting regions. */
|
|
95
|
+
segmentSemantic(image: {
|
|
96
|
+
data: Uint8ClampedArray;
|
|
97
|
+
width: number;
|
|
98
|
+
height: number;
|
|
99
|
+
}, opts?: SemanticSegmentOptions, progress?: SemanticSegmentProgress): Promise<SemanticSegmentation>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* DI token for the semantic segmenter, defaulting to the in-library
|
|
103
|
+
* jax-ai-js-backed implementation. Hosts override it to supply their own.
|
|
104
|
+
*/
|
|
105
|
+
export declare const SEMANTIC_SEGMENTER: InjectionToken<ISemanticSegmenter>;
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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;
|
|
@@ -0,0 +1,49 @@
|
|
|
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): Promise<RetinalSegmenter>;
|
|
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: dropping a pending promise and returning
|
|
40
|
+
* would let the load finish afterwards, re-populate {@link instances}, and
|
|
41
|
+
* strand a live ORT session that nothing owns — disposal would report success
|
|
42
|
+
* while leaking. At 590 MB that leak is not survivable.
|
|
43
|
+
*/
|
|
44
|
+
dispose(modelId?: string): Promise<void>;
|
|
45
|
+
private disposeOne;
|
|
46
|
+
private requireModel;
|
|
47
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RetinalSegmenterService, never>;
|
|
48
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<RetinalSegmenterService>;
|
|
49
|
+
}
|
|
@@ -72,6 +72,12 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
72
72
|
label: string;
|
|
73
73
|
}[];
|
|
74
74
|
yoloModelId: string;
|
|
75
|
+
/** Retinal-layer checkpoints offered by the host's registry, and the active one. */
|
|
76
|
+
retinalModels: {
|
|
77
|
+
id: string;
|
|
78
|
+
label: string;
|
|
79
|
+
}[];
|
|
80
|
+
retinalModelId: string;
|
|
75
81
|
selectPlotType: EventEmitter<PlotType>;
|
|
76
82
|
/** Intensity (LINE) mode: add another colored line ROI + inset trace. */
|
|
77
83
|
addProfileLine: EventEmitter<void>;
|
|
@@ -117,6 +123,12 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
117
123
|
yoloModelChange: EventEmitter<string>;
|
|
118
124
|
/** Open the YOLO parameter dialog. */
|
|
119
125
|
openYoloParams: EventEmitter<void>;
|
|
126
|
+
/** Run retinal-layer segmentation over the current view. */
|
|
127
|
+
segmentRetinal: EventEmitter<void>;
|
|
128
|
+
/** Pick the retinal-layer checkpoint. */
|
|
129
|
+
retinalModelChange: EventEmitter<string>;
|
|
130
|
+
/** Open the retinal-layer parameter dialog. */
|
|
131
|
+
openRetinalParams: EventEmitter<void>;
|
|
120
132
|
/** SAM model picker (jit-ui#90 P1). */
|
|
121
133
|
samModelChange: EventEmitter<string>;
|
|
122
134
|
wandSensitivityChange: EventEmitter<number | undefined>;
|
|
@@ -147,6 +159,8 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
147
159
|
samMenuItems: MenuItem[];
|
|
148
160
|
/** Same stable-array reasoning as {@link samMenuItems}. */
|
|
149
161
|
yoloMenuItems: MenuItem[];
|
|
162
|
+
/** Same stable-array reasoning as {@link samMenuItems}. */
|
|
163
|
+
retinalMenuItems: MenuItem[];
|
|
150
164
|
/** Rebuild the SAM model menu when the model list or active selection
|
|
151
165
|
* changes (keeps the array reference stable across other CD ticks). */
|
|
152
166
|
ngOnChanges(changes: SimpleChanges): void;
|
|
@@ -177,5 +191,5 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
177
191
|
get isIntensityCapable(): boolean;
|
|
178
192
|
showHelp(): void;
|
|
179
193
|
static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarComponent, never>;
|
|
180
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "yoloModels": { "alias": "yoloModels"; "required": false; }; "yoloModelId": { "alias": "yoloModelId"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "detectYolo": "detectYolo"; "yoloModelChange": "yoloModelChange"; "openYoloParams": "openYoloParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
194
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "yoloModels": { "alias": "yoloModels"; "required": false; }; "yoloModelId": { "alias": "yoloModelId"; "required": false; }; "retinalModels": { "alias": "retinalModels"; "required": false; }; "retinalModelId": { "alias": "retinalModelId"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "detectYolo": "detectYolo"; "yoloModelChange": "yoloModelChange"; "openYoloParams": "openYoloParams"; "segmentRetinal": "segmentRetinal"; "retinalModelChange": "retinalModelChange"; "openRetinalParams": "openRetinalParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
181
195
|
}
|
|
@@ -14,6 +14,8 @@ import { SamPointToolService } from './toolbar/segmentation/sam-point-tool.servi
|
|
|
14
14
|
import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool.service';
|
|
15
15
|
import { YoloDetectToolService } from './toolbar/segmentation/yolo-detect-tool.service';
|
|
16
16
|
import type { IInstanceSegmenter, InstanceSegmentOptions } from './contracts/instance-segmenter.contract';
|
|
17
|
+
import { RetinalLayerToolService } from './toolbar/segmentation/retinal-layer-tool.service';
|
|
18
|
+
import type { ISemanticSegmenter, SemanticSegmentOptions } from './contracts/semantic-segmenter.contract';
|
|
17
19
|
import { ToolbarToolVisibility } from './contracts/toolbar-config';
|
|
18
20
|
import { VizConfig } from './contracts/viz-config';
|
|
19
21
|
import * as i0 from "@angular/core";
|
|
@@ -28,6 +30,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
28
30
|
private cellSegmentTool;
|
|
29
31
|
private yoloDetectTool;
|
|
30
32
|
private instanceSegmenter;
|
|
33
|
+
private semanticSegmenter;
|
|
34
|
+
private retinalLayerTool;
|
|
31
35
|
private samPointTool;
|
|
32
36
|
private regionOps;
|
|
33
37
|
private vizConfig?;
|
|
@@ -137,6 +141,18 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
137
141
|
* encode the scale and crowding each checkpoint was trained for. */
|
|
138
142
|
showYoloParams: boolean;
|
|
139
143
|
yoloParams: InstanceSegmentOptions;
|
|
144
|
+
/** Retinal-layer checkpoints with a configured URL, and the active one.
|
|
145
|
+
* Only VNet ships — see the registry for why the ResUNet-a entries are off. */
|
|
146
|
+
retinalModels: {
|
|
147
|
+
id: string;
|
|
148
|
+
label: string;
|
|
149
|
+
}[];
|
|
150
|
+
retinalModelId: string;
|
|
151
|
+
showRetinalParams: boolean;
|
|
152
|
+
retinalParams: SemanticSegmentOptions;
|
|
153
|
+
/** Download size of the active retinal model, surfaced in the dialog because
|
|
154
|
+
* ~590 MB is worth knowing before clicking Run rather than after. */
|
|
155
|
+
get retinalModelSizeMb(): number;
|
|
140
156
|
/** SAM download/segment toast state (bound by the `sam` p-toast template). */
|
|
141
157
|
samStatus: string;
|
|
142
158
|
samProgress: number;
|
|
@@ -247,7 +263,7 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
247
263
|
private wheelListener?;
|
|
248
264
|
/** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
|
|
249
265
|
private readonly scrubber;
|
|
250
|
-
constructor(state: ImageStatePort, plotService: IVisualizer, messageService: MessageService, ngZone: NgZone, cdr: ChangeDetectorRef, session: VisualizerStore, samTool: SamToolService, cellSegmentTool: CellSegmentToolService, yoloDetectTool: YoloDetectToolService, instanceSegmenter: IInstanceSegmenter, samPointTool: SamPointToolService, regionOps: RegionOpsService, vizConfig?: VizConfig | undefined);
|
|
266
|
+
constructor(state: ImageStatePort, plotService: IVisualizer, messageService: MessageService, ngZone: NgZone, cdr: ChangeDetectorRef, session: VisualizerStore, samTool: SamToolService, cellSegmentTool: CellSegmentToolService, yoloDetectTool: YoloDetectToolService, instanceSegmenter: IInstanceSegmenter, semanticSegmenter: ISemanticSegmenter, retinalLayerTool: RetinalLayerToolService, samPointTool: SamPointToolService, regionOps: RegionOpsService, vizConfig?: VizConfig | undefined);
|
|
251
267
|
/**
|
|
252
268
|
* Plot types offered in the selector for the current image:
|
|
253
269
|
* - Outside **test mode**, only the curated default set (descriptors with a
|
|
@@ -406,6 +422,26 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
406
422
|
* in a field rather than being pointed at one. Reuses the shared segmentation
|
|
407
423
|
* toast so progress and status read the same as the SAM/cellpose tools. */
|
|
408
424
|
detectYolo(): Promise<void>;
|
|
425
|
+
/**
|
|
426
|
+
* Defaults for the retinal tool.
|
|
427
|
+
*
|
|
428
|
+
* `classThreshold` is 0.5 to match the reference worker, which thresholds each
|
|
429
|
+
* class channel independently rather than taking an argmax — with a softmax
|
|
430
|
+
* output that makes it argmax plus a confidence floor.
|
|
431
|
+
*
|
|
432
|
+
* `downsamplingFactor` is 1 rather than YOLO's 5: these checkpoints were
|
|
433
|
+
* trained on native 40x patches, so the model wants full resolution and
|
|
434
|
+
* downsampling actively hurts.
|
|
435
|
+
*/
|
|
436
|
+
private defaultRetinalParams;
|
|
437
|
+
/** Switching checkpoint re-seeds the parameters, since the defaults belong to
|
|
438
|
+
* the model rather than to the tool. */
|
|
439
|
+
onRetinalModelChange(id: string): void;
|
|
440
|
+
openRetinalParams(): void;
|
|
441
|
+
resetRetinalParams(): void;
|
|
442
|
+
/** Segment retinal layers across the current view. Like YOLO this takes no
|
|
443
|
+
* prompt — it labels every pixel — and reuses the shared segmentation toast. */
|
|
444
|
+
segmentRetinal(): Promise<void>;
|
|
409
445
|
/** Shared driver for the box-prompt segment tools: wires the tool's status +
|
|
410
446
|
* download progress into the sticky `sam` toast, runs `op`, and reports the
|
|
411
447
|
* region count. Keeps the toast open until the run settles (bar hits 100%). */
|
|
@@ -449,6 +485,6 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
449
485
|
onSelectPlotType(type: PlotType): void;
|
|
450
486
|
/** Deactivate whatever tool is armed and clear every tool mode. */
|
|
451
487
|
private deactivateActiveTool;
|
|
452
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, null, null, { optional: true; }]>;
|
|
488
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, null, null, null, null, { optional: true; }]>;
|
|
453
489
|
static ɵcmp: i0.ɵɵComponentDeclaration<VisualizerComponent, "visualizer", never, { "toolbarTools": { "alias": "toolbarTools"; "required": false; }; "testMode": { "alias": "testMode"; "required": false; }; }, { "isStackEvent": "isStackEvent"; "isGrayscaleEvent": "isGrayscaleEvent"; }, never, ["*"], false, never>;
|
|
454
490
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jax-data-science/sci-image-visualizer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"description": "Browser-based scientific image visualization for Angular — gigapixel whole-slide (OpenSeadragon), 2D/3D (Plotly) and WebGPU (napari-js) rendering, with region/annotation editing and in-browser SAM segmentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"cellpose-js": "^0.4.1",
|
|
38
38
|
"fast-png": "^6.4.0",
|
|
39
|
+
"jax-ai-js": "^0.1.0",
|
|
39
40
|
"napari-js": "^0.11.0",
|
|
40
41
|
"openseadragon": "^6.0.2",
|
|
41
42
|
"plotly.js-dist-min": "^3.0.1",
|