@jax-data-science/sci-image-visualizer 0.1.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/LICENSE +21 -0
- package/README.md +404 -0
- package/esm2022/index.mjs +52 -0
- package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
- package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
- package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
- package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
- package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
- package/esm2022/lib/contracts/display-types.mjs +8 -0
- package/esm2022/lib/contracts/image.contract.mjs +11 -0
- package/esm2022/lib/contracts/intensity.mjs +28 -0
- package/esm2022/lib/contracts/plot-type.mjs +112 -0
- package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
- package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
- package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
- package/esm2022/lib/contracts/sam.contract.mjs +12 -0
- package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
- package/esm2022/lib/contracts/viz-config.mjs +3 -0
- package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
- package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
- package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
- package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
- package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
- package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
- package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
- package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
- package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
- package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
- package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
- package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
- package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
- package/esm2022/lib/models/bezier.mjs +120 -0
- package/esm2022/lib/models/class-preset.mjs +46 -0
- package/esm2022/lib/models/geometry.mjs +198 -0
- package/esm2022/lib/models/region.mjs +166 -0
- package/esm2022/lib/models/shape.mjs +53 -0
- package/esm2022/lib/plot.utilities.mjs +693 -0
- package/esm2022/lib/processing/image-converter.service.mjs +90 -0
- package/esm2022/lib/processing/image-crop.mjs +62 -0
- package/esm2022/lib/processing/processing-image.mjs +50 -0
- package/esm2022/lib/provide-visualization.mjs +63 -0
- package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
- package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
- package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
- package/esm2022/lib/region-ops.service.mjs +301 -0
- package/esm2022/lib/render-orchestrator.mjs +87 -0
- package/esm2022/lib/routing-visualizer.service.mjs +533 -0
- package/esm2022/lib/store/class-color.util.mjs +52 -0
- package/esm2022/lib/store/region-store.service.mjs +1029 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
- package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
- package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
- package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
- package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
- package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
- package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
- package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
- package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
- package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
- package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
- package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
- package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
- package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
- package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
- package/esm2022/lib/visualization.module.mjs +162 -0
- package/esm2022/lib/visualizer.component.mjs +1597 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
- package/fesm2022/mask.worker.js +989 -0
- package/fesm2022/onnx-sam.worker.js +202 -0
- package/index.d.ts +39 -0
- package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
- package/lib/contracts/capabilities.contract.d.ts +30 -0
- package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
- package/lib/contracts/colormap-lut.d.ts +21 -0
- package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
- package/lib/contracts/display-types.d.ts +41 -0
- package/lib/contracts/image.contract.d.ts +72 -0
- package/lib/contracts/intensity.d.ts +18 -0
- package/lib/contracts/plot-type.d.ts +108 -0
- package/lib/contracts/ports/image-state.port.d.ts +29 -0
- package/lib/contracts/ports/preferences.port.d.ts +19 -0
- package/lib/contracts/ports/region-io.port.d.ts +29 -0
- package/lib/contracts/ports/tile-access.port.d.ts +27 -0
- package/lib/contracts/region-editor-api.contract.d.ts +83 -0
- package/lib/contracts/region-overlay.contract.d.ts +47 -0
- package/lib/contracts/region-store.contract.d.ts +74 -0
- package/lib/contracts/sam.contract.d.ts +98 -0
- package/lib/contracts/toolbar-config.d.ts +24 -0
- package/lib/contracts/visualizer.contract.d.ts +346 -0
- package/lib/contracts/viz-config.d.ts +25 -0
- package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
- package/lib/implementations/base-store-visualizer.d.ts +71 -0
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
- package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
- package/lib/implementations/osd/display-pipeline.d.ts +47 -0
- package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
- package/lib/implementations/osd/osd-coords.d.ts +16 -0
- package/lib/implementations/osd/osd-lib.d.ts +19 -0
- package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
- package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
- package/lib/implementations/osd/slice-cache.d.ts +161 -0
- package/lib/implementations/osd/tile-client.d.ts +32 -0
- package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
- package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
- package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
- package/lib/implementations/plotly/plotly.service.d.ts +594 -0
- package/lib/implementations/simple-slice-access.service.d.ts +64 -0
- package/lib/models/bezier.d.ts +59 -0
- package/lib/models/class-preset.d.ts +39 -0
- package/lib/models/geometry.d.ts +66 -0
- package/lib/models/region.d.ts +132 -0
- package/lib/models/shape.d.ts +44 -0
- package/lib/plot.utilities.d.ts +107 -0
- package/lib/processing/image-converter.service.d.ts +28 -0
- package/lib/processing/image-crop.d.ts +18 -0
- package/lib/processing/processing-image.d.ts +17 -0
- package/lib/provide-visualization.d.ts +34 -0
- package/lib/region-editor/mask-raster.d.ts +61 -0
- package/lib/region-editor/mask-worker.d.ts +7 -0
- package/lib/region-editor/region-editor.component.d.ts +305 -0
- package/lib/region-ops.service.d.ts +92 -0
- package/lib/render-orchestrator.d.ts +59 -0
- package/lib/routing-visualizer.service.d.ts +227 -0
- package/lib/store/class-color.util.d.ts +26 -0
- package/lib/store/region-store.service.d.ts +315 -0
- package/lib/store/visualizer-store.service.d.ts +123 -0
- package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
- package/lib/toolbar/crop/slide-crop.d.ts +30 -0
- package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
- package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
- package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
- package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
- package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
- package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
- package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
- package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
- package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
- package/lib/toolbar/toolbar.component.d.ts +167 -0
- package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
- package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
- package/lib/toolbar/wand/wand.service.d.ts +133 -0
- package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
- package/lib/visualization.module.d.ts +45 -0
- package/lib/visualizer.component.d.ts +378 -0
- package/package.json +73 -0
- package/src/lib/assets/3d-coordinates.svg +49 -0
- package/src/lib/assets/cells.svg +1 -0
- package/src/lib/assets/click.svg +1 -0
- package/src/lib/assets/colormap-luts.json +1 -0
- package/src/lib/assets/contour.svg +48 -0
- package/src/lib/assets/cube-3d.svg +1 -0
- package/src/lib/assets/edit-selection-white.svg +47 -0
- package/src/lib/assets/heatmap.svg +1 -0
- package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
- package/src/lib/assets/icons/colormap-accent.png +0 -0
- package/src/lib/assets/icons/colormap-afmhot.png +0 -0
- package/src/lib/assets/icons/colormap-autumn.png +0 -0
- package/src/lib/assets/icons/colormap-berlin.png +0 -0
- package/src/lib/assets/icons/colormap-binary.png +0 -0
- package/src/lib/assets/icons/colormap-blackbody.png +0 -0
- package/src/lib/assets/icons/colormap-bluered.png +0 -0
- package/src/lib/assets/icons/colormap-blues.png +0 -0
- package/src/lib/assets/icons/colormap-bone.png +0 -0
- package/src/lib/assets/icons/colormap-brbg.png +0 -0
- package/src/lib/assets/icons/colormap-brg.png +0 -0
- package/src/lib/assets/icons/colormap-bugn.png +0 -0
- package/src/lib/assets/icons/colormap-bupu.png +0 -0
- package/src/lib/assets/icons/colormap-bwr.png +0 -0
- package/src/lib/assets/icons/colormap-cividis.png +0 -0
- package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
- package/src/lib/assets/icons/colormap-cool.png +0 -0
- package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
- package/src/lib/assets/icons/colormap-copper.png +0 -0
- package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
- package/src/lib/assets/icons/colormap-dark2.png +0 -0
- package/src/lib/assets/icons/colormap-earth.png +0 -0
- package/src/lib/assets/icons/colormap-electric.png +0 -0
- package/src/lib/assets/icons/colormap-flag.png +0 -0
- package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
- package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
- package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
- package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
- package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
- package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
- package/src/lib/assets/icons/colormap-gnbu.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
- package/src/lib/assets/icons/colormap-gray.png +0 -0
- package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greens.png +0 -0
- package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greys.png +0 -0
- package/src/lib/assets/icons/colormap-hot.png +0 -0
- package/src/lib/assets/icons/colormap-hsv.png +0 -0
- package/src/lib/assets/icons/colormap-inferno.png +0 -0
- package/src/lib/assets/icons/colormap-jet.png +0 -0
- package/src/lib/assets/icons/colormap-magma.png +0 -0
- package/src/lib/assets/icons/colormap-managua.png +0 -0
- package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
- package/src/lib/assets/icons/colormap-ocean.png +0 -0
- package/src/lib/assets/icons/colormap-oranges.png +0 -0
- package/src/lib/assets/icons/colormap-orrd.png +0 -0
- package/src/lib/assets/icons/colormap-pastel2.png +0 -0
- package/src/lib/assets/icons/colormap-picnic.png +0 -0
- package/src/lib/assets/icons/colormap-pink.png +0 -0
- package/src/lib/assets/icons/colormap-piyg.png +0 -0
- package/src/lib/assets/icons/colormap-placeholder.png +0 -0
- package/src/lib/assets/icons/colormap-plasma.png +0 -0
- package/src/lib/assets/icons/colormap-portland.png +0 -0
- package/src/lib/assets/icons/colormap-prgn.png +0 -0
- package/src/lib/assets/icons/colormap-prism.png +0 -0
- package/src/lib/assets/icons/colormap-pubu.png +0 -0
- package/src/lib/assets/icons/colormap-pubugn.png +0 -0
- package/src/lib/assets/icons/colormap-puor.png +0 -0
- package/src/lib/assets/icons/colormap-purd.png +0 -0
- package/src/lib/assets/icons/colormap-purples.png +0 -0
- package/src/lib/assets/icons/colormap-rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-rdbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdgy.png +0 -0
- package/src/lib/assets/icons/colormap-rdpu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
- package/src/lib/assets/icons/colormap-reds.png +0 -0
- package/src/lib/assets/icons/colormap-seismic.png +0 -0
- package/src/lib/assets/icons/colormap-set1.png +0 -0
- package/src/lib/assets/icons/colormap-set2.png +0 -0
- package/src/lib/assets/icons/colormap-set3.png +0 -0
- package/src/lib/assets/icons/colormap-spectral.png +0 -0
- package/src/lib/assets/icons/colormap-spring.png +0 -0
- package/src/lib/assets/icons/colormap-summer.png +0 -0
- package/src/lib/assets/icons/colormap-tab10.png +0 -0
- package/src/lib/assets/icons/colormap-tab20.png +0 -0
- package/src/lib/assets/icons/colormap-tab20b.png +0 -0
- package/src/lib/assets/icons/colormap-tab20c.png +0 -0
- package/src/lib/assets/icons/colormap-terrain.png +0 -0
- package/src/lib/assets/icons/colormap-turbo.png +0 -0
- package/src/lib/assets/icons/colormap-twilight.png +0 -0
- package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
- package/src/lib/assets/icons/colormap-vanimo.png +0 -0
- package/src/lib/assets/icons/colormap-viridis.png +0 -0
- package/src/lib/assets/icons/colormap-winter.png +0 -0
- package/src/lib/assets/icons/colormap-wistia.png +0 -0
- package/src/lib/assets/icons/colormap-ylgn.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
- package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
- package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
- package/src/lib/assets/invert.svg +1 -0
- package/src/lib/assets/isosurface.svg +7 -0
- package/src/lib/assets/mask.svg +1 -0
- package/src/lib/assets/paintbrush.svg +1 -0
- package/src/lib/assets/picture-segmentation.svg +43 -0
- package/src/lib/assets/polygon-vertices.svg +13 -0
- package/src/lib/assets/polyline.svg +3 -0
- package/src/lib/assets/redo.svg +39 -0
- package/src/lib/assets/region-editor.svg +37 -0
- package/src/lib/assets/region-move.svg +7 -0
- package/src/lib/assets/smooth.svg +56 -0
- package/src/lib/assets/spline-chart.svg +7 -0
- package/src/lib/assets/surface.svg +37 -0
- package/src/lib/assets/test/download-379.svg +1 -0
- package/src/lib/assets/test/download-98.svg +1 -0
- package/src/lib/assets/test/json-1.svg +1 -0
- package/src/lib/assets/test/load-53.svg +1 -0
- package/src/lib/assets/test/mask-44.svg +1 -0
- package/src/lib/assets/test/mask-download-44.svg +15 -0
- package/src/lib/assets/test/mask-save-44.svg +11 -0
- package/src/lib/assets/test/overlay-1.svg +1 -0
- package/src/lib/assets/test/overlay-4.svg +1 -0
- package/src/lib/assets/test/region-26.svg +1 -0
- package/src/lib/assets/test/save-56.svg +1 -0
- package/src/lib/assets/test/upload-43.svg +1 -0
- package/src/lib/assets/to-bezier.svg +15 -0
- package/src/lib/assets/to-polygon.svg +11 -0
- package/src/lib/assets/undo.svg +38 -0
- package/src/lib/assets/vertex-add.svg +12 -0
- package/src/lib/assets/vertex-delete.svg +12 -0
- package/src/lib/assets/wand.svg +54 -0
- package/src/lib/assets/zoom-box.svg +10 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { ClassPreset, PresetSet } from '../models/class-preset';
|
|
4
|
+
import { PreferencesPort } from '../contracts/ports/preferences.port';
|
|
5
|
+
import { IImageMetadata } from '../contracts/image.contract';
|
|
6
|
+
import { IChannelState } from '../contracts/channel-histogram-api.contract';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
/**
|
|
9
|
+
* Backend-neutral store for the **visualization session** — the state that
|
|
10
|
+
* describes *how* the current image is being viewed, independent of which
|
|
11
|
+
* rendering backend (Plotly or OpenSeadragon) is on screen:
|
|
12
|
+
*
|
|
13
|
+
* - the active colormap / LUT and its reverse-scale toggle,
|
|
14
|
+
* - the current image's physical metadata (µm/pixel etc.),
|
|
15
|
+
* - the classification label → colour map,
|
|
16
|
+
* - which on-canvas tool is currently active.
|
|
17
|
+
*
|
|
18
|
+
* Both backends read and write this single instance, so they stay in lock-step
|
|
19
|
+
* regardless of which one is rendering. Previously this state lived inside
|
|
20
|
+
* `PlotlyService`, which forced the OpenSeadragon backend to depend on the
|
|
21
|
+
* Plotly service purely to read the shared colormap; this store removes that
|
|
22
|
+
* coupling — neither backend owns the session state.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: region *geometry* (the drawn shapes / per-image region cache) still
|
|
25
|
+
* lives in `PlotlyService` for now; extracting it into a sibling region store
|
|
26
|
+
* is a separate follow-up.
|
|
27
|
+
*/
|
|
28
|
+
export declare class VisualizerStore {
|
|
29
|
+
private readonly http;
|
|
30
|
+
private readonly prefsPort;
|
|
31
|
+
private readonly colormap$;
|
|
32
|
+
private readonly reverseScale$;
|
|
33
|
+
private readonly imageMeta$;
|
|
34
|
+
private readonly channelStates$;
|
|
35
|
+
private defaultChannelStates;
|
|
36
|
+
private readonly grayscale$;
|
|
37
|
+
private readonly invert$;
|
|
38
|
+
private presetSet;
|
|
39
|
+
private readonly presetSet$;
|
|
40
|
+
private readonly savePresets$;
|
|
41
|
+
private presetsLoaded;
|
|
42
|
+
private readonly activeTool$;
|
|
43
|
+
constructor(http?: HttpClient | null, prefsPort?: PreferencesPort | null);
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the colormap tree's LUT keys (e.g. "GREYS_LUT") to their colour
|
|
46
|
+
* arrays from a served JSON asset. The ~630 KB of LUT data lives outside the
|
|
47
|
+
* bundle (as `/assets/plotting/colormap-luts.json`) so it never enters the TS
|
|
48
|
+
* / webpack module graph — that data-in-source was the main driver of the
|
|
49
|
+
* build's >2 GB heap. Named Plotly scales (e.g. "Viridis") aren't keys in the
|
|
50
|
+
* JSON, so they're left untouched. The default LUT ("Greys Inv" → the Plotly
|
|
51
|
+
* "Greys" scale) needs no JSON, so the first render works before this resolves.
|
|
52
|
+
*/
|
|
53
|
+
private loadColormapLuts;
|
|
54
|
+
private initPresetPersistence;
|
|
55
|
+
private normalizePresetSet;
|
|
56
|
+
/** Queue a debounced save. No-ops until the initial load resolves (and when no port is bound). */
|
|
57
|
+
private queuePresetSave;
|
|
58
|
+
/** Persist the current preset set immediately, bypassing the incremental-edit
|
|
59
|
+
* debounce. Used for explicit bulk actions (Apply / Import / Reset). */
|
|
60
|
+
private savePresetsNow;
|
|
61
|
+
/** The available colormap options (tree for the LUT dropdown). */
|
|
62
|
+
getColormapOptions(): any;
|
|
63
|
+
getColormap(): Observable<any>;
|
|
64
|
+
/** Synchronous current value — for render paths that read it while building
|
|
65
|
+
* a frame (e.g. Plotly's colorscale) rather than subscribing. */
|
|
66
|
+
currentColormap(): any;
|
|
67
|
+
setColormap(colormap: any): void;
|
|
68
|
+
getReverseScale(): Observable<boolean>;
|
|
69
|
+
currentReverseScale(): boolean;
|
|
70
|
+
setReverseScale(reverse: boolean): void;
|
|
71
|
+
getImageMeta(): Observable<IImageMetadata[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Patch the physical pixel size (µm/pixel) onto the current image meta and
|
|
74
|
+
* re-emit, without touching the derived channels. The OSD tile descriptor
|
|
75
|
+
* (from the server's Bio-Formats `/tiles/info`) is authoritative for scaled
|
|
76
|
+
* images — `imageInfo.imageMeta` often carries no mpp — so the renderer feeds
|
|
77
|
+
* it here once the descriptor loads, letting area/scale consumers (the Region
|
|
78
|
+
* Editor) show µm²/mm² instead of px². No-op when neither axis is positive.
|
|
79
|
+
*/
|
|
80
|
+
setPhysicalPixelSize(mppX?: number, mppY?: number): void;
|
|
81
|
+
setImageMeta(imageMeta: IImageMetadata[]): void;
|
|
82
|
+
/** Channels for an image:
|
|
83
|
+
* - RGB (rgbChannels ≥ 3) → Red/Green/Blue;
|
|
84
|
+
* - multichannel fluorescence (channelCount > 1) → one channel per band,
|
|
85
|
+
* named/tinted from the server `channelInfo` when present, else a palette;
|
|
86
|
+
* - otherwise a single grayscale "Intensity" channel. */
|
|
87
|
+
private deriveChannels;
|
|
88
|
+
getChannelStates(): Observable<IChannelState[]>;
|
|
89
|
+
currentChannelStates(): IChannelState[];
|
|
90
|
+
/** Patch one channel by index (immutable update). */
|
|
91
|
+
setChannelState(index: number, partial: Partial<IChannelState>): void;
|
|
92
|
+
/** Replace all channel states (used by auto/reset). */
|
|
93
|
+
setChannelStates(states: IChannelState[]): void;
|
|
94
|
+
/** Reset one channel's display window, gamma AND colour to the values derived
|
|
95
|
+
* for the current image (full 0..255 range, gamma 1, the channel's default
|
|
96
|
+
* tint). Falls back to neutral defaults if no baseline was captured. */
|
|
97
|
+
resetChannelState(index: number): void;
|
|
98
|
+
getGrayscale(): Observable<boolean>;
|
|
99
|
+
currentGrayscale(): boolean;
|
|
100
|
+
setGrayscale(on: boolean): void;
|
|
101
|
+
getInvert(): Observable<boolean>;
|
|
102
|
+
currentInvert(): boolean;
|
|
103
|
+
setInvert(on: boolean): void;
|
|
104
|
+
/** Name→colour view of the current preset set (compat for backend renderers
|
|
105
|
+
* that still read a Map). */
|
|
106
|
+
getClassificationColors(): Map<string, string>;
|
|
107
|
+
/** Upsert a single class colour (used by the editor's per-class colour picker). */
|
|
108
|
+
setClassificationColor(label: string, color: string): void;
|
|
109
|
+
getPresetSet(): PresetSet;
|
|
110
|
+
getPresetSet$(): Observable<PresetSet>;
|
|
111
|
+
setPresetSet(set: PresetSet): void;
|
|
112
|
+
/** Add or update a class (keyed by exact stored `name`). */
|
|
113
|
+
upsertClass(preset: ClassPreset): void;
|
|
114
|
+
removeClass(name: string): void;
|
|
115
|
+
resetPresets(): void;
|
|
116
|
+
/** Force a (debounced) write-back of the current preset set. */
|
|
117
|
+
persistPresets(): void;
|
|
118
|
+
getActiveTool$(): Observable<string | null>;
|
|
119
|
+
getActiveTool(): string | null;
|
|
120
|
+
setActiveTool(tool: string | null): void;
|
|
121
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerStore, [{ optional: true; }, { optional: true; }]>;
|
|
122
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<VisualizerStore>;
|
|
123
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { WandService } from '../wand/wand.service';
|
|
2
|
+
import { WandToolHost } from '../wand/wand-tool.service';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/** Brush parameters. `size` is the brush *diameter* in matrix (image) pixels. */
|
|
5
|
+
export interface BrushOptions {
|
|
6
|
+
size?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The brush host is identical to the wand's — both need the cached-image
|
|
10
|
+
* coordinate frame, the overlay container, the coordinate transform, and
|
|
11
|
+
* read/write access to the shared region list. The brush ignores the wand's
|
|
12
|
+
* pixel values (it paints a geometric disc rather than flood-filling by colour),
|
|
13
|
+
* so reusing {@link WandToolHost} lets each backend bind the same host object.
|
|
14
|
+
*/
|
|
15
|
+
export type BrushToolHost = WandToolHost;
|
|
16
|
+
/**
|
|
17
|
+
* QuPath-style brush tool. Painting a stroke unions a disc of the configured
|
|
18
|
+
* size into the active region as the cursor drags; holding <kbd>Shift</kbd>
|
|
19
|
+
* subtracts instead (eraser brush), matching the wand's modifier. The stroke is
|
|
20
|
+
* accumulated as a bbox-relative mask (exactly like {@link WandToolService}) so
|
|
21
|
+
* the brush inherits the wand's adopt-existing-region and merge-on-touch
|
|
22
|
+
* behaviour, then the union boundary is traced back into a polygon Region.
|
|
23
|
+
*
|
|
24
|
+
* No pixel sampling and no cursor indicator: the painted stroke itself shows the
|
|
25
|
+
* brush size. The overlay canvas only captures the pointer.
|
|
26
|
+
*
|
|
27
|
+
* Lifecycle mirrors the wand: a backend binds its host once, then toggles the
|
|
28
|
+
* tool with `setMode(true | false, options)`.
|
|
29
|
+
*
|
|
30
|
+
* Holes / donuts (jit-ui#85): brushing a ring that encloses an unpainted area
|
|
31
|
+
* keeps the enclosed hole — {@link WandService.maskToPolygons} traces interior
|
|
32
|
+
* rings into {@link Polygon.holes}, which adopt/merge preserve (the re-rasterize
|
|
33
|
+
* punches the holes back out). The OpenSeadragon overlay renders them with
|
|
34
|
+
* even-odd fill and GeoJSON round-trips them as extra Polygon rings. The Plotly
|
|
35
|
+
* (Heatmap) backend currently renders the filled exterior only.
|
|
36
|
+
*/
|
|
37
|
+
export declare class BrushToolService {
|
|
38
|
+
private wandService;
|
|
39
|
+
private host;
|
|
40
|
+
private overlay;
|
|
41
|
+
private active;
|
|
42
|
+
/**
|
|
43
|
+
* Accumulated brush region (bbox-relative mask). Persists across mouseup so a
|
|
44
|
+
* subsequent stroke extends the *same* region until the user switches tool —
|
|
45
|
+
* matching QuPath, and the wand.
|
|
46
|
+
*/
|
|
47
|
+
private stroke;
|
|
48
|
+
/** Id of the region this stroke is editing (null = a fresh region). For an
|
|
49
|
+
* erase that splits the region, this tracks the largest resulting piece. */
|
|
50
|
+
private strokeRegionId;
|
|
51
|
+
/** Readback ratio/origin signature the active stroke is tied to; a change (zoom/pan) invalidates
|
|
52
|
+
* it so it isn't re-committed at a different scale (jit-ui#102). */
|
|
53
|
+
private strokeRatioSig;
|
|
54
|
+
/** Ids of the extra regions produced when an erase stroke splits a region
|
|
55
|
+
* into disconnected pieces — reused across drag ticks so the split pieces
|
|
56
|
+
* keep stable identities instead of being recreated each tick. */
|
|
57
|
+
private strokeExtraIds;
|
|
58
|
+
/** Previous cursor position (matrix coords) within the current drag, so fast
|
|
59
|
+
* drags paint a continuous stroke rather than disconnected dabs. */
|
|
60
|
+
private lastMatrix;
|
|
61
|
+
private dragging;
|
|
62
|
+
private size;
|
|
63
|
+
private readonly boundMouseDown;
|
|
64
|
+
private readonly boundMouseMove;
|
|
65
|
+
private readonly boundMouseUp;
|
|
66
|
+
constructor(wandService: WandService);
|
|
67
|
+
/** Wire the tool to its host. Must be called once before `setMode(true)`. */
|
|
68
|
+
bindHost(host: BrushToolHost): void;
|
|
69
|
+
/** Toggle the brush on/off. */
|
|
70
|
+
setMode(active: boolean, options?: BrushOptions): void;
|
|
71
|
+
/** Live-update the brush size (matrix-pixel diameter). */
|
|
72
|
+
setSize(size: number): void;
|
|
73
|
+
/** Drop the active brush region so the next stroke starts a new one. */
|
|
74
|
+
clearActiveRegion(): void;
|
|
75
|
+
private createOverlay;
|
|
76
|
+
private destroyOverlay;
|
|
77
|
+
private onMouseDown;
|
|
78
|
+
private onMouseMove;
|
|
79
|
+
private onMouseUp;
|
|
80
|
+
private resetStroke;
|
|
81
|
+
/**
|
|
82
|
+
* Paint (or erase) a disc — or a swept line of discs since the previous
|
|
83
|
+
* tick — into the active region's accumulator mask, re-trace the boundary,
|
|
84
|
+
* and commit the updated Region. Shift = erase.
|
|
85
|
+
*/
|
|
86
|
+
private applyAtClient;
|
|
87
|
+
/**
|
|
88
|
+
* OR (add) or AND-NOT (erase) a filled disc of `radius` matrix-pixels centred
|
|
89
|
+
* at (cx, cy) into the accumulator, growing its bbox to fit when adding.
|
|
90
|
+
*/
|
|
91
|
+
private stampDisc;
|
|
92
|
+
/**
|
|
93
|
+
* Ensure the accumulator's bbox covers [px0,py0,px1,py1) (clamped to the
|
|
94
|
+
* image), allocating/copying a larger mask when it must grow. Returns false if
|
|
95
|
+
* the requested box is empty after clamping.
|
|
96
|
+
*/
|
|
97
|
+
private ensureStrokeCovers;
|
|
98
|
+
/** True if (matrixX, matrixY) lies inside the current stroke accumulator. */
|
|
99
|
+
private pointInStroke;
|
|
100
|
+
/**
|
|
101
|
+
* Remove the region(s) referenced by the stroke and reset it — used when an
|
|
102
|
+
* erase stroke deletes every pixel.
|
|
103
|
+
*/
|
|
104
|
+
private dropActiveShape;
|
|
105
|
+
/** A region's closed-polygon vertices (image/data coords), or null when it
|
|
106
|
+
* isn't a fillable closed polygon (rectangles, open polylines). */
|
|
107
|
+
private regionVerts;
|
|
108
|
+
/** Whether a region can be faithfully rasterized at this zoom (rx/ry = data units per matrix px).
|
|
109
|
+
* Editing rasterizes + re-traces the region, so one whose matrix-space bbox exceeds the
|
|
110
|
+
* rasterizer cap would come back rescaled/corrupted (e.g. a whole-slide annotation while zoomed
|
|
111
|
+
* out) — skip adopt/merge for those and only edit them near native zoom (jit-ui#102). */
|
|
112
|
+
private fitsForEdit;
|
|
113
|
+
/** Convert a region's hole rings from image/data coords into matrix coords so
|
|
114
|
+
* they line up with the stroke accumulator (jit-ui#85). */
|
|
115
|
+
private holesToMatrix;
|
|
116
|
+
/**
|
|
117
|
+
* If (matrixX, matrixY) falls inside an existing region, rasterize it into the
|
|
118
|
+
* stroke accumulator so subsequent brush input extends it rather than starting
|
|
119
|
+
* a new region.
|
|
120
|
+
*/
|
|
121
|
+
private tryAdoptShapeAt;
|
|
122
|
+
/**
|
|
123
|
+
* Fold every region whose rasterized mask overlaps the stroke mask into the
|
|
124
|
+
* stroke and remove it from the list, so brushing across regions merges them.
|
|
125
|
+
*/
|
|
126
|
+
private mergeOverlappingShapes;
|
|
127
|
+
/**
|
|
128
|
+
* Commit the stroke's connected pieces as regions — the largest keeps the
|
|
129
|
+
* stroke's region identity, additional pieces (from an erase that split the
|
|
130
|
+
* region) become their own regions tracked in `strokeExtraIds` so they keep
|
|
131
|
+
* stable identities across drag ticks. The store mints ids on commit, which
|
|
132
|
+
* we read back so subsequent ticks replace the same regions.
|
|
133
|
+
*/
|
|
134
|
+
private commitComponents;
|
|
135
|
+
/**
|
|
136
|
+
* Build a Region for one component and insert it into `regions`: replace the
|
|
137
|
+
* region with `id` in place if it exists, otherwise append a new one. Returns
|
|
138
|
+
* the Region instance (its id is minted later by the store when new).
|
|
139
|
+
*/
|
|
140
|
+
private upsertComponent;
|
|
141
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<BrushToolService, never>;
|
|
142
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<BrushToolService>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CachedImageData } from '../wand/wand-tool.service';
|
|
2
|
+
/** A client-side crop of the loaded image — the browser equivalent of the JIT
|
|
3
|
+
* server slide-crop, taken from the pixels already in the viewer. */
|
|
4
|
+
export interface CroppedImage {
|
|
5
|
+
/** RGBA pixels of the crop, row-major. */
|
|
6
|
+
data: Uint8ClampedArray;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
/** Crop origin in image (matrix) coords — lets masks map back to the frame. */
|
|
10
|
+
matrixX0: number;
|
|
11
|
+
matrixY0: number;
|
|
12
|
+
}
|
|
13
|
+
/** A box in image *data* coordinates (as stored on a Rectangle region). */
|
|
14
|
+
export interface DataBox {
|
|
15
|
+
x0: number;
|
|
16
|
+
y0: number;
|
|
17
|
+
x1: number;
|
|
18
|
+
y1: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Browser "slide crop": extract the box region (data coords) from a cached image
|
|
22
|
+
* frame as an RGBA image, clamped to the image bounds. Grayscale frames are
|
|
23
|
+
* replicated across R/G/B; RGB tuples are copied through. Returns null when the
|
|
24
|
+
* box is empty/outside the image.
|
|
25
|
+
*
|
|
26
|
+
* The output feeds an automatic segmenter (e.g. cellpose-SAM): run it on the
|
|
27
|
+
* crop, then offset the resulting masks by (matrixX0, matrixY0) to place them
|
|
28
|
+
* back on the full frame.
|
|
29
|
+
*/
|
|
30
|
+
export declare function cropImageRegion(cached: CachedImageData, frameIndex: number, box: DataBox): CroppedImage | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { WandService } from '../wand/wand.service';
|
|
3
|
+
import { WandToolHost } from '../wand/wand-tool.service';
|
|
4
|
+
import { ICellSegmenter } from '../../contracts/cell-segmenter.contract';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Automatic cell segmentation inside drawn boxes (jit-ui#90). For each rectangle
|
|
8
|
+
* it client slide-crops the box from the loaded image, runs an automatic
|
|
9
|
+
* segmenter (cellpose-SAM, supplied by the host via {@link ICellSegmenter}) on
|
|
10
|
+
* the crop, and turns every cell instance into a region — offset back onto the
|
|
11
|
+
* frame. The prompt rectangle is replaced by its cell regions.
|
|
12
|
+
*
|
|
13
|
+
* This is the complement to the promptable SAM tool: cellpose-SAM can't take a
|
|
14
|
+
* box prompt, so the box just bounds the crop it segments automatically.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CellSegmentToolService {
|
|
17
|
+
private wandService;
|
|
18
|
+
private host;
|
|
19
|
+
readonly status$: BehaviorSubject<string>;
|
|
20
|
+
readonly busy$: BehaviorSubject<boolean>;
|
|
21
|
+
readonly progress$: BehaviorSubject<number>;
|
|
22
|
+
constructor(wandService: WandService);
|
|
23
|
+
bindHost(host: WandToolHost): void;
|
|
24
|
+
/**
|
|
25
|
+
* Crop + cellpose-segment every rectangle; append the cell regions and drop
|
|
26
|
+
* the prompt rectangles that produced cells. Returns the number of regions added.
|
|
27
|
+
*/
|
|
28
|
+
segmentBoxes(segmenter: ICellSegmenter): Promise<number>;
|
|
29
|
+
private makeRegion;
|
|
30
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CellSegmentToolService, never>;
|
|
31
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<CellSegmentToolService>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ICellSegmenter, CellSegmentation, CellSegmentProgress } from '../../contracts/cell-segmenter.contract';
|
|
2
|
+
import type { Cellpose } from 'cellpose-js';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Default in-library {@link ICellSegmenter} backed by cellpose-js (WebGPU/WASM
|
|
6
|
+
* ONNX) — so the toolbar's automatic **Cellpose** tool works out of the box in
|
|
7
|
+
* any app, with no host wiring (jit-ui#90). `cellpose-js` is a peer dependency
|
|
8
|
+
* and is **lazy-imported** on first use, so apps that never run Cellpose pay
|
|
9
|
+
* nothing for it. A host can still override the {@link CELL_SEGMENTER} token to
|
|
10
|
+
* supply a different implementation.
|
|
11
|
+
*
|
|
12
|
+
* Owns a single shared {@link Cellpose} instance (one model load, one WebGPU
|
|
13
|
+
* session, one worker). {@link getModel} exposes that instance so a host's
|
|
14
|
+
* processing pipeline can reuse it instead of loading the ~588 MB model twice.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CellposeSegmenterService implements ICellSegmenter {
|
|
17
|
+
private instance;
|
|
18
|
+
private loading;
|
|
19
|
+
private modelUrl;
|
|
20
|
+
/** Point the segmenter at a different hosted CPSAM ONNX (before first use). */
|
|
21
|
+
setModelUrl(url: string): void;
|
|
22
|
+
segmentCells(image: {
|
|
23
|
+
data: Uint8ClampedArray;
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
}, progress?: CellSegmentProgress): Promise<CellSegmentation>;
|
|
27
|
+
/**
|
|
28
|
+
* The shared Cellpose instance, lazily created on first use and reused
|
|
29
|
+
* thereafter (deduped across concurrent callers). Exposed so a host pipeline
|
|
30
|
+
* engine can run richer `segment()` options on the same instance. `onProgress`
|
|
31
|
+
* reports raw downloaded/total bytes (`total` is null when unknown); `onStatus`
|
|
32
|
+
* reports worker-init phase strings.
|
|
33
|
+
*/
|
|
34
|
+
getModel(onProgress?: (loaded: number, total: number | null) => void, onStatus?: (status: string) => void): Promise<Cellpose>;
|
|
35
|
+
/** Whether the shared model is already loaded (warm). */
|
|
36
|
+
isLoaded(): boolean;
|
|
37
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CellposeSegmenterService, never>;
|
|
38
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<CellposeSegmenterService>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ISamSession, SamEmbedding, SamMaskResult, SamModelDef, SamPrompt } from '../../contracts/sam.contract';
|
|
2
|
+
export declare class OnnxSamSession implements ISamSession {
|
|
3
|
+
private mode;
|
|
4
|
+
private loaded;
|
|
5
|
+
private model;
|
|
6
|
+
private inputSize;
|
|
7
|
+
private worker;
|
|
8
|
+
private seq;
|
|
9
|
+
private readonly pending;
|
|
10
|
+
private encoder;
|
|
11
|
+
private decoder;
|
|
12
|
+
private nextToken;
|
|
13
|
+
private readonly embeddings;
|
|
14
|
+
loadModel(model: SamModelDef, onProgress?: (fraction: number) => void): Promise<void>;
|
|
15
|
+
isLoaded(): boolean;
|
|
16
|
+
embed(image: {
|
|
17
|
+
data: Uint8ClampedArray;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
}): Promise<SamEmbedding>;
|
|
21
|
+
decode(embedding: SamEmbedding, prompt: SamPrompt): Promise<SamMaskResult>;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
private ensureWorker;
|
|
24
|
+
private call;
|
|
25
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Override where onnxruntime-web loads its WASM sidecars from. See module docs. */
|
|
2
|
+
export declare function setOrtWasmBase(base: string): void;
|
|
3
|
+
/** The configured onnxruntime-web WASM base (default `'/assets/ort/'`). */
|
|
4
|
+
export declare function getOrtWasmBase(): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SamModelDef } from '../../contracts/sam.contract';
|
|
2
|
+
/**
|
|
3
|
+
* Registry of promptable SAM models for the browser segment tool.
|
|
4
|
+
*
|
|
5
|
+
* micro-sam (microscopy-finetuned, promptable) and patho-sam (histopathology-
|
|
6
|
+
* finetuned) are entries; SAM3 slots in here once its quantized ONNX export and
|
|
7
|
+
* a `variant: 'sam3'` decoder path are published. The
|
|
8
|
+
* automatic cellpose-SAM model is intentionally NOT here — it's not promptable
|
|
9
|
+
* and lives in the processing-pipeline's cellpose engine as the automatic tool.
|
|
10
|
+
*
|
|
11
|
+
* `encoderUrl` / `decoderUrl` point at the public HF-hosted ONNX pairs by
|
|
12
|
+
* default, so the segment tools work out of the box in any host (mirroring the
|
|
13
|
+
* cellpose default). A host can repoint them at its own hosting (e.g. a GCS
|
|
14
|
+
* bucket) at runtime via {@link setSamModelUrls}; an empty URL disables a model
|
|
15
|
+
* (the session throws a clear "not configured" error). Export/quantization
|
|
16
|
+
* tooling: the sibling `browser-onnx-tools` project; see docs/sam-segmentation-design.md.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SAM_MODELS: SamModelDef[];
|
|
19
|
+
/** Initial default model id (overridden at runtime once the host configures a
|
|
20
|
+
* hosted model via {@link setSamModelUrls}). */
|
|
21
|
+
export declare const DEFAULT_SAM_MODEL_ID: string;
|
|
22
|
+
/** Look up a model by id; falls back to the active default when id is
|
|
23
|
+
* unknown/absent. */
|
|
24
|
+
export declare function getSamModel(id?: string): SamModelDef;
|
|
25
|
+
/** The active default model id (what the tools/picker use when unset). */
|
|
26
|
+
export declare function getDefaultSamModelId(): string;
|
|
27
|
+
/** Set the active default model id (must be a registered model). */
|
|
28
|
+
export declare function setDefaultSamModel(id: string): void;
|
|
29
|
+
/** True when a model has both ONNX URLs configured (i.e. it can actually run). */
|
|
30
|
+
export declare function isSamModelReady(model: SamModelDef): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Point a registered model at its hosted ONNX pair (HF / GCS). Called by the
|
|
33
|
+
* host's composition root once hosting is decided, keeping deployment URLs out
|
|
34
|
+
* of the library.
|
|
35
|
+
*/
|
|
36
|
+
export declare function setSamModelUrls(id: string, encoderUrl: string, decoderUrl: string): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as ort from 'onnxruntime-web';
|
|
2
|
+
import type { SamPrompt } from '../../contracts/sam.contract';
|
|
3
|
+
/**
|
|
4
|
+
* Shared, DOM-free SAM inference primitives used by BOTH inference backends:
|
|
5
|
+
* the in-process session (`OnnxSamSession`, main thread) and the dedicated
|
|
6
|
+
* Web Worker (`onnx-sam.worker`). Keeping these here avoids duplicating the
|
|
7
|
+
* preprocessing / decoder-prompt math and keeps the worker DOM-free (it can't
|
|
8
|
+
* import sam-prompt.ts, which pulls in DOM types).
|
|
9
|
+
*/
|
|
10
|
+
export declare const PIXEL_MEAN: number[];
|
|
11
|
+
export declare const PIXEL_STD: number[];
|
|
12
|
+
/** Encoder output for one image, reused across prompts. */
|
|
13
|
+
export interface CoreEmbedding {
|
|
14
|
+
data: Float32Array;
|
|
15
|
+
dims: number[];
|
|
16
|
+
scale: number;
|
|
17
|
+
imageWidth: number;
|
|
18
|
+
imageHeight: number;
|
|
19
|
+
}
|
|
20
|
+
/** Decoder output: a binary mask at original-image resolution. */
|
|
21
|
+
export interface CoreMask {
|
|
22
|
+
mask: Uint8Array;
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
iou: number;
|
|
26
|
+
}
|
|
27
|
+
/** Fetch a model as an ArrayBuffer, streaming progress and caching in the Cache
|
|
28
|
+
* API (so it isn't re-downloaded). Cache hit → progress jumps to 1. Works on
|
|
29
|
+
* the main thread and in a worker (both have `fetch` + `caches`). */
|
|
30
|
+
export declare function fetchModel(url: string, onProgress?: (f: number) => void): Promise<ArrayBuffer>;
|
|
31
|
+
/** Resize (long side → size, bilinear), pad to size×size, SAM-normalize → CHW float32. */
|
|
32
|
+
export declare function preprocess(data: Uint8ClampedArray, width: number, height: number, size: number, scale: number): Float32Array;
|
|
33
|
+
/** Assemble decoder `point_coords`/`point_labels` from a prompt, in encoder
|
|
34
|
+
* (resized) space. Box = two points labelled 2/3; points use 1/0; a points-only
|
|
35
|
+
* prompt pads the absent box slot with `[0,0]` labelled -1. */
|
|
36
|
+
export declare function buildDecoderPrompt(prompt: SamPrompt, scale: number): {
|
|
37
|
+
pointCoords: Float32Array;
|
|
38
|
+
pointLabels: Float32Array;
|
|
39
|
+
numPoints: number;
|
|
40
|
+
};
|
|
41
|
+
/** Binarize mask logits (`> threshold`) into a 0/1 mask. */
|
|
42
|
+
export declare function binarizeMask(logits: Float32Array, threshold?: number): Uint8Array;
|
|
43
|
+
/** Index of the highest-scoring mask in a multimask decoder output. */
|
|
44
|
+
export declare function bestMaskIndex(iou: Float32Array | number[]): number;
|
|
45
|
+
/** Run the encoder on an RGBA image → a reusable embedding. */
|
|
46
|
+
export declare function runEncoder(encoder: ort.InferenceSession, data: Uint8ClampedArray, width: number, height: number, inputSize: number): Promise<CoreEmbedding>;
|
|
47
|
+
/** Run the decoder for one prompt against a cached embedding → a binary mask. */
|
|
48
|
+
export declare function runDecoder(decoder: ort.InferenceSession, e: CoreEmbedding, prompt: SamPrompt): Promise<CoreMask>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { WandService } from '../wand/wand.service';
|
|
3
|
+
import { WandToolHost } from '../wand/wand-tool.service';
|
|
4
|
+
import { ISamSession } from '../../contracts/sam.contract';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/** Interactive SAM point-prompt tool (jit-ui#90, P1).
|
|
7
|
+
*
|
|
8
|
+
* Each plain (positive) click segments the clicked object as its OWN new region
|
|
9
|
+
* — clicking a second fiber doesn't extend the first into it. Shift/Alt-click
|
|
10
|
+
* adds a negative (exclude) point that refines the CURRENT object, re-running
|
|
11
|
+
* the decoder against the (cached) image embedding and updating its live region.
|
|
12
|
+
* `commit()` (Enter) finalises early; `clear()` (Esc) discards the current
|
|
13
|
+
* prompt + its preview region. Mirrors the wand/brush on-canvas tool pattern and
|
|
14
|
+
* reuses WandService.maskToPolygons + the shared region store.
|
|
15
|
+
*
|
|
16
|
+
* Inference is behind {@link ISamSession} (lazy onnxruntime-web in production,
|
|
17
|
+
* a fake in tests).
|
|
18
|
+
*/
|
|
19
|
+
export declare class SamPointToolService {
|
|
20
|
+
private wandService;
|
|
21
|
+
private host;
|
|
22
|
+
private overlay;
|
|
23
|
+
private session;
|
|
24
|
+
/** In-flight session load — dedupes a warm-up preload against the first click. */
|
|
25
|
+
private sessionLoad;
|
|
26
|
+
private model;
|
|
27
|
+
private embedding;
|
|
28
|
+
private embeddingKey;
|
|
29
|
+
/** Accumulated point prompts, in image (matrix) coords. */
|
|
30
|
+
private points;
|
|
31
|
+
/** Id of the in-progress (preview) region being refined, if committed to store. */
|
|
32
|
+
private regionId;
|
|
33
|
+
readonly status$: BehaviorSubject<string>;
|
|
34
|
+
readonly busy$: BehaviorSubject<boolean>;
|
|
35
|
+
/** Encoder-download progress (0..1) on the first click; -1 when not downloading. */
|
|
36
|
+
readonly progress$: BehaviorSubject<number>;
|
|
37
|
+
private readonly boundMouseDown;
|
|
38
|
+
constructor(wandService: WandService);
|
|
39
|
+
bindHost(host: WandToolHost): void;
|
|
40
|
+
setModel(id: string): void;
|
|
41
|
+
/** Test seam: inject a fake/alternate session. */
|
|
42
|
+
useSession(session: ISamSession): void;
|
|
43
|
+
setMode(active: boolean): void;
|
|
44
|
+
/** Eagerly load the model in the background (fire-and-forget). Safe to call
|
|
45
|
+
* repeatedly — it dedupes against an in-flight load and an existing session. */
|
|
46
|
+
preload(): void;
|
|
47
|
+
/** Finalise the current object: keep its region, start fresh next click. */
|
|
48
|
+
commit(): void;
|
|
49
|
+
/** Discard the in-progress prompt + its preview region. */
|
|
50
|
+
clear(): void;
|
|
51
|
+
private createOverlay;
|
|
52
|
+
private destroyOverlay;
|
|
53
|
+
private onMouseDown;
|
|
54
|
+
/** Replace (or insert) the in-progress preview region in the shared store. */
|
|
55
|
+
private upsertPreview;
|
|
56
|
+
private ensureSession;
|
|
57
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SamPointToolService, never>;
|
|
58
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SamPointToolService>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CachedImageData } from '../wand/wand-tool.service';
|
|
2
|
+
import { SamPrompt } from '../../contracts/sam.contract';
|
|
3
|
+
/**
|
|
4
|
+
* Pure helpers for SAM inference — no DOM, no ORT, no `this` — so they're unit
|
|
5
|
+
* testable. The session uses these to assemble decoder inputs and to convert
|
|
6
|
+
* the cached image frame into the RGBA buffer the encoder preprocesses.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Build an RGBA buffer (the encoder's input image) from a cached image frame.
|
|
10
|
+
* Grayscale frames are replicated across R/G/B; RGB tuples are copied through.
|
|
11
|
+
* Out-of-range/missing pixels become opaque black.
|
|
12
|
+
*/
|
|
13
|
+
export declare function frameToRgba(cached: CachedImageData, frameIndex: number): Uint8ClampedArray;
|
|
14
|
+
/**
|
|
15
|
+
* Assemble the SAM decoder's `point_coords` / `point_labels` from a prompt.
|
|
16
|
+
*
|
|
17
|
+
* Coordinates are scaled into the encoder's resized space (`scale =
|
|
18
|
+
* inputSize / max(imageW, imageH)`). A box is encoded as two points labelled
|
|
19
|
+
* `2` (top-left) and `3` (bottom-right); positive/negative points use `1`/`0`.
|
|
20
|
+
* When there are points but no box, a padding point `[0,0]` labelled `-1` is
|
|
21
|
+
* appended (the decoder expects the box slots padded when absent).
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildDecoderPrompt(prompt: SamPrompt, scale: number): {
|
|
24
|
+
pointCoords: Float32Array;
|
|
25
|
+
pointLabels: Float32Array;
|
|
26
|
+
numPoints: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Binarize SAM mask logits (`> threshold`, default 0 per SAM's mask_threshold)
|
|
30
|
+
* into a 0/1 mask the contour tracer (WandService.maskToPolygons) consumes.
|
|
31
|
+
*/
|
|
32
|
+
export declare function binarizeMask(logits: Float32Array | number[], threshold?: number): Uint8Array;
|
|
33
|
+
/** Index of the highest-scoring mask in a multimask decoder output. */
|
|
34
|
+
export declare function bestMaskIndex(iouPredictions: Float32Array | number[]): number;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { WandService } from '../wand/wand.service';
|
|
3
|
+
import { WandToolHost } from '../wand/wand-tool.service';
|
|
4
|
+
import { ISamSession } from '../../contracts/sam.contract';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/** The SAM tool binds to the same host the wand/brush use, so each backend
|
|
7
|
+
* reuses its existing pixel readback + coordinate transform + region store. */
|
|
8
|
+
export type SamToolHost = WandToolHost;
|
|
9
|
+
/**
|
|
10
|
+
* Box-prompted SAM segmentation tool (jit-ui#90, P0). On `segmentBoxes()` it
|
|
11
|
+
* reads every rectangle region, runs the (cached) encoder once for the image,
|
|
12
|
+
* then runs the decoder per box, traces each mask to a polygon (reusing
|
|
13
|
+
* WandService.maskToPolygons) and commits them as regions.
|
|
14
|
+
*
|
|
15
|
+
* Inference is abstracted behind {@link ISamSession}: production uses the
|
|
16
|
+
* onnxruntime-web session (lazy-imported so ORT never loads in unit tests);
|
|
17
|
+
* tests inject a fake via {@link useSession}.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SamToolService {
|
|
20
|
+
private wandService;
|
|
21
|
+
private host;
|
|
22
|
+
private session;
|
|
23
|
+
private model;
|
|
24
|
+
/** Cached encoder embedding + the key (image identity) it was computed for. */
|
|
25
|
+
private embedding;
|
|
26
|
+
private embeddingKey;
|
|
27
|
+
/** Status text + busy flag for a spinner / toast in the host. */
|
|
28
|
+
readonly status$: BehaviorSubject<string>;
|
|
29
|
+
readonly busy$: BehaviorSubject<boolean>;
|
|
30
|
+
/** Encoder-download progress: -1 = not downloading, 0..1 = downloading. */
|
|
31
|
+
readonly progress$: BehaviorSubject<number>;
|
|
32
|
+
constructor(wandService: WandService);
|
|
33
|
+
/** Bind to the active backend's host (called by the backend, like the wand). */
|
|
34
|
+
bindHost(host: SamToolHost): void;
|
|
35
|
+
/** Choose the registered model to use; invalidates any cached embedding. */
|
|
36
|
+
setModel(id: string): void;
|
|
37
|
+
/** Drop the cached embedding (e.g. after the image/slice changes). */
|
|
38
|
+
invalidateEmbedding(): void;
|
|
39
|
+
/** Test seam: inject a fake/alternate inference session. */
|
|
40
|
+
useSession(session: ISamSession): void;
|
|
41
|
+
/**
|
|
42
|
+
* Segment every rectangle region with a box prompt and append the resulting
|
|
43
|
+
* masks as new polygon regions. Returns how many regions were added.
|
|
44
|
+
*/
|
|
45
|
+
segmentBoxes(): Promise<number>;
|
|
46
|
+
private makeRegion;
|
|
47
|
+
private ensureSession;
|
|
48
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SamToolService, never>;
|
|
49
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SamToolService>;
|
|
50
|
+
}
|