larvitar 1.1.2 → 1.2.3

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.
@@ -1,4 +1,4 @@
1
- name: CI
1
+ name: CI-docs
2
2
 
3
3
  # Controls when the action will run. Triggers the workflow on push or pull request
4
4
  # events but only for the master branch
@@ -1,6 +1,6 @@
1
1
  # This is a basic workflow to help you get started with Actions
2
2
 
3
- name: CI
3
+ name: CI-Release
4
4
 
5
5
  # Controls when the action will run. Triggers the workflow on push or pull request
6
6
  # events but only for the master branch
@@ -30,17 +30,8 @@ jobs:
30
30
  cd ${{ github.workspace }}
31
31
  rm -rf docs
32
32
 
33
- # Install npm-cli-login
34
- - name: Install npm-cli-login
35
- run: |
36
- npm install -g npm-cli-login
37
-
38
- # Login to npm
39
- - name: Login to npm
40
- run: |
41
- npm-cli-login -u '${{ secrets.NPM_USERNAME }}' -p '${{ secrets.NPM_PASSWORD }}' -e '${{ secrets.NPM_EMAIL }}'
42
-
43
33
  # Publish to npm
44
34
  - name: Publish to npm
45
35
  run: |
36
+ echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
46
37
  npm publish
package/README.md CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  ## Dicom Image Toolkit for CornestoneJS
8
8
 
9
- ### Current version: 1.1.2
9
+ ### Current version: 1.2.3
10
10
 
11
- ### Latest Stable version: 1.1.2
11
+ ### Latest Stable version: 1.2.3
12
12
 
13
- ### Latest Published Release: 1.1.2
13
+ ### Latest Published Release: 1.2.3
14
14
 
15
15
  This library provides common DICOM functionalities to be used in web-applications: it's wrapper that simplifies the use of cornestone-js environment.
16
16
  Orthogonal multiplanar reformat is included as well as custom loader/exporter for nrrd files and [Vuex](https://vuex.vuejs.org/) custom integration.
@@ -278,6 +278,7 @@ let parseFile = function (file) {
278
278
  imageObject.metadata.frameTime = metadata["x00181063"];
279
279
  imageObject.metadata.frameDelay = metadata["x00181066"];
280
280
  }
281
+ imageObject.metadata.isMultiframe = isMultiframe;
281
282
  imageObject.metadata.windowCenter = metadata["x00281050"];
282
283
  imageObject.metadata.windowWidth = metadata["x00281051"];
283
284
  imageObject.metadata.minPixelValue = metadata["x00280106"];
@@ -171,6 +171,22 @@ class Larvitar_Store {
171
171
  }
172
172
  }
173
173
 
174
+ /**
175
+ * Removes all the series from the store
176
+ * @function resetSeriesIds
177
+ */
178
+ resetSeriesIds(seriesId) {
179
+ if (this.VUEX_STORE) {
180
+ let dispatch = "resetSeriesIds";
181
+ let route = this.vuex_module
182
+ ? this.vuex_module + "/" + dispatch
183
+ : dispatch;
184
+ this.vuex_store.dispatch(route, seriesId);
185
+ } else {
186
+ delete this.state.series[seriesId];
187
+ }
188
+ }
189
+
174
190
  /**
175
191
  * Set a value into store
176
192
  * @function set
@@ -38,10 +38,14 @@ export const updateLarvitarManager = function (imageObject, customId) {
38
38
  if (larvitarManager === null) {
39
39
  larvitarManager = {};
40
40
  }
41
+
41
42
  let seriesId = customId || imageObject.seriesUID;
42
43
  let data = { ...imageObject };
43
- if (data.isMultiframe) {
44
- buildMultiFrameImage(seriesId, data);
44
+
45
+ if (data.metadata.isMultiframe) {
46
+ seriesId = customId || imageObject.metadata.seriesUID;
47
+ updateLoadedStack(data, larvitarManager, customId);
48
+ buildMultiFrameImage(seriesId, larvitarManager[seriesId]);
45
49
  } else {
46
50
  updateLoadedStack(data, larvitarManager, customId);
47
51
  }
@@ -14,6 +14,11 @@ const { getters, setters } = segModule;
14
14
  import { setToolActive, setToolDisabled } from "./main";
15
15
  import { isElement } from "../imageUtils";
16
16
 
17
+ // custom code
18
+ import { setLabelmap3DForElement } from "./setLabelMap3D";
19
+ // override function
20
+ setters.labelmap3DForElement = setLabelmap3DForElement;
21
+
17
22
  // General segmentation cs tools module configuration
18
23
  const config = {
19
24
  arrayType: 0,
@@ -172,7 +177,7 @@ export function initSegmentationModule(customConfig) {
172
177
  * @returns {Promise} - Return a promise which will resolve when segmentation mask is added
173
178
  */
