@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,202 @@
|
|
|
1
|
+
// src/lib/toolbar/segmentation/onnx-sam.worker.ts
|
|
2
|
+
import * as ort2 from "onnxruntime-web";
|
|
3
|
+
|
|
4
|
+
// src/lib/toolbar/segmentation/sam-onnx-core.ts
|
|
5
|
+
import * as ort from "onnxruntime-web";
|
|
6
|
+
var PIXEL_MEAN = [123.675, 116.28, 103.53];
|
|
7
|
+
var PIXEL_STD = [58.395, 57.12, 57.375];
|
|
8
|
+
var MODEL_CACHE = "sam-onnx";
|
|
9
|
+
async function fetchModel(url, onProgress) {
|
|
10
|
+
const cache = typeof caches !== "undefined" ? await caches.open(MODEL_CACHE).catch(() => null) : null;
|
|
11
|
+
const hit = cache ? await cache.match(url) : null;
|
|
12
|
+
if (hit) {
|
|
13
|
+
onProgress?.(1);
|
|
14
|
+
return await hit.arrayBuffer();
|
|
15
|
+
}
|
|
16
|
+
const resp = await fetch(url);
|
|
17
|
+
if (!resp.ok)
|
|
18
|
+
throw new Error(`fetch ${url} failed: HTTP ${resp.status}`);
|
|
19
|
+
const total = Number(resp.headers.get("content-length")) || 0;
|
|
20
|
+
if (!resp.body || !total) {
|
|
21
|
+
const buf = await resp.arrayBuffer();
|
|
22
|
+
onProgress?.(1);
|
|
23
|
+
if (cache)
|
|
24
|
+
await cache.put(url, new Response(buf)).catch(() => void 0);
|
|
25
|
+
return buf;
|
|
26
|
+
}
|
|
27
|
+
const reader = resp.body.getReader();
|
|
28
|
+
const chunks = [];
|
|
29
|
+
let received = 0;
|
|
30
|
+
for (; ; ) {
|
|
31
|
+
const { done, value } = await reader.read();
|
|
32
|
+
if (done)
|
|
33
|
+
break;
|
|
34
|
+
chunks.push(value);
|
|
35
|
+
received += value.length;
|
|
36
|
+
onProgress?.(received / total);
|
|
37
|
+
}
|
|
38
|
+
const out = new Uint8Array(received);
|
|
39
|
+
let off = 0;
|
|
40
|
+
for (const c of chunks) {
|
|
41
|
+
out.set(c, off);
|
|
42
|
+
off += c.length;
|
|
43
|
+
}
|
|
44
|
+
if (cache) {
|
|
45
|
+
await cache.put(url, new Response(out.slice(), { headers: { "content-length": String(received) } })).catch(() => void 0);
|
|
46
|
+
}
|
|
47
|
+
onProgress?.(1);
|
|
48
|
+
return out.buffer;
|
|
49
|
+
}
|
|
50
|
+
function preprocess(data, width, height, size, scale) {
|
|
51
|
+
const newW = Math.round(width * scale);
|
|
52
|
+
const newH = Math.round(height * scale);
|
|
53
|
+
const chw = new Float32Array(3 * size * size);
|
|
54
|
+
for (let y = 0; y < newH; y++) {
|
|
55
|
+
const sy = Math.min(height - 1, y / scale);
|
|
56
|
+
const y0 = Math.floor(sy);
|
|
57
|
+
const fy = sy - y0;
|
|
58
|
+
const y1 = Math.min(height - 1, y0 + 1);
|
|
59
|
+
for (let x = 0; x < newW; x++) {
|
|
60
|
+
const sx = Math.min(width - 1, x / scale);
|
|
61
|
+
const x0 = Math.floor(sx);
|
|
62
|
+
const fx = sx - x0;
|
|
63
|
+
const x1 = Math.min(width - 1, x0 + 1);
|
|
64
|
+
for (let c = 0; c < 3; c++) {
|
|
65
|
+
const p00 = data[(y0 * width + x0) * 4 + c];
|
|
66
|
+
const p01 = data[(y0 * width + x1) * 4 + c];
|
|
67
|
+
const p10 = data[(y1 * width + x0) * 4 + c];
|
|
68
|
+
const p11 = data[(y1 * width + x1) * 4 + c];
|
|
69
|
+
const top = p00 + (p01 - p00) * fx;
|
|
70
|
+
const bot = p10 + (p11 - p10) * fx;
|
|
71
|
+
const v = top + (bot - top) * fy;
|
|
72
|
+
chw[c * size * size + y * size + x] = (v - PIXEL_MEAN[c]) / PIXEL_STD[c];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return chw;
|
|
77
|
+
}
|
|
78
|
+
function buildDecoderPrompt(prompt, scale) {
|
|
79
|
+
const coords = [];
|
|
80
|
+
const labels = [];
|
|
81
|
+
for (const p of prompt.points ?? []) {
|
|
82
|
+
coords.push(p.x * scale, p.y * scale);
|
|
83
|
+
labels.push(p.label);
|
|
84
|
+
}
|
|
85
|
+
if (prompt.box) {
|
|
86
|
+
const b = prompt.box;
|
|
87
|
+
coords.push(b.x0 * scale, b.y0 * scale);
|
|
88
|
+
labels.push(2);
|
|
89
|
+
coords.push(b.x1 * scale, b.y1 * scale);
|
|
90
|
+
labels.push(3);
|
|
91
|
+
} else if (labels.length > 0) {
|
|
92
|
+
coords.push(0, 0);
|
|
93
|
+
labels.push(-1);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
pointCoords: Float32Array.from(coords),
|
|
97
|
+
pointLabels: Float32Array.from(labels),
|
|
98
|
+
numPoints: labels.length
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function binarizeMask(logits, threshold = 0) {
|
|
102
|
+
const out = new Uint8Array(logits.length);
|
|
103
|
+
for (let i = 0; i < logits.length; i++)
|
|
104
|
+
out[i] = logits[i] > threshold ? 1 : 0;
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
function bestMaskIndex(iou) {
|
|
108
|
+
let best = 0;
|
|
109
|
+
for (let i = 1; i < iou.length; i++)
|
|
110
|
+
if (iou[i] > iou[best])
|
|
111
|
+
best = i;
|
|
112
|
+
return best;
|
|
113
|
+
}
|
|
114
|
+
async function runEncoder(encoder2, data, width, height, inputSize2) {
|
|
115
|
+
const scale = inputSize2 / Math.max(width, height);
|
|
116
|
+
const input = preprocess(data, width, height, inputSize2, scale);
|
|
117
|
+
const out = await encoder2.run({
|
|
118
|
+
[encoder2.inputNames[0]]: new ort.Tensor("float32", input, [1, 3, inputSize2, inputSize2])
|
|
119
|
+
});
|
|
120
|
+
const emb = out[encoder2.outputNames[0]];
|
|
121
|
+
return {
|
|
122
|
+
data: emb.data,
|
|
123
|
+
dims: emb.dims,
|
|
124
|
+
scale,
|
|
125
|
+
imageWidth: width,
|
|
126
|
+
imageHeight: height
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function runDecoder(decoder2, e, prompt) {
|
|
130
|
+
const { pointCoords, pointLabels, numPoints } = buildDecoderPrompt(prompt, e.scale);
|
|
131
|
+
const out = await decoder2.run({
|
|
132
|
+
image_embeddings: new ort.Tensor("float32", e.data, e.dims),
|
|
133
|
+
point_coords: new ort.Tensor("float32", pointCoords, [1, numPoints, 2]),
|
|
134
|
+
point_labels: new ort.Tensor("float32", pointLabels, [1, numPoints]),
|
|
135
|
+
mask_input: new ort.Tensor("float32", new Float32Array(256 * 256), [1, 1, 256, 256]),
|
|
136
|
+
has_mask_input: new ort.Tensor("float32", Float32Array.from([0]), [1]),
|
|
137
|
+
orig_im_size: new ort.Tensor("float32", Float32Array.from([e.imageHeight, e.imageWidth]), [2])
|
|
138
|
+
});
|
|
139
|
+
const masks = out["masks"] ?? out[decoder2.outputNames[0]];
|
|
140
|
+
const iou = out["iou_predictions"]?.data ?? Float32Array.from([1]);
|
|
141
|
+
const [, m, h, w] = masks.dims;
|
|
142
|
+
const idx = bestMaskIndex(iou.slice(0, m));
|
|
143
|
+
const logits = masks.data.subarray(idx * h * w, (idx + 1) * h * w);
|
|
144
|
+
return { mask: binarizeMask(logits), width: w, height: h, iou: iou[idx] ?? 1 };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/lib/toolbar/segmentation/onnx-sam.worker.ts
|
|
148
|
+
var encoder = null;
|
|
149
|
+
var decoder = null;
|
|
150
|
+
var inputSize = 1024;
|
|
151
|
+
var nextToken = 1;
|
|
152
|
+
var embeddings = /* @__PURE__ */ new Map();
|
|
153
|
+
function post(msg, transfer = []) {
|
|
154
|
+
self.postMessage(msg, transfer);
|
|
155
|
+
}
|
|
156
|
+
async function onLoad(msg) {
|
|
157
|
+
ort2.env.wasm.wasmPaths = msg.wasmPaths;
|
|
158
|
+
inputSize = msg.inputSize || 1024;
|
|
159
|
+
const hasGpu = typeof navigator !== "undefined" && "gpu" in navigator;
|
|
160
|
+
const eps = msg.encoderProviders && msg.encoderProviders.length ? msg.encoderProviders : hasGpu ? ["webgpu", "wasm"] : ["wasm"];
|
|
161
|
+
const encBuf = await fetchModel(msg.encoderUrl, (f) => post({ id: msg.id, type: "progress", fraction: f }));
|
|
162
|
+
const decBuf = await fetchModel(msg.decoderUrl);
|
|
163
|
+
encoder = await ort2.InferenceSession.create(encBuf, { executionProviders: eps });
|
|
164
|
+
decoder = await ort2.InferenceSession.create(decBuf, { executionProviders: ["wasm"] });
|
|
165
|
+
post({ id: msg.id, type: "loaded" });
|
|
166
|
+
}
|
|
167
|
+
async function onEmbed(msg) {
|
|
168
|
+
if (!encoder)
|
|
169
|
+
throw new Error("SAM encoder not loaded.");
|
|
170
|
+
const data = new Uint8ClampedArray(msg.buffer);
|
|
171
|
+
const e = await runEncoder(encoder, data, msg.width, msg.height, inputSize);
|
|
172
|
+
const token = nextToken++;
|
|
173
|
+
embeddings.set(token, e);
|
|
174
|
+
if (embeddings.size > 3)
|
|
175
|
+
embeddings.delete(embeddings.keys().next().value);
|
|
176
|
+
post({ id: msg.id, type: "embedded", token, scale: e.scale, imageWidth: e.imageWidth, imageHeight: e.imageHeight, dims: e.dims });
|
|
177
|
+
}
|
|
178
|
+
async function onDecode(msg) {
|
|
179
|
+
if (!decoder)
|
|
180
|
+
throw new Error("SAM decoder not loaded.");
|
|
181
|
+
const e = embeddings.get(msg.token);
|
|
182
|
+
if (!e)
|
|
183
|
+
throw new Error("SAM embedding expired; re-encode the image.");
|
|
184
|
+
const r = await runDecoder(decoder, e, msg.prompt);
|
|
185
|
+
post(
|
|
186
|
+
{ id: msg.id, type: "decoded", width: r.width, height: r.height, iou: r.iou, buffer: r.mask.buffer },
|
|
187
|
+
[r.mask.buffer]
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
self.onmessage = async (ev) => {
|
|
191
|
+
const msg = ev.data;
|
|
192
|
+
try {
|
|
193
|
+
if (msg.type === "load")
|
|
194
|
+
await onLoad(msg);
|
|
195
|
+
else if (msg.type === "embed")
|
|
196
|
+
await onEmbed(msg);
|
|
197
|
+
else if (msg.type === "decode")
|
|
198
|
+
await onDecode(msg);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
post({ id: msg.id, type: "error", error: err instanceof Error ? err.message : String(err) });
|
|
201
|
+
}
|
|
202
|
+
};
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API of the jax-image-visualization library. External consumers import
|
|
3
|
+
* ONLY from here (`@jax-data-science/sci-image-visualizer`); everything else under `lib/` is
|
|
4
|
+
* internal. The surface is contracts + tokens + neutral models + the Angular
|
|
5
|
+
* module, plus the routing service the host's composition root binds the tokens
|
|
6
|
+
* to (`useExisting`).
|
|
7
|
+
*/
|
|
8
|
+
export * from './lib/contracts/visualizer.contract';
|
|
9
|
+
export * from './lib/contracts/region-editor-api.contract';
|
|
10
|
+
export * from './lib/contracts/ports/image-state.port';
|
|
11
|
+
export * from './lib/contracts/ports/tile-access.port';
|
|
12
|
+
export * from './lib/contracts/ports/region-io.port';
|
|
13
|
+
export * from './lib/contracts/ports/preferences.port';
|
|
14
|
+
export * from './lib/contracts/channel-histogram-api.contract';
|
|
15
|
+
export * from './lib/contracts/viz-config';
|
|
16
|
+
export * from './lib/contracts/image.contract';
|
|
17
|
+
export * from './lib/contracts/plot-type';
|
|
18
|
+
export * from './lib/contracts/toolbar-config';
|
|
19
|
+
export * from './lib/contracts/capabilities.contract';
|
|
20
|
+
export * from './lib/contracts/region-overlay.contract';
|
|
21
|
+
export * from './lib/contracts/display-types';
|
|
22
|
+
export * from './lib/contracts/sam.contract';
|
|
23
|
+
export * from './lib/contracts/cell-segmenter.contract';
|
|
24
|
+
export { CellposeSegmenterService } from './lib/toolbar/segmentation/cellpose-segmenter.service';
|
|
25
|
+
export { ProcessingImage } from './lib/processing/processing-image';
|
|
26
|
+
export { cropImage, CropImageOptions } from './lib/processing/image-crop';
|
|
27
|
+
export { ImageConverterService } from './lib/processing/image-converter.service';
|
|
28
|
+
export { SAM_MODELS, DEFAULT_SAM_MODEL_ID, getSamModel, isSamModelReady, setSamModelUrls, setDefaultSamModel, getDefaultSamModelId, } from './lib/toolbar/segmentation/sam-model-registry';
|
|
29
|
+
export { setOrtWasmBase, getOrtWasmBase } from './lib/toolbar/segmentation/ort-runtime-config';
|
|
30
|
+
export * from './lib/models/region';
|
|
31
|
+
export * from './lib/models/class-preset';
|
|
32
|
+
export { ShapeSelection } from './lib/models/shape';
|
|
33
|
+
export { VisualizationModule } from './lib/visualization.module';
|
|
34
|
+
export { RoutingVisualizerService } from './lib/routing-visualizer.service';
|
|
35
|
+
export { provideVisualization } from './lib/provide-visualization';
|
|
36
|
+
export { VisualizerComponent } from './lib/visualizer.component';
|
|
37
|
+
export { RegionEditorComponent } from './lib/region-editor/region-editor.component';
|
|
38
|
+
export { HexColorPickerComponent } from './lib/hex-color-picker/hex-color-picker.component';
|
|
39
|
+
export { ChannelHistogramComponent } from './lib/channel-histogram/channel-histogram.component';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { TreeNode } from 'primeng/api';
|
|
3
|
+
import { IChannelHistogramApi, IChannelState, IHistogram } from '../contracts/channel-histogram-api.contract';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Channels & Histogram pane: a non-modal, resizable, draggable dialog for
|
|
7
|
+
* brightness/contrast (per-channel display window), gamma, channel
|
|
8
|
+
* visibility/colour, the intensity histogram, and the colormap/reverse/invert
|
|
9
|
+
* controls (moved here from the toolbar). Every edit flows through
|
|
10
|
+
* {@link CHANNEL_HISTOGRAM_API} into the shared store, and both rendering
|
|
11
|
+
* backends recolor the displayed image live. The pane depends only on the
|
|
12
|
+
* contract, never the concrete visualizer.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ChannelHistogramComponent implements OnInit, OnDestroy {
|
|
15
|
+
private api;
|
|
16
|
+
/** Dialog visibility, two-way bound so the host (toolbar button) can open it. */
|
|
17
|
+
visible: boolean;
|
|
18
|
+
visibleChange: EventEmitter<boolean>;
|
|
19
|
+
readonly histogramDiv = "channel-histogram-plot";
|
|
20
|
+
readonly lutColors: readonly {
|
|
21
|
+
name: string;
|
|
22
|
+
color: string;
|
|
23
|
+
}[];
|
|
24
|
+
channels: IChannelState[];
|
|
25
|
+
selected: IChannelState | null;
|
|
26
|
+
invert: boolean;
|
|
27
|
+
logScale: boolean;
|
|
28
|
+
/** Bounded retries while the (async) histogram sampling resolves. */
|
|
29
|
+
private histRetries;
|
|
30
|
+
/** The selected channel's current histogram. Native bit depth (with
|
|
31
|
+
* observed/range fields) for >8-bit images, else the 8-bit client histogram.
|
|
32
|
+
* Drives the plot, the native window labels, and the export-button gate. */
|
|
33
|
+
hist: IHistogram | null;
|
|
34
|
+
private histSub?;
|
|
35
|
+
/** Which window control is being adjusted right now — drives a small
|
|
36
|
+
* non-blocking activity spinner next to that slider, since the image recolor
|
|
37
|
+
* isn't instant on large stacks. Cleared a short moment after movement stops
|
|
38
|
+
* (the recolor isn't directly observable, so this is a trailing heuristic). */
|
|
39
|
+
activeAdjust: 'min' | 'max' | 'gamma' | null;
|
|
40
|
+
private adjustTimer?;
|
|
41
|
+
colormapOptions: any;
|
|
42
|
+
selectedColormap: any;
|
|
43
|
+
private subs;
|
|
44
|
+
/** Keeps the Plotly histogram sized to the (resizable) dialog body. */
|
|
45
|
+
private resizeObserver?;
|
|
46
|
+
constructor(api: IChannelHistogramApi);
|
|
47
|
+
/** Per-channel pseudo-colour only applies when there's more than one channel
|
|
48
|
+
* (RGB / fluorescence). A single grayscale channel uses the colormap instead. */
|
|
49
|
+
get multichannel(): boolean;
|
|
50
|
+
ngOnInit(): void;
|
|
51
|
+
ngOnDestroy(): void;
|
|
52
|
+
onVisibleChange(v: boolean): void;
|
|
53
|
+
/** p-dialog (onShow): the plot div now exists, so draw the histogram and keep
|
|
54
|
+
* it sized to the dialog (which is resizable) via a ResizeObserver. */
|
|
55
|
+
onShow(): void;
|
|
56
|
+
private teardownResize;
|
|
57
|
+
selectChannel(ch: IChannelState): void;
|
|
58
|
+
onMinChange(value: any): void;
|
|
59
|
+
onMaxChange(value: any): void;
|
|
60
|
+
onGammaChange(value: any): void;
|
|
61
|
+
/** Flag a control as actively adjusting so its spinner shows; auto-clears a
|
|
62
|
+
* short moment after the last change (kept alive while the user keeps
|
|
63
|
+
* dragging, since each change resets the timer). */
|
|
64
|
+
private markBusy;
|
|
65
|
+
onVisibleToggle(ch: IChannelState, value: boolean): void;
|
|
66
|
+
onColorChange(ch: IChannelState, color: string): void;
|
|
67
|
+
onColormap(node: TreeNode): void;
|
|
68
|
+
onInvert(value: boolean): void;
|
|
69
|
+
toggleLog(value: boolean): void;
|
|
70
|
+
/** Quick-assign a preset LUT colour to a channel (Fiji palette). */
|
|
71
|
+
setPreset(ch: IChannelState, color: string): void;
|
|
72
|
+
/** Export the displayed composite as a publication-ready PNG (8-bit figure). */
|
|
73
|
+
exportComposite(): void;
|
|
74
|
+
/** Export the underlying data as a true-16-bit multi-band TIFF (server-side). */
|
|
75
|
+
exportData(): void;
|
|
76
|
+
/** True when the current channel histogram is native >8-bit (16-bit etc.):
|
|
77
|
+
* gates the native slider labelling and the 16-bit TIFF export button. */
|
|
78
|
+
get is16bit(): boolean;
|
|
79
|
+
/** The native value range the 8-bit display window maps onto — the observed
|
|
80
|
+
* pixel extremes for a native histogram, else plain 0..255 (so 8-bit images
|
|
81
|
+
* pass through unchanged). The 8-bit tile is server-stretched across this
|
|
82
|
+
* range, so it's the best client-side native↔display mapping. */
|
|
83
|
+
private obsRange;
|
|
84
|
+
/** 8-bit display value (0..255) → native units. */
|
|
85
|
+
private toNative;
|
|
86
|
+
/** Native units → clamped 8-bit display value (0..255). */
|
|
87
|
+
private toDisp;
|
|
88
|
+
/** Selected channel window endpoints in native units (for the sliders). */
|
|
89
|
+
get minNative(): number;
|
|
90
|
+
get maxNative(): number;
|
|
91
|
+
/** Native slider bounds + step (256 display steps across the native range). */
|
|
92
|
+
get sliderMin(): number;
|
|
93
|
+
get sliderMax(): number;
|
|
94
|
+
get sliderStep(): number;
|
|
95
|
+
auto(): void;
|
|
96
|
+
reset(): void;
|
|
97
|
+
/** Saturation-based auto-window over a (native or 8-bit) histogram: pick
|
|
98
|
+
* [min,max] so ~`saturation` of pixels clip at each end, dropping a dominant
|
|
99
|
+
* first/last bin (background/padding). Returns native bin values. */
|
|
100
|
+
private autoWindow;
|
|
101
|
+
/** Fetch the selected channel's histogram (native for 16-bit, else 8-bit) and
|
|
102
|
+
* (re)draw it. Async — the native path hits the server; the 8-bit path may be
|
|
103
|
+
* null until tile sampling resolves, so we retry a few times. */
|
|
104
|
+
private loadHistogram;
|
|
105
|
+
private renderHistogram;
|
|
106
|
+
/** Move only the min/max marker lines (cheap) without recomputing counts. */
|
|
107
|
+
private updateMarkers;
|
|
108
|
+
private markerShapes;
|
|
109
|
+
private histogramLayout;
|
|
110
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ChannelHistogramComponent, never>;
|
|
111
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ChannelHistogramComponent, "channel-histogram", never, { "visible": { "alias": "visible"; "required": false; }; }, { "visibleChange": "visibleChange"; }, never, never, false, never>;
|
|
112
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional features a viewer backend may or may not support. Consumers query
|
|
3
|
+
* `IImageViewer.capabilities` and degrade gracefully (hide a toolbar item,
|
|
4
|
+
* skip a render path) rather than calling a method that no-ops.
|
|
5
|
+
*
|
|
6
|
+
* The Plotly backend supports the scientific/data features; the OpenSeadragon
|
|
7
|
+
* backend supports `ImageDisplay` (large zoomable image) but not the
|
|
8
|
+
* scalar-data features. The eventual router picks a backend per plot type.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum ViewerFeature {
|
|
11
|
+
/** Display a raster/tiled image (HEATMAP/RGB image, OSD's strength). */
|
|
12
|
+
ImageDisplay = "imageDisplay",
|
|
13
|
+
/** 3D scene rendering (SURFACE, SCATTER3D, ISOSURFACE). */
|
|
14
|
+
Surface3D = "surface3d",
|
|
15
|
+
/** Live colormap/LUT applied to a raw scalar matrix. */
|
|
16
|
+
ScalarColormap = "scalarColormap",
|
|
17
|
+
/** Read back the displayed pixel matrix (getDisplayedPixelData). */
|
|
18
|
+
PixelReadback = "pixelReadback",
|
|
19
|
+
/** Server re-fetch of a higher-res crop on zoom (vs native tiling). */
|
|
20
|
+
HighDefZoom = "highDefZoom",
|
|
21
|
+
/** Per-z-plane slider over an image stack. */
|
|
22
|
+
StackSlider = "stackSlider",
|
|
23
|
+
/** Live isosurface intensity bounds (isomin/isomax) — see IIsosurfaceControls. */
|
|
24
|
+
Isosurface = "isosurface"
|
|
25
|
+
}
|
|
26
|
+
export interface ViewerCapabilities {
|
|
27
|
+
has(feature: ViewerFeature): boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Build a `ViewerCapabilities` from a fixed feature set. */
|
|
30
|
+
export declare function capabilitiesOf(features: Iterable<ViewerFeature>): ViewerCapabilities;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Result of an automatic cell segmenter: a per-pixel instance label map for the
|
|
4
|
+
* input image (0 = background, 1..count = instances), row-major.
|
|
5
|
+
*/
|
|
6
|
+
export interface CellSegmentation {
|
|
7
|
+
labels: Uint32Array;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
count: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Port for an automatic (prompt-free) cell segmenter — e.g. cellpose-SAM. The
|
|
14
|
+
* library calls this on a client slide-crop of a drawn box. By default the
|
|
15
|
+
* {@link CELL_SEGMENTER} token resolves to the library's own cellpose-js-backed
|
|
16
|
+
* {@link CellposeSegmenterService} (so the toolbar Cellpose tool works out of the
|
|
17
|
+
* box), but a host can override the token to supply a different implementation
|
|
18
|
+
* (e.g. a server-side segmenter) (jit-ui#90).
|
|
19
|
+
*/
|
|
20
|
+
/** Progress/status callbacks for a segmentation run (all optional). */
|
|
21
|
+
export interface CellSegmentProgress {
|
|
22
|
+
/** Model-download progress, 0..1 (first run only). */
|
|
23
|
+
onProgress?: (fraction: number) => void;
|
|
24
|
+
/** Human-readable phase, e.g. 'Running inference (tile 3/8)…'. */
|
|
25
|
+
onStatus?: (status: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export interface ICellSegmenter {
|
|
28
|
+
/** Segment all cells in an RGBA image into an instance label map. */
|
|
29
|
+
segmentCells(image: {
|
|
30
|
+
data: Uint8ClampedArray;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}, progress?: CellSegmentProgress): Promise<CellSegmentation>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* DI token for the automatic cell segmenter. Defaults to the in-library
|
|
37
|
+
* {@link CellposeSegmenterService} (cellpose-js, lazy-loaded); override it in the
|
|
38
|
+
* host to swap implementations.
|
|
39
|
+
*/
|
|
40
|
+
export declare const CELL_SEGMENTER: InjectionToken<ICellSegmenter>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { IImageMetadata } from './image.contract';
|
|
4
|
+
import { ColormapNode } from './display-types';
|
|
5
|
+
/**
|
|
6
|
+
* Display state for one image channel in the Channels & Histogram pane.
|
|
7
|
+
* `min`/`max` are the display window (contrast/brightness) in raw 0..255 space;
|
|
8
|
+
* `gamma` shapes the transfer curve; `color` is the channel's display tint
|
|
9
|
+
* (used when compositing multiple channels); `visible` toggles it on/off.
|
|
10
|
+
*/
|
|
11
|
+
export interface IChannelState {
|
|
12
|
+
index: number;
|
|
13
|
+
name: string;
|
|
14
|
+
/** Display tint as `#rrggbb`. */
|
|
15
|
+
color: string;
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
18
|
+
gamma: number;
|
|
19
|
+
visible: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** The standard LUT pseudo-colours (mirrors Fiji's Merge Channels palette) for
|
|
22
|
+
* quick per-channel assignment. */
|
|
23
|
+
export declare const LUT_COLORS: ReadonlyArray<{
|
|
24
|
+
name: string;
|
|
25
|
+
color: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** A binned intensity histogram for one channel.
|
|
28
|
+
*
|
|
29
|
+
* The 8-bit client path (RGB / 8-bit grayscale, sampled from displayed tiles)
|
|
30
|
+
* leaves the native fields undefined and `bins` span 0..255. The native path
|
|
31
|
+
* (>8-bit images, from the server `/histogram` endpoint) fills `bitDepth`,
|
|
32
|
+
* `rangeMin`/`rangeMax` (the pixel-type span the bins cover) and
|
|
33
|
+
* `observedMin`/`observedMax` (the actual extremes seen), so the dialog can
|
|
34
|
+
* label the window/axis in native units. */
|
|
35
|
+
export interface IHistogram {
|
|
36
|
+
/** Left edge of each bin (0..255 for the 8-bit path, native units otherwise). */
|
|
37
|
+
bins: number[];
|
|
38
|
+
/** Pixel count per bin, length = bin count. */
|
|
39
|
+
counts: number[];
|
|
40
|
+
/** Largest bin count, for y-axis scaling. */
|
|
41
|
+
max: number;
|
|
42
|
+
/** Native bit depth (8, 16, …) — present only on the native server path. */
|
|
43
|
+
bitDepth?: number;
|
|
44
|
+
/** Pixel-type range the bins span (e.g. 0..65535), native path only. */
|
|
45
|
+
rangeMin?: number;
|
|
46
|
+
rangeMax?: number;
|
|
47
|
+
/** Actual min/max pixel observed (auto-contrast seed), native path only. */
|
|
48
|
+
observedMin?: number;
|
|
49
|
+
observedMax?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Public, implementation-agnostic surface the Channels & Histogram pane (an
|
|
53
|
+
* external consumer of the visualization package) depends on. It exposes the
|
|
54
|
+
* per-channel display window/gamma/visibility, an intensity histogram computed
|
|
55
|
+
* from the currently-displayed pixels, and the LUT/colormap controls that used
|
|
56
|
+
* to live in the toolbar. The pane injects this via {@link CHANNEL_HISTOGRAM_API}
|
|
57
|
+
* (bound to the concrete `RoutingVisualizerService` with `useExisting`) so it
|
|
58
|
+
* never reaches the implementation. Every setter live-updates whichever backend
|
|
59
|
+
* (Plotly or OpenSeadragon) is on screen.
|
|
60
|
+
*/
|
|
61
|
+
export interface IChannelHistogramApi {
|
|
62
|
+
/** The channels of the current image (1 for grayscale, 3 for RGB). */
|
|
63
|
+
getChannels$(): Observable<IChannelState[]>;
|
|
64
|
+
/** Patch one channel's state (window/gamma/visibility/color); live-updates. */
|
|
65
|
+
setChannelState(index: number, partial: Partial<IChannelState>): void;
|
|
66
|
+
/** Auto-window the given channels by saturating `saturation` (0..1) of pixels
|
|
67
|
+
* at each end of their histogram. */
|
|
68
|
+
autoContrast(indices: number[], saturation: number): void;
|
|
69
|
+
/** Reset the given channels to their derived defaults: full display range
|
|
70
|
+
* (0..255), gamma 1, and the channel's default tint colour. */
|
|
71
|
+
resetContrast(indices: number[]): void;
|
|
72
|
+
/** Binned histogram for a channel from the displayed pixels, or null when no
|
|
73
|
+
* image/pixels are available. Synchronous 8-bit path (used by auto-contrast). */
|
|
74
|
+
getHistogram(channelIndex: number, bins: number): IHistogram | null;
|
|
75
|
+
/** Histogram for a channel as an async stream: emits the **native** bit-depth
|
|
76
|
+
* histogram (from the server) for >8-bit images, else the 8-bit client
|
|
77
|
+
* histogram. The dialog uses this so 16-bit stacks get a true distribution. */
|
|
78
|
+
getHistogram$(channelIndex: number, bins: number): Observable<IHistogram | null>;
|
|
79
|
+
getColormap(): Observable<ColormapNode | null>;
|
|
80
|
+
setColormap(colormap: ColormapNode): void;
|
|
81
|
+
getColormapOptions(): ColormapNode[];
|
|
82
|
+
getReverseScale(): Observable<boolean>;
|
|
83
|
+
setReverseScale(reverse: boolean): void;
|
|
84
|
+
getGrayscale$(): Observable<boolean>;
|
|
85
|
+
setGrayscale(on: boolean): void;
|
|
86
|
+
getInvert$(): Observable<boolean>;
|
|
87
|
+
setInvert(on: boolean): void;
|
|
88
|
+
getImageMeta(): Observable<IImageMetadata[]>;
|
|
89
|
+
/** Export the displayed image — composited with the current per-channel
|
|
90
|
+
* pseudo-colours / window / colormap — as a publication-ready PNG download. */
|
|
91
|
+
exportComposite(): void;
|
|
92
|
+
/** Export the underlying image data — all (or the visible) channels at native
|
|
93
|
+
* bit depth — as a data-preserving multi-band TIFF download. Server-side, so
|
|
94
|
+
* it keeps true 16-bit values (the PNG composite is an 8-bit figure). Only
|
|
95
|
+
* meaningful for >8-bit images; a no-op on backends that can't provide it. */
|
|
96
|
+
exportData(): void;
|
|
97
|
+
}
|
|
98
|
+
export declare const CHANNEL_HISTOGRAM_API: InjectionToken<IChannelHistogramApi>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colormap → RGB lookup-table builder.
|
|
3
|
+
*
|
|
4
|
+
* Lets a non-Plotly backend (OpenSeadragon) recolor grayscale pixels with the
|
|
5
|
+
* exact same colormaps the Plotly heatmap uses. The input is a colormap value
|
|
6
|
+
* straight from `COLORMAP_OPTIONS` (see plot.utilities) — the same value handed
|
|
7
|
+
* to Plotly's `colorscale` — so the two surfaces stay in lock-step.
|
|
8
|
+
*
|
|
9
|
+
* A colormap value is either:
|
|
10
|
+
* - an array of `[stop, color]` pairs (the matplotlib `*_LUT` constants), or
|
|
11
|
+
* - a Plotly built-in scale *name* string (e.g. 'Viridis', 'Reds').
|
|
12
|
+
* `color` may be `'rgb(r, g, b)'` or `'#rrggbb'`/`'#rgb'`.
|
|
13
|
+
*/
|
|
14
|
+
export type Rgb = [number, number, number];
|
|
15
|
+
/**
|
|
16
|
+
* Build a 256-entry RGB lookup table from a `COLORMAP_OPTIONS` value, indexed by
|
|
17
|
+
* grayscale value 0–255. Returns null if the value can't be resolved to a scale.
|
|
18
|
+
* @param value array of `[stop, color]` pairs, or a Plotly built-in name string
|
|
19
|
+
* @param reverse mirror the scale (matches Plotly's `reversescale`)
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildColormapLut(value: unknown, reverse?: boolean): Rgb[] | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Region } from '../models/region';
|
|
2
|
+
/**
|
|
3
|
+
* The viewport math the on-canvas tools (wand, vertex eraser) need, abstracted
|
|
4
|
+
* away from any specific backend. Plotly implements it over its axis objects
|
|
5
|
+
* (`_fullLayout.xaxis/yaxis`), OpenSeadragon over its viewport API. This is what
|
|
6
|
+
* lets the tools run on either backend.
|
|
7
|
+
*
|
|
8
|
+
* "Data coordinates" are the backend's shape coordinate space: Plotly data
|
|
9
|
+
* coords for Plotly, image-pixel coords for OpenSeadragon. The tools convert
|
|
10
|
+
* data -> image-matrix using the per-image ratio they already get from the host
|
|
11
|
+
* (`getCachedImageData().ratios` / `getCachedImageRatio()`), so that division
|
|
12
|
+
* stays in the tool and the transform stays purely screen<->data.
|
|
13
|
+
*/
|
|
14
|
+
export interface ICoordinateTransform {
|
|
15
|
+
/** Client (mouse event) pixel -> data coordinates. */
|
|
16
|
+
clientToData(clientX: number, clientY: number): {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
};
|
|
20
|
+
/** A length in data coordinates -> length in screen pixels. */
|
|
21
|
+
dataLengthToScreen(dataLength: number): number;
|
|
22
|
+
/** True once the viewport is laid out and conversions are valid. */
|
|
23
|
+
isReady(): boolean;
|
|
24
|
+
}
|
|
25
|
+
/** What a backend supplies so the tools can attach + convert coordinates. */
|
|
26
|
+
export interface IViewportHost {
|
|
27
|
+
/** The element the tool's canvas overlay attaches to (fills the plot area). */
|
|
28
|
+
getOverlayContainer(): HTMLElement | null;
|
|
29
|
+
/** Live coordinate transform for the current viewport state. */
|
|
30
|
+
getCoordinateTransform(): ICoordinateTransform;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Region read/write access for the on-canvas tools, in the backend-neutral
|
|
34
|
+
* {@link Region} model — so the wand and vertex eraser operate on regions, not
|
|
35
|
+
* a backend's own shape representation. Both backends implement this by
|
|
36
|
+
* delegating to the shared RegionStore.
|
|
37
|
+
*/
|
|
38
|
+
export interface IRegionDataHost {
|
|
39
|
+
/** Current regions (neutral model). */
|
|
40
|
+
getRegions(): Region[];
|
|
41
|
+
/** Commit the (possibly mutated) region set; the backend renders + emits. */
|
|
42
|
+
setRegions(regions: Region[]): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publication-critical types that were `any` on the public contracts
|
|
3
|
+
* (refactoring plan, Step 6). Deliberately permissive — every field optional —
|
|
4
|
+
* so existing hosts (which pass PrimeNG `TreeNode`s and plain option objects)
|
|
5
|
+
* keep compiling unchanged while consumers finally get named shapes.
|
|
6
|
+
*/
|
|
7
|
+
/** A colormap value as Plotly understands it: a built-in scale name (e.g.
|
|
8
|
+
* 'Viridis') or an inline array of `[stop, color]` pairs. */
|
|
9
|
+
export type ColormapValue = string | Array<[number, string]>;
|
|
10
|
+
/** One node of the colormap selector tree (structurally compatible with the
|
|
11
|
+
* PrimeNG `TreeNode`s the host passes): group nodes carry `children`, leaf
|
|
12
|
+
* nodes carry the scale in `data.value` (+ a preview image in `data.src`). */
|
|
13
|
+
export interface ColormapNode {
|
|
14
|
+
label?: string;
|
|
15
|
+
data?: {
|
|
16
|
+
value?: ColormapValue;
|
|
17
|
+
src?: string;
|
|
18
|
+
};
|
|
19
|
+
children?: ColormapNode[];
|
|
20
|
+
}
|
|
21
|
+
/** Magic-wand pixel-comparison space. */
|
|
22
|
+
export type WandType = 'GRAY' | 'RGB' | 'LAB_DISTANCE';
|
|
23
|
+
/** Options for the magic-wand tool (QuPath-style). */
|
|
24
|
+
export interface IWandOptions {
|
|
25
|
+
type?: WandType;
|
|
26
|
+
/** Gaussian sigma applied to the patch before thresholding. */
|
|
27
|
+
sigma?: number;
|
|
28
|
+
/** Higher = stricter (smaller selection) for GRAY/RGB; higher = looser for
|
|
29
|
+
* LAB_DISTANCE — matches QuPath. */
|
|
30
|
+
sensitivity?: number;
|
|
31
|
+
/** Square patch size in pixels (must be odd). */
|
|
32
|
+
patchSize?: number;
|
|
33
|
+
/** When true, skip blur/threshold and flood-fill at exact-match
|
|
34
|
+
* (Cmd/Ctrl-click in QuPath). */
|
|
35
|
+
simpleMode?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Options for the brush region tool (QuPath-style). */
|
|
38
|
+
export interface IBrushOptions {
|
|
39
|
+
/** Brush diameter in matrix (image) pixels. */
|
|
40
|
+
size?: number;
|
|
41
|
+
}
|