larvitar 1.5.14 → 2.0.2
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/.vscode/settings.json +4 -0
- package/README.md +78 -48
- package/bundler/webpack.common.js +27 -0
- package/bundler/webpack.dev.js +23 -0
- package/bundler/webpack.prod.js +19 -0
- package/decs.d.ts +12 -0
- package/dist/imaging/MetaDataReadable.d.ts +40 -0
- package/dist/imaging/MetaDataTypes.d.ts +3489 -0
- package/dist/imaging/imageAnonymization.d.ts +12 -0
- package/dist/imaging/imageColormaps.d.ts +47 -0
- package/dist/imaging/imageContours.d.ts +18 -0
- package/dist/imaging/imageIo.d.ts +42 -0
- package/dist/imaging/imageLayers.d.ts +56 -0
- package/dist/imaging/imageLoading.d.ts +65 -0
- package/dist/imaging/imageParsing.d.ts +46 -0
- package/dist/imaging/imagePresets.d.ts +43 -0
- package/dist/imaging/imageRendering.d.ts +238 -0
- package/dist/imaging/imageReslice.d.ts +14 -0
- package/dist/imaging/imageStore.d.ts +121 -0
- package/dist/imaging/imageTags.d.ts +22 -0
- package/dist/imaging/imageTools.d.ts +20 -0
- package/dist/imaging/imageUtils.d.ts +165 -0
- package/dist/imaging/loaders/commonLoader.d.ts +103 -0
- package/dist/imaging/loaders/dicomLoader.d.ts +29 -0
- package/dist/imaging/loaders/fileLoader.d.ts +33 -0
- package/dist/imaging/loaders/multiframeLoader.d.ts +37 -0
- package/dist/imaging/loaders/nrrdLoader.d.ts +113 -0
- package/dist/imaging/loaders/resliceLoader.d.ts +15 -0
- package/dist/imaging/monitors/memory.d.ts +41 -0
- package/dist/imaging/monitors/performance.d.ts +23 -0
- package/dist/imaging/parsers/ecg.d.ts +15 -0
- package/dist/imaging/parsers/nrrd.d.ts +3 -0
- package/dist/imaging/tools/custom/4dSliceScrollTool.d.ts +12 -0
- package/dist/imaging/tools/custom/BorderMagnifyTool.d.ts +18 -0
- package/dist/imaging/tools/custom/contourTool.d.ts +409 -0
- package/dist/imaging/tools/custom/diameterTool.d.ts +18 -0
- package/dist/imaging/tools/custom/editMaskTool.d.ts +22 -0
- package/dist/imaging/tools/custom/ellipticalRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/polygonSegmentationMixin.d.ts +54 -0
- package/dist/imaging/tools/custom/polylineScissorsTool.d.ts +11 -0
- package/dist/imaging/tools/custom/rectangleRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/seedTool.d.ts +0 -0
- package/dist/imaging/tools/custom/setLabelMap3D.d.ts +39 -0
- package/dist/imaging/tools/custom/thresholdsBrushTool.d.ts +19 -0
- package/dist/imaging/tools/default.d.ts +53 -0
- package/dist/imaging/tools/interaction.d.ts +30 -0
- package/dist/imaging/tools/io.d.ts +38 -0
- package/dist/imaging/tools/main.d.ts +81 -0
- package/dist/imaging/tools/segmentation.d.ts +125 -0
- package/dist/imaging/tools/state.d.ts +17 -0
- package/dist/imaging/tools/strategies/eraseFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/fillFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/index.d.ts +2 -0
- package/dist/imaging/waveforms/ecg.d.ts +39 -0
- package/dist/index.d.ts +35 -0
- package/dist/larvitar.js +90104 -0
- package/dist/larvitar.js.map +1 -0
- package/imaging/MetaDataReadable.ts +41 -0
- package/imaging/MetaDataTypes.ts +3491 -0
- package/imaging/dataDictionary.json +5328 -5328
- package/imaging/{imageAnonymization.js → imageAnonymization.ts} +41 -13
- package/imaging/{imageColormaps.js → imageColormaps.ts} +48 -30
- package/imaging/{imageContours.js → imageContours.ts} +24 -22
- package/imaging/{imageIo.js → imageIo.ts} +89 -52
- package/imaging/{imageLayers.js → imageLayers.ts} +31 -14
- package/imaging/{imageLoading.js → imageLoading.ts} +107 -43
- package/imaging/{imageParsing.js → imageParsing.ts} +160 -80
- package/imaging/{imagePresets.js → imagePresets.ts} +44 -11
- package/imaging/imageRendering.ts +1091 -0
- package/imaging/{imageReslice.js → imageReslice.ts} +18 -9
- package/imaging/imageStore.ts +487 -0
- package/imaging/imageTags.ts +609 -0
- package/imaging/imageTools.js +2 -1
- package/imaging/{imageUtils.js → imageUtils.ts} +211 -701
- package/imaging/loaders/{commonLoader.js → commonLoader.ts} +73 -24
- package/imaging/loaders/{dicomLoader.js → dicomLoader.ts} +25 -5
- package/imaging/loaders/{fileLoader.js → fileLoader.ts} +5 -5
- package/imaging/loaders/{multiframeLoader.js → multiframeLoader.ts} +145 -90
- package/imaging/loaders/{nrrdLoader.js → nrrdLoader.ts} +231 -64
- package/imaging/loaders/{resliceLoader.js → resliceLoader.ts} +51 -20
- package/imaging/monitors/{memory.js → memory.ts} +54 -8
- package/imaging/monitors/performance.ts +34 -0
- package/imaging/parsers/ecg.ts +54 -0
- package/imaging/tools/README.md +27 -0
- package/imaging/tools/custom/4dSliceScrollTool.js +47 -46
- package/imaging/tools/custom/BorderMagnifyTool.js +99 -0
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +534 -0
- package/imaging/tools/custom/polylineScissorsTool.js +1 -1
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +564 -0
- package/imaging/tools/{setLabelMap3D.js → custom/setLabelMap3D.ts} +19 -25
- package/imaging/tools/{default.js → default.ts} +119 -33
- package/imaging/tools/{interaction.js → interaction.ts} +42 -23
- package/imaging/tools/{io.js → io.ts} +47 -31
- package/imaging/tools/{main.js → main.ts} +105 -40
- package/imaging/tools/{segmentation.js → segmentation.ts} +95 -68
- package/imaging/tools/{state.js → state.ts} +7 -12
- package/imaging/tools/types.d.ts +243 -0
- package/imaging/types.d.ts +200 -0
- package/imaging/waveforms/ecg.ts +191 -0
- package/{index.js → index.ts} +53 -14
- package/jsdoc.json +1 -1
- package/package.json +35 -14
- package/tsconfig.json +102 -0
- package/imaging/imageRendering.js +0 -860
- package/imaging/imageStore.js +0 -322
- package/modules/vuex/larvitar.js +0 -187
- /package/imaging/tools/{polygonSegmentationMixin.js → custom/polygonSegmentationMixin.js} +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import sha256 from "crypto-js/sha256";
|
|
8
8
|
import Hex from "crypto-js/enc-hex";
|
|
9
9
|
import { forEach } from "lodash";
|
|
10
|
+
import { Instance, MetaData, Series } from "./types";
|
|
10
11
|
|
|
11
12
|
const SH = [
|
|
12
13
|
"x00080050" // Accession Number,
|
|
@@ -111,43 +112,70 @@ const TAGS = [
|
|
|
111
112
|
* @param {Object} series - Cornerstone series object
|
|
112
113
|
* @returns {Object} anonymized_series: Cornerstone anonymized series object
|
|
113
114
|
*/
|
|
114
|
-
export const anonymize = function (series) {
|
|
115
|
-
forEach(series.instances, function (instance) {
|
|
115
|
+
export const anonymize = function (series: Series) {
|
|
116
|
+
forEach(series.instances, function (instance: Instance) {
|
|
116
117
|
forEach(TAGS, function (tag) {
|
|
117
118
|
if (tag in instance.metadata) {
|
|
118
|
-
let
|
|
119
|
+
let tag_meta = tag as keyof MetaData;
|
|
120
|
+
|
|
121
|
+
let anonymized_value = sha256(
|
|
122
|
+
(instance.metadata[tag_meta] || "").toString()
|
|
123
|
+
).toString(Hex);
|
|
124
|
+
|
|
119
125
|
// Patient Tag Anonymization
|
|
120
|
-
if (
|
|
121
|
-
instance.metadata[
|
|
126
|
+
if (tag_meta === "x00100010") {
|
|
127
|
+
instance.metadata[tag_meta] =
|
|
122
128
|
"Anonymized^" + anonymized_value.substring(0, 6);
|
|
123
129
|
}
|
|
124
130
|
// Short string
|
|
125
|
-
else if (
|
|
126
|
-
instance.metadata[
|
|
131
|
+
else if (tag_meta === "x00080050") {
|
|
132
|
+
instance.metadata[tag_meta] = anonymized_value.substring(0, 16);
|
|
127
133
|
}
|
|
128
134
|
// Required, empty if unknown
|
|
129
|
-
else if (OPTIONAL.includes(tag) === true) {
|
|
130
|
-
|
|
135
|
+
/*else if (OPTIONAL.includes(tag) === true) {
|
|
136
|
+
tag_meta = tag as keyof MetaData;
|
|
137
|
+
instance.metadata[tag_meta] = "";
|
|
138
|
+
}*/
|
|
139
|
+
else if (tag_meta === "x00100030") {
|
|
140
|
+
instance.metadata[tag_meta] = "";
|
|
141
|
+
} else if (tag_meta === "x00080090") {
|
|
142
|
+
instance.metadata[tag_meta] = "";
|
|
143
|
+
} else if (tag_meta === "x00100020") {
|
|
144
|
+
instance.metadata[tag_meta] = "";
|
|
145
|
+
} else if (tag_meta === "x00100040") {
|
|
146
|
+
instance.metadata[tag_meta] = "";
|
|
147
|
+
} else if (tag_meta === "x00200010") {
|
|
148
|
+
instance.metadata[tag_meta] = "";
|
|
131
149
|
}
|
|
132
150
|
// Optional
|
|
133
151
|
else if (REMOVE.includes(tag) === true) {
|
|
134
|
-
|
|
152
|
+
//tag_meta = tag as keyof MetaData;
|
|
153
|
+
delete instance.metadata[tag_meta];
|
|
135
154
|
}
|
|
136
155
|
// Default sha256
|
|
137
156
|
else {
|
|
138
|
-
|
|
157
|
+
tag_meta = tag as keyof MetaData;
|
|
158
|
+
if (instance.metadata[tag_meta] === "string") {
|
|
159
|
+
instance.metadata[tag_meta] = anonymized_value as any;
|
|
160
|
+
}
|
|
161
|
+
//TODO-ts: check if this case has to be applied only on strings
|
|
162
|
+
//or also on numbers and if any type could be correct to force solution
|
|
163
|
+
//or find another solution
|
|
139
164
|
}
|
|
140
165
|
}
|
|
141
166
|
});
|
|
167
|
+
|
|
142
168
|
instance.metadata["x00120062"] = "YES"; // Patient Identity Removed Attribute
|
|
143
169
|
instance.metadata.seriesUID = instance.metadata["x0020000e"];
|
|
144
170
|
instance.metadata.instanceUID = instance.metadata["x00080018"];
|
|
145
171
|
instance.metadata.studyUID = instance.metadata["x0020000d"];
|
|
146
172
|
instance.metadata.accessionNumber = instance.metadata["x00080050"];
|
|
147
173
|
instance.metadata.studyDescription = instance.metadata["x00081030"];
|
|
148
|
-
instance.metadata.patientName = instance.metadata["x00100010"];
|
|
174
|
+
instance.metadata.patientName = instance.metadata["x00100010"] as string;
|
|
149
175
|
instance.metadata.patientBirthdate = instance.metadata["x00100030"];
|
|
150
|
-
instance.metadata.seriesDescription = instance.metadata[
|
|
176
|
+
instance.metadata.seriesDescription = instance.metadata[
|
|
177
|
+
"x0008103e"
|
|
178
|
+
] as string;
|
|
151
179
|
instance.metadata.anonymized = true;
|
|
152
180
|
});
|
|
153
181
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import cornerstone from "cornerstone-core";
|
|
7
|
+
import { EnabledElementLayer } from "cornerstone-core";
|
|
7
8
|
import { each } from "lodash";
|
|
8
9
|
|
|
9
10
|
/*
|
|
@@ -33,8 +34,12 @@ export function getColormapsList() {
|
|
|
33
34
|
* @param {String} colormapName - the new colormap name
|
|
34
35
|
* @param {Array} colors - array containing 255 rgb colors (ie [[r,g,b], [r,g,b], ...])
|
|
35
36
|
*/
|
|
36
|
-
export function addColorMap(
|
|
37
|
-
|
|
37
|
+
export function addColorMap(
|
|
38
|
+
colormapId: string,
|
|
39
|
+
colormapName: string,
|
|
40
|
+
colors: Array<Array<number>>
|
|
41
|
+
) {
|
|
42
|
+
const colormap = cornerstone.colors.getColormap(colormapId, null);
|
|
38
43
|
colormap.setColorSchemeName(colormapName);
|
|
39
44
|
let noc = colors.length;
|
|
40
45
|
colormap.setNumberOfColors(noc);
|
|
@@ -54,31 +59,35 @@ export function addColorMap(colormapId, colormapName, colors) {
|
|
|
54
59
|
* @param {HTMLCanvasElement} canvas - target canvas
|
|
55
60
|
* @param {String} colormapId - the colormap name
|
|
56
61
|
*/
|
|
57
|
-
export function fillPixelData(canvas, colormapId) {
|
|
62
|
+
export function fillPixelData(canvas: HTMLCanvasElement, colormapId: string) {
|
|
58
63
|
const ctx = canvas.getContext("2d");
|
|
59
64
|
const height = canvas.height;
|
|
60
65
|
const width = canvas.width;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
if (ctx) {
|
|
67
|
+
const colorbar = ctx.createImageData(width, height);
|
|
68
|
+
const colormap = cornerstone.colors.getColormap(colormapId, {});
|
|
69
|
+
const lookupTable = colormap.createLookupTable();
|
|
70
|
+
|
|
71
|
+
// Set the min and max values then the lookup table
|
|
72
|
+
// will be able to return the right color for this range
|
|
73
|
+
lookupTable.setTableRange(0, width);
|
|
74
|
+
|
|
75
|
+
// Update the colorbar pixel by pixel
|
|
76
|
+
for (let col = 0; col < width; col++) {
|
|
77
|
+
const color = lookupTable.mapValue(col);
|
|
78
|
+
|
|
79
|
+
for (let row = 0; row < height; row++) {
|
|
80
|
+
const pixel = (col + row * width) * 4;
|
|
81
|
+
colorbar.data[pixel] = color[0];
|
|
82
|
+
colorbar.data[pixel + 1] = color[1];
|
|
83
|
+
colorbar.data[pixel + 2] = color[2];
|
|
84
|
+
colorbar.data[pixel + 3] = color[3];
|
|
85
|
+
}
|
|
79
86
|
}
|
|
87
|
+
ctx.putImageData(colorbar, 0, 0);
|
|
88
|
+
} else {
|
|
89
|
+
console.error("No context found for canvas", canvas);
|
|
80
90
|
}
|
|
81
|
-
ctx.putImageData(colorbar, 0, 0);
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
/**
|
|
@@ -88,21 +97,30 @@ export function fillPixelData(canvas, colormapId) {
|
|
|
88
97
|
* @param {String} colormapId - the colormap name
|
|
89
98
|
* @param {Array} viewportNames - List of viewports where to apply preset
|
|
90
99
|
*/
|
|
91
|
-
export function applyColorMap(
|
|
100
|
+
export function applyColorMap(
|
|
101
|
+
colormapId: string,
|
|
102
|
+
viewportNames?: Array<string>
|
|
103
|
+
) {
|
|
92
104
|
// for retro-compatibility
|
|
93
105
|
if (!viewportNames) {
|
|
94
106
|
viewportNames = cornerstone.getEnabledElements().map(e => e.element.id);
|
|
95
107
|
}
|
|
96
|
-
let colormap = cornerstone.colors.getColormap(colormapId);
|
|
108
|
+
let colormap = cornerstone.colors.getColormap(colormapId, {});
|
|
97
109
|
|
|
98
110
|
each(viewportNames, viewportName => {
|
|
99
111
|
let element = document.getElementById(viewportName);
|
|
100
|
-
|
|
112
|
+
|
|
113
|
+
if (!element) {
|
|
114
|
+
console.error("No element with id:", viewportName);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let enabledElement: EnabledElementLayer;
|
|
101
119
|
|
|
102
120
|
try {
|
|
103
121
|
enabledElement = cornerstone.getEnabledElement(element);
|
|
104
122
|
} catch {
|
|
105
|
-
console.
|
|
123
|
+
console.error("No enabledElement with id", viewportName);
|
|
106
124
|
return;
|
|
107
125
|
}
|
|
108
126
|
|
|
@@ -125,11 +143,11 @@ export function applyColorMap(colormapId, viewportNames) {
|
|
|
125
143
|
/**
|
|
126
144
|
* Converts an HSV (Hue, Saturation, Value) color to RGB (Red, Green, Blue) color value
|
|
127
145
|
* @param {Number} hue A number representing the hue color value
|
|
128
|
-
* @param {
|
|
129
|
-
* @param {
|
|
130
|
-
* @returns {
|
|
146
|
+
* @param {Number} sat A number representing the saturation color value
|
|
147
|
+
* @param {Number} val A number representing the value color value
|
|
148
|
+
* @returns {Number[]} An RGB color array
|
|
131
149
|
*/
|
|
132
|
-
export function HSVToRGB(hue, sat, val) {
|
|
150
|
+
export function HSVToRGB(hue: number, sat: number, val: number) {
|
|
133
151
|
if (hue > 1) {
|
|
134
152
|
throw new Error("HSVToRGB expects hue < 1");
|
|
135
153
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
// external libraries
|
|
7
7
|
import { each, range } from "lodash";
|
|
8
|
+
import { vec2 } from "cornerstone-core";
|
|
9
|
+
import { Contours } from "./types";
|
|
8
10
|
|
|
9
11
|
/*
|
|
10
12
|
* This module provides the following functions to be exported:
|
|
@@ -22,15 +24,15 @@ import { each, range } from "lodash";
|
|
|
22
24
|
* @returns {Number} Number of array elements consumed
|
|
23
25
|
*/
|
|
24
26
|
export const parseContours = function (
|
|
25
|
-
contoursData,
|
|
26
|
-
pointBatchSize,
|
|
27
|
-
segmentationName,
|
|
28
|
-
viewports
|
|
27
|
+
contoursData: { [key: string]: Uint8Array }, // TODO-ts: check if this is correct @mronzoni
|
|
28
|
+
pointBatchSize: number,
|
|
29
|
+
segmentationName: string,
|
|
30
|
+
viewports: Array<string>
|
|
29
31
|
) {
|
|
30
|
-
let contours = {};
|
|
32
|
+
let contours: Contours = {};
|
|
31
33
|
each(viewports, viewport => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
contours[viewport] = {};
|
|
35
|
+
contours[viewport][segmentationName] = [];
|
|
34
36
|
let points = contoursData[viewport];
|
|
35
37
|
|
|
36
38
|
if (!points) {
|
|
@@ -70,15 +72,15 @@ export const parseContours = function (
|
|
|
70
72
|
* @param {Array} data - Raw data (array of pixel values)
|
|
71
73
|
*/
|
|
72
74
|
const populateContoursObject = function (
|
|
73
|
-
pointBatchSize,
|
|
74
|
-
contours,
|
|
75
|
-
lineNumber,
|
|
76
|
-
sliceNumber,
|
|
77
|
-
segmentationName,
|
|
78
|
-
viewport,
|
|
79
|
-
data
|
|
75
|
+
pointBatchSize: number,
|
|
76
|
+
contours: Contours,
|
|
77
|
+
lineNumber: number,
|
|
78
|
+
sliceNumber: number,
|
|
79
|
+
segmentationName: string,
|
|
80
|
+
viewport: string,
|
|
81
|
+
data: Uint8Array
|
|
80
82
|
) {
|
|
81
|
-
let coords = [];
|
|
83
|
+
let coords: Array<{ x: number; y: number; lines: Array<vec2> }> = [];
|
|
82
84
|
|
|
83
85
|
for (let i = 0; i < data.length; i += pointBatchSize) {
|
|
84
86
|
let xy = data.slice(i, pointBatchSize + i);
|
|
@@ -134,11 +136,11 @@ const populateContoursObject = function (
|
|
|
134
136
|
* @returns {Number} Number of array elements consumed
|
|
135
137
|
*/
|
|
136
138
|
const extractSlicePoints = function (
|
|
137
|
-
contours,
|
|
138
|
-
pointBatchSize,
|
|
139
|
-
slicePoints,
|
|
140
|
-
segmentationName,
|
|
141
|
-
viewport
|
|
139
|
+
contours: Contours,
|
|
140
|
+
pointBatchSize: number,
|
|
141
|
+
slicePoints: Uint8Array, // TODO-ts: check if this is correct @mronzoni
|
|
142
|
+
segmentationName: string,
|
|
143
|
+
viewport: string
|
|
142
144
|
) {
|
|
143
145
|
// read slice number and number of lines for current slice, then remove from array
|
|
144
146
|
let sliceNumber = slicePoints[0];
|
|
@@ -152,12 +154,12 @@ const extractSlicePoints = function (
|
|
|
152
154
|
|
|
153
155
|
let numberOfPoints = 0;
|
|
154
156
|
contours[viewport][segmentationName][sliceNumber] = {
|
|
155
|
-
lines: []
|
|
157
|
+
lines: [] as vec2[][]
|
|
156
158
|
};
|
|
157
159
|
|
|
158
160
|
if (numberOfLines) {
|
|
159
161
|
// for each line
|
|
160
|
-
each(range(numberOfLines), function (l) {
|
|
162
|
+
each(range(numberOfLines), function (l: number) {
|
|
161
163
|
// get number of points for current line
|
|
162
164
|
let numberOfPointsPerLine = slicePoints[0];
|
|
163
165
|
// compute coordinates size
|
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
getMeanValue,
|
|
12
12
|
getDistanceBetweenSlices,
|
|
13
13
|
getTypedArrayFromDataType
|
|
14
|
-
} from "./imageUtils
|
|
15
|
-
import
|
|
14
|
+
} from "./imageUtils";
|
|
15
|
+
import store from "./imageStore";
|
|
16
16
|
import { parse } from "./parsers/nrrd";
|
|
17
17
|
import { checkMemoryAllocation } from "./monitors/memory";
|
|
18
|
+
import { Series, Header, Volume, TypedArray } from "./types";
|
|
18
19
|
|
|
19
20
|
/*
|
|
20
21
|
* This module provides the following functions to be exported:
|
|
@@ -30,41 +31,50 @@ import { checkMemoryAllocation } from "./monitors/memory";
|
|
|
30
31
|
* @param {Object} series - Cornerstone series object
|
|
31
32
|
* @returns {Object} header: image metadata
|
|
32
33
|
*/
|
|
33
|
-
export const buildHeader = function (series) {
|
|
34
|
-
let header = {};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
export const buildHeader = function (series: Series) {
|
|
35
|
+
let header: Partial<Header> = {};
|
|
36
|
+
|
|
37
|
+
forEach(series.imageIds, function (imageId: string) {
|
|
38
|
+
header[imageId] = series.instances[imageId].metadata;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
let volume: Partial<Volume> = {};
|
|
42
|
+
|
|
43
|
+
volume.imageIds = series.imageIds;
|
|
44
|
+
volume.seriesId = series.instances[series.imageIds[0]].metadata.seriesUID;
|
|
45
|
+
volume.rows =
|
|
40
46
|
series.instances[series.imageIds[0]].metadata.rows ||
|
|
41
47
|
series.instances[series.imageIds[0]].metadata.x00280010;
|
|
42
|
-
|
|
48
|
+
volume.cols =
|
|
43
49
|
series.instances[series.imageIds[0]].metadata.cols ||
|
|
44
50
|
series.instances[series.imageIds[0]].metadata.x00280011;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
volume.slope = series.instances[series.imageIds[0]].metadata.slope as number;
|
|
52
|
+
volume.repr = series.instances[series.imageIds[0]].metadata.repr as string;
|
|
53
|
+
volume.intercept = series.instances[series.imageIds[0]].metadata
|
|
54
|
+
.intercept as number;
|
|
55
|
+
volume.imagePosition = series.instances[series.imageIds[0]].metadata
|
|
56
|
+
.imagePosition as [number, number];
|
|
57
|
+
volume.numberOfSlices = series.imageIds.length;
|
|
58
|
+
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
volume.imageOrientation = getMeanValue(
|
|
54
61
|
series,
|
|
55
62
|
"imageOrientation",
|
|
56
63
|
true
|
|
57
|
-
);
|
|
64
|
+
) as number[];
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
volume.pixelSpacing = getMeanValue(series, "pixelSpacing", true) as [
|
|
68
|
+
number,
|
|
69
|
+
number
|
|
70
|
+
];
|
|
71
|
+
// volume.maxPixelValue = getMeanValue(series, "maxPixelValue", false);
|
|
72
|
+
// volume.minPixelValue = getMeanValue(series, "minPixelValue", false);
|
|
73
|
+
volume.sliceThickness = getDistanceBetweenSlices(series, 0, 1);
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return header;
|
|
75
|
+
header.volume = volume as Volume;
|
|
76
|
+
|
|
77
|
+
return header as Header;
|
|
68
78
|
};
|
|
69
79
|
|
|
70
80
|
/**
|
|
@@ -74,12 +84,12 @@ export const buildHeader = function (series) {
|
|
|
74
84
|
* @returns {Promise} A promise which will resolve to a pixel data array or fail if an error occurs
|
|
75
85
|
*/
|
|
76
86
|
|
|
77
|
-
export const getCachedPixelData = function (imageId) {
|
|
87
|
+
export const getCachedPixelData = function (imageId: string) {
|
|
78
88
|
let cachedImage = find(cornerstone.imageCache.cachedImages, [
|
|
79
89
|
"imageId",
|
|
80
90
|
imageId
|
|
81
91
|
]);
|
|
82
|
-
let promise = new Promise((resolve, reject) => {
|
|
92
|
+
let promise = new Promise<number[]>((resolve, reject) => {
|
|
83
93
|
if (cachedImage && cachedImage.image) {
|
|
84
94
|
resolve(cachedImage.image.getPixelData());
|
|
85
95
|
} else {
|
|
@@ -99,37 +109,48 @@ export const getCachedPixelData = function (imageId) {
|
|
|
99
109
|
* @param {Bool} useSeriesData - Flag to force using "series" data instead of cached ones
|
|
100
110
|
* @returns {Array} Contiguous pixel array
|
|
101
111
|
*/
|
|
102
|
-
export const buildData = function (series, useSeriesData) {
|
|
112
|
+
export const buildData = function (series: Series, useSeriesData: boolean) {
|
|
103
113
|
if (checkMemoryAllocation(series.bytes)) {
|
|
104
114
|
let t0 = performance.now();
|
|
105
115
|
let repr = series.instances[series.imageIds[0]].metadata.repr;
|
|
106
116
|
let rows =
|
|
107
|
-
series.instances[series.imageIds[0]].metadata.rows ||
|
|
108
|
-
series.instances[series.imageIds[0]].metadata.x00280010;
|
|
117
|
+
(series.instances[series.imageIds[0]].metadata.rows as number) ||
|
|
118
|
+
(series.instances[series.imageIds[0]].metadata.x00280010 as number);
|
|
109
119
|
let cols =
|
|
110
|
-
series.instances[series.imageIds[0]].metadata.cols ||
|
|
111
|
-
series.instances[series.imageIds[0]].metadata.x00280011;
|
|
120
|
+
(series.instances[series.imageIds[0]].metadata.cols as number) ||
|
|
121
|
+
(series.instances[series.imageIds[0]].metadata.x00280011 as number);
|
|
112
122
|
let len = rows * cols * series.imageIds.length;
|
|
113
123
|
|
|
114
|
-
|
|
124
|
+
if (!repr) {
|
|
125
|
+
throw new Error("Image representation metadata not found");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let typedArray = getTypedArrayFromDataType(repr as string);
|
|
129
|
+
|
|
130
|
+
if (!typedArray) {
|
|
131
|
+
throw new Error("Image representation not supported");
|
|
132
|
+
}
|
|
133
|
+
|
|
115
134
|
let data = new typedArray(len);
|
|
116
135
|
let offsetData = 0;
|
|
117
136
|
|
|
118
137
|
// use input data or cached data
|
|
119
138
|
if (useSeriesData) {
|
|
120
|
-
forEach(series.imageIds, function (imageId) {
|
|
139
|
+
forEach(series.imageIds, function (imageId: string) {
|
|
121
140
|
const sliceData = series.instances[imageId].pixelData;
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
if (sliceData) {
|
|
142
|
+
data.set(sliceData, offsetData);
|
|
143
|
+
offsetData += sliceData.length;
|
|
144
|
+
}
|
|
124
145
|
});
|
|
125
146
|
let t1 = performance.now();
|
|
126
147
|
console.log(`Call to buildData took ${t1 - t0} milliseconds.`);
|
|
127
148
|
return data;
|
|
128
149
|
} else {
|
|
129
|
-
|
|
150
|
+
store.addSeriesId(series.seriesUID, series.imageIds);
|
|
130
151
|
let image_counter = 0;
|
|
131
|
-
forEach(series.imageIds, function (imageId) {
|
|
132
|
-
getCachedPixelData(imageId).then(sliceData => {
|
|
152
|
+
forEach(series.imageIds, function (imageId: string) {
|
|
153
|
+
getCachedPixelData(imageId).then((sliceData: number[]) => {
|
|
133
154
|
data.set(sliceData, offsetData);
|
|
134
155
|
offsetData += sliceData.length;
|
|
135
156
|
image_counter += 1;
|
|
@@ -154,26 +175,42 @@ export const buildData = function (series, useSeriesData) {
|
|
|
154
175
|
* @param {Function} resolve - Promise resolve function
|
|
155
176
|
* @param {Function} reject - Promise reject function
|
|
156
177
|
*/
|
|
157
|
-
export const buildDataAsync = function (
|
|
178
|
+
export const buildDataAsync = function (
|
|
179
|
+
series: Series,
|
|
180
|
+
time: number,
|
|
181
|
+
resolve: (response: TypedArray) => void,
|
|
182
|
+
reject: (response: string) => void
|
|
183
|
+
) {
|
|
158
184
|
const memoryAllocation = checkMemoryAllocation(series.bytes);
|
|
185
|
+
|
|
159
186
|
if (memoryAllocation) {
|
|
160
187
|
let t0 = performance.now();
|
|
161
188
|
let repr = series.instances[series.imageIds[0]].metadata.repr;
|
|
162
189
|
let rows =
|
|
163
|
-
series.instances[series.imageIds[0]].metadata.rows ||
|
|
164
|
-
series.instances[series.imageIds[0]].metadata.x00280010;
|
|
190
|
+
(series.instances[series.imageIds[0]].metadata.rows as number) ||
|
|
191
|
+
(series.instances[series.imageIds[0]].metadata.x00280010 as number);
|
|
165
192
|
let cols =
|
|
166
|
-
series.instances[series.imageIds[0]].metadata.cols ||
|
|
167
|
-
series.instances[series.imageIds[0]].metadata.x00280011;
|
|
193
|
+
(series.instances[series.imageIds[0]].metadata.cols as number) ||
|
|
194
|
+
(series.instances[series.imageIds[0]].metadata.x00280011 as number);
|
|
168
195
|
let len = rows * cols * series.imageIds.length;
|
|
169
|
-
|
|
196
|
+
|
|
197
|
+
if (!repr) {
|
|
198
|
+
throw new Error("Image representation metadata not found");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let typedArray = getTypedArrayFromDataType(repr as string);
|
|
202
|
+
|
|
203
|
+
if (!typedArray) {
|
|
204
|
+
throw new Error("Image representation not supported");
|
|
205
|
+
}
|
|
206
|
+
|
|
170
207
|
let data = new typedArray(len);
|
|
171
208
|
let offsetData = 0;
|
|
172
209
|
|
|
173
210
|
let imageIds = series.imageIds.slice();
|
|
174
|
-
|
|
211
|
+
store.addSeriesId(series.seriesUID, series.imageIds);
|
|
175
212
|
|
|
176
|
-
function runFillPixelData(data) {
|
|
213
|
+
function runFillPixelData(data: TypedArray) {
|
|
177
214
|
let imageId = imageIds.shift();
|
|
178
215
|
if (imageId) {
|
|
179
216
|
getCachedPixelData(imageId).then(sliceData => {
|
|
@@ -207,8 +244,8 @@ export const buildDataAsync = function (series, time, resolve, reject) {
|
|
|
207
244
|
* @param {ArrayBuffer} bufferArray - buffer array from nrrd file
|
|
208
245
|
* @returns {Array} Parsed pixel data array
|
|
209
246
|
*/
|
|
210
|
-
export const importNRRDImage = function (bufferArray) {
|
|
247
|
+
export const importNRRDImage = function (bufferArray: ArrayBuffer) {
|
|
211
248
|
// get the data
|
|
212
|
-
let volume = parse(bufferArray, {});
|
|
249
|
+
let volume = parse(bufferArray, { headerOnly: false });
|
|
213
250
|
return volume;
|
|
214
251
|
};
|
|
@@ -8,6 +8,7 @@ import cornerstone from "cornerstone-core";
|
|
|
8
8
|
|
|
9
9
|
// internal libraries
|
|
10
10
|
import { isElement } from "./imageUtils";
|
|
11
|
+
import { Series } from "./types";
|
|
11
12
|
|
|
12
13
|
/*
|
|
13
14
|
* This module provides the following functions to be exported:
|
|
@@ -25,17 +26,20 @@ import { isElement } from "./imageUtils";
|
|
|
25
26
|
* @param {Object} options - layer options {opacity:float, colormap: str}
|
|
26
27
|
* @returns {Object} Cornerstone layer object
|
|
27
28
|
*/
|
|
28
|
-
export const buildLayer = function (
|
|
29
|
-
|
|
29
|
+
export const buildLayer = function (
|
|
30
|
+
series: Series,
|
|
31
|
+
tag: string,
|
|
32
|
+
options: { opacity: number; colormap: string }
|
|
33
|
+
) {
|
|
30
34
|
let layer = {
|
|
31
35
|
imageIds: series.imageIds,
|
|
32
36
|
currentImageIdIndex: Math.floor(series.imageIds.length / 2),
|
|
33
37
|
options: {
|
|
34
38
|
name: tag,
|
|
35
|
-
opacity:
|
|
39
|
+
opacity: options?.opacity ? options?.opacity : 1.0,
|
|
36
40
|
visible: true,
|
|
37
41
|
viewport: {
|
|
38
|
-
colormap:
|
|
42
|
+
colormap: options?.colormap ? options?.colormap : "gray"
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
};
|
|
@@ -47,18 +51,28 @@ export const buildLayer = function (series, tag, options) {
|
|
|
47
51
|
* @instance
|
|
48
52
|
* @function updateLayer
|
|
49
53
|
* @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
|
|
50
|
-
* @param {
|
|
54
|
+
* @param {string} layer - The layer id
|
|
51
55
|
* @param {Object} options - The new layer's options
|
|
52
56
|
*/
|
|
53
|
-
export const updateLayer = function (
|
|
57
|
+
export const updateLayer = function (
|
|
58
|
+
elementId: string | HTMLElement,
|
|
59
|
+
layerId: string,
|
|
60
|
+
options: { opacity: number; colormap: string }
|
|
61
|
+
) {
|
|
54
62
|
let element = isElement(elementId)
|
|
55
|
-
? elementId
|
|
56
|
-
: document.getElementById(elementId);
|
|
63
|
+
? (elementId as HTMLElement)
|
|
64
|
+
: document.getElementById(elementId as string);
|
|
57
65
|
if (!element) {
|
|
58
66
|
console.log("not element");
|
|
59
67
|
return;
|
|
60
68
|
}
|
|
61
69
|
let layer = cornerstone.getLayer(element, layerId);
|
|
70
|
+
if (!layer.options) {
|
|
71
|
+
layer["options"] = {};
|
|
72
|
+
}
|
|
73
|
+
if (!layer.viewport) {
|
|
74
|
+
layer["viewport"] = {};
|
|
75
|
+
}
|
|
62
76
|
layer.options.opacity =
|
|
63
77
|
options.opacity != null ? options.opacity : layer.options.opacity;
|
|
64
78
|
layer.viewport.colormap = options.colormap
|
|
@@ -74,10 +88,10 @@ export const updateLayer = function (elementId, layerId, options) {
|
|
|
74
88
|
* @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
|
|
75
89
|
* @returns {Object} layer - The active layer object
|
|
76
90
|
*/
|
|
77
|
-
export const getActiveLayer = function (elementId) {
|
|
91
|
+
export const getActiveLayer = function (elementId: string | HTMLElement) {
|
|
78
92
|
let element = isElement(elementId)
|
|
79
|
-
? elementId
|
|
80
|
-
: document.getElementById(elementId);
|
|
93
|
+
? (elementId as HTMLElement)
|
|
94
|
+
: document.getElementById(elementId as string);
|
|
81
95
|
if (!element) {
|
|
82
96
|
console.log("not element");
|
|
83
97
|
return;
|
|
@@ -92,10 +106,13 @@ export const getActiveLayer = function (elementId) {
|
|
|
92
106
|
* @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
|
|
93
107
|
* @param {String} layerId - The id of the layer
|
|
94
108
|
*/
|
|
95
|
-
export const setActiveLayer = function (
|
|
109
|
+
export const setActiveLayer = function (
|
|
110
|
+
elementId: string | HTMLElement,
|
|
111
|
+
layerId: string
|
|
112
|
+
) {
|
|
96
113
|
let element = isElement(elementId)
|
|
97
|
-
? elementId
|
|
98
|
-
: document.getElementById(elementId);
|
|
114
|
+
? (elementId as HTMLElement)
|
|
115
|
+
: document.getElementById(elementId as string);
|
|
99
116
|
if (!element) {
|
|
100
117
|
console.log("not element");
|
|
101
118
|
return;
|