174
179
  export function addSegmentationMask(props, data, elementId) {
175
- let promise = new Promise(resolve => {
180
+ let promise = new Promise(async resolve => {
176
181
  let element = isElement(elementId)
177
182
  ? elementId
178
183
  : document.getElementById(elementId);
@@ -180,7 +185,12 @@ export function addSegmentationMask(props, data, elementId) {
180
185
  console.error("invalid html element: " + elementId);
181
186
  return;
182
187
  }
183
- setters.labelmap3DForElement(element, data.buffer, props.labelId);
188
+
189
+ const res = await setters.labelmap3DForElement(
190
+ element,
191
+ data.buffer,
192
+ props.labelId
193
+ );
184
194
  // if user set a color property, use that color for all segments on the labelmap
185
195
  let lut = props.color
186
196
  ? generateUniformLUT(props.color, props.opacity)
@@ -30,3 +30,9 @@ TODO
30
30
  # Larvitar segmentation API
31
31
 
32
32
  TODO
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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * This is a custom version of setLabelMap3D from cs tools source code
3
+ * This let us implement a non-blocking version of the for loop that loads 3d labelmaps on a volume
4
+ * @ronzim
5
+ */
6
+
7
+ // import getElement from "./getElement";
8
+ // import { getToolState } from "../../../stateManagement/toolState.js";
9
+ // import state from "./state";
10
+ // import getSegmentsOnPixelData from "./getSegmentsOnPixeldata";
11
+ // import { triggerLabelmapModifiedEvent } from "../../../util/segmentation";
12
+ // import ARRAY_TYPES from "./arrayTypes";
13
+ // import { getModule } from "../../index.js";
14
+
15
+ const ARRAY_TYPES = {
16
+ UINT_16_ARRAY: 0,
17
+ FLOAT_32_ARRAY: 1
18
+ };
19
+ const { UINT_16_ARRAY, FLOAT_32_ARRAY } = ARRAY_TYPES;
20
+
21
+ import cornerstoneTools from "cornerstone-tools/dist/cornerstoneTools.js";
22
+ const { triggerLabelmapModifiedEvent } = cornerstoneTools.importInternal(
23
+ "util/segmentationUtils"
24
+ );
25
+ const getModule = cornerstoneTools.getModule;
26
+ const getToolState = cornerstoneTools.getToolState;
27
+ const storeGetters = cornerstoneTools.store.getters;
28
+ const state = getModule("segmentation").state;
29
+
30
+ /**
31
+ * These function are reported since they are not exposed by cs tools
32
+ *
33
+ */
34
+
35
+ // from getSegmentsOnPixelData.js
36
+ function getSegmentsOnPixelData(pixelData) {
37
+ return [...new Set(pixelData)];
38
+ }
39
+
40
+ // from getElement.js
41
+ function getElement(elementOrEnabledElementUID) {
42
+ if (elementOrEnabledElementUID instanceof HTMLElement) {
43
+ return elementOrEnabledElementUID;
44
+ }
45
+
46
+ return storeGetters.enabledElementByUID(elementOrEnabledElementUID);
47
+ }
48
+
49
+ /**
50
+ * Takes a 16-bit encoded `ArrayBuffer` and stores it as a `Labelmap3D` for the
51
+ * `BrushStackState` associated with the element.
52
+ *
53
+ * @param {HTMLElement|string} elementOrEnabledElementUID The cornerstone
54
+ * enabled element or its UUID.
55
+ * @param {ArrayBuffer} buffer
56
+ * @param {number} labelmapIndex The index to store the labelmap under.
57
+ * @param {Object[]} metadata = [] Any metadata about the segments.
58
+ * @param {number[][]} [segmentsOnLabelmapArray] An array of array of segments on each imageIdIndex.
59
+ * If not present, is calculated.
60
+ * @param {colorLUTIndex} [colorLUTIndex = 0] The index of the colorLUT to use to render the segmentation.
61
+ * @returns {null}
62
+ */
63
+ async function setLabelmap3DForElement(
64
+ elementOrEnabledElementUID,
65
+ buffer,
66
+ labelmapIndex,
67
+ metadata = [],
68
+ segmentsOnLabelmapArray,
69
+ colorLUTIndex = 0
70
+ ) {
71
+ const element = getElement(elementOrEnabledElementUID);
72
+
73
+ if (!element) {
74
+ return;
75
+ }
76
+
77
+ const stackState = getToolState(element, "stack");
78
+ const numberOfFrames = stackState.data[0].imageIds.length;
79
+ const firstImageId = stackState.data[0].imageIds[0];
80
+
81
+ const res = await setLabelmap3DByFirstImageId(
82
+ firstImageId,
83
+ buffer,
84
+ labelmapIndex,
85
+ metadata,
86
+ numberOfFrames,
87
+ segmentsOnLabelmapArray,
88
+ colorLUTIndex
89
+ );
90
+
91
+ triggerLabelmapModifiedEvent(element, labelmapIndex);
92
+
93
+ return res;
94
+ }
95
+
96
+ /**
97
+ * Takes an 16-bit encoded `ArrayBuffer` and stores it as a `Labelmap3D` for
98
+ * the `BrushStackState` associated with the firstImageId.
99
+ *
100
+ * @param {HTMLElement|string} firstImageId The firstImageId of the series to
101
+ * store the segmentation on.
102
+ * @param {ArrayBuffer} buffer
103
+ * @param {number} labelmapIndex The index to store the labelmap under.
104
+ * @param {Object[]} metadata = [] Any metadata about the segments.
105
+ * @param {number} numberOfFrames The number of frames, required to set up the
106
+ * relevant labelmap2D views.
107
+ * @param {number[][]} [segmentsOnLabelmapArray] An array of array of segments on each imageIdIndex.
108
+ * If not present, is calculated.
109
+ * @param {colorLUTIndex} [colorLUTIndex = 0] The index of the colorLUT to use to render the segmentation.
110
+ * @returns {null}
111
+ */
112
+ function setLabelmap3DByFirstImageId(
113
+ firstImageId,
114
+ buffer,
115
+ labelmapIndex,
116
+ metadata = [],
117
+ numberOfFrames,
118
+ segmentsOnLabelmapArray,
119
+ colorLUTIndex = 0
120
+ ) {
121
+ const { configuration } = getModule("segmentation");
122
+
123
+ let brushStackState = state.series[firstImageId];
124
+
125
+ if (!brushStackState) {
126
+ state.series[firstImageId] = {
127
+ activeLabelmapIndex: labelmapIndex,
128
+ labelmaps3D: []
129
+ };
130
+
131
+ brushStackState = state.series[firstImageId];
132
+ }
133
+
134
+ brushStackState.labelmaps3D[labelmapIndex] = {
135
+ buffer,
136
+ labelmaps2D: [],
137
+ metadata,
138
+ activeSegmentIndex: 1,
139
+ colorLUTIndex,
140
+ segmentsHidden: [],
141
+ undo: [],
142
+ redo: []
143
+ };
144
+
145
+ const labelmaps2D = brushStackState.labelmaps3D[labelmapIndex].labelmaps2D;
146
+ const slicelengthInBytes = buffer.byteLength / numberOfFrames;
147
+
148
+ /* non-blocking implementation by @ronzim */
149
+
150
+ return new Promise(resolve => {
151
+ function setSingleSlice(i, numberOfFrames) {
152
+ var pixelData = void 0;
153
+
154
+ switch (configuration.arrayType) {
155
+ case UINT_16_ARRAY:
156
+ pixelData = new Uint16Array(
157
+ buffer,
158
+ slicelengthInBytes * i, // 2 bytes/voxel
159
+ slicelengthInBytes / 2
160
+ );
161
+ break;
162
+
163
+ case FLOAT_32_ARRAY:
164
+ pixelData = new Float32Array(
165
+ buffer,
166
+ slicelengthInBytes * i,
167
+ slicelengthInBytes / 4
168
+ );
169
+ break;
170
+
171
+ default:
172
+ throw new Error(
173
+ "Unsupported Array Type ".concat(configuration.arrayType)
174
+ );
175
+ }
176
+
177
+ var segmentsOnLabelmap = segmentsOnLabelmapArray
178
+ ? segmentsOnLabelmapArray[i]
179
+ : getSegmentsOnPixelData(pixelData);
180
+
181
+ if (
182
+ segmentsOnLabelmap &&
183
+ segmentsOnLabelmap.some(function (segment) {
184
+ return segment;
185
+ })
186
+ ) {
187
+ labelmaps2D[i] = {
188
+ pixelData: pixelData,
189
+ segmentsOnLabelmap: segmentsOnLabelmap
190
+ };
191
+ }
192
+
193
+ ++i;
194
+
195
+ if (i < numberOfFrames) {
196
+ setTimeout(() => {
197
+ setSingleSlice(i, numberOfFrames);
198
+ }, 0);
199
+ } else {
200
+ resolve("OK");
201
+ }
202
+ }
203
+
204
+ setSingleSlice(0, numberOfFrames);
205
+ });
206
+
207
+ /* original implementation
208
+
209
+ for (let i = 0; i < numberOfFrames; i++) {
210
+ let pixelData;
211
+
212
+ switch (configuration.arrayType) {
213
+ case UINT_16_ARRAY:
214
+ pixelData = new Uint16Array(
215
+ buffer,
216
+ slicelengthInBytes * i, // 2 bytes/voxel
217
+ slicelengthInBytes / 2
218
+ );
219
+
220
+ break;
221
+
222
+ case FLOAT_32_ARRAY:
223
+ pixelData = new Float32Array(
224
+ buffer,
225
+ slicelengthInBytes * i,
226
+ slicelengthInBytes / 4
227
+ );
228
+ break;
229
+
230
+ default:
231
+ throw new Error(`Unsupported Array Type ${configuration.arrayType}`);
232
+ }
233
+
234
+ const segmentsOnLabelmap = segmentsOnLabelmapArray
235
+ ? segmentsOnLabelmapArray[i]
236
+ : getSegmentsOnPixelData(pixelData);
237
+
238
+ if (segmentsOnLabelmap && segmentsOnLabelmap.some(segment => segment)) {
239
+ labelmaps2D[i] = {
240
+ pixelData,
241
+ segmentsOnLabelmap
242
+ };
243
+ }
244
+ }
245
+ */
246
+ }
247
+
248
+ export { setLabelmap3DByFirstImageId, setLabelmap3DForElement };
@@ -99,6 +99,7 @@ export default {
99
99
  },
100
100
  removeSeriesIds: ({ state }, seriesId) =>
101
101
  Vue.delete(state.series, seriesId),
102
+ resetSeriesIds: ({ state }) => Vue.set(state.series, {}),
102
103
  setErrorLog: () => {}, // TODO LT pass elementId
103
104
 
104
105
  // Series fields setters
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "medical",
7
7
  "cornerstone"
8
8
  ],
9
- "version": "1.1.2",
9
+ "version": "1.2.3",
10
10
  "description": "javascript library for parsing, loading, rendering and interacting with DICOM images",
11
11
  "repository": {
12
12
  "url": "https://github.com/dvisionlab/Larvitar.git",