@jax-data-science/sci-image-visualizer 0.2.7 → 0.2.9
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 +5 -1
- package/esm2022/lib/contracts/instance-segmenter.contract.mjs +12 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +24 -9
- package/esm2022/lib/toolbar/segmentation/yolo-model-registry.mjs +99 -0
- package/esm2022/lib/toolbar/segmentation/yolo-segmenter.service.mjs +165 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +294 -9
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/index.d.ts +3 -0
- package/lib/contracts/instance-segmenter.contract.d.ts +100 -0
- package/lib/toolbar/segmentation/yolo-model-registry.d.ts +59 -0
- package/lib/toolbar/segmentation/yolo-segmenter.service.d.ts +50 -0
- package/package.json +3 -2
package/index.d.ts
CHANGED
|
@@ -22,10 +22,13 @@ export * from './lib/contracts/display-types';
|
|
|
22
22
|
export * from './lib/contracts/sam.contract';
|
|
23
23
|
export * from './lib/contracts/cell-segmenter.contract';
|
|
24
24
|
export { CellposeSegmenterService } from './lib/toolbar/segmentation/cellpose-segmenter.service';
|
|
25
|
+
export * from './lib/contracts/instance-segmenter.contract';
|
|
26
|
+
export { YoloSegmenterService } from './lib/toolbar/segmentation/yolo-segmenter.service';
|
|
25
27
|
export { ProcessingImage } from './lib/processing/processing-image';
|
|
26
28
|
export { cropImage, CropImageOptions } from './lib/processing/image-crop';
|
|
27
29
|
export { ImageConverterService } from './lib/processing/image-converter.service';
|
|
28
30
|
export { SAM_MODELS, DEFAULT_SAM_MODEL_ID, getSamModel, isSamModelReady, setSamModelUrls, setDefaultSamModel, getDefaultSamModelId, } from './lib/toolbar/segmentation/sam-model-registry';
|
|
31
|
+
export { YOLO_MODELS, DEFAULT_YOLO_MODEL_ID, getYoloModel, isYoloModelReady, setYoloModelUrls, setDefaultYoloModel, getDefaultYoloModelId, YoloModelDef, } from './lib/toolbar/segmentation/yolo-model-registry';
|
|
29
32
|
export { setOrtWasmBase, getOrtWasmBase } from './lib/toolbar/segmentation/ort-runtime-config';
|
|
30
33
|
export * from './lib/models/region';
|
|
31
34
|
export * from './lib/models/class-preset';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Port for an automatic **instance** segmenter — one that returns discrete,
|
|
4
|
+
* classified objects rather than a per-pixel label map.
|
|
5
|
+
*
|
|
6
|
+
* This is a sibling of {@link ICellSegmenter}, not a replacement. Cellpose-style
|
|
7
|
+
* segmenters produce one exclusive label per pixel, which is exactly right for
|
|
8
|
+
* densely packed cells; a detector like YOLO produces overlapping instances,
|
|
9
|
+
* each with its own class and confidence, and flattening those into a label map
|
|
10
|
+
* would silently discard both the overlap and the classification. The two
|
|
11
|
+
* outputs are genuinely different shapes, so they get different ports.
|
|
12
|
+
*
|
|
13
|
+
* Geometry is returned as polygon rings in **image pixels**, ready to become
|
|
14
|
+
* {@link Region}s without a second conversion step.
|
|
15
|
+
*/
|
|
16
|
+
/** A closed ring; the last point is NOT a repeat of the first. */
|
|
17
|
+
export type InstanceRing = Array<[number, number]>;
|
|
18
|
+
/** One polygon with optional interior rings (donut holes). */
|
|
19
|
+
export interface InstancePolygon {
|
|
20
|
+
exterior: InstanceRing;
|
|
21
|
+
holes: InstanceRing[];
|
|
22
|
+
}
|
|
23
|
+
/** One detected object. */
|
|
24
|
+
export interface InstanceDetection {
|
|
25
|
+
/**
|
|
26
|
+
* Outline(s) of this instance. Usually one, but a mask that splits under
|
|
27
|
+
* thresholding yields several — they all belong to the same detection.
|
|
28
|
+
*/
|
|
29
|
+
polygons: InstancePolygon[];
|
|
30
|
+
/** `[x1, y1, x2, y2]` in image pixels. */
|
|
31
|
+
box: [number, number, number, number];
|
|
32
|
+
/** Model confidence, 0..1. */
|
|
33
|
+
score: number;
|
|
34
|
+
classId: number;
|
|
35
|
+
className: string;
|
|
36
|
+
}
|
|
37
|
+
export interface InstanceSegmentation {
|
|
38
|
+
detections: InstanceDetection[];
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
/** Class names indexed by class id, from the model's own metadata. */
|
|
42
|
+
classNames: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Tunables for one run.
|
|
46
|
+
*
|
|
47
|
+
* Three separate thresholds are in play and they are easy to conflate:
|
|
48
|
+
* `confidence` and `iouThreshold` act per tile, while `mergeThreshold` governs
|
|
49
|
+
* the cross-tile reconciliation. They come from different stages of the
|
|
50
|
+
* pipeline and are not interchangeable.
|
|
51
|
+
*/
|
|
52
|
+
export interface InstanceSegmentOptions {
|
|
53
|
+
/** Which registered model to run. Defaults to the registry's default. */
|
|
54
|
+
modelId?: string;
|
|
55
|
+
/** Minimum detection confidence, per tile. */
|
|
56
|
+
confidence?: number;
|
|
57
|
+
/** NMS IoU within a single tile. */
|
|
58
|
+
iouThreshold?: number;
|
|
59
|
+
/** Intersection-over-smaller threshold for merging across tile seams. */
|
|
60
|
+
mergeThreshold?: number;
|
|
61
|
+
/** Tile overlap, percent. */
|
|
62
|
+
overlapX?: number;
|
|
63
|
+
overlapY?: number;
|
|
64
|
+
/** Probability at which a mask pixel counts as inside. */
|
|
65
|
+
maskThreshold?: number;
|
|
66
|
+
/** Cap on detections per tile. */
|
|
67
|
+
maxDetections?: number;
|
|
68
|
+
/** Keep only these class ids; omit for all classes the model declares. */
|
|
69
|
+
classFilter?: number[];
|
|
70
|
+
/** Douglas-Peucker tolerance in pixels for outline simplification; 0 disables. */
|
|
71
|
+
simplifyTolerance?: number;
|
|
72
|
+
/** Drop instances whose outline area is below this, in square pixels. */
|
|
73
|
+
minArea?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Progress/status callbacks (all optional).
|
|
77
|
+
*
|
|
78
|
+
* Deliberately mirrors {@link CellSegmentProgress} in shape rather than importing
|
|
79
|
+
* it — the two contracts stay independently versionable.
|
|
80
|
+
*/
|
|
81
|
+
export interface InstanceSegmentProgress {
|
|
82
|
+
/** Model-download progress, 0..1 (first run only). */
|
|
83
|
+
onProgress?: (fraction: number) => void;
|
|
84
|
+
/** Human-readable phase, e.g. 'Running inference (tile 3/8)…'. */
|
|
85
|
+
onStatus?: (status: string) => void;
|
|
86
|
+
}
|
|
87
|
+
export interface IInstanceSegmenter {
|
|
88
|
+
/** Detect and outline all instances in an RGBA image. */
|
|
89
|
+
segmentInstances(image: {
|
|
90
|
+
data: Uint8ClampedArray;
|
|
91
|
+
width: number;
|
|
92
|
+
height: number;
|
|
93
|
+
}, opts?: InstanceSegmentOptions, progress?: InstanceSegmentProgress): Promise<InstanceSegmentation>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* DI token for the automatic instance segmenter. Defaults to the in-library
|
|
97
|
+
* {@link YoloSegmenterService} (yolo-segdetect-js, lazy-loaded); override it in
|
|
98
|
+
* the host to swap implementations — for example to delegate to the server.
|
|
99
|
+
*/
|
|
100
|
+
export declare const INSTANCE_SEGMENTER: InjectionToken<IInstanceSegmenter>;
|
|
@@ -0,0 +1,59 @@
|
|
|
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;
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
}
|
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.9",
|
|
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",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"napari-js": "^0.11.0",
|
|
40
40
|
"openseadragon": "^6.0.2",
|
|
41
41
|
"plotly.js-dist-min": "^3.0.1",
|
|
42
|
-
"tslib": "^2.3.0"
|
|
42
|
+
"tslib": "^2.3.0",
|
|
43
|
+
"yolo-segdetect-js": "^0.1.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"@angular/animations": "^17.3.0",
|