larvitar 1.3.0-beta.0 → 1.3.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/README.md +3 -3
- package/imaging/imageIo.js +10 -3
- package/imaging/imageLoading.js +19 -3
- package/imaging/imageParsing.js +33 -6
- package/imaging/imageRendering.js +67 -1
- package/imaging/imageStore.js +18 -0
- package/imaging/imageUtils.js +3 -10
- package/imaging/loaders/nrrdLoader.js +28 -26
- package/imaging/tools/interaction.js +1 -0
- package/imaging/tools/segmentation.js +15 -0
- package/index.js +6 -3
- package/modules/vuex/larvitar.js +18 -0
- package/package.json +3 -3
- package/template/.gitkeep +0 -0
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
## Dicom Image Toolkit for CornestoneJS
|
|
8
8
|
|
|
9
|
-
### Current version: 1.2
|
|
9
|
+
### Current version: 1.3.2
|
|
10
10
|
|
|
11
|
-
### Latest Stable version: 1.2
|
|
11
|
+
### Latest Stable version: 1.3.2
|
|
12
12
|
|
|
13
|
-
### Latest Published Release: 1.2
|
|
13
|
+
### Latest Published Release: 1.3.2
|
|
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.
|
package/imaging/imageIo.js
CHANGED
|
@@ -122,18 +122,25 @@ export const buildData = function (series, useSeriesData) {
|
|
|
122
122
|
data.set(sliceData, offsetData);
|
|
123
123
|
offsetData += sliceData.length;
|
|
124
124
|
});
|
|
125
|
+
let t1 = performance.now();
|
|
126
|
+
console.log(`Call to buildData took ${t1 - t0} milliseconds.`);
|
|
127
|
+
return data;
|
|
125
128
|
} else {
|
|
126
129
|
larvitar_store.addSeriesIds(series.seriesUID, series.imageIds);
|
|
130
|
+
let image_counter = 0;
|
|
127
131
|
forEach(series.imageIds, function (imageId) {
|
|
128
132
|
getCachedPixelData(imageId).then(sliceData => {
|
|
129
133
|
data.set(sliceData, offsetData);
|
|
130
134
|
offsetData += sliceData.length;
|
|
135
|
+
image_counter += 1;
|
|
136
|
+
if (image_counter == series.imageIds.length) {
|
|
137
|
+
let t1 = performance.now();
|
|
138
|
+
console.log(`Call to buildData took ${t1 - t0} milliseconds.`);
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
131
141
|
});
|
|
132
142
|
});
|
|
133
143
|
}
|
|
134
|
-
let t1 = performance.now();
|
|
135
|
-
console.log(`Call to buildData took ${t1 - t0} milliseconds.`);
|
|
136
|
-
return data;
|
|
137
144
|
} else {
|
|
138
145
|
throw new Error("Data has not been builded: not enough memory");
|
|
139
146
|
}
|
package/imaging/imageLoading.js
CHANGED
|
@@ -139,9 +139,16 @@ export const updateLoadedStack = function (
|
|
|
139
139
|
let ssid = seriesData.metadata.studyUID;
|
|
140
140
|
let iid = seriesData.metadata.instanceUID;
|
|
141
141
|
let seriesDescription = seriesData.metadata.seriesDescription;
|
|
142
|
+
let numberOfSlices = seriesData.metadata["x00540081"]
|
|
143
|
+
? seriesData.metadata["x00540081"]
|
|
144
|
+
: seriesData.metadata["x00201002"];
|
|
142
145
|
let numberOfFrames = seriesData.metadata["x00280008"];
|
|
143
146
|
let modality = seriesData.metadata["x00080060"];
|
|
144
147
|
let isMultiframe = numberOfFrames > 1 ? true : false;
|
|
148
|
+
let numberOfTemporalPositions = seriesData.metadata["x00200105"];
|
|
149
|
+
let acquisitionNumberAttribute = seriesData.metadata["x00200012"];
|
|
150
|
+
let is4D = seriesData.metadata.is4D;
|
|
151
|
+
|
|
145
152
|
let color = cornerstoneWADOImageLoader.isColorImage(
|
|
146
153
|
seriesData.metadata["x00280004"]
|
|
147
154
|
);
|
|
@@ -157,15 +164,22 @@ export const updateLoadedStack = function (
|
|
|
157
164
|
larvitarSeriesInstanceUID: sid,
|
|
158
165
|
seriesUID: sid,
|
|
159
166
|
studyUID: ssid,
|
|
160
|
-
numberOfImages: 0,
|
|
167
|
+
numberOfImages: is4D ? acquisitionNumberAttribute : 0,
|
|
168
|
+
numberOfSlices: numberOfSlices,
|
|
161
169
|
numberOfFrames: numberOfFrames,
|
|
170
|
+
numberOfTemporalPositions: numberOfTemporalPositions,
|
|
162
171
|
isMultiframe: isMultiframe,
|
|
172
|
+
is4D: is4D,
|
|
163
173
|
modality: modality,
|
|
164
174
|
color: color,
|
|
165
175
|
bytes: 0
|
|
166
176
|
};
|
|
167
177
|
}
|
|
168
178
|
|
|
179
|
+
const sortMethods = is4D
|
|
180
|
+
? ["imagePosition", "contentTime"]
|
|
181
|
+
: ["imagePosition"];
|
|
182
|
+
|
|
169
183
|
// if the parsed file is a new series instance, keep it
|
|
170
184
|
if (isNewInstance(allSeriesStack[id].instances, iid)) {
|
|
171
185
|
// generate an imageId for the file and store it
|
|
@@ -176,7 +190,9 @@ export const updateLoadedStack = function (
|
|
|
176
190
|
);
|
|
177
191
|
|
|
178
192
|
allSeriesStack[id].imageIds.push(imageId);
|
|
179
|
-
|
|
193
|
+
if (is4D === false) {
|
|
194
|
+
allSeriesStack[id].numberOfImages += 1;
|
|
195
|
+
}
|
|
180
196
|
allSeriesStack[id].bytes += seriesData.file.size;
|
|
181
197
|
// store needed instance tags
|
|
182
198
|
allSeriesStack[id].instances[imageId] = {
|
|
@@ -187,7 +203,7 @@ export const updateLoadedStack = function (
|
|
|
187
203
|
// order images in stack
|
|
188
204
|
allSeriesStack[id].imageIds = getSortedStack(
|
|
189
205
|
allSeriesStack[id],
|
|
190
|
-
|
|
206
|
+
sortMethods,
|
|
191
207
|
true
|
|
192
208
|
);
|
|
193
209
|
// populate the ordered dictionary of instanceUIDs
|
package/imaging/imageParsing.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// external libraries
|
|
6
6
|
import { parseDicom } from "dicom-parser";
|
|
7
7
|
import { forEach, each, has, pick } from "lodash";
|
|
8
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
9
|
|
|
9
10
|
// internal libraries
|
|
10
11
|
import { getPixelRepresentation, randomId, parseTag } from "./imageUtils.js";
|
|
@@ -126,10 +127,17 @@ export const parseDataSet = function (dataSet, metadata, customFilter) {
|
|
|
126
127
|
* @function parseNextFile
|
|
127
128
|
* @param {Array} parsingQueue - Array of queued files to be parsed
|
|
128
129
|
* @param {Object} allSeriesStack - Series stack object to be populated
|
|
130
|
+
* @param {string} uuid - Series uuid to be used if series instance uuid is missing
|
|
129
131
|
* @param {Function} resolve - Promise resolve function
|
|
130
132
|
* @param {Function} reject - Promise reject function
|
|
131
133
|
*/
|
|
132
|
-
let parseNextFile = function (
|
|
134
|
+
let parseNextFile = function (
|
|
135
|
+
parsingQueue,
|
|
136
|
+
allSeriesStack,
|
|
137
|
+
uuid,
|
|
138
|
+
resolve,
|
|
139
|
+
reject
|
|
140
|
+
) {
|
|
133
141
|
// initialize t0 on first file of the queue
|
|
134
142
|
if (
|
|
135
143
|
Object.keys(allSeriesStack).length === 0 &&
|
|
@@ -155,22 +163,24 @@ let parseNextFile = function (parsingQueue, allSeriesStack, resolve, reject) {
|
|
|
155
163
|
let t1 = performance.now();
|
|
156
164
|
console.log(`Call to readFiles took ${t1 - t0} milliseconds.`);
|
|
157
165
|
file = null;
|
|
158
|
-
reject(
|
|
166
|
+
reject("Available memory is not enough");
|
|
159
167
|
return;
|
|
160
168
|
} else {
|
|
161
169
|
// parse the file and wait for results
|
|
162
170
|
parseFile(file)
|
|
163
171
|
.then(seriesData => {
|
|
172
|
+
// use generated series uid if not found in dicom file
|
|
173
|
+
seriesData.metadata.seriesUID = seriesData.metadata.seriesUID || uuid;
|
|
164
174
|
// add file to cornerstoneWADOImageLoader file manager
|
|
165
175
|
updateLoadedStack(seriesData, allSeriesStack);
|
|
166
176
|
// proceed with the next file to parse
|
|
167
|
-
parseNextFile(parsingQueue, allSeriesStack, resolve, reject);
|
|
177
|
+
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
168
178
|
seriesData = null;
|
|
169
179
|
file = null;
|
|
170
180
|
})
|
|
171
181
|
.catch(err => {
|
|
172
182
|
console.warn(err);
|
|
173
|
-
parseNextFile(parsingQueue, allSeriesStack, resolve, reject);
|
|
183
|
+
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
174
184
|
file = null;
|
|
175
185
|
});
|
|
176
186
|
}
|
|
@@ -193,7 +203,8 @@ let parseFiles = function (fileList) {
|
|
|
193
203
|
}
|
|
194
204
|
});
|
|
195
205
|
return new Promise((resolve, reject) => {
|
|
196
|
-
|
|
206
|
+
const uuid = uuidv4();
|
|
207
|
+
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
197
208
|
});
|
|
198
209
|
};
|
|
199
210
|
|
|
@@ -220,6 +231,14 @@ let parseFile = function (file) {
|
|
|
220
231
|
let metadata = {};
|
|
221
232
|
parseDataSet(dataSet, metadata);
|
|
222
233
|
|
|
234
|
+
let temporalPositionIdentifier = metadata["x00200100"]; // Temporal order of a dynamic or functional set of Images.
|
|
235
|
+
let numberOfTemporalPositions = metadata["x00200105"]; // Total number of temporal positions prescribed.
|
|
236
|
+
const is4D =
|
|
237
|
+
(temporalPositionIdentifier !== undefined) &
|
|
238
|
+
(numberOfTemporalPositions > 1)
|
|
239
|
+
? true
|
|
240
|
+
: false;
|
|
241
|
+
|
|
223
242
|
let numberOfFrames = metadata["x00280008"];
|
|
224
243
|
let isMultiframe = numberOfFrames > 1 ? true : false;
|
|
225
244
|
// Overwrite SOPInstanceUID to manage multiframes.
|
|
@@ -279,6 +298,14 @@ let parseFile = function (file) {
|
|
|
279
298
|
imageObject.metadata.frameDelay = metadata["x00181066"];
|
|
280
299
|
}
|
|
281
300
|
imageObject.metadata.isMultiframe = isMultiframe;
|
|
301
|
+
if (is4D) {
|
|
302
|
+
imageObject.metadata.temporalPositionIdentifier =
|
|
303
|
+
metadata["x00200100"];
|
|
304
|
+
imageObject.metadata.numberOfTemporalPositions =
|
|
305
|
+
metadata["x00200105"];
|
|
306
|
+
imageObject.metadata.contentTime = metadata["x00080033"];
|
|
307
|
+
}
|
|
308
|
+
imageObject.metadata.is4D = is4D;
|
|
282
309
|
imageObject.metadata.windowCenter = metadata["x00281050"];
|
|
283
310
|
imageObject.metadata.windowWidth = metadata["x00281051"];
|
|
284
311
|
imageObject.metadata.minPixelValue = metadata["x00280106"];
|
|
@@ -292,7 +319,7 @@ let parseFile = function (file) {
|
|
|
292
319
|
}
|
|
293
320
|
}
|
|
294
321
|
} catch (err) {
|
|
295
|
-
console.
|
|
322
|
+
console.warn(err);
|
|
296
323
|
reject("can not read this file");
|
|
297
324
|
}
|
|
298
325
|
};
|
|
@@ -369,16 +369,29 @@ export const updateImage = function (
|
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
371
|
let imageId = series.imageIds[imageIndex];
|
|
372
|
+
|
|
372
373
|
if (imageId) {
|
|
374
|
+
if (series.is4D) {
|
|
375
|
+
const timestamp = series.instances[imageId].metadata.contentTime;
|
|
376
|
+
const timeId =
|
|
377
|
+
series.instances[imageId].metadata.temporalPositionIdentifier - 1; // timeId from 0 to N
|
|
378
|
+
larvitar_store.set("timeId", [elementId, timeId]);
|
|
379
|
+
larvitar_store.set("timestamp", [elementId, timestamp]);
|
|
380
|
+
}
|
|
381
|
+
|
|
373
382
|
if (cacheImage) {
|
|
374
383
|
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
|
375
384
|
cornerstone.displayImage(element, image);
|
|
376
385
|
larvitar_store.set("sliceId", [elementId, imageIndex]);
|
|
386
|
+
larvitar_store.set("minPixelValue", [elementId, image.minPixelValue]);
|
|
387
|
+
larvitar_store.set("maxPixelValue", [elementId, image.maxPixelValue]);
|
|
377
388
|
});
|
|
378
389
|
} else {
|
|
379
390
|
cornerstone.loadImage(imageId).then(function (image) {
|
|
380
391
|
cornerstone.displayImage(element, image);
|
|
381
392
|
larvitar_store.set("sliceId", [elementId, imageIndex]);
|
|
393
|
+
larvitar_store.set("minPixelValue", [elementId, image.minPixelValue]);
|
|
394
|
+
larvitar_store.set("maxPixelValue", [elementId, image.maxPixelValue]);
|
|
382
395
|
});
|
|
383
396
|
}
|
|
384
397
|
} else {
|
|
@@ -512,6 +525,26 @@ export const updateViewportData = function (
|
|
|
512
525
|
case "Rotate":
|
|
513
526
|
larvitar_store.set("rotation", [elementId, viewportData.rotation]);
|
|
514
527
|
break;
|
|
528
|
+
case "mouseWheel":
|
|
529
|
+
const isTimeserie = larvitar_store.get(
|
|
530
|
+
"viewports",
|
|
531
|
+
elementId,
|
|
532
|
+
"isTimeserie"
|
|
533
|
+
);
|
|
534
|
+
if (isTimeserie) {
|
|
535
|
+
const index = viewportData.newImageIdIndex;
|
|
536
|
+
const timeId = larvitar_store.get("viewports", elementId, "timeIds")[
|
|
537
|
+
index
|
|
538
|
+
];
|
|
539
|
+
const timestamp = larvitar_store.get(
|
|
540
|
+
"viewports",
|
|
541
|
+
elementId,
|
|
542
|
+
"timestamps"
|
|
543
|
+
)[index];
|
|
544
|
+
larvitar_store.set("timeId", [elementId, timeId]);
|
|
545
|
+
larvitar_store.set("timestamp", [elementId, timestamp]);
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
515
548
|
default:
|
|
516
549
|
break;
|
|
517
550
|
}
|
|
@@ -536,6 +569,22 @@ export const storeViewportData = function (image, elementId, viewport, data) {
|
|
|
536
569
|
larvitar_store.set("minSliceId", [elementId, 0]);
|
|
537
570
|
larvitar_store.set("sliceId", [elementId, data.imageIndex]);
|
|
538
571
|
larvitar_store.set("maxSliceId", [elementId, data.numberOfSlices - 1]);
|
|
572
|
+
|
|
573
|
+
if (data.isTimeserie) {
|
|
574
|
+
larvitar_store.set("minTimeId", [elementId, 0]);
|
|
575
|
+
larvitar_store.set("timeId", [elementId, data.timeIndex]);
|
|
576
|
+
larvitar_store.set("maxTimeId", [
|
|
577
|
+
elementId,
|
|
578
|
+
data.numberOfTemporalPositions - 1
|
|
579
|
+
]);
|
|
580
|
+
let maxSliceId = data.numberOfSlices * data.numberOfTemporalPositions - 1;
|
|
581
|
+
larvitar_store.set("maxSliceId", [elementId, maxSliceId]);
|
|
582
|
+
|
|
583
|
+
larvitar_store.set("timestamp", [elementId, data.timestamp]);
|
|
584
|
+
larvitar_store.set("timestamps", [elementId, data.timestamps]);
|
|
585
|
+
larvitar_store.set("timeIds", [elementId, data.timeIds]);
|
|
586
|
+
}
|
|
587
|
+
|
|
539
588
|
larvitar_store.set("defaultViewport", [
|
|
540
589
|
elementId,
|
|
541
590
|
viewport.scale,
|
|
@@ -674,6 +723,24 @@ let getSeriesData = function (series, defaultProps) {
|
|
|
674
723
|
data.numberOfSlices = series.imageIds.length;
|
|
675
724
|
data.imageIndex = 0;
|
|
676
725
|
data.imageId = series.imageIds[data.imageIndex];
|
|
726
|
+
} else if (series.is4D) {
|
|
727
|
+
data.isMultiframe = false;
|
|
728
|
+
data.isTimeserie = true;
|
|
729
|
+
// check with real indices
|
|
730
|
+
data.numberOfSlices = series.numberOfImages;
|
|
731
|
+
data.numberOfTemporalPositions = series.numberOfTemporalPositions;
|
|
732
|
+
data.imageIndex = 0;
|
|
733
|
+
data.timeIndex = 0;
|
|
734
|
+
data.timestamp = series.instances[series.imageIds[0]].metadata["x00080033"];
|
|
735
|
+
data.imageId = series.imageIds[data.imageIndex];
|
|
736
|
+
data.timestamps = [];
|
|
737
|
+
data.timeIds = [];
|
|
738
|
+
each(series.imageIds, function (imageId) {
|
|
739
|
+
data.timestamps.push(series.instances[imageId].metadata.contentTime);
|
|
740
|
+
data.timeIds.push(
|
|
741
|
+
series.instances[imageId].metadata.temporalPositionIdentifier - 1 // timeId from 0 to N
|
|
742
|
+
);
|
|
743
|
+
});
|
|
677
744
|
} else {
|
|
678
745
|
data.isMultiframe = false;
|
|
679
746
|
data.numberOfSlices =
|
|
@@ -692,7 +759,6 @@ let getSeriesData = function (series, defaultProps) {
|
|
|
692
759
|
data.imageId = series.imageIds[data.imageIndex];
|
|
693
760
|
}
|
|
694
761
|
data.isColor = series.color;
|
|
695
|
-
data.isTimeserie = false; // TODO 4D
|
|
696
762
|
|
|
697
763
|
// rows, cols and x y z spacing
|
|
698
764
|
data.rows = series.instances[series.imageIds[0]].metadata["x00280010"];
|
package/imaging/imageStore.js
CHANGED
|
@@ -15,6 +15,12 @@ const DEFAULT_VIEWPORT = {
|
|
|
15
15
|
minSliceId: 0,
|
|
16
16
|
maxSliceId: 0,
|
|
17
17
|
sliceId: 0,
|
|
18
|
+
minTimeId: 0,
|
|
19
|
+
maxTimeId: 0,
|
|
20
|
+
timeId: 0,
|
|
21
|
+
timestamp: 0,
|
|
22
|
+
timestamps: [],
|
|
23
|
+
timeIds: [],
|
|
18
24
|
rows: 0,
|
|
19
25
|
cols: 0,
|
|
20
26
|
spacing_x: 0.0,
|
|
@@ -232,6 +238,18 @@ class Larvitar_Store {
|
|
|
232
238
|
this.state["viewports"][data[0]]["maxSliceId"] = data[1];
|
|
233
239
|
} else if (field == "sliceId") {
|
|
234
240
|
this.state["viewports"][data[0]]["sliceId"] = data[1];
|
|
241
|
+
} else if (field == "minTimeId") {
|
|
242
|
+
this.state["viewports"][data[0]]["minTimeId"] = data[1];
|
|
243
|
+
} else if (field == "maxTimeId") {
|
|
244
|
+
this.state["viewports"][data[0]]["maxTimeId"] = data[1];
|
|
245
|
+
} else if (field == "timeId") {
|
|
246
|
+
this.state["viewports"][data[0]]["timeId"] = data[1];
|
|
247
|
+
} else if (field == "timestamp") {
|
|
248
|
+
this.state["viewports"][data[0]]["timestamp"] = data[1];
|
|
249
|
+
} else if (field == "timestamps") {
|
|
250
|
+
this.state["viewports"][data[0]]["timestamps"] = data[1];
|
|
251
|
+
} else if (field == "timeIds") {
|
|
252
|
+
this.state["viewports"][data[0]]["timeIds"] = data[1];
|
|
235
253
|
} else if (field == "isColor") {
|
|
236
254
|
this.state["viewports"][data[0]]["isColor"] = data[1];
|
|
237
255
|
} else if (field == "isMultiframe") {
|
package/imaging/imageUtils.js
CHANGED
|
@@ -205,8 +205,8 @@ export const getPixelTypedArray = function (dataSet, pixelDataElement) {
|
|
|
205
205
|
* @instance
|
|
206
206
|
* @function getSortedStack
|
|
207
207
|
* @param {Object} seriesData - The dataset
|
|
208
|
-
* @param {Array} sortPriorities -
|
|
209
|
-
* @param {Bool} returnSuccessMethod -
|
|
208
|
+
* @param {Array} sortPriorities - An array which represents the priority tasks
|
|
209
|
+
* @param {Bool} returnSuccessMethod - Boolean for returning the success method
|
|
210
210
|
* @return {Object} The sorted stack
|
|
211
211
|
*/
|
|
212
212
|
export const getSortedStack = function (
|
|
@@ -868,14 +868,7 @@ let sortStackCallback = function (seriesData, imageId, method) {
|
|
|
868
868
|
return instanceNumber;
|
|
869
869
|
|
|
870
870
|
case "contentTime":
|
|
871
|
-
|
|
872
|
-
seriesData.instances[imageId].metadata.x00181090;
|
|
873
|
-
var contentTime = seriesData.instances[imageId].metadata.x00080033;
|
|
874
|
-
if (cardiacNumberOfImages && cardiacNumberOfImages > 1 && contentTime) {
|
|
875
|
-
return contentTime;
|
|
876
|
-
} else {
|
|
877
|
-
throw "Not a time series: cardiacNumberOfImages tag not available or <= 1.";
|
|
878
|
-
}
|
|
871
|
+
return seriesData.instances[imageId].metadata.x00080033;
|
|
879
872
|
|
|
880
873
|
case "imagePosition":
|
|
881
874
|
var p = seriesData.instances[imageId].metadata.imagePosition;
|
|
@@ -62,33 +62,35 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
|
|
|
62
62
|
// need to extract header from nrrd file format
|
|
63
63
|
// sizes, spaceDirections and spaceOrigin
|
|
64
64
|
|
|
65
|
+
const index = volume.header.kinds[0] == "domain" ? 0 : 1;
|
|
66
|
+
|
|
65
67
|
let spacing_x = Math.sqrt(
|
|
66
|
-
volume.header["space directions"][0][0] *
|
|
67
|
-
volume.header["space directions"][0][0] +
|
|
68
|
-
volume.header["space directions"][0][1] *
|
|
69
|
-
volume.header["space directions"][0][1] +
|
|
70
|
-
volume.header["space directions"][0][2] *
|
|
71
|
-
volume.header["space directions"][0][2]
|
|
68
|
+
volume.header["space directions"][index + 0][0] *
|
|
69
|
+
volume.header["space directions"][index + 0][0] +
|
|
70
|
+
volume.header["space directions"][index + 0][1] *
|
|
71
|
+
volume.header["space directions"][index + 0][1] +
|
|
72
|
+
volume.header["space directions"][index + 0][2] *
|
|
73
|
+
volume.header["space directions"][index + 0][2]
|
|
72
74
|
);
|
|
73
75
|
let spacing_y = Math.sqrt(
|
|
74
|
-
volume.header["space directions"][1][0] *
|
|
75
|
-
volume.header["space directions"][1][0] +
|
|
76
|
-
volume.header["space directions"][1][1] *
|
|
77
|
-
volume.header["space directions"][1][1] +
|
|
78
|
-
volume.header["space directions"][1][2] *
|
|
79
|
-
volume.header["space directions"][1][2]
|
|
76
|
+
volume.header["space directions"][index + 1][0] *
|
|
77
|
+
volume.header["space directions"][index + 1][0] +
|
|
78
|
+
volume.header["space directions"][index + 1][1] *
|
|
79
|
+
volume.header["space directions"][index + 1][1] +
|
|
80
|
+
volume.header["space directions"][index + 1][2] *
|
|
81
|
+
volume.header["space directions"][index + 1][2]
|
|
80
82
|
);
|
|
81
83
|
let spacing_z = Math.sqrt(
|
|
82
|
-
volume.header["space directions"][2][0] *
|
|
83
|
-
volume.header["space directions"][2][0] +
|
|
84
|
-
volume.header["space directions"][2][1] *
|
|
85
|
-
volume.header["space directions"][2][1] +
|
|
86
|
-
volume.header["space directions"][2][2] *
|
|
87
|
-
volume.header["space directions"][2][2]
|
|
84
|
+
volume.header["space directions"][index + 2][0] *
|
|
85
|
+
volume.header["space directions"][index + 2][0] +
|
|
86
|
+
volume.header["space directions"][index + 2][1] *
|
|
87
|
+
volume.header["space directions"][index + 2][1] +
|
|
88
|
+
volume.header["space directions"][index + 2][2] *
|
|
89
|
+
volume.header["space directions"][index + 2][2]
|
|
88
90
|
);
|
|
89
|
-
header.volume.rows = volume.header.sizes[1];
|
|
90
|
-
header.volume.cols = volume.header.sizes[0];
|
|
91
|
-
header.volume.numberOfSlices = volume.header.sizes[2];
|
|
91
|
+
header.volume.rows = volume.header.sizes[index + 1];
|
|
92
|
+
header.volume.cols = volume.header.sizes[index + 0];
|
|
93
|
+
header.volume.numberOfSlices = volume.header.sizes[index + 2];
|
|
92
94
|
header.volume.imagePosition = volume.header["space origin"];
|
|
93
95
|
header.volume.pixelSpacing = [spacing_x, spacing_y];
|
|
94
96
|
header.volume.sliceThickness = spacing_z;
|
|
@@ -107,11 +109,11 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
|
|
|
107
109
|
? custom_header.acquisition_date
|
|
108
110
|
: "";
|
|
109
111
|
|
|
110
|
-
let rows = volume.header.sizes[1];
|
|
111
|
-
let cols = volume.header.sizes[0];
|
|
112
|
-
let frames = volume.header.sizes[2];
|
|
113
|
-
let iop = volume.header["space directions"][0].concat(
|
|
114
|
-
volume.header["space directions"][1]
|
|
112
|
+
let rows = volume.header.sizes[index + 1];
|
|
113
|
+
let cols = volume.header.sizes[index + 0];
|
|
114
|
+
let frames = volume.header.sizes[index + 2];
|
|
115
|
+
let iop = volume.header["space directions"][index + 0].concat(
|
|
116
|
+
volume.header["space directions"][index + 1]
|
|
115
117
|
);
|
|
116
118
|
let firstIpp = header.volume.imagePosition;
|
|
117
119
|
let w = getNormalOrientation(iop);
|
|
@@ -158,6 +158,7 @@ export const toggleMouseToolsListeners = function (elementId, disable) {
|
|
|
158
158
|
// mouse wheel handler
|
|
159
159
|
function mouseWheelHandler(evt) {
|
|
160
160
|
larvitar_store.set("sliceId", [evt.target.id, evt.detail.newImageIdIndex]);
|
|
161
|
+
updateViewportData(evt.srcElement.id, evt.detail, "mouseWheel");
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
if (disable) {
|
|
@@ -13,6 +13,8 @@ const { getters, setters } = segModule;
|
|
|
13
13
|
// internal libraries
|
|
14
14
|
import { setToolActive, setToolDisabled } from "./main";
|
|
15
15
|
import { isElement } from "../imageUtils";
|
|
16
|
+
import { larvitar_store } from "../imageStore";
|
|
17
|
+
import { updateStackToolState } from "../imageTools";
|
|
16
18
|
|
|
17
19
|
// custom code
|
|
18
20
|
import { setLabelmap3DForElement } from "./setLabelMap3D";
|
|
@@ -199,6 +201,19 @@ export function addSegmentationMask(props, data, elementId) {
|
|
|
199
201
|
// bind labelmap to colorLUT
|
|
200
202
|
let labelmap3d = getters.labelmap3D(element, props.labelId);
|
|
201
203
|
setters.colorLUTIndexForLabelmap3D(labelmap3d, props.labelId);
|
|
204
|
+
|
|
205
|
+
// set current imageIdIndex in tool state
|
|
206
|
+
let currentImageIdIndex = larvitar_store.get(
|
|
207
|
+
"viewports",
|
|
208
|
+
elementId,
|
|
209
|
+
"sliceId"
|
|
210
|
+
);
|
|
211
|
+
if (currentImageIdIndex !== "error" && currentImageIdIndex >= 0) {
|
|
212
|
+
updateStackToolState(elementId, currentImageIdIndex);
|
|
213
|
+
} else {
|
|
214
|
+
console.error("Cannot get currentImageIdIndex");
|
|
215
|
+
}
|
|
216
|
+
|
|
202
217
|
resolve();
|
|
203
218
|
});
|
|
204
219
|
return promise;
|
package/index.js
CHANGED
|
@@ -5,7 +5,6 @@ console.log(`LARVITAR v${VERSION}`);
|
|
|
5
5
|
import cornerstone from "cornerstone-core";
|
|
6
6
|
import cornerstoneTools from "cornerstone-tools";
|
|
7
7
|
import cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
|
|
8
|
-
console.log(`WADO v${cornerstoneWADOImageLoader.version}`);
|
|
9
8
|
const segModule = cornerstoneTools.getModule("segmentation");
|
|
10
9
|
|
|
11
10
|
import larvitarModule from "./modules/vuex/larvitar";
|
|
@@ -143,7 +142,9 @@ import {
|
|
|
143
142
|
setActiveSegment,
|
|
144
143
|
undoLastStroke,
|
|
145
144
|
redoLastStroke,
|
|
146
|
-
setBrushProps
|
|
145
|
+
setBrushProps,
|
|
146
|
+
hexToRgb,
|
|
147
|
+
rgbToHex
|
|
147
148
|
} from "./imaging/tools/segmentation";
|
|
148
149
|
|
|
149
150
|
import {
|
|
@@ -366,5 +367,7 @@ export {
|
|
|
366
367
|
setActiveSegment,
|
|
367
368
|
undoLastStroke,
|
|
368
369
|
redoLastStroke,
|
|
369
|
-
setBrushProps
|
|
370
|
+
setBrushProps,
|
|
371
|
+
hexToRgb,
|
|
372
|
+
rgbToHex
|
|
370
373
|
};
|
package/modules/vuex/larvitar.js
CHANGED
|
@@ -8,6 +8,12 @@ const DEFAULT_VIEWPORT = {
|
|
|
8
8
|
minSliceId: 0,
|
|
9
9
|
maxSliceId: 0,
|
|
10
10
|
sliceId: 0,
|
|
11
|
+
minTimeId: 0,
|
|
12
|
+
maxTimeId: 0,
|
|
13
|
+
timeId: 0,
|
|
14
|
+
timestamp: 0,
|
|
15
|
+
timestamps: [],
|
|
16
|
+
timeIds: [],
|
|
11
17
|
rows: 0,
|
|
12
18
|
cols: 0,
|
|
13
19
|
spacing_x: 0.0,
|
|
@@ -128,6 +134,18 @@ export default {
|
|
|
128
134
|
commit("viewport", { id, d: { maxSliceId } }),
|
|
129
135
|
setSliceId: ({ commit }, [id, sliceId]) =>
|
|
130
136
|
commit("viewport", { id, d: { sliceId } }),
|
|
137
|
+
setMinTimeId: ({ commit }, [id, minTimeId]) =>
|
|
138
|
+
commit("viewport", { id, d: { minTimeId } }),
|
|
139
|
+
setMaxTimeId: ({ commit }, [id, maxTimeId]) =>
|
|
140
|
+
commit("viewport", { id, d: { maxTimeId } }),
|
|
141
|
+
setTimeId: ({ commit }, [id, timeId]) =>
|
|
142
|
+
commit("viewport", { id, d: { timeId } }),
|
|
143
|
+
setTimestamp: ({ commit }, [id, timestamp]) =>
|
|
144
|
+
commit("viewport", { id, d: { timestamp } }),
|
|
145
|
+
setTimestamps: ({ commit }, [id, timestamps]) =>
|
|
146
|
+
commit("viewport", { id, d: { timestamps } }),
|
|
147
|
+
setTimeIds: ({ commit }, [id, timeIds]) =>
|
|
148
|
+
commit("viewport", { id, d: { timeIds } }),
|
|
131
149
|
setIsColor: ({ commit }, [id, isColor]) =>
|
|
132
150
|
commit("viewport", { id, d: { isColor } }),
|
|
133
151
|
setIsMultiframe: ({ commit }, [id, isMultiframe]) =>
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"medical",
|
|
7
7
|
"cornerstone"
|
|
8
8
|
],
|
|
9
|
-
"version": "1.3.
|
|
9
|
+
"version": "1.3.2",
|
|
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",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"@rollup/plugin-commonjs": "^17.1.0",
|
|
29
29
|
"cornerstone-core": "^2.6.1",
|
|
30
30
|
"cornerstone-file-image-loader": "^0.3.0",
|
|
31
|
-
"cornerstone-tools": "^6.0.
|
|
32
|
-
"cornerstone-wado-image-loader": "
|
|
31
|
+
"cornerstone-tools": "^6.0.7",
|
|
32
|
+
"cornerstone-wado-image-loader": "^4.1.3",
|
|
33
33
|
"cornerstone-web-image-loader": "^2.1.1",
|
|
34
34
|
"crypto-js": "^4.1.1",
|
|
35
35
|
"dicom-character-set": "^1.0.3",
|
|
File without changes
|