larvitar 2.0.5 → 2.0.7
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/README.md +2 -2
- package/dist/imaging/imageRendering.d.ts +1 -71
- package/dist/imaging/imageStore.d.ts +5 -0
- package/dist/imaging/loaders/commonLoader.d.ts +4 -4
- package/dist/imaging/loaders/nrrdLoader.d.ts +1 -51
- package/dist/larvitar.js +13 -1
- package/dist/larvitar.js.map +1 -1
- package/imaging/tools/types.d.ts +19 -19
- package/imaging/types.d.ts +110 -2
- package/package.json +7 -2
- package/.github/workflows/build-docs.yml +0 -59
- package/.github/workflows/codeql-analysis.yml +0 -71
- package/.github/workflows/deploy.yml +0 -37
- package/.vscode/settings.json +0 -4
- package/CODE_OF_CONDUCT.md +0 -76
- package/MIGRATION.md +0 -25
- package/bundler/webpack.common.js +0 -27
- package/bundler/webpack.dev.js +0 -23
- package/bundler/webpack.prod.js +0 -19
- package/decs.d.ts +0 -12
- package/dist/imaging/MetaDataReadable.d.ts +0 -41
- package/dist/imaging/MetaDataTypes.d.ts +0 -3489
- package/imaging/dataDictionary.json +0 -21866
- package/imaging/imageAnonymization.ts +0 -135
- package/imaging/imageColormaps.ts +0 -217
- package/imaging/imageContours.ts +0 -196
- package/imaging/imageIo.ts +0 -251
- package/imaging/imageLayers.ts +0 -121
- package/imaging/imageLoading.ts +0 -299
- package/imaging/imageParsing.ts +0 -444
- package/imaging/imagePresets.ts +0 -156
- package/imaging/imageRendering.ts +0 -1091
- package/imaging/imageReslice.ts +0 -87
- package/imaging/imageStore.ts +0 -487
- package/imaging/imageTags.ts +0 -609
- package/imaging/imageTools.js +0 -708
- package/imaging/imageUtils.ts +0 -1079
- package/imaging/loaders/commonLoader.ts +0 -275
- package/imaging/loaders/dicomLoader.ts +0 -66
- package/imaging/loaders/fileLoader.ts +0 -71
- package/imaging/loaders/multiframeLoader.ts +0 -435
- package/imaging/loaders/nrrdLoader.ts +0 -630
- package/imaging/loaders/resliceLoader.ts +0 -205
- package/imaging/monitors/memory.ts +0 -151
- package/imaging/monitors/performance.ts +0 -34
- package/imaging/parsers/ecg.ts +0 -54
- package/imaging/parsers/nrrd.js +0 -485
- package/imaging/tools/custom/4dSliceScrollTool.js +0 -146
- package/imaging/tools/custom/BorderMagnifyTool.js +0 -99
- package/imaging/tools/custom/contourTool.js +0 -1884
- package/imaging/tools/custom/diameterTool.js +0 -141
- package/imaging/tools/custom/editMaskTool.js +0 -141
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +0 -534
- package/imaging/tools/custom/polygonSegmentationMixin.js +0 -245
- package/imaging/tools/custom/polylineScissorsTool.js +0 -59
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +0 -564
- package/imaging/tools/custom/seedTool.js +0 -342
- package/imaging/tools/custom/setLabelMap3D.ts +0 -242
- package/imaging/tools/custom/thresholdsBrushTool.js +0 -161
- package/imaging/tools/default.ts +0 -594
- package/imaging/tools/interaction.ts +0 -266
- package/imaging/tools/io.ts +0 -229
- package/imaging/tools/main.ts +0 -427
- package/imaging/tools/segmentation.ts +0 -532
- package/imaging/tools/segmentations.md +0 -38
- package/imaging/tools/state.ts +0 -74
- package/imaging/tools/strategies/eraseFreehand.js +0 -76
- package/imaging/tools/strategies/fillFreehand.js +0 -79
- package/imaging/tools/strategies/index.js +0 -2
- package/imaging/waveforms/ecg.ts +0 -191
- package/index.ts +0 -431
- package/jsdoc.json +0 -52
- package/rollup.config.js +0 -51
- package/template/.gitkeep +0 -0
- package/tsconfig.json +0 -102
- /package/imaging/{MetaDataReadable.ts → MetaDataReadable.d.ts} +0 -0
- /package/imaging/{MetaDataTypes.ts → MetaDataTypes.d.ts} +0 -0
|
@@ -1,532 +0,0 @@
|
|
|
1
|
-
/** @module imaging/tools/segmentation
|
|
2
|
-
* @desc This file provides functionalities
|
|
3
|
-
* for handling masks and luts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// external libraries
|
|
7
|
-
import cornerstone, { Viewport } from "cornerstone-core";
|
|
8
|
-
import cornerstoneTools from "cornerstone-tools";
|
|
9
|
-
import { cloneDeep, extend, values, sum } from "lodash";
|
|
10
|
-
const segModule = cornerstoneTools.getModule("segmentation");
|
|
11
|
-
const { getters, setters } = segModule;
|
|
12
|
-
|
|
13
|
-
// internal libraries
|
|
14
|
-
import { setToolActive, setToolDisabled } from "./main";
|
|
15
|
-
import { isElement } from "../imageUtils";
|
|
16
|
-
import store from "../imageStore";
|
|
17
|
-
import { updateStackToolState } from "../imageTools";
|
|
18
|
-
|
|
19
|
-
// custom code
|
|
20
|
-
import { setLabelmap3DForElement } from "./custom/setLabelMap3D";
|
|
21
|
-
import {
|
|
22
|
-
BrushProperties,
|
|
23
|
-
MaskProperties,
|
|
24
|
-
MaskVisualizations,
|
|
25
|
-
SegmentationConfig
|
|
26
|
-
} from "./types.d";
|
|
27
|
-
import type { TypedArray } from "../types";
|
|
28
|
-
// override function
|
|
29
|
-
setters.labelmap3DForElement = setLabelmap3DForElement;
|
|
30
|
-
|
|
31
|
-
// General segmentation cs tools module configuration
|
|
32
|
-
const config: SegmentationConfig = {
|
|
33
|
-
arrayType: 0,
|
|
34
|
-
renderOutline: false,
|
|
35
|
-
renderFill: true,
|
|
36
|
-
shouldRenderInactiveLabelmaps: true,
|
|
37
|
-
radius: 10,
|
|
38
|
-
minRadius: 1,
|
|
39
|
-
maxRadius: 50,
|
|
40
|
-
segmentsPerLabelmap: 10,
|
|
41
|
-
fillAlpha: 0.9,
|
|
42
|
-
fillAlphaInactive: 0.9,
|
|
43
|
-
outlineAlpha: 1.0,
|
|
44
|
-
outlineAlphaInactive: 1.0,
|
|
45
|
-
outlineWidth: 1,
|
|
46
|
-
storeHistory: true
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// ====================================================
|
|
50
|
-
// utils to convert from hex to rgb and vice-versa ====
|
|
51
|
-
// ====================================================
|
|
52
|
-
|
|
53
|
-
function componentToHex(c: number) {
|
|
54
|
-
var hex = c.toString(16);
|
|
55
|
-
return hex.length == 1 ? "0" + hex : hex;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function rgbToHex(c: number[]) {
|
|
59
|
-
let r = componentToHex(c[0]);
|
|
60
|
-
let g = componentToHex(c[1]);
|
|
61
|
-
let b = componentToHex(c[2]);
|
|
62
|
-
return "#" + r + g + b;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function hexToRgb(hex: string) {
|
|
66
|
-
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
67
|
-
|
|
68
|
-
if (!result) {
|
|
69
|
-
console.error("Error parsing hex color");
|
|
70
|
-
return [0, 0, 0];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return [
|
|
74
|
-
parseInt(result[1], 16),
|
|
75
|
-
parseInt(result[2], 16),
|
|
76
|
-
parseInt(result[3], 16)
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Convert color from hsv to rgb
|
|
82
|
-
* @param {Array} color as [h,s,v] 0-1
|
|
83
|
-
* @returns color as [r,g,b] 0-255
|
|
84
|
-
*/
|
|
85
|
-
function HSVtoRGB([h, s, v]: [number, number, number]) {
|
|
86
|
-
var r, g, b, i, f, p, q, t;
|
|
87
|
-
i = Math.floor(h * 6);
|
|
88
|
-
f = h * 6 - i;
|
|
89
|
-
p = v * (1 - s);
|
|
90
|
-
q = v * (1 - f * s);
|
|
91
|
-
t = v * (1 - (1 - f) * s);
|
|
92
|
-
switch (i % 6) {
|
|
93
|
-
case 0:
|
|
94
|
-
(r = v), (g = t), (b = p);
|
|
95
|
-
break;
|
|
96
|
-
case 1:
|
|
97
|
-
(r = q), (g = v), (b = p);
|
|
98
|
-
break;
|
|
99
|
-
case 2:
|
|
100
|
-
(r = p), (g = v), (b = t);
|
|
101
|
-
break;
|
|
102
|
-
case 3:
|
|
103
|
-
(r = p), (g = q), (b = v);
|
|
104
|
-
break;
|
|
105
|
-
case 4:
|
|
106
|
-
(r = t), (g = p), (b = v);
|
|
107
|
-
break;
|
|
108
|
-
case 5:
|
|
109
|
-
(r = v), (g = p), (b = q);
|
|
110
|
-
break;
|
|
111
|
-
default:
|
|
112
|
-
(r = v), (g = t), (b = p);
|
|
113
|
-
console.error("HSVtoRGB: Input color must be [h,s,v] 0-1");
|
|
114
|
-
}
|
|
115
|
-
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Force cs tools refresh on all enabled images
|
|
120
|
-
*/
|
|
121
|
-
export function forceRender() {
|
|
122
|
-
let enabledElements = cornerstone.getEnabledElements();
|
|
123
|
-
enabledElements.forEach(enEl => {
|
|
124
|
-
cornerstone.updateImage(enEl.element);
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Generate the custom LUT - single volume version
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
|
-
function generateLUT(opacity: number) {
|
|
133
|
-
let lut = new Array(segModule.configuration.segmentsPerLabelmap).fill(0);
|
|
134
|
-
lut = lut.map((e, i) => {
|
|
135
|
-
return HSVtoRGB([i / lut.length, 1, 1]).concat(Math.round(opacity * 255));
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return lut;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Generate the custom LUT - multiple volume version
|
|
143
|
-
* @param {String} hex_color - color for LUT in the #RRGGBB form
|
|
144
|
-
* @param {NUmber} opacity - number between 0 and 1
|
|
145
|
-
*/
|
|
146
|
-
function generateUniformLUT(hex_color: string, opacity: number) {
|
|
147
|
-
let lut = new Array(segModule.configuration.segmentsPerLabelmap);
|
|
148
|
-
let rgb_color = hexToRgb(hex_color);
|
|
149
|
-
let rgba_color = rgb_color.concat(Math.round(opacity * 255));
|
|
150
|
-
lut.fill(rgba_color);
|
|
151
|
-
return lut;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Set color for label
|
|
156
|
-
* @param {Number} labelId
|
|
157
|
-
* @param {String} color in hex format
|
|
158
|
-
*/
|
|
159
|
-
export function setLabelColor(labelId: string, color: string) {
|
|
160
|
-
let volumeId = 0; // TODO MULTIVOLUME
|
|
161
|
-
let rgb = hexToRgb(color);
|
|
162
|
-
let rgba = [...rgb, 128];
|
|
163
|
-
setters.colorForSegmentIndexOfColorLUT(volumeId, labelId, rgba);
|
|
164
|
-
// force render ? depends on image visualization (render all ?)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Get color from label
|
|
169
|
-
* @param {Number} labelId
|
|
170
|
-
* @returns {String} Color in hex format
|
|
171
|
-
*/
|
|
172
|
-
export function getLabelColor(labelId: string) {
|
|
173
|
-
let volumeId = 0; // TODO MULTIVOLUME
|
|
174
|
-
let rgba = getters.colorForSegmentIndexColorLUT(volumeId, labelId);
|
|
175
|
-
return rgbToHex(rgba);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* A function to group all settings to load before masks
|
|
180
|
-
* @param {Object} customConfig - Object containing override values for segmentation module config
|
|
181
|
-
*/
|
|
182
|
-
export function initSegmentationModule(customConfig: SegmentationConfig) {
|
|
183
|
-
// set configuration
|
|
184
|
-
segModule.configuration = cloneDeep(config);
|
|
185
|
-
extend(segModule.configuration, customConfig);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Add segmentation mask to segmentation module
|
|
190
|
-
* @param {Object} props - The mask properties (labelId, color and opacity)
|
|
191
|
-
* @param {TypedArray} - The mask data array
|
|
192
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
193
|
-
* @returns {Promise} - Return a promise which will resolve when segmentation mask is added
|
|
194
|
-
*/
|
|
195
|
-
|
|
196
|
-
export function addSegmentationMask(
|
|
197
|
-
props: MaskProperties,
|
|
198
|
-
data: TypedArray,
|
|
199
|
-
elementId: string | HTMLElement
|
|
200
|
-
) {
|
|
201
|
-
let promise = new Promise<void>(async resolve => {
|
|
202
|
-
let element = isElement(elementId)
|
|
203
|
-
? (elementId as HTMLElement)
|
|
204
|
-
: document.getElementById(elementId as string);
|
|
205
|
-
if (!element) {
|
|
206
|
-
console.error("invalid html element: " + elementId);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const res = await setters.labelmap3DForElement(
|
|
211
|
-
element,
|
|
212
|
-
data.buffer,
|
|
213
|
-
props.labelId
|
|
214
|
-
);
|
|
215
|
-
// if user set a color property, use that color for all segments on the labelmap
|
|
216
|
-
let lut = props.color
|
|
217
|
-
? generateUniformLUT(props.color, props.opacity)
|
|
218
|
-
: generateLUT(props.opacity);
|
|
219
|
-
setters.colorLUT(props.labelId, lut);
|
|
220
|
-
// bind labelmap to colorLUT
|
|
221
|
-
let labelmap3d = getters.labelmap3D(element, props.labelId);
|
|
222
|
-
setters.colorLUTIndexForLabelmap3D(labelmap3d, props.labelId);
|
|
223
|
-
|
|
224
|
-
// set current imageIdIndex in tool state
|
|
225
|
-
let currentImageIdIndex = store.get(["viewports", element.id, "sliceId"]);
|
|
226
|
-
if (currentImageIdIndex !== "error" && currentImageIdIndex >= 0) {
|
|
227
|
-
updateStackToolState(element.id, currentImageIdIndex);
|
|
228
|
-
} else {
|
|
229
|
-
console.error("Cannot get currentImageIdIndex");
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
resolve();
|
|
233
|
-
});
|
|
234
|
-
return promise;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Set a new mask slice into the labelmap buffer
|
|
239
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
240
|
-
* @param {Number} sliceIndex - the index of the new mask slice
|
|
241
|
-
* @param {ArrayBuffer} pixelData - the pixelData array
|
|
242
|
-
*/
|
|
243
|
-
export function loadMaskSlice(
|
|
244
|
-
elementId: string | HTMLElement,
|
|
245
|
-
sliceIndex: number,
|
|
246
|
-
pixelData: TypedArray
|
|
247
|
-
) {
|
|
248
|
-
// optimization: if pixelData contains no labels, return
|
|
249
|
-
if (sum(pixelData) === 0) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
let element = isElement(elementId)
|
|
254
|
-
? (elementId as HTMLElement)
|
|
255
|
-
: document.getElementById(elementId as string);
|
|
256
|
-
if (!element) {
|
|
257
|
-
console.error("invalid html element: " + elementId);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
let volumeId = 0; // TODO for multivolume
|
|
261
|
-
// get labelmap buffer
|
|
262
|
-
let labelmaps2D = getters.labelmap3D(element, volumeId).labelmaps2D;
|
|
263
|
-
|
|
264
|
-
// add if not alresdy present
|
|
265
|
-
if (!labelmaps2D[sliceIndex]) {
|
|
266
|
-
labelmaps2D[sliceIndex] = {};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
labelmaps2D[sliceIndex].pixelData = pixelData;
|
|
270
|
-
setters.updateSegmentsOnLabelmap2D(labelmaps2D[sliceIndex]);
|
|
271
|
-
cornerstone.updateImage(element);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Activate a specific labelmap through its labelId
|
|
276
|
-
* @param {Number} labelId - The labelmap id to activate
|
|
277
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
278
|
-
*/
|
|
279
|
-
export function setActiveLabelmap(
|
|
280
|
-
labelId: number,
|
|
281
|
-
elementId: string | HTMLElement
|
|
282
|
-
) {
|
|
283
|
-
let element = isElement(elementId)
|
|
284
|
-
? (elementId as HTMLElement)
|
|
285
|
-
: document.getElementById(elementId as string);
|
|
286
|
-
if (!element) {
|
|
287
|
-
console.error("invalid html element: " + elementId);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
setters.activeLabelmapIndex(element, labelId);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Get active labelmap for target element
|
|
295
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
296
|
-
* @returns {Object} The active labelmap object that contains the buffer
|
|
297
|
-
*/
|
|
298
|
-
export function getActiveLabelmapBuffer(elementId: string | HTMLElement) {
|
|
299
|
-
let element = isElement(elementId)
|
|
300
|
-
? (elementId as HTMLElement)
|
|
301
|
-
: document.getElementById(elementId as string);
|
|
302
|
-
if (!element) {
|
|
303
|
-
console.error("invalid html element: " + elementId);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
return getters.activeLabelmapBuffer(element);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Activate a specific segment through its index
|
|
311
|
-
* @param {Number} segmentIndex - The segment index to activate
|
|
312
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
313
|
-
*/
|
|
314
|
-
export function setActiveSegment(
|
|
315
|
-
segmentIndex: number,
|
|
316
|
-
elementId: string | HTMLElement
|
|
317
|
-
) {
|
|
318
|
-
let element = isElement(elementId)
|
|
319
|
-
? (elementId as HTMLElement)
|
|
320
|
-
: document.getElementById(elementId as string);
|
|
321
|
-
if (!element) {
|
|
322
|
-
console.error("invalid html element: " + elementId);
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
setters.activeSegmentIndex(element, segmentIndex);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Change opacity for active label
|
|
330
|
-
* @param {Number} opacity - The desired opacity value
|
|
331
|
-
*/
|
|
332
|
-
export function setActiveLabelOpacity(opacity: number) {
|
|
333
|
-
segModule.configuration.fillAlpha = opacity;
|
|
334
|
-
forceRender();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Change opacity for inactive labels
|
|
339
|
-
* @param {Number} opacity - The desired opacity value
|
|
340
|
-
*/
|
|
341
|
-
export function setInactiveLabelOpacity(opacity: number) {
|
|
342
|
-
segModule.configuration.fillAlphaInactive = opacity;
|
|
343
|
-
forceRender();
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Toggle mask visibility
|
|
348
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
349
|
-
* @param {Number} labelId - The id of the mask label
|
|
350
|
-
*/
|
|
351
|
-
export function toggleVisibility(
|
|
352
|
-
elementId: string | HTMLElement,
|
|
353
|
-
labelId: number
|
|
354
|
-
) {
|
|
355
|
-
let element = isElement(elementId)
|
|
356
|
-
? (elementId as HTMLElement)
|
|
357
|
-
: document.getElementById(elementId as string);
|
|
358
|
-
if (!element) {
|
|
359
|
-
console.error("invalid html element: " + elementId);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
let volumeId = 0; // TODO MULTIVOLUME
|
|
363
|
-
setters.toggleSegmentVisibility(element, labelId, volumeId);
|
|
364
|
-
cornerstone.updateImage(element);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Toggle between 'contours mode' and 'filled mode'
|
|
369
|
-
* @param {Bool} toggle - Contour mode enabled if true
|
|
370
|
-
*/
|
|
371
|
-
export function toggleContourMode(toggle: boolean) {
|
|
372
|
-
if (toggle) {
|
|
373
|
-
segModule.configuration.fillAlpha = 0.0;
|
|
374
|
-
segModule.configuration.fillAlphaInactive = 0.0;
|
|
375
|
-
segModule.configuration.outlineAlpha = 1.0;
|
|
376
|
-
segModule.configuration.outlineAlphaInactive = 1.0;
|
|
377
|
-
segModule.configuration.outlineWidth = 3;
|
|
378
|
-
} else {
|
|
379
|
-
segModule.configuration.fillAlpha = config.fillAlpha;
|
|
380
|
-
segModule.configuration.fillAlphaInactive = config.fillAlphaInactive;
|
|
381
|
-
segModule.configuration.outlineAlpha = config.outlineAlpha;
|
|
382
|
-
segModule.configuration.outlineAlphaInactive = config.outlineAlphaInactive;
|
|
383
|
-
segModule.configuration.outlineWidth = config.outlineWidth;
|
|
384
|
-
}
|
|
385
|
-
forceRender();
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Set mask appearance props
|
|
390
|
-
* @param {Object} maskProps - The mask appearance props (labelId, visualization [0=filled, 1=contour, 2=hidden], opacity (if mode=0), between 0 and 1)
|
|
391
|
-
*/
|
|
392
|
-
|
|
393
|
-
export function setMaskProps(props: MaskProperties) {
|
|
394
|
-
// Lut index and segment values are hardcoded because they will depend on design choices:
|
|
395
|
-
// eg single/multiple volumes for segmentations
|
|
396
|
-
let lutIndex = props.labelId;
|
|
397
|
-
let labelIndex = props.labelId;
|
|
398
|
-
// let segmentValue = MAP_VALUES[props.id];
|
|
399
|
-
let segmentValue = 1; // binary mask, segment 1 is the only to be affected by color & opacity
|
|
400
|
-
let currentColor = getters.colorForSegmentIndexColorLUT(
|
|
401
|
-
props.labelId,
|
|
402
|
-
segmentValue
|
|
403
|
-
);
|
|
404
|
-
let htmlelement = document.getElementById("axial");
|
|
405
|
-
|
|
406
|
-
let newColor = currentColor;
|
|
407
|
-
switch (props.visualization) {
|
|
408
|
-
// full
|
|
409
|
-
case MaskVisualizations.FILL:
|
|
410
|
-
segModule.configuration.renderOutline = true;
|
|
411
|
-
getters.isSegmentVisible(htmlelement, segmentValue, labelIndex)
|
|
412
|
-
? null
|
|
413
|
-
: setters.toggleSegmentVisibility(
|
|
414
|
-
htmlelement,
|
|
415
|
-
segmentValue,
|
|
416
|
-
labelIndex
|
|
417
|
-
);
|
|
418
|
-
newColor[3] = Math.round(props.opacity * 255);
|
|
419
|
-
setters.colorForSegmentIndexOfColorLUT(lutIndex, segmentValue, newColor);
|
|
420
|
-
break;
|
|
421
|
-
// contours
|
|
422
|
-
case MaskVisualizations.CONTOUR:
|
|
423
|
-
segModule.configuration.renderOutline = true;
|
|
424
|
-
getters.isSegmentVisible(htmlelement, segmentValue, labelIndex)
|
|
425
|
-
? null
|
|
426
|
-
: setters.toggleSegmentVisibility(
|
|
427
|
-
htmlelement,
|
|
428
|
-
segmentValue,
|
|
429
|
-
labelIndex
|
|
430
|
-
);
|
|
431
|
-
newColor[3] = 0;
|
|
432
|
-
setters.colorForSegmentIndexOfColorLUT(lutIndex, segmentValue, newColor);
|
|
433
|
-
break;
|
|
434
|
-
// hidden
|
|
435
|
-
case MaskVisualizations.HIDDEN:
|
|
436
|
-
setters.toggleSegmentVisibility(htmlelement, segmentValue, labelIndex);
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
forceRender();
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Clear state for segmentation module
|
|
444
|
-
*/
|
|
445
|
-
export function clearSegmentationState() {
|
|
446
|
-
segModule.state.series = {};
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Enable brushing
|
|
451
|
-
* NOTE: if options contains `thresholds`, ThresholdsBrush is activated, otherwise BrushTool is activated.
|
|
452
|
-
* Anyway, the activated tool name is returned
|
|
453
|
-
* @param {Object} options - An object containing configuration values (eg radius, thresholds, etc...)
|
|
454
|
-
*/
|
|
455
|
-
export function enableBrushTool(viewports: string[], options: BrushProperties) {
|
|
456
|
-
setBrushProps(options);
|
|
457
|
-
const brushType = "thresholds" in options ? "ThresholdsBrush" : "Brush";
|
|
458
|
-
setToolActive(brushType, undefined, viewports);
|
|
459
|
-
return brushType;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Disable brushing
|
|
464
|
-
* This function disables both brush tools, if found active on `viewports`
|
|
465
|
-
* @param {String} toolToActivate - The name of the tool to activate after removing the brush @optional
|
|
466
|
-
*/
|
|
467
|
-
export function disableBrushTool(viewports: string[], toolToActivate?: string) {
|
|
468
|
-
viewports.forEach((viewport: string) => {
|
|
469
|
-
const el = document.getElementById(viewport);
|
|
470
|
-
if (cornerstoneTools.isToolActiveForElement(el, "ThresholdsBrush")) {
|
|
471
|
-
setToolDisabled("ThresholdsBrush", [viewport]);
|
|
472
|
-
}
|
|
473
|
-
if (cornerstoneTools.isToolActiveForElement(el, "Brush")) {
|
|
474
|
-
setToolDisabled("Brush", [viewport]);
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
if (toolToActivate) {
|
|
479
|
-
setToolActive(toolToActivate);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Change the brush props
|
|
485
|
-
* @param {Object} props - The new brush props {radius: number[px], thresholds: array[min,max]}
|
|
486
|
-
*/
|
|
487
|
-
export function setBrushProps(props: BrushProperties) {
|
|
488
|
-
extend(segModule.configuration, props);
|
|
489
|
-
forceRender();
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Undo last brush operation (stroke)
|
|
494
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
495
|
-
*/
|
|
496
|
-
export function undoLastStroke(elementId: string | HTMLElement) {
|
|
497
|
-
let element = isElement(elementId)
|
|
498
|
-
? (elementId as HTMLElement)
|
|
499
|
-
: document.getElementById(elementId as string);
|
|
500
|
-
if (!element) {
|
|
501
|
-
console.error("invalid html element: " + elementId);
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
let activeLabelMapIndex = segModule.getters.activeLabelmapIndex(element);
|
|
505
|
-
setters.undo(element, activeLabelMapIndex);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Redo last brush operation (stroke)
|
|
510
|
-
* @param {String} elementId - The target html element Id or its DOM HTMLElement
|
|
511
|
-
*/
|
|
512
|
-
export function redoLastStroke(elementId: string | HTMLElement) {
|
|
513
|
-
let element = isElement(elementId)
|
|
514
|
-
? (elementId as HTMLElement)
|
|
515
|
-
: document.getElementById(elementId as string);
|
|
516
|
-
if (!element) {
|
|
517
|
-
console.error("invalid html element: " + elementId);
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
let activeLabelMapIndex = segModule.getters.activeLabelmapIndex(element);
|
|
521
|
-
setters.redo(element, activeLabelMapIndex);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Delete mask from state
|
|
526
|
-
* @param {Number} labelId - The labelmap id to delete
|
|
527
|
-
*/
|
|
528
|
-
export function deleteMask(labelId: number) {
|
|
529
|
-
let masks = values(segModule.state.series)[0].labelmaps3D;
|
|
530
|
-
delete masks[labelId];
|
|
531
|
-
forceRender();
|
|
532
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Introduction
|
|
2
|
-
|
|
3
|
-
This guide explains the key concepts of Larvitar **segmentation masks management**, based on cornerstone tools's **segmentation module**.
|
|
4
|
-
|
|
5
|
-
# Definitions
|
|
6
|
-
|
|
7
|
-
- _segmentation mask_ or just _mask_: the set of pixels that belongs to a specific structure
|
|
8
|
-
- _volume_: a buffer containing one or more segmentations
|
|
9
|
-
- _label_: the value inside a volume that identifies a specific segmentation
|
|
10
|
-
|
|
11
|
-
# CS tools
|
|
12
|
-
|
|
13
|
-
## Segmentation module structure
|
|
14
|
-
|
|
15
|
-
In cs tools world, the different volumes are defined _labelmaps_, while the different labels in a volume are _segments_. Labelmaps can support up to 2^16 segments.
|
|
16
|
-
The values in the volume (ie, labels) define which color will be used from the LUT map: in fact, segments get the color from the lutmap and can be shown/hidden one by one.
|
|
17
|
-
Each labelmaps can be linked to a different colormap and has a active / inactive property that affects the rendering style (see configuration).
|
|
18
|
-
|
|
19
|
-
> setters.toggleSegmentVisibility(htmlelement,segmentvalue,labelmapid)
|
|
20
|
-
> setters.colorForSegmentIndexOfColorLUT(colorLutIndex, segmentValue, colorRGBAarray)
|
|
21
|
-
|
|
22
|
-
## Configuration
|
|
23
|
-
|
|
24
|
-
TODO
|
|
25
|
-
|
|
26
|
-
# Larvitar segmentation management
|
|
27
|
-
|
|
28
|
-
TODO
|
|
29
|
-
|
|
30
|
-
# Larvitar segmentation API
|
|
31
|
-
|
|
32
|
-
To enable brush tools, user can call directly the `setToolActive` / `setToolDisabled` api, in this case he has to handle brush type (thresholds or not), props (radius etc) and tool switching. Otherwise, Larvitar implements the utility functions `enableBrushTool` and `disableBrushTool` that internally handle brush type and props with a single call.
|
|
33
|
-
|
|
34
|
-
# Customization
|
|
35
|
-
|
|
36
|
-
Some function in larvitar overrides the default behaviour of cornerstone tools, here is a list of them:
|
|
37
|
-
|
|
38
|
-
- `setters.labelmap3DForElement` is overridden to have a non-blocking behaviour, the custom code is in ./setLabelMap3D.js, same as original code in cs tools repo.
|
package/imaging/tools/state.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/** @module imaging/tools/state
|
|
2
|
-
* @desc This file provides functionalities
|
|
3
|
-
* for handling tools' state
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// external libraries
|
|
7
|
-
import cornerstoneTools from "cornerstone-tools";
|
|
8
|
-
import { each } from "lodash";
|
|
9
|
-
|
|
10
|
-
import type { ToolState } from "./types";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {*} elementId
|
|
15
|
-
*/
|
|
16
|
-
const saveToolState = function (elementId: string) {
|
|
17
|
-
const elementToolStateManager = cornerstoneTools.getElementToolStateManager(
|
|
18
|
-
document.getElementById(elementId)
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
const currentState = elementToolStateManager.saveToolState();
|
|
22
|
-
|
|
23
|
-
return currentState;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
* @param {*} elementId
|
|
29
|
-
* @param {*} allToolState
|
|
30
|
-
*/
|
|
31
|
-
const restoreToolState = function (elementId: string, allToolState: ToolState) {
|
|
32
|
-
const elementToolStateManager = cornerstoneTools.getElementToolStateManager(
|
|
33
|
-
document.getElementById(elementId)
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
each(allToolState, (imageState, imageId) => {
|
|
37
|
-
each(imageState, (toolState, toolName) => {
|
|
38
|
-
elementToolStateManager.restoreImageIdToolState(imageId, {
|
|
39
|
-
[toolName]: toolState
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// EXAMPLE OF CORRECT USE OF TOOL STATE MANAGER
|
|
46
|
-
|
|
47
|
-
/*
|
|
48
|
-
// Declare state manager
|
|
49
|
-
const stateManager = cornerstoneTools.newImageIdSpecificToolStateManager();
|
|
50
|
-
|
|
51
|
-
// Get enabled element (cornerstone.getEnabledElement)
|
|
52
|
-
const imageId = "imagefile:0";
|
|
53
|
-
const testElement = cornerstone.getEnabledElements().slice().pop();
|
|
54
|
-
testElement.image = { imageId };
|
|
55
|
-
// const testElement = {
|
|
56
|
-
// image: {
|
|
57
|
-
// imageId
|
|
58
|
-
// }
|
|
59
|
-
// };
|
|
60
|
-
|
|
61
|
-
// Setup with some initial data
|
|
62
|
-
const toolType = "EllipticalRoi";
|
|
63
|
-
stateManager.restoreImageIdToolState(imageId, {
|
|
64
|
-
[toolType]: { data: state_to_load[imageId] }
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Add more data
|
|
68
|
-
stateManager.add(testElement, toolType, "addedData");
|
|
69
|
-
|
|
70
|
-
// Check the results
|
|
71
|
-
const allToolState = stateManager.saveToolState();
|
|
72
|
-
}
|
|
73
|
-
*/
|
|
74
|
-
export { saveToolState, restoreToolState };
|