@ohif/app 3.7.0-beta.1 → 3.7.0-beta.100
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/dist/{917.bundle.06cb87cf1c42b9f20bd2.js → 12.bundle.34050abb042540a90a3a.js} +6 -6
- package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.253395f320b72180da63.js} +6 -6
- package/dist/{351.bundle.2d4bc19d8b493879dbbe.js → 181.bundle.169383e9b1a0358b44e8.js} +227 -205
- package/dist/{351.css → 181.css} +1 -1
- package/dist/{744.bundle.392a124e4ee20ad6a657.js → 19.bundle.eef637102334f1832ba9.js} +246 -385
- package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.96bbb4547a346fe3921f.js} +1420 -750
- package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
- package/dist/221.bundle.0b9f7401294213262852.js +1723 -0
- package/dist/221.css +2 -0
- package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
- package/dist/{976.bundle.2305e2f8244de8ccbaee.js → 236.bundle.7768b3e29b1fd618b9ec.js} +89 -105
- package/dist/{55.bundle.5f5e5ef9087b0beee35c.js → 250.bundle.8084960e3318cda37317.js} +52 -36
- package/dist/{973.bundle.a51bb5b329263c9e75e2.js → 281.bundle.39cb630582cf8114fd5e.js} +18 -14
- package/dist/{82.bundle.da0c036b2e572719dbc5.js → 342.bundle.6edc7ac022b4218ec99f.js} +1776 -482
- package/dist/{404.bundle.902c664e3e581335a5d2.js → 359.bundle.a8f6cf2f90fe05fc346d.js} +46 -131
- package/dist/{192.bundle.4fee0d418ae32bb7ab30.js → 370.bundle.4e1276530ba7757347f5.js} +113 -99
- package/dist/{790.bundle.aa7a8bebec3483d90f3f.js → 410.bundle.5a330214c0c31cbf2551.js} +11 -9
- package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.af0a207c29b109f84159.js} +49 -17
- package/dist/{569.bundle.0ed8d8178ffd43fe1c34.js → 451.bundle.9fd36f52ff69594f0669.js} +98 -107
- package/dist/{581.bundle.360d26f97b37da8faec3.js → 471.bundle.b3d77b83b1593c09a504.js} +78 -98
- package/dist/{199.bundle.290ef76d4ab8358e29bb.js → 506.bundle.ac385e382f79bbf4f306.js} +11 -14
- package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 530.bundle.a03b6f942ace3e1baa1e.js} +726 -447
- package/dist/579.css +1 -0
- package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
- package/dist/613.bundle.f75fd669ddfe4ecf9c1b.js +532 -0
- package/dist/{984.bundle.4e002ef9b3468bddb68b.js → 663.bundle.3863ac49f85320c8ca96.js} +68 -38
- package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
- package/dist/{50.bundle.03f455d441f2138ce49e.js → 687.bundle.ebd1260565c31ab9ad08.js} +218 -9
- package/dist/{331.bundle.bd0c13931a21d53086c9.js → 743.bundle.4bfe6e562ffb2c22708f.js} +26281 -21326
- package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.7528cba56a1407357144.js} +95 -64
- package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
- package/dist/{283.bundle.7cf0fabc69d49a8a2c5d.js → 782.bundle.8c17030f76b75035564c.js} +123 -80
- package/dist/{642.bundle.89f15ef57fbad2a1019b.js → 814.bundle.cc0ed4fc75cb16e34f02.js} +6 -6
- package/dist/{707.bundle.832f50366089fd23e93b.js → 82.bundle.7a89cd8b81eb7e1e5c66.js} +1187 -808
- package/dist/{799.bundle.ca18bdf4abe4d9abea26.js → 822.bundle.0545d6dbb49515aa04ee.js} +81 -34
- package/dist/831.bundle.83658f62fcc769043605.js +16700 -0
- package/dist/{953.bundle.9e3a7225f07336bfcb41.js → 886.bundle.4b3a7f2079d085fdbcb3.js} +34 -29
- package/dist/945.min.worker.js +1 -1
- package/dist/945.min.worker.js.map +1 -1
- package/dist/{270.bundle.4564621556b0f963a004.js → 957.bundle.71558794566041f37a92.js} +7110 -997
- package/dist/{208.bundle.29fe1842c33000edc75d.js → 99.bundle.c6876aaaa3ff2f1b08a1.js} +86 -105
- package/dist/_redirects +1 -1
- package/dist/app-config.js +35 -17
- package/dist/app.bundle.css +13 -12
- package/dist/{app.bundle.3ad961046deb5f47678a.js → app.bundle.dc0e89948c4f4568fe20.js} +73720 -68191
- package/dist/assets/yandex-browser-manifest.json +1 -1
- package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
- package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
- package/dist/{dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js → dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js} +5 -4
- package/dist/es6-shim.min.js +3569 -2
- package/dist/google.js +8 -7
- package/dist/index.html +2 -1
- package/dist/{index.worker.1c69152d710fa7b84bce.worker.js → index.worker.e62ecca63f1a2e124230.worker.js} +2 -2
- package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +1 -0
- package/dist/init-service-worker.js +3 -5
- package/dist/oidc-client.min.js +10857 -39
- package/dist/polyfill.min.js +184 -1
- package/dist/silent-refresh.html +18 -9
- package/dist/sw.js +1 -1
- package/package.json +20 -22
- package/dist/616.bundle.b76c70aeeed8b142593c.js +0 -685
- package/dist/780.bundle.fd0f13dc92e9caa0581e.js +0 -4769
- package/dist/index.worker.1c69152d710fa7b84bce.worker.js.map +0 -1
- /package/dist/{806.css → 19.css} +0 -0
- /package/dist/{55.css → 250.css} +0 -0
- /package/dist/{707.css → 82.css} +0 -0
|
@@ -0,0 +1,1723 @@
|
|
|
1
|
+
(self["webpackChunk"] = self["webpackChunk"] || []).push([[221,579],{
|
|
2
|
+
|
|
3
|
+
/***/ 9943:
|
|
4
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
// ESM COMPAT FLAG
|
|
8
|
+
__webpack_require__.r(__webpack_exports__);
|
|
9
|
+
|
|
10
|
+
// EXPORTS
|
|
11
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
12
|
+
"default": () => (/* binding */ cornerstone_dicom_seg_src)
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/package.json
|
|
16
|
+
const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-cornerstone-dicom-seg"}');
|
|
17
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/id.js
|
|
18
|
+
|
|
19
|
+
const id = package_namespaceObject.u2;
|
|
20
|
+
const SOPClassHandlerName = 'dicom-seg';
|
|
21
|
+
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
|
|
22
|
+
|
|
23
|
+
// EXTERNAL MODULE: ../../../node_modules/react/index.js
|
|
24
|
+
var react = __webpack_require__(43001);
|
|
25
|
+
// EXTERNAL MODULE: ../../core/src/index.ts + 65 modules
|
|
26
|
+
var src = __webpack_require__(71771);
|
|
27
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 331 modules
|
|
28
|
+
var esm = __webpack_require__(3743);
|
|
29
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/adapters/dist/adapters.es.js
|
|
30
|
+
var adapters_es = __webpack_require__(91202);
|
|
31
|
+
// EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
|
|
32
|
+
var dcmjs_es = __webpack_require__(67540);
|
|
33
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/utils/dicomlabToRGB.ts
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Converts a CIELAB color to an RGB color using the dcmjs library.
|
|
38
|
+
* @param cielab - The CIELAB color to convert.
|
|
39
|
+
* @returns The RGB color as an array of three integers between 0 and 255.
|
|
40
|
+
*/
|
|
41
|
+
function dicomlabToRGB(cielab) {
|
|
42
|
+
const rgb = dcmjs_es["default"].data.Colors.dicomlab2RGB(cielab).map(x => Math.round(x * 255));
|
|
43
|
+
return rgb;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/getSopClassHandlerModule.js
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.66.4'];
|
|
53
|
+
let loadPromises = {};
|
|
54
|
+
function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
|
|
55
|
+
const instance = instances[0];
|
|
56
|
+
const {
|
|
57
|
+
StudyInstanceUID,
|
|
58
|
+
SeriesInstanceUID,
|
|
59
|
+
SOPInstanceUID,
|
|
60
|
+
SeriesDescription,
|
|
61
|
+
SeriesNumber,
|
|
62
|
+
SeriesDate,
|
|
63
|
+
SOPClassUID,
|
|
64
|
+
wadoRoot,
|
|
65
|
+
wadoUri,
|
|
66
|
+
wadoUriRoot
|
|
67
|
+
} = instance;
|
|
68
|
+
const displaySet = {
|
|
69
|
+
Modality: 'SEG',
|
|
70
|
+
loading: false,
|
|
71
|
+
isReconstructable: true,
|
|
72
|
+
// by default for now since it is a volumetric SEG currently
|
|
73
|
+
displaySetInstanceUID: src.utils.guid(),
|
|
74
|
+
SeriesDescription,
|
|
75
|
+
SeriesNumber,
|
|
76
|
+
SeriesDate,
|
|
77
|
+
SOPInstanceUID,
|
|
78
|
+
SeriesInstanceUID,
|
|
79
|
+
StudyInstanceUID,
|
|
80
|
+
SOPClassHandlerId: SOPClassHandlerId,
|
|
81
|
+
SOPClassUID,
|
|
82
|
+
referencedImages: null,
|
|
83
|
+
referencedSeriesInstanceUID: null,
|
|
84
|
+
referencedDisplaySetInstanceUID: null,
|
|
85
|
+
isDerivedDisplaySet: true,
|
|
86
|
+
isLoaded: false,
|
|
87
|
+
isHydrated: false,
|
|
88
|
+
segments: {},
|
|
89
|
+
sopClassUids,
|
|
90
|
+
instance,
|
|
91
|
+
instances: [instance],
|
|
92
|
+
wadoRoot,
|
|
93
|
+
wadoUriRoot,
|
|
94
|
+
wadoUri,
|
|
95
|
+
isOverlayDisplaySet: true
|
|
96
|
+
};
|
|
97
|
+
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
|
98
|
+
if (!referencedSeriesSequence) {
|
|
99
|
+
console.error('ReferencedSeriesSequence is missing for the SEG');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const referencedSeries = referencedSeriesSequence[0] || referencedSeriesSequence;
|
|
103
|
+
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
|
|
104
|
+
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
|
105
|
+
displaySet.getReferenceDisplaySet = () => {
|
|
106
|
+
const {
|
|
107
|
+
displaySetService
|
|
108
|
+
} = servicesManager.services;
|
|
109
|
+
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(displaySet.referencedSeriesInstanceUID);
|
|
110
|
+
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
|
|
111
|
+
throw new Error('Referenced DisplaySet is missing for the SEG');
|
|
112
|
+
}
|
|
113
|
+
const referencedDisplaySet = referencedDisplaySets[0];
|
|
114
|
+
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
|
115
|
+
|
|
116
|
+
// Todo: this needs to be able to work with other reference volumes (other than streaming) such as nifti, etc.
|
|
117
|
+
displaySet.referencedVolumeURI = referencedDisplaySet.displaySetInstanceUID;
|
|
118
|
+
const referencedVolumeId = `cornerstoneStreamingImageVolume:${displaySet.referencedVolumeURI}`;
|
|
119
|
+
displaySet.referencedVolumeId = referencedVolumeId;
|
|
120
|
+
return referencedDisplaySet;
|
|
121
|
+
};
|
|
122
|
+
displaySet.load = async _ref => {
|
|
123
|
+
let {
|
|
124
|
+
headers
|
|
125
|
+
} = _ref;
|
|
126
|
+
return await _load(displaySet, servicesManager, extensionManager, headers);
|
|
127
|
+
};
|
|
128
|
+
return [displaySet];
|
|
129
|
+
}
|
|
130
|
+
function _load(segDisplaySet, servicesManager, extensionManager, headers) {
|
|
131
|
+
const {
|
|
132
|
+
SOPInstanceUID
|
|
133
|
+
} = segDisplaySet;
|
|
134
|
+
const {
|
|
135
|
+
segmentationService
|
|
136
|
+
} = servicesManager.services;
|
|
137
|
+
if ((segDisplaySet.loading || segDisplaySet.isLoaded) && loadPromises[SOPInstanceUID] && _segmentationExists(segDisplaySet, segmentationService)) {
|
|
138
|
+
return loadPromises[SOPInstanceUID];
|
|
139
|
+
}
|
|
140
|
+
segDisplaySet.loading = true;
|
|
141
|
+
|
|
142
|
+
// We don't want to fire multiple loads, so we'll wait for the first to finish
|
|
143
|
+
// and also return the same promise to any other callers.
|
|
144
|
+
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => {
|
|
145
|
+
if (!segDisplaySet.segments || Object.keys(segDisplaySet.segments).length === 0) {
|
|
146
|
+
await _loadSegments({
|
|
147
|
+
extensionManager,
|
|
148
|
+
servicesManager,
|
|
149
|
+
segDisplaySet,
|
|
150
|
+
headers
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const suppressEvents = true;
|
|
154
|
+
segmentationService.createSegmentationForSEGDisplaySet(segDisplaySet, null, suppressEvents).then(() => {
|
|
155
|
+
segDisplaySet.loading = false;
|
|
156
|
+
resolve();
|
|
157
|
+
}).catch(error => {
|
|
158
|
+
segDisplaySet.loading = false;
|
|
159
|
+
reject(error);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
return loadPromises[SOPInstanceUID];
|
|
163
|
+
}
|
|
164
|
+
async function _loadSegments(_ref2) {
|
|
165
|
+
let {
|
|
166
|
+
extensionManager,
|
|
167
|
+
servicesManager,
|
|
168
|
+
segDisplaySet,
|
|
169
|
+
headers
|
|
170
|
+
} = _ref2;
|
|
171
|
+
const utilityModule = extensionManager.getModuleEntry('@ohif/extension-cornerstone.utilityModule.common');
|
|
172
|
+
const {
|
|
173
|
+
segmentationService
|
|
174
|
+
} = servicesManager.services;
|
|
175
|
+
const {
|
|
176
|
+
dicomLoaderService
|
|
177
|
+
} = utilityModule.exports;
|
|
178
|
+
const arrayBuffer = await dicomLoaderService.findDicomDataPromise(segDisplaySet, null, headers);
|
|
179
|
+
const cachedReferencedVolume = esm.cache.getVolume(segDisplaySet.referencedVolumeId);
|
|
180
|
+
if (!cachedReferencedVolume) {
|
|
181
|
+
throw new Error('Referenced Volume is missing for the SEG, and stack viewport SEG is not supported yet');
|
|
182
|
+
}
|
|
183
|
+
const {
|
|
184
|
+
imageIds
|
|
185
|
+
} = cachedReferencedVolume;
|
|
186
|
+
|
|
187
|
+
// Todo: what should be defaults here
|
|
188
|
+
const tolerance = 0.001;
|
|
189
|
+
const skipOverlapping = true;
|
|
190
|
+
esm.eventTarget.addEventListener(adapters_es/* Enums */.Y.Events.SEGMENTATION_LOAD_PROGRESS, evt => {
|
|
191
|
+
const {
|
|
192
|
+
percentComplete
|
|
193
|
+
} = evt.detail;
|
|
194
|
+
segmentationService._broadcastEvent(segmentationService.EVENTS.SEGMENT_LOADING_COMPLETE, {
|
|
195
|
+
percentComplete
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
const results = await adapters_es.adaptersSEG.Cornerstone3D.Segmentation.generateToolState(imageIds, arrayBuffer, esm.metaData, {
|
|
199
|
+
skipOverlapping,
|
|
200
|
+
tolerance,
|
|
201
|
+
eventTarget: esm.eventTarget,
|
|
202
|
+
triggerEvent: esm.triggerEvent
|
|
203
|
+
});
|
|
204
|
+
results.segMetadata.data.forEach((data, i) => {
|
|
205
|
+
if (i > 0) {
|
|
206
|
+
data.rgba = dicomlabToRGB(data.RecommendedDisplayCIELabValue);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
Object.assign(segDisplaySet, results);
|
|
210
|
+
}
|
|
211
|
+
function _segmentationExists(segDisplaySet, segmentationService) {
|
|
212
|
+
// This should be abstracted with the CornerstoneCacheService
|
|
213
|
+
return segmentationService.getSegmentation(segDisplaySet.displaySetInstanceUID);
|
|
214
|
+
}
|
|
215
|
+
function getSopClassHandlerModule(_ref3) {
|
|
216
|
+
let {
|
|
217
|
+
servicesManager,
|
|
218
|
+
extensionManager
|
|
219
|
+
} = _ref3;
|
|
220
|
+
const getDisplaySetsFromSeries = instances => {
|
|
221
|
+
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
|
222
|
+
};
|
|
223
|
+
return [{
|
|
224
|
+
name: 'dicom-seg',
|
|
225
|
+
sopClassUids,
|
|
226
|
+
getDisplaySetsFromSeries
|
|
227
|
+
}];
|
|
228
|
+
}
|
|
229
|
+
/* harmony default export */ const src_getSopClassHandlerModule = (getSopClassHandlerModule);
|
|
230
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/getHangingProtocolModule.ts
|
|
231
|
+
const segProtocol = {
|
|
232
|
+
id: '@ohif/seg',
|
|
233
|
+
// Don't store this hanging protocol as it applies to the currently active
|
|
234
|
+
// display set by default
|
|
235
|
+
// cacheId: null,
|
|
236
|
+
name: 'Segmentations',
|
|
237
|
+
// Just apply this one when specifically listed
|
|
238
|
+
protocolMatchingRules: [],
|
|
239
|
+
toolGroupIds: ['default'],
|
|
240
|
+
// -1 would be used to indicate active only, whereas other values are
|
|
241
|
+
// the number of required priors referenced - so 0 means active with
|
|
242
|
+
// 0 or more priors.
|
|
243
|
+
numberOfPriorsReferenced: 0,
|
|
244
|
+
// Default viewport is used to define the viewport when
|
|
245
|
+
// additional viewports are added using the layout tool
|
|
246
|
+
defaultViewport: {
|
|
247
|
+
viewportOptions: {
|
|
248
|
+
viewportType: 'stack',
|
|
249
|
+
toolGroupId: 'default',
|
|
250
|
+
allowUnmatchedView: true
|
|
251
|
+
},
|
|
252
|
+
displaySets: [{
|
|
253
|
+
id: 'segDisplaySetId',
|
|
254
|
+
matchedDisplaySetsIndex: -1
|
|
255
|
+
}]
|
|
256
|
+
},
|
|
257
|
+
displaySetSelectors: {
|
|
258
|
+
segDisplaySetId: {
|
|
259
|
+
seriesMatchingRules: [{
|
|
260
|
+
attribute: 'Modality',
|
|
261
|
+
constraint: {
|
|
262
|
+
equals: 'SEG'
|
|
263
|
+
}
|
|
264
|
+
}]
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
stages: [{
|
|
268
|
+
name: 'Segmentations',
|
|
269
|
+
viewportStructure: {
|
|
270
|
+
layoutType: 'grid',
|
|
271
|
+
properties: {
|
|
272
|
+
rows: 1,
|
|
273
|
+
columns: 1
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
viewports: [{
|
|
277
|
+
viewportOptions: {
|
|
278
|
+
allowUnmatchedView: true
|
|
279
|
+
},
|
|
280
|
+
displaySets: [{
|
|
281
|
+
id: 'segDisplaySetId'
|
|
282
|
+
}]
|
|
283
|
+
}]
|
|
284
|
+
}]
|
|
285
|
+
};
|
|
286
|
+
function getHangingProtocolModule() {
|
|
287
|
+
return [{
|
|
288
|
+
name: segProtocol.id,
|
|
289
|
+
protocol: segProtocol
|
|
290
|
+
}];
|
|
291
|
+
}
|
|
292
|
+
/* harmony default export */ const src_getHangingProtocolModule = (getHangingProtocolModule);
|
|
293
|
+
|
|
294
|
+
// EXTERNAL MODULE: ./state/index.js + 1 modules
|
|
295
|
+
var state = __webpack_require__(62657);
|
|
296
|
+
// EXTERNAL MODULE: ../../../extensions/default/src/index.ts + 76 modules
|
|
297
|
+
var default_src = __webpack_require__(56342);
|
|
298
|
+
// EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
|
|
299
|
+
var prop_types = __webpack_require__(3827);
|
|
300
|
+
var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
|
|
301
|
+
// EXTERNAL MODULE: ../../ui/src/index.js + 485 modules
|
|
302
|
+
var ui_src = __webpack_require__(71783);
|
|
303
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/panels/callInputDialog.tsx
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
function callInputDialog(uiDialogService, label, callback) {
|
|
307
|
+
const dialogId = 'enter-segment-label';
|
|
308
|
+
const onSubmitHandler = _ref => {
|
|
309
|
+
let {
|
|
310
|
+
action,
|
|
311
|
+
value
|
|
312
|
+
} = _ref;
|
|
313
|
+
switch (action.id) {
|
|
314
|
+
case 'save':
|
|
315
|
+
callback(value.label, action.id);
|
|
316
|
+
break;
|
|
317
|
+
case 'cancel':
|
|
318
|
+
callback('', action.id);
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
uiDialogService.dismiss({
|
|
322
|
+
id: dialogId
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
if (uiDialogService) {
|
|
326
|
+
uiDialogService.create({
|
|
327
|
+
id: dialogId,
|
|
328
|
+
centralize: true,
|
|
329
|
+
isDraggable: false,
|
|
330
|
+
showOverlay: true,
|
|
331
|
+
content: ui_src/* Dialog */.Vq,
|
|
332
|
+
contentProps: {
|
|
333
|
+
title: 'Segment',
|
|
334
|
+
value: {
|
|
335
|
+
label
|
|
336
|
+
},
|
|
337
|
+
noCloseButton: true,
|
|
338
|
+
onClose: () => uiDialogService.dismiss({
|
|
339
|
+
id: dialogId
|
|
340
|
+
}),
|
|
341
|
+
actions: [{
|
|
342
|
+
id: 'cancel',
|
|
343
|
+
text: 'Cancel',
|
|
344
|
+
type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
|
|
345
|
+
}, {
|
|
346
|
+
id: 'save',
|
|
347
|
+
text: 'Confirm',
|
|
348
|
+
type: ui_src/* ButtonEnums.type */.LZ.dt.primary
|
|
349
|
+
}],
|
|
350
|
+
onSubmit: onSubmitHandler,
|
|
351
|
+
body: _ref2 => {
|
|
352
|
+
let {
|
|
353
|
+
value,
|
|
354
|
+
setValue
|
|
355
|
+
} = _ref2;
|
|
356
|
+
return /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
|
|
357
|
+
label: "Enter the segment label",
|
|
358
|
+
labelClassName: "text-white text-[14px] leading-[1.2]",
|
|
359
|
+
autoFocus: true,
|
|
360
|
+
className: "border-primary-main bg-black",
|
|
361
|
+
type: "text",
|
|
362
|
+
value: value.label,
|
|
363
|
+
onChange: event => {
|
|
364
|
+
event.persist();
|
|
365
|
+
setValue(value => ({
|
|
366
|
+
...value,
|
|
367
|
+
label: event.target.value
|
|
368
|
+
}));
|
|
369
|
+
},
|
|
370
|
+
onKeyPress: event => {
|
|
371
|
+
if (event.key === 'Enter') {
|
|
372
|
+
onSubmitHandler({
|
|
373
|
+
value,
|
|
374
|
+
action: {
|
|
375
|
+
id: 'save'
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/* harmony default export */ const panels_callInputDialog = (callInputDialog);
|
|
387
|
+
// EXTERNAL MODULE: ../../../node_modules/react-color/es/index.js + 219 modules
|
|
388
|
+
var es = __webpack_require__(22831);
|
|
389
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/panels/colorPickerDialog.css
|
|
390
|
+
// extracted by mini-css-extract-plugin
|
|
391
|
+
|
|
392
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/panels/colorPickerDialog.tsx
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
function callColorPickerDialog(uiDialogService, rgbaColor, callback) {
|
|
398
|
+
const dialogId = 'pick-color';
|
|
399
|
+
const onSubmitHandler = _ref => {
|
|
400
|
+
let {
|
|
401
|
+
action,
|
|
402
|
+
value
|
|
403
|
+
} = _ref;
|
|
404
|
+
switch (action.id) {
|
|
405
|
+
case 'save':
|
|
406
|
+
callback(value.rgbaColor, action.id);
|
|
407
|
+
break;
|
|
408
|
+
case 'cancel':
|
|
409
|
+
callback('', action.id);
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
uiDialogService.dismiss({
|
|
413
|
+
id: dialogId
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
if (uiDialogService) {
|
|
417
|
+
uiDialogService.create({
|
|
418
|
+
id: dialogId,
|
|
419
|
+
centralize: true,
|
|
420
|
+
isDraggable: false,
|
|
421
|
+
showOverlay: true,
|
|
422
|
+
content: ui_src/* Dialog */.Vq,
|
|
423
|
+
contentProps: {
|
|
424
|
+
title: 'Segment Color',
|
|
425
|
+
value: {
|
|
426
|
+
rgbaColor
|
|
427
|
+
},
|
|
428
|
+
noCloseButton: true,
|
|
429
|
+
onClose: () => uiDialogService.dismiss({
|
|
430
|
+
id: dialogId
|
|
431
|
+
}),
|
|
432
|
+
actions: [{
|
|
433
|
+
id: 'cancel',
|
|
434
|
+
text: 'Cancel',
|
|
435
|
+
type: 'primary'
|
|
436
|
+
}, {
|
|
437
|
+
id: 'save',
|
|
438
|
+
text: 'Save',
|
|
439
|
+
type: 'secondary'
|
|
440
|
+
}],
|
|
441
|
+
onSubmit: onSubmitHandler,
|
|
442
|
+
body: _ref2 => {
|
|
443
|
+
let {
|
|
444
|
+
value,
|
|
445
|
+
setValue
|
|
446
|
+
} = _ref2;
|
|
447
|
+
const handleChange = color => {
|
|
448
|
+
setValue({
|
|
449
|
+
rgbaColor: color.rgb
|
|
450
|
+
});
|
|
451
|
+
};
|
|
452
|
+
return /*#__PURE__*/react.createElement(es/* ChromePicker */.AI, {
|
|
453
|
+
color: value.rgbaColor,
|
|
454
|
+
onChange: handleChange,
|
|
455
|
+
presetColors: [],
|
|
456
|
+
width: 300
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/* harmony default export */ const colorPickerDialog = (callColorPickerDialog);
|
|
464
|
+
// EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
|
|
465
|
+
var dist_es = __webpack_require__(69190);
|
|
466
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
function PanelSegmentation(_ref) {
|
|
475
|
+
let {
|
|
476
|
+
servicesManager,
|
|
477
|
+
commandsManager,
|
|
478
|
+
extensionManager,
|
|
479
|
+
configuration
|
|
480
|
+
} = _ref;
|
|
481
|
+
const {
|
|
482
|
+
segmentationService,
|
|
483
|
+
viewportGridService,
|
|
484
|
+
uiDialogService
|
|
485
|
+
} = servicesManager.services;
|
|
486
|
+
const {
|
|
487
|
+
t
|
|
488
|
+
} = (0,dist_es/* useTranslation */.$G)('PanelSegmentation');
|
|
489
|
+
const [selectedSegmentationId, setSelectedSegmentationId] = (0,react.useState)(null);
|
|
490
|
+
const [segmentationConfiguration, setSegmentationConfiguration] = (0,react.useState)(segmentationService.getConfiguration());
|
|
491
|
+
const [segmentations, setSegmentations] = (0,react.useState)(() => segmentationService.getSegmentations());
|
|
492
|
+
(0,react.useEffect)(() => {
|
|
493
|
+
// ~~ Subscription
|
|
494
|
+
const added = segmentationService.EVENTS.SEGMENTATION_ADDED;
|
|
495
|
+
const updated = segmentationService.EVENTS.SEGMENTATION_UPDATED;
|
|
496
|
+
const removed = segmentationService.EVENTS.SEGMENTATION_REMOVED;
|
|
497
|
+
const subscriptions = [];
|
|
498
|
+
[added, updated, removed].forEach(evt => {
|
|
499
|
+
const {
|
|
500
|
+
unsubscribe
|
|
501
|
+
} = segmentationService.subscribe(evt, () => {
|
|
502
|
+
const segmentations = segmentationService.getSegmentations();
|
|
503
|
+
setSegmentations(segmentations);
|
|
504
|
+
setSegmentationConfiguration(segmentationService.getConfiguration());
|
|
505
|
+
});
|
|
506
|
+
subscriptions.push(unsubscribe);
|
|
507
|
+
});
|
|
508
|
+
return () => {
|
|
509
|
+
subscriptions.forEach(unsub => {
|
|
510
|
+
unsub();
|
|
511
|
+
});
|
|
512
|
+
};
|
|
513
|
+
}, []);
|
|
514
|
+
const getToolGroupIds = segmentationId => {
|
|
515
|
+
const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation(segmentationId);
|
|
516
|
+
return toolGroupIds;
|
|
517
|
+
};
|
|
518
|
+
const onSegmentationAdd = async () => {
|
|
519
|
+
commandsManager.runCommand('createEmptySegmentationForViewport');
|
|
520
|
+
};
|
|
521
|
+
const onSegmentationClick = segmentationId => {
|
|
522
|
+
segmentationService.setActiveSegmentationForToolGroup(segmentationId);
|
|
523
|
+
};
|
|
524
|
+
const onSegmentationDelete = segmentationId => {
|
|
525
|
+
segmentationService.remove(segmentationId);
|
|
526
|
+
};
|
|
527
|
+
const onSegmentAdd = segmentationId => {
|
|
528
|
+
segmentationService.addSegment(segmentationId);
|
|
529
|
+
};
|
|
530
|
+
const onSegmentClick = (segmentationId, segmentIndex) => {
|
|
531
|
+
segmentationService.setActiveSegment(segmentationId, segmentIndex);
|
|
532
|
+
const toolGroupIds = getToolGroupIds(segmentationId);
|
|
533
|
+
toolGroupIds.forEach(toolGroupId => {
|
|
534
|
+
// const toolGroupId =
|
|
535
|
+
segmentationService.setActiveSegmentationForToolGroup(segmentationId, toolGroupId);
|
|
536
|
+
segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex, toolGroupId);
|
|
537
|
+
});
|
|
538
|
+
};
|
|
539
|
+
const onSegmentEdit = (segmentationId, segmentIndex) => {
|
|
540
|
+
const segmentation = segmentationService.getSegmentation(segmentationId);
|
|
541
|
+
const segment = segmentation.segments[segmentIndex];
|
|
542
|
+
const {
|
|
543
|
+
label
|
|
544
|
+
} = segment;
|
|
545
|
+
panels_callInputDialog(uiDialogService, label, (label, actionId) => {
|
|
546
|
+
if (label === '') {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
segmentationService.setSegmentLabel(segmentationId, segmentIndex, label);
|
|
550
|
+
});
|
|
551
|
+
};
|
|
552
|
+
const onSegmentationEdit = segmentationId => {
|
|
553
|
+
const segmentation = segmentationService.getSegmentation(segmentationId);
|
|
554
|
+
const {
|
|
555
|
+
label
|
|
556
|
+
} = segmentation;
|
|
557
|
+
panels_callInputDialog(uiDialogService, label, (label, actionId) => {
|
|
558
|
+
if (label === '') {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
segmentationService.addOrUpdateSegmentation({
|
|
562
|
+
id: segmentationId,
|
|
563
|
+
label
|
|
564
|
+
}, false,
|
|
565
|
+
// suppress event
|
|
566
|
+
true // notYetUpdatedAtSource
|
|
567
|
+
);
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const onSegmentColorClick = (segmentationId, segmentIndex) => {
|
|
572
|
+
const segmentation = segmentationService.getSegmentation(segmentationId);
|
|
573
|
+
const segment = segmentation.segments[segmentIndex];
|
|
574
|
+
const {
|
|
575
|
+
color,
|
|
576
|
+
opacity
|
|
577
|
+
} = segment;
|
|
578
|
+
const rgbaColor = {
|
|
579
|
+
r: color[0],
|
|
580
|
+
g: color[1],
|
|
581
|
+
b: color[2],
|
|
582
|
+
a: opacity / 255.0
|
|
583
|
+
};
|
|
584
|
+
colorPickerDialog(uiDialogService, rgbaColor, (newRgbaColor, actionId) => {
|
|
585
|
+
if (actionId === 'cancel') {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
segmentationService.setSegmentRGBAColor(segmentationId, segmentIndex, [newRgbaColor.r, newRgbaColor.g, newRgbaColor.b, newRgbaColor.a * 255.0]);
|
|
589
|
+
});
|
|
590
|
+
};
|
|
591
|
+
const onSegmentDelete = (segmentationId, segmentIndex) => {
|
|
592
|
+
segmentationService.removeSegment(segmentationId, segmentIndex);
|
|
593
|
+
};
|
|
594
|
+
const onToggleSegmentVisibility = (segmentationId, segmentIndex) => {
|
|
595
|
+
const segmentation = segmentationService.getSegmentation(segmentationId);
|
|
596
|
+
const segmentInfo = segmentation.segments[segmentIndex];
|
|
597
|
+
const isVisible = !segmentInfo.isVisible;
|
|
598
|
+
const toolGroupIds = getToolGroupIds(segmentationId);
|
|
599
|
+
|
|
600
|
+
// Todo: right now we apply the visibility to all tool groups
|
|
601
|
+
toolGroupIds.forEach(toolGroupId => {
|
|
602
|
+
segmentationService.setSegmentVisibility(segmentationId, segmentIndex, isVisible, toolGroupId);
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
const onToggleSegmentLock = (segmentationId, segmentIndex) => {
|
|
606
|
+
segmentationService.toggleSegmentLocked(segmentationId, segmentIndex);
|
|
607
|
+
};
|
|
608
|
+
const onToggleSegmentationVisibility = segmentationId => {
|
|
609
|
+
segmentationService.toggleSegmentationVisibility(segmentationId);
|
|
610
|
+
};
|
|
611
|
+
const _setSegmentationConfiguration = (0,react.useCallback)((segmentationId, key, value) => {
|
|
612
|
+
segmentationService.setConfiguration({
|
|
613
|
+
segmentationId,
|
|
614
|
+
[key]: value
|
|
615
|
+
});
|
|
616
|
+
}, [segmentationService]);
|
|
617
|
+
const onSegmentationDownload = segmentationId => {
|
|
618
|
+
commandsManager.runCommand('downloadSegmentation', {
|
|
619
|
+
segmentationId
|
|
620
|
+
});
|
|
621
|
+
};
|
|
622
|
+
const storeSegmentation = async segmentationId => {
|
|
623
|
+
const datasources = extensionManager.getActiveDataSource();
|
|
624
|
+
const displaySetInstanceUIDs = await (0,default_src.createReportAsync)({
|
|
625
|
+
servicesManager,
|
|
626
|
+
getReport: () => commandsManager.runCommand('storeSegmentation', {
|
|
627
|
+
segmentationId,
|
|
628
|
+
dataSource: datasources[0]
|
|
629
|
+
}),
|
|
630
|
+
reportType: 'Segmentation'
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Show the exported report in the active viewport as read only (similar to SR)
|
|
634
|
+
if (displaySetInstanceUIDs) {
|
|
635
|
+
// clear the segmentation that we exported, similar to the storeMeasurement
|
|
636
|
+
// where we remove the measurements and prompt again the user if they would like
|
|
637
|
+
// to re-read the measurements in a SR read only viewport
|
|
638
|
+
segmentationService.remove(segmentationId);
|
|
639
|
+
viewportGridService.setDisplaySetsForViewport({
|
|
640
|
+
viewportId: viewportGridService.getActiveViewportId(),
|
|
641
|
+
displaySetInstanceUIDs
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
|
|
646
|
+
className: "ohif-scrollbar flex min-h-0 flex-auto select-none flex-col justify-between overflow-auto"
|
|
647
|
+
}, /*#__PURE__*/react.createElement(ui_src/* SegmentationGroupTable */.cX, {
|
|
648
|
+
title: t('Segmentations'),
|
|
649
|
+
segmentations: segmentations,
|
|
650
|
+
disableEditing: configuration.disableEditing,
|
|
651
|
+
activeSegmentationId: selectedSegmentationId || '',
|
|
652
|
+
onSegmentationAdd: onSegmentationAdd,
|
|
653
|
+
onSegmentationClick: onSegmentationClick,
|
|
654
|
+
onSegmentationDelete: onSegmentationDelete,
|
|
655
|
+
onSegmentationDownload: onSegmentationDownload,
|
|
656
|
+
storeSegmentation: storeSegmentation,
|
|
657
|
+
onSegmentationEdit: onSegmentationEdit,
|
|
658
|
+
onSegmentClick: onSegmentClick,
|
|
659
|
+
onSegmentEdit: onSegmentEdit,
|
|
660
|
+
onSegmentAdd: onSegmentAdd,
|
|
661
|
+
onSegmentColorClick: onSegmentColorClick,
|
|
662
|
+
onSegmentDelete: onSegmentDelete,
|
|
663
|
+
onToggleSegmentVisibility: onToggleSegmentVisibility,
|
|
664
|
+
onToggleSegmentLock: onToggleSegmentLock,
|
|
665
|
+
onToggleSegmentationVisibility: onToggleSegmentationVisibility,
|
|
666
|
+
showDeleteSegment: true,
|
|
667
|
+
segmentationConfig: {
|
|
668
|
+
initialConfig: segmentationConfiguration
|
|
669
|
+
},
|
|
670
|
+
setRenderOutline: value => _setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value),
|
|
671
|
+
setOutlineOpacityActive: value => _setSegmentationConfiguration(selectedSegmentationId, 'outlineOpacity', value),
|
|
672
|
+
setRenderFill: value => _setSegmentationConfiguration(selectedSegmentationId, 'renderFill', value),
|
|
673
|
+
setRenderInactiveSegmentations: value => _setSegmentationConfiguration(selectedSegmentationId, 'renderInactiveSegmentations', value),
|
|
674
|
+
setOutlineWidthActive: value => _setSegmentationConfiguration(selectedSegmentationId, 'outlineWidthActive', value),
|
|
675
|
+
setFillAlpha: value => _setSegmentationConfiguration(selectedSegmentationId, 'fillAlpha', value),
|
|
676
|
+
setFillAlphaInactive: value => _setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
|
|
677
|
+
})));
|
|
678
|
+
}
|
|
679
|
+
PanelSegmentation.propTypes = {
|
|
680
|
+
commandsManager: prop_types_default().shape({
|
|
681
|
+
runCommand: (prop_types_default()).func.isRequired
|
|
682
|
+
}),
|
|
683
|
+
servicesManager: prop_types_default().shape({
|
|
684
|
+
services: prop_types_default().shape({
|
|
685
|
+
segmentationService: prop_types_default().shape({
|
|
686
|
+
getSegmentation: (prop_types_default()).func.isRequired,
|
|
687
|
+
getSegmentations: (prop_types_default()).func.isRequired,
|
|
688
|
+
toggleSegmentationVisibility: (prop_types_default()).func.isRequired,
|
|
689
|
+
subscribe: (prop_types_default()).func.isRequired,
|
|
690
|
+
EVENTS: (prop_types_default()).object.isRequired
|
|
691
|
+
}).isRequired
|
|
692
|
+
}).isRequired
|
|
693
|
+
}).isRequired
|
|
694
|
+
};
|
|
695
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 348 modules
|
|
696
|
+
var dist_esm = __webpack_require__(14957);
|
|
697
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/panels/SegmentationToolbox.tsx
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
const {
|
|
702
|
+
segmentation: segmentationUtils
|
|
703
|
+
} = dist_esm.utilities;
|
|
704
|
+
const TOOL_TYPES = {
|
|
705
|
+
CIRCULAR_BRUSH: 'CircularBrush',
|
|
706
|
+
SPHERE_BRUSH: 'SphereBrush',
|
|
707
|
+
CIRCULAR_ERASER: 'CircularEraser',
|
|
708
|
+
SPHERE_ERASER: 'SphereEraser',
|
|
709
|
+
CIRCLE_SCISSOR: 'CircleScissor',
|
|
710
|
+
RECTANGLE_SCISSOR: 'RectangleScissor',
|
|
711
|
+
SPHERE_SCISSOR: 'SphereScissor',
|
|
712
|
+
THRESHOLD_CIRCULAR_BRUSH: 'ThresholdCircularBrush',
|
|
713
|
+
THRESHOLD_SPHERE_BRUSH: 'ThresholdSphereBrush'
|
|
714
|
+
};
|
|
715
|
+
const ACTIONS = {
|
|
716
|
+
SET_TOOL_CONFIG: 'SET_TOOL_CONFIG',
|
|
717
|
+
SET_ACTIVE_TOOL: 'SET_ACTIVE_TOOL'
|
|
718
|
+
};
|
|
719
|
+
const initialState = {
|
|
720
|
+
Brush: {
|
|
721
|
+
brushSize: 15,
|
|
722
|
+
mode: 'CircularBrush' // Can be 'CircularBrush' or 'SphereBrush'
|
|
723
|
+
},
|
|
724
|
+
|
|
725
|
+
Eraser: {
|
|
726
|
+
brushSize: 15,
|
|
727
|
+
mode: 'CircularEraser' // Can be 'CircularEraser' or 'SphereEraser'
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
Scissors: {
|
|
731
|
+
brushSize: 15,
|
|
732
|
+
mode: 'CircleScissor' // E.g., 'CircleScissor', 'RectangleScissor', or 'SphereScissor'
|
|
733
|
+
},
|
|
734
|
+
|
|
735
|
+
ThresholdBrush: {
|
|
736
|
+
brushSize: 15,
|
|
737
|
+
thresholdRange: [-500, 500]
|
|
738
|
+
},
|
|
739
|
+
activeTool: null
|
|
740
|
+
};
|
|
741
|
+
function toolboxReducer(state, action) {
|
|
742
|
+
switch (action.type) {
|
|
743
|
+
case ACTIONS.SET_TOOL_CONFIG:
|
|
744
|
+
const {
|
|
745
|
+
tool,
|
|
746
|
+
config
|
|
747
|
+
} = action.payload;
|
|
748
|
+
return {
|
|
749
|
+
...state,
|
|
750
|
+
[tool]: {
|
|
751
|
+
...state[tool],
|
|
752
|
+
...config
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
case ACTIONS.SET_ACTIVE_TOOL:
|
|
756
|
+
return {
|
|
757
|
+
...state,
|
|
758
|
+
activeTool: action.payload
|
|
759
|
+
};
|
|
760
|
+
default:
|
|
761
|
+
return state;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
function SegmentationToolbox(_ref) {
|
|
765
|
+
let {
|
|
766
|
+
servicesManager,
|
|
767
|
+
extensionManager
|
|
768
|
+
} = _ref;
|
|
769
|
+
const {
|
|
770
|
+
toolbarService,
|
|
771
|
+
segmentationService,
|
|
772
|
+
toolGroupService
|
|
773
|
+
} = servicesManager.services;
|
|
774
|
+
const [viewportGrid] = (0,ui_src/* useViewportGrid */.O_)();
|
|
775
|
+
const {
|
|
776
|
+
viewports,
|
|
777
|
+
activeViewportId
|
|
778
|
+
} = viewportGrid;
|
|
779
|
+
const [toolsEnabled, setToolsEnabled] = (0,react.useState)(false);
|
|
780
|
+
const [state, dispatch] = (0,react.useReducer)(toolboxReducer, initialState);
|
|
781
|
+
const updateActiveTool = (0,react.useCallback)(() => {
|
|
782
|
+
if (!viewports?.size || activeViewportId === undefined) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const viewport = viewports.get(activeViewportId);
|
|
786
|
+
if (!viewport) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
dispatch({
|
|
790
|
+
type: ACTIONS.SET_ACTIVE_TOOL,
|
|
791
|
+
payload: toolGroupService.getActiveToolForViewport(viewport.viewportId)
|
|
792
|
+
});
|
|
793
|
+
}, [activeViewportId, viewports, toolGroupService, dispatch]);
|
|
794
|
+
const setToolActive = (0,react.useCallback)(toolName => {
|
|
795
|
+
toolbarService.recordInteraction({
|
|
796
|
+
interactionType: 'tool',
|
|
797
|
+
commands: [{
|
|
798
|
+
commandName: 'setToolActive',
|
|
799
|
+
commandOptions: {
|
|
800
|
+
toolName
|
|
801
|
+
}
|
|
802
|
+
}]
|
|
803
|
+
});
|
|
804
|
+
dispatch({
|
|
805
|
+
type: ACTIONS.SET_ACTIVE_TOOL,
|
|
806
|
+
payload: toolName
|
|
807
|
+
});
|
|
808
|
+
}, [toolbarService, dispatch]);
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* sets the tools enabled IF there are segmentations
|
|
812
|
+
*/
|
|
813
|
+
(0,react.useEffect)(() => {
|
|
814
|
+
const events = [segmentationService.EVENTS.SEGMENTATION_ADDED, segmentationService.EVENTS.SEGMENTATION_UPDATED, segmentationService.EVENTS.SEGMENTATION_REMOVED];
|
|
815
|
+
const unsubscriptions = [];
|
|
816
|
+
events.forEach(event => {
|
|
817
|
+
const {
|
|
818
|
+
unsubscribe
|
|
819
|
+
} = segmentationService.subscribe(event, () => {
|
|
820
|
+
const segmentations = segmentationService.getSegmentations();
|
|
821
|
+
const activeSegmentation = segmentations?.find(seg => seg.isActive);
|
|
822
|
+
setToolsEnabled(activeSegmentation?.segmentCount > 0);
|
|
823
|
+
});
|
|
824
|
+
unsubscriptions.push(unsubscribe);
|
|
825
|
+
});
|
|
826
|
+
updateActiveTool();
|
|
827
|
+
return () => {
|
|
828
|
+
unsubscriptions.forEach(unsubscribe => unsubscribe());
|
|
829
|
+
};
|
|
830
|
+
}, [activeViewportId, viewports, segmentationService, updateActiveTool]);
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Update the active tool when the toolbar state changes
|
|
834
|
+
*/
|
|
835
|
+
(0,react.useEffect)(() => {
|
|
836
|
+
const {
|
|
837
|
+
unsubscribe
|
|
838
|
+
} = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED, () => {
|
|
839
|
+
updateActiveTool();
|
|
840
|
+
});
|
|
841
|
+
return () => {
|
|
842
|
+
unsubscribe();
|
|
843
|
+
};
|
|
844
|
+
}, [toolbarService, updateActiveTool]);
|
|
845
|
+
(0,react.useEffect)(() => {
|
|
846
|
+
// if the active tool is not a brush tool then do nothing
|
|
847
|
+
if (!Object.values(TOOL_TYPES).includes(state.activeTool)) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// if the tool is Segmentation and it is enabled then do nothing
|
|
852
|
+
if (toolsEnabled) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// if the tool is Segmentation and it is disabled, then switch
|
|
857
|
+
// back to the window level tool to not confuse the user when no
|
|
858
|
+
// segmentation is active or when there is no segment in the segmentation
|
|
859
|
+
setToolActive('WindowLevel');
|
|
860
|
+
}, [toolsEnabled, state.activeTool, setToolActive]);
|
|
861
|
+
const updateBrushSize = (0,react.useCallback)((toolName, brushSize) => {
|
|
862
|
+
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
|
|
863
|
+
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize, toolName);
|
|
864
|
+
});
|
|
865
|
+
}, [toolGroupService]);
|
|
866
|
+
const onBrushSizeChange = (0,react.useCallback)((valueAsStringOrNumber, toolCategory) => {
|
|
867
|
+
const value = Number(valueAsStringOrNumber);
|
|
868
|
+
_getToolNamesFromCategory(toolCategory).forEach(toolName => {
|
|
869
|
+
updateBrushSize(toolName, value);
|
|
870
|
+
});
|
|
871
|
+
dispatch({
|
|
872
|
+
type: ACTIONS.SET_TOOL_CONFIG,
|
|
873
|
+
payload: {
|
|
874
|
+
tool: toolCategory,
|
|
875
|
+
config: {
|
|
876
|
+
brushSize: value
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}, [toolGroupService, dispatch]);
|
|
881
|
+
const handleRangeChange = (0,react.useCallback)(newRange => {
|
|
882
|
+
if (newRange[0] === state.ThresholdBrush.thresholdRange[0] && newRange[1] === state.ThresholdBrush.thresholdRange[1]) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const toolNames = _getToolNamesFromCategory('ThresholdBrush');
|
|
886
|
+
toolNames.forEach(toolName => {
|
|
887
|
+
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
|
|
888
|
+
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
|
889
|
+
toolGroup.setToolConfiguration(toolName, {
|
|
890
|
+
strategySpecificConfiguration: {
|
|
891
|
+
THRESHOLD_INSIDE_CIRCLE: {
|
|
892
|
+
threshold: newRange
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
dispatch({
|
|
899
|
+
type: ACTIONS.SET_TOOL_CONFIG,
|
|
900
|
+
payload: {
|
|
901
|
+
tool: 'ThresholdBrush',
|
|
902
|
+
config: {
|
|
903
|
+
thresholdRange: newRange
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}, [toolGroupService, dispatch, state.ThresholdBrush.thresholdRange]);
|
|
908
|
+
return /*#__PURE__*/react.createElement(ui_src/* AdvancedToolbox */.bY, {
|
|
909
|
+
title: "Segmentation Tools",
|
|
910
|
+
items: [{
|
|
911
|
+
name: 'Brush',
|
|
912
|
+
icon: 'icon-tool-brush',
|
|
913
|
+
disabled: !toolsEnabled,
|
|
914
|
+
active: state.activeTool === TOOL_TYPES.CIRCULAR_BRUSH || state.activeTool === TOOL_TYPES.SPHERE_BRUSH,
|
|
915
|
+
onClick: () => setToolActive(TOOL_TYPES.CIRCULAR_BRUSH),
|
|
916
|
+
options: [{
|
|
917
|
+
name: 'Radius (mm)',
|
|
918
|
+
id: 'brush-radius',
|
|
919
|
+
type: 'range',
|
|
920
|
+
min: 0.5,
|
|
921
|
+
max: 99.5,
|
|
922
|
+
value: state.Brush.brushSize,
|
|
923
|
+
step: 0.5,
|
|
924
|
+
onChange: value => onBrushSizeChange(value, 'Brush')
|
|
925
|
+
}, {
|
|
926
|
+
name: 'Mode',
|
|
927
|
+
type: 'radio',
|
|
928
|
+
id: 'brush-mode',
|
|
929
|
+
value: state.Brush.mode,
|
|
930
|
+
values: [{
|
|
931
|
+
value: TOOL_TYPES.CIRCULAR_BRUSH,
|
|
932
|
+
label: 'Circle'
|
|
933
|
+
}, {
|
|
934
|
+
value: TOOL_TYPES.SPHERE_BRUSH,
|
|
935
|
+
label: 'Sphere'
|
|
936
|
+
}],
|
|
937
|
+
onChange: value => setToolActive(value)
|
|
938
|
+
}]
|
|
939
|
+
}, {
|
|
940
|
+
name: 'Eraser',
|
|
941
|
+
icon: 'icon-tool-eraser',
|
|
942
|
+
disabled: !toolsEnabled,
|
|
943
|
+
active: state.activeTool === TOOL_TYPES.CIRCULAR_ERASER || state.activeTool === TOOL_TYPES.SPHERE_ERASER,
|
|
944
|
+
onClick: () => setToolActive(TOOL_TYPES.CIRCULAR_ERASER),
|
|
945
|
+
options: [{
|
|
946
|
+
name: 'Radius (mm)',
|
|
947
|
+
type: 'range',
|
|
948
|
+
id: 'eraser-radius',
|
|
949
|
+
min: 0.5,
|
|
950
|
+
max: 99.5,
|
|
951
|
+
value: state.Eraser.brushSize,
|
|
952
|
+
step: 0.5,
|
|
953
|
+
onChange: value => onBrushSizeChange(value, 'Eraser')
|
|
954
|
+
}, {
|
|
955
|
+
name: 'Mode',
|
|
956
|
+
type: 'radio',
|
|
957
|
+
id: 'eraser-mode',
|
|
958
|
+
value: state.Eraser.mode,
|
|
959
|
+
values: [{
|
|
960
|
+
value: TOOL_TYPES.CIRCULAR_ERASER,
|
|
961
|
+
label: 'Circle'
|
|
962
|
+
}, {
|
|
963
|
+
value: TOOL_TYPES.SPHERE_ERASER,
|
|
964
|
+
label: 'Sphere'
|
|
965
|
+
}],
|
|
966
|
+
onChange: value => setToolActive(value)
|
|
967
|
+
}]
|
|
968
|
+
}, {
|
|
969
|
+
name: 'Scissor',
|
|
970
|
+
icon: 'icon-tool-scissor',
|
|
971
|
+
disabled: !toolsEnabled,
|
|
972
|
+
active: state.activeTool === TOOL_TYPES.CIRCLE_SCISSOR || state.activeTool === TOOL_TYPES.RECTANGLE_SCISSOR || state.activeTool === TOOL_TYPES.SPHERE_SCISSOR,
|
|
973
|
+
onClick: () => setToolActive(TOOL_TYPES.CIRCLE_SCISSOR),
|
|
974
|
+
options: [{
|
|
975
|
+
name: 'Mode',
|
|
976
|
+
type: 'radio',
|
|
977
|
+
value: state.Scissors.mode,
|
|
978
|
+
id: 'scissor-mode',
|
|
979
|
+
values: [{
|
|
980
|
+
value: TOOL_TYPES.CIRCLE_SCISSOR,
|
|
981
|
+
label: 'Circle'
|
|
982
|
+
}, {
|
|
983
|
+
value: TOOL_TYPES.RECTANGLE_SCISSOR,
|
|
984
|
+
label: 'Rectangle'
|
|
985
|
+
}, {
|
|
986
|
+
value: TOOL_TYPES.SPHERE_SCISSOR,
|
|
987
|
+
label: 'Sphere'
|
|
988
|
+
}],
|
|
989
|
+
onChange: value => setToolActive(value)
|
|
990
|
+
}]
|
|
991
|
+
}, {
|
|
992
|
+
name: 'Threshold Tool',
|
|
993
|
+
icon: 'icon-tool-threshold',
|
|
994
|
+
disabled: !toolsEnabled,
|
|
995
|
+
active: state.activeTool === TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH || state.activeTool === TOOL_TYPES.THRESHOLD_SPHERE_BRUSH,
|
|
996
|
+
onClick: () => setToolActive(TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH),
|
|
997
|
+
options: [{
|
|
998
|
+
name: 'Radius (mm)',
|
|
999
|
+
id: 'threshold-radius',
|
|
1000
|
+
type: 'range',
|
|
1001
|
+
min: 0.5,
|
|
1002
|
+
max: 99.5,
|
|
1003
|
+
value: state.ThresholdBrush.brushSize,
|
|
1004
|
+
step: 0.5,
|
|
1005
|
+
onChange: value => onBrushSizeChange(value, 'ThresholdBrush')
|
|
1006
|
+
}, {
|
|
1007
|
+
name: 'Mode',
|
|
1008
|
+
type: 'radio',
|
|
1009
|
+
id: 'threshold-mode',
|
|
1010
|
+
value: state.activeTool,
|
|
1011
|
+
values: [{
|
|
1012
|
+
value: TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH,
|
|
1013
|
+
label: 'Circle'
|
|
1014
|
+
}, {
|
|
1015
|
+
value: TOOL_TYPES.THRESHOLD_SPHERE_BRUSH,
|
|
1016
|
+
label: 'Sphere'
|
|
1017
|
+
}],
|
|
1018
|
+
onChange: value => setToolActive(value)
|
|
1019
|
+
}, {
|
|
1020
|
+
type: 'custom',
|
|
1021
|
+
id: 'segmentation-threshold-range',
|
|
1022
|
+
children: () => {
|
|
1023
|
+
return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement("div", {
|
|
1024
|
+
className: "bg-secondary-light h-[1px]"
|
|
1025
|
+
}), /*#__PURE__*/react.createElement("div", {
|
|
1026
|
+
className: "mt-1 text-[13px] text-white"
|
|
1027
|
+
}, "Threshold"), /*#__PURE__*/react.createElement(ui_src/* InputDoubleRange */.R0, {
|
|
1028
|
+
values: state.ThresholdBrush.thresholdRange,
|
|
1029
|
+
onChange: handleRangeChange,
|
|
1030
|
+
minValue: -1000,
|
|
1031
|
+
maxValue: 1000,
|
|
1032
|
+
step: 1,
|
|
1033
|
+
showLabel: true,
|
|
1034
|
+
allowNumberEdit: true,
|
|
1035
|
+
showAdjustmentArrows: false
|
|
1036
|
+
}));
|
|
1037
|
+
}
|
|
1038
|
+
}]
|
|
1039
|
+
}]
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function _getToolNamesFromCategory(category) {
|
|
1043
|
+
let toolNames = [];
|
|
1044
|
+
switch (category) {
|
|
1045
|
+
case 'Brush':
|
|
1046
|
+
toolNames = ['CircularBrush', 'SphereBrush'];
|
|
1047
|
+
break;
|
|
1048
|
+
case 'Eraser':
|
|
1049
|
+
toolNames = ['CircularEraser', 'SphereEraser'];
|
|
1050
|
+
break;
|
|
1051
|
+
case 'ThresholdBrush':
|
|
1052
|
+
toolNames = ['ThresholdCircularBrush', 'ThresholdSphereBrush'];
|
|
1053
|
+
break;
|
|
1054
|
+
default:
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
1057
|
+
return toolNames;
|
|
1058
|
+
}
|
|
1059
|
+
/* harmony default export */ const panels_SegmentationToolbox = (SegmentationToolbox);
|
|
1060
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/getPanelModule.tsx
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
const getPanelModule = _ref => {
|
|
1066
|
+
let {
|
|
1067
|
+
commandsManager,
|
|
1068
|
+
servicesManager,
|
|
1069
|
+
extensionManager,
|
|
1070
|
+
configuration
|
|
1071
|
+
} = _ref;
|
|
1072
|
+
const {
|
|
1073
|
+
customizationService
|
|
1074
|
+
} = servicesManager.services;
|
|
1075
|
+
const wrappedPanelSegmentation = configuration => {
|
|
1076
|
+
const [appConfig] = (0,state/* useAppConfig */.M)();
|
|
1077
|
+
const disableEditingForMode = customizationService.get('segmentation.disableEditing');
|
|
1078
|
+
return /*#__PURE__*/react.createElement(PanelSegmentation, {
|
|
1079
|
+
commandsManager: commandsManager,
|
|
1080
|
+
servicesManager: servicesManager,
|
|
1081
|
+
extensionManager: extensionManager,
|
|
1082
|
+
configuration: {
|
|
1083
|
+
...configuration,
|
|
1084
|
+
disableEditing: appConfig.disableEditing || disableEditingForMode?.value
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
};
|
|
1088
|
+
const wrappedPanelSegmentationWithTools = configuration => {
|
|
1089
|
+
const [appConfig] = (0,state/* useAppConfig */.M)();
|
|
1090
|
+
return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(panels_SegmentationToolbox, {
|
|
1091
|
+
commandsManager: commandsManager,
|
|
1092
|
+
servicesManager: servicesManager,
|
|
1093
|
+
extensionManager: extensionManager,
|
|
1094
|
+
configuration: {
|
|
1095
|
+
...configuration
|
|
1096
|
+
}
|
|
1097
|
+
}), /*#__PURE__*/react.createElement(PanelSegmentation, {
|
|
1098
|
+
commandsManager: commandsManager,
|
|
1099
|
+
servicesManager: servicesManager,
|
|
1100
|
+
extensionManager: extensionManager,
|
|
1101
|
+
configuration: {
|
|
1102
|
+
...configuration
|
|
1103
|
+
}
|
|
1104
|
+
}));
|
|
1105
|
+
};
|
|
1106
|
+
return [{
|
|
1107
|
+
name: 'panelSegmentation',
|
|
1108
|
+
iconName: 'tab-segmentation',
|
|
1109
|
+
iconLabel: 'Segmentation',
|
|
1110
|
+
label: 'Segmentation',
|
|
1111
|
+
component: wrappedPanelSegmentation
|
|
1112
|
+
}, {
|
|
1113
|
+
name: 'panelSegmentationWithTools',
|
|
1114
|
+
iconName: 'tab-segmentation',
|
|
1115
|
+
iconLabel: 'Segmentation',
|
|
1116
|
+
label: 'Segmentation',
|
|
1117
|
+
component: wrappedPanelSegmentationWithTools
|
|
1118
|
+
}];
|
|
1119
|
+
};
|
|
1120
|
+
/* harmony default export */ const src_getPanelModule = (getPanelModule);
|
|
1121
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Updates the viewports in preparation for rendering segmentations.
|
|
1126
|
+
* Evaluates each viewport to determine which need modifications,
|
|
1127
|
+
* then for those viewports, changes them to a volume type and ensures
|
|
1128
|
+
* they are ready for segmentation rendering.
|
|
1129
|
+
*
|
|
1130
|
+
* @param {Object} params - Parameters for the function.
|
|
1131
|
+
* @param params.viewportId - ID of the viewport to be updated.
|
|
1132
|
+
* @param params.loadFn - Function to load the segmentation data.
|
|
1133
|
+
* @param params.servicesManager - The services manager.
|
|
1134
|
+
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
|
1135
|
+
*
|
|
1136
|
+
* @returns Returns true upon successful update of viewports for segmentation rendering.
|
|
1137
|
+
*/
|
|
1138
|
+
async function updateViewportsForSegmentationRendering(_ref) {
|
|
1139
|
+
let {
|
|
1140
|
+
viewportId,
|
|
1141
|
+
loadFn,
|
|
1142
|
+
servicesManager,
|
|
1143
|
+
referencedDisplaySetInstanceUID
|
|
1144
|
+
} = _ref;
|
|
1145
|
+
const {
|
|
1146
|
+
cornerstoneViewportService,
|
|
1147
|
+
segmentationService,
|
|
1148
|
+
viewportGridService
|
|
1149
|
+
} = servicesManager.services;
|
|
1150
|
+
const viewport = getTargetViewport({
|
|
1151
|
+
viewportId,
|
|
1152
|
+
viewportGridService
|
|
1153
|
+
});
|
|
1154
|
+
const targetViewportId = viewport.viewportOptions.viewportId;
|
|
1155
|
+
referencedDisplaySetInstanceUID = referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0];
|
|
1156
|
+
const updatedViewports = getUpdatedViewportsForSegmentation({
|
|
1157
|
+
servicesManager,
|
|
1158
|
+
viewportId,
|
|
1159
|
+
referencedDisplaySetInstanceUID
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// create Segmentation callback which needs to be waited until
|
|
1163
|
+
// the volume is created (if coming from stack)
|
|
1164
|
+
const createSegmentationForVolume = async () => {
|
|
1165
|
+
const segmentationId = await loadFn();
|
|
1166
|
+
segmentationService.hydrateSegmentation(segmentationId);
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
// the reference volume that is used to draw the segmentation. so check if the
|
|
1170
|
+
// volume exists in the cache (the target Viewport is already a volume viewport)
|
|
1171
|
+
const volumeExists = Array.from(esm.cache._volumeCache.keys()).some(volumeId => volumeId.includes(referencedDisplaySetInstanceUID));
|
|
1172
|
+
updatedViewports.forEach(async viewport => {
|
|
1173
|
+
viewport.viewportOptions = {
|
|
1174
|
+
...viewport.viewportOptions,
|
|
1175
|
+
viewportType: 'volume',
|
|
1176
|
+
needsRerendering: true
|
|
1177
|
+
};
|
|
1178
|
+
const viewportId = viewport.viewportId;
|
|
1179
|
+
const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
|
1180
|
+
const prevCamera = csViewport.getCamera();
|
|
1181
|
+
|
|
1182
|
+
// only run the createSegmentationForVolume for the targetViewportId
|
|
1183
|
+
// since the rest will get handled by cornerstoneViewportService
|
|
1184
|
+
if (volumeExists && viewportId === targetViewportId) {
|
|
1185
|
+
await createSegmentationForVolume();
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
const createNewSegmentationWhenVolumeMounts = async evt => {
|
|
1189
|
+
const isTheActiveViewportVolumeMounted = evt.detail.volumeActors?.find(ac => ac.uid.includes(referencedDisplaySetInstanceUID));
|
|
1190
|
+
|
|
1191
|
+
// Note: make sure to re-grab the viewport since it might have changed
|
|
1192
|
+
// during the time it took for the volume to be mounted, for instance
|
|
1193
|
+
// the stack viewport has been changed to a volume viewport
|
|
1194
|
+
const volumeViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
|
1195
|
+
volumeViewport.setCamera(prevCamera);
|
|
1196
|
+
volumeViewport.element.removeEventListener(esm.Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, createNewSegmentationWhenVolumeMounts);
|
|
1197
|
+
if (!isTheActiveViewportVolumeMounted) {
|
|
1198
|
+
// it means it is one of those other updated viewports so just update the camera
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
if (viewportId === targetViewportId) {
|
|
1202
|
+
await createSegmentationForVolume();
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
csViewport.element.addEventListener(esm.Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, createNewSegmentationWhenVolumeMounts);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
// Set the displaySets for the viewports that require to be updated
|
|
1209
|
+
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
|
1210
|
+
return true;
|
|
1211
|
+
}
|
|
1212
|
+
const getTargetViewport = _ref2 => {
|
|
1213
|
+
let {
|
|
1214
|
+
viewportId,
|
|
1215
|
+
viewportGridService
|
|
1216
|
+
} = _ref2;
|
|
1217
|
+
const {
|
|
1218
|
+
viewports,
|
|
1219
|
+
activeViewportId
|
|
1220
|
+
} = viewportGridService.getState();
|
|
1221
|
+
const targetViewportId = viewportId || activeViewportId;
|
|
1222
|
+
const viewport = viewports.get(targetViewportId);
|
|
1223
|
+
return viewport;
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* Retrieves a list of viewports that require updates in preparation for segmentation rendering.
|
|
1228
|
+
* This function evaluates viewports based on their compatibility with the provided segmentation's
|
|
1229
|
+
* frame of reference UID and appends them to the updated list if they should render the segmentation.
|
|
1230
|
+
*
|
|
1231
|
+
* @param {Object} params - Parameters for the function.
|
|
1232
|
+
* @param params.viewportId - the ID of the viewport to be updated.
|
|
1233
|
+
* @param params.servicesManager - The services manager
|
|
1234
|
+
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
|
1235
|
+
*
|
|
1236
|
+
* @returns {Array} Returns an array of viewports that require updates for segmentation rendering.
|
|
1237
|
+
*/
|
|
1238
|
+
function getUpdatedViewportsForSegmentation(_ref3) {
|
|
1239
|
+
let {
|
|
1240
|
+
viewportId,
|
|
1241
|
+
servicesManager,
|
|
1242
|
+
referencedDisplaySetInstanceUID
|
|
1243
|
+
} = _ref3;
|
|
1244
|
+
const {
|
|
1245
|
+
hangingProtocolService,
|
|
1246
|
+
displaySetService,
|
|
1247
|
+
segmentationService,
|
|
1248
|
+
viewportGridService
|
|
1249
|
+
} = servicesManager.services;
|
|
1250
|
+
const {
|
|
1251
|
+
viewports
|
|
1252
|
+
} = viewportGridService.getState();
|
|
1253
|
+
const viewport = getTargetViewport({
|
|
1254
|
+
viewportId,
|
|
1255
|
+
viewportGridService
|
|
1256
|
+
});
|
|
1257
|
+
const targetViewportId = viewport.viewportOptions.viewportId;
|
|
1258
|
+
const displaySetInstanceUIDs = viewports.get(targetViewportId).displaySetInstanceUIDs;
|
|
1259
|
+
const referenceDisplaySetInstanceUID = referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0];
|
|
1260
|
+
const referencedDisplaySet = displaySetService.getDisplaySetByUID(referenceDisplaySetInstanceUID);
|
|
1261
|
+
const segmentationFrameOfReferenceUID = referencedDisplaySet.instances[0].FrameOfReferenceUID;
|
|
1262
|
+
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(targetViewportId, referenceDisplaySetInstanceUID);
|
|
1263
|
+
viewports.forEach((viewport, viewportId) => {
|
|
1264
|
+
if (targetViewportId === viewportId || updatedViewports.find(v => v.viewportId === viewportId)) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewport.displaySetInstanceUIDs, segmentationFrameOfReferenceUID);
|
|
1268
|
+
if (shouldDisplaySeg) {
|
|
1269
|
+
updatedViewports.push({
|
|
1270
|
+
viewportId,
|
|
1271
|
+
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
|
1272
|
+
viewportOptions: {
|
|
1273
|
+
viewportType: 'volume',
|
|
1274
|
+
needsRerendering: true
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
return updatedViewports;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/commandsModule.ts
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
const {
|
|
1291
|
+
Cornerstone3D: {
|
|
1292
|
+
Segmentation: {
|
|
1293
|
+
generateLabelMaps2DFrom3D,
|
|
1294
|
+
generateSegmentation
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
} = adapters_es.adaptersSEG;
|
|
1298
|
+
const {
|
|
1299
|
+
downloadDICOMData
|
|
1300
|
+
} = adapters_es.helpers;
|
|
1301
|
+
const commandsModule = _ref => {
|
|
1302
|
+
let {
|
|
1303
|
+
servicesManager,
|
|
1304
|
+
extensionManager
|
|
1305
|
+
} = _ref;
|
|
1306
|
+
const {
|
|
1307
|
+
uiNotificationService,
|
|
1308
|
+
segmentationService,
|
|
1309
|
+
uiDialogService,
|
|
1310
|
+
displaySetService,
|
|
1311
|
+
viewportGridService
|
|
1312
|
+
} = servicesManager.services;
|
|
1313
|
+
const actions = {
|
|
1314
|
+
/**
|
|
1315
|
+
* Retrieves a list of viewports that require updates in preparation for segmentation rendering.
|
|
1316
|
+
* This function evaluates viewports based on their compatibility with the provided segmentation's
|
|
1317
|
+
* frame of reference UID and appends them to the updated list if they should render the segmentation.
|
|
1318
|
+
*
|
|
1319
|
+
* @param {Object} params - Parameters for the function.
|
|
1320
|
+
* @param params.viewportId - the ID of the viewport to be updated.
|
|
1321
|
+
* @param params.servicesManager - The services manager
|
|
1322
|
+
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
|
1323
|
+
*
|
|
1324
|
+
* @returns {Array} Returns an array of viewports that require updates for segmentation rendering.
|
|
1325
|
+
*/
|
|
1326
|
+
getUpdatedViewportsForSegmentation: getUpdatedViewportsForSegmentation,
|
|
1327
|
+
/**
|
|
1328
|
+
* Creates an empty segmentation for a specified viewport.
|
|
1329
|
+
* It first checks if the display set associated with the viewport is reconstructable.
|
|
1330
|
+
* If not, it raises a notification error. Otherwise, it creates a new segmentation
|
|
1331
|
+
* for the display set after handling the necessary steps for making the viewport
|
|
1332
|
+
* a volume viewport first
|
|
1333
|
+
*
|
|
1334
|
+
* @param {Object} params - Parameters for the function.
|
|
1335
|
+
* @param params.viewportId - the target viewport ID.
|
|
1336
|
+
*
|
|
1337
|
+
*/
|
|
1338
|
+
createEmptySegmentationForViewport: async _ref2 => {
|
|
1339
|
+
let {
|
|
1340
|
+
viewportId
|
|
1341
|
+
} = _ref2;
|
|
1342
|
+
const viewport = getTargetViewport({
|
|
1343
|
+
viewportId,
|
|
1344
|
+
viewportGridService
|
|
1345
|
+
});
|
|
1346
|
+
// Todo: add support for multiple display sets
|
|
1347
|
+
const displaySetInstanceUID = viewport.displaySetInstanceUIDs[0];
|
|
1348
|
+
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
|
1349
|
+
if (!displaySet.isReconstructable) {
|
|
1350
|
+
uiNotificationService.show({
|
|
1351
|
+
title: 'Segmentation',
|
|
1352
|
+
message: 'Segmentation is not supported for non-reconstructible displaysets yet',
|
|
1353
|
+
type: 'error'
|
|
1354
|
+
});
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
updateViewportsForSegmentationRendering({
|
|
1358
|
+
viewportId,
|
|
1359
|
+
servicesManager,
|
|
1360
|
+
loadFn: async () => {
|
|
1361
|
+
const currentSegmentations = segmentationService.getSegmentations();
|
|
1362
|
+
const segmentationId = await segmentationService.createSegmentationForDisplaySet(displaySetInstanceUID, {
|
|
1363
|
+
label: `Segmentation ${currentSegmentations.length + 1}`
|
|
1364
|
+
});
|
|
1365
|
+
const toolGroupId = viewport.viewportOptions.toolGroupId;
|
|
1366
|
+
await segmentationService.addSegmentationRepresentationToToolGroup(toolGroupId, segmentationId);
|
|
1367
|
+
|
|
1368
|
+
// Add only one segment for now
|
|
1369
|
+
segmentationService.addSegment(segmentationId, {
|
|
1370
|
+
toolGroupId,
|
|
1371
|
+
segmentIndex: 1,
|
|
1372
|
+
properties: {
|
|
1373
|
+
label: 'Segment 1'
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
return segmentationId;
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
},
|
|
1380
|
+
/**
|
|
1381
|
+
* Loads segmentations for a specified viewport.
|
|
1382
|
+
* The function prepares the viewport for rendering, then loads the segmentation details.
|
|
1383
|
+
* Additionally, if the segmentation has scalar data, it is set for the corresponding label map volume.
|
|
1384
|
+
*
|
|
1385
|
+
* @param {Object} params - Parameters for the function.
|
|
1386
|
+
* @param params.segmentations - Array of segmentations to be loaded.
|
|
1387
|
+
* @param params.viewportId - the target viewport ID.
|
|
1388
|
+
*
|
|
1389
|
+
*/
|
|
1390
|
+
loadSegmentationsForViewport: async _ref3 => {
|
|
1391
|
+
let {
|
|
1392
|
+
segmentations,
|
|
1393
|
+
viewportId
|
|
1394
|
+
} = _ref3;
|
|
1395
|
+
updateViewportsForSegmentationRendering({
|
|
1396
|
+
viewportId,
|
|
1397
|
+
servicesManager,
|
|
1398
|
+
loadFn: async () => {
|
|
1399
|
+
// Todo: handle adding more than one segmentation
|
|
1400
|
+
const viewport = getTargetViewport({
|
|
1401
|
+
viewportId,
|
|
1402
|
+
viewportGridService
|
|
1403
|
+
});
|
|
1404
|
+
const displaySetInstanceUID = viewport.displaySetInstanceUIDs[0];
|
|
1405
|
+
const segmentation = segmentations[0];
|
|
1406
|
+
const segmentationId = segmentation.id;
|
|
1407
|
+
const label = segmentation.label;
|
|
1408
|
+
const segments = segmentation.segments;
|
|
1409
|
+
delete segmentation.segments;
|
|
1410
|
+
await segmentationService.createSegmentationForDisplaySet(displaySetInstanceUID, {
|
|
1411
|
+
segmentationId,
|
|
1412
|
+
label
|
|
1413
|
+
});
|
|
1414
|
+
if (segmentation.scalarData) {
|
|
1415
|
+
const labelmapVolume = segmentationService.getLabelmapVolume(segmentationId);
|
|
1416
|
+
labelmapVolume.scalarData.set(segmentation.scalarData);
|
|
1417
|
+
}
|
|
1418
|
+
segmentationService.addOrUpdateSegmentation(segmentation);
|
|
1419
|
+
const toolGroupId = viewport.viewportOptions.toolGroupId;
|
|
1420
|
+
await segmentationService.addSegmentationRepresentationToToolGroup(toolGroupId, segmentationId);
|
|
1421
|
+
segments.forEach(segment => {
|
|
1422
|
+
if (segment === null) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
segmentationService.addSegment(segmentationId, {
|
|
1426
|
+
segmentIndex: segment.segmentIndex,
|
|
1427
|
+
toolGroupId,
|
|
1428
|
+
properties: {
|
|
1429
|
+
color: segment.color,
|
|
1430
|
+
label: segment.label,
|
|
1431
|
+
opacity: segment.opacity,
|
|
1432
|
+
isLocked: segment.isLocked,
|
|
1433
|
+
visibility: segment.isVisible,
|
|
1434
|
+
active: segmentation.activeSegmentIndex === segment.segmentIndex
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1437
|
+
});
|
|
1438
|
+
if (segmentation.centroidsIJK) {
|
|
1439
|
+
segmentationService.setCentroids(segmentation.id, segmentation.centroidsIJK);
|
|
1440
|
+
}
|
|
1441
|
+
return segmentationId;
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
},
|
|
1445
|
+
/**
|
|
1446
|
+
* Loads segmentation display sets for a specified viewport.
|
|
1447
|
+
* Depending on the modality of the display set (SEG or RTSTRUCT),
|
|
1448
|
+
* it chooses the appropriate service function to create
|
|
1449
|
+
* the segmentation for the display set.
|
|
1450
|
+
* The function then prepares the viewport for rendering segmentation.
|
|
1451
|
+
*
|
|
1452
|
+
* @param {Object} params - Parameters for the function.
|
|
1453
|
+
* @param params.viewportId - ID of the viewport where the segmentation display sets should be loaded.
|
|
1454
|
+
* @param params.displaySets - Array of display sets to be loaded for segmentation.
|
|
1455
|
+
*
|
|
1456
|
+
*/
|
|
1457
|
+
loadSegmentationDisplaySetsForViewport: async _ref4 => {
|
|
1458
|
+
let {
|
|
1459
|
+
viewportId,
|
|
1460
|
+
displaySets
|
|
1461
|
+
} = _ref4;
|
|
1462
|
+
// Todo: handle adding more than one segmentation
|
|
1463
|
+
const displaySet = displaySets[0];
|
|
1464
|
+
updateViewportsForSegmentationRendering({
|
|
1465
|
+
viewportId,
|
|
1466
|
+
servicesManager,
|
|
1467
|
+
referencedDisplaySetInstanceUID: displaySet.referencedDisplaySetInstanceUID,
|
|
1468
|
+
loadFn: async () => {
|
|
1469
|
+
const segDisplaySet = displaySet;
|
|
1470
|
+
const suppressEvents = false;
|
|
1471
|
+
const serviceFunction = segDisplaySet.Modality === 'SEG' ? 'createSegmentationForSEGDisplaySet' : 'createSegmentationForRTDisplaySet';
|
|
1472
|
+
const boundFn = segmentationService[serviceFunction].bind(segmentationService);
|
|
1473
|
+
const segmentationId = await boundFn(segDisplaySet, null, suppressEvents);
|
|
1474
|
+
return segmentationId;
|
|
1475
|
+
}
|
|
1476
|
+
});
|
|
1477
|
+
},
|
|
1478
|
+
/**
|
|
1479
|
+
* Generates a segmentation from a given segmentation ID.
|
|
1480
|
+
* This function retrieves the associated segmentation and
|
|
1481
|
+
* its referenced volume, extracts label maps from the
|
|
1482
|
+
* segmentation volume, and produces segmentation data
|
|
1483
|
+
* alongside associated metadata.
|
|
1484
|
+
*
|
|
1485
|
+
* @param {Object} params - Parameters for the function.
|
|
1486
|
+
* @param params.segmentationId - ID of the segmentation to be generated.
|
|
1487
|
+
* @param params.options - Optional configuration for the generation process.
|
|
1488
|
+
*
|
|
1489
|
+
* @returns Returns the generated segmentation data.
|
|
1490
|
+
*/
|
|
1491
|
+
generateSegmentation: _ref5 => {
|
|
1492
|
+
let {
|
|
1493
|
+
segmentationId,
|
|
1494
|
+
options = {}
|
|
1495
|
+
} = _ref5;
|
|
1496
|
+
const segmentation = dist_esm.segmentation.state.getSegmentation(segmentationId);
|
|
1497
|
+
const {
|
|
1498
|
+
referencedVolumeId
|
|
1499
|
+
} = segmentation.representationData.LABELMAP;
|
|
1500
|
+
const segmentationVolume = esm.cache.getVolume(segmentationId);
|
|
1501
|
+
const referencedVolume = esm.cache.getVolume(referencedVolumeId);
|
|
1502
|
+
const referencedImages = referencedVolume.getCornerstoneImages();
|
|
1503
|
+
const labelmapObj = generateLabelMaps2DFrom3D(segmentationVolume);
|
|
1504
|
+
|
|
1505
|
+
// Generate fake metadata as an example
|
|
1506
|
+
labelmapObj.metadata = [];
|
|
1507
|
+
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
|
|
1508
|
+
labelmapObj.segmentsOnLabelmap.forEach(segmentIndex => {
|
|
1509
|
+
// segmentation service already has a color for each segment
|
|
1510
|
+
const segment = segmentationInOHIF?.segments[segmentIndex];
|
|
1511
|
+
const {
|
|
1512
|
+
label,
|
|
1513
|
+
color
|
|
1514
|
+
} = segment;
|
|
1515
|
+
const RecommendedDisplayCIELabValue = dcmjs_es["default"].data.Colors.rgb2DICOMLAB(color.slice(0, 3).map(value => value / 255)).map(value => Math.round(value));
|
|
1516
|
+
const segmentMetadata = {
|
|
1517
|
+
SegmentNumber: segmentIndex.toString(),
|
|
1518
|
+
SegmentLabel: label,
|
|
1519
|
+
SegmentAlgorithmType: 'MANUAL',
|
|
1520
|
+
SegmentAlgorithmName: 'OHIF Brush',
|
|
1521
|
+
RecommendedDisplayCIELabValue,
|
|
1522
|
+
SegmentedPropertyCategoryCodeSequence: {
|
|
1523
|
+
CodeValue: 'T-D0050',
|
|
1524
|
+
CodingSchemeDesignator: 'SRT',
|
|
1525
|
+
CodeMeaning: 'Tissue'
|
|
1526
|
+
},
|
|
1527
|
+
SegmentedPropertyTypeCodeSequence: {
|
|
1528
|
+
CodeValue: 'T-D0050',
|
|
1529
|
+
CodingSchemeDesignator: 'SRT',
|
|
1530
|
+
CodeMeaning: 'Tissue'
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
labelmapObj.metadata[segmentIndex] = segmentMetadata;
|
|
1534
|
+
});
|
|
1535
|
+
const generatedSegmentation = generateSegmentation(referencedImages, labelmapObj, esm.metaData, options);
|
|
1536
|
+
return generatedSegmentation;
|
|
1537
|
+
},
|
|
1538
|
+
/**
|
|
1539
|
+
* Downloads a segmentation based on the provided segmentation ID.
|
|
1540
|
+
* This function retrieves the associated segmentation and
|
|
1541
|
+
* uses it to generate the corresponding DICOM dataset, which
|
|
1542
|
+
* is then downloaded with an appropriate filename.
|
|
1543
|
+
*
|
|
1544
|
+
* @param {Object} params - Parameters for the function.
|
|
1545
|
+
* @param params.segmentationId - ID of the segmentation to be downloaded.
|
|
1546
|
+
*
|
|
1547
|
+
*/
|
|
1548
|
+
downloadSegmentation: _ref6 => {
|
|
1549
|
+
let {
|
|
1550
|
+
segmentationId
|
|
1551
|
+
} = _ref6;
|
|
1552
|
+
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
|
|
1553
|
+
const generatedSegmentation = actions.generateSegmentation({
|
|
1554
|
+
segmentationId
|
|
1555
|
+
});
|
|
1556
|
+
downloadDICOMData(generatedSegmentation.dataset, `${segmentationInOHIF.label}`);
|
|
1557
|
+
},
|
|
1558
|
+
/**
|
|
1559
|
+
* Stores a segmentation based on the provided segmentationId into a specified data source.
|
|
1560
|
+
* The SeriesDescription is derived from user input or defaults to the segmentation label,
|
|
1561
|
+
* and in its absence, defaults to 'Research Derived Series'.
|
|
1562
|
+
*
|
|
1563
|
+
* @param {Object} params - Parameters for the function.
|
|
1564
|
+
* @param params.segmentationId - ID of the segmentation to be stored.
|
|
1565
|
+
* @param params.dataSource - Data source where the generated segmentation will be stored.
|
|
1566
|
+
*
|
|
1567
|
+
* @returns {Object|void} Returns the naturalized report if successfully stored,
|
|
1568
|
+
* otherwise throws an error.
|
|
1569
|
+
*/
|
|
1570
|
+
storeSegmentation: async _ref7 => {
|
|
1571
|
+
let {
|
|
1572
|
+
segmentationId,
|
|
1573
|
+
dataSource
|
|
1574
|
+
} = _ref7;
|
|
1575
|
+
const promptResult = await (0,default_src.createReportDialogPrompt)(uiDialogService, {
|
|
1576
|
+
extensionManager
|
|
1577
|
+
});
|
|
1578
|
+
if (promptResult.action !== 1 && promptResult.value) {
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
const segmentation = segmentationService.getSegmentation(segmentationId);
|
|
1582
|
+
if (!segmentation) {
|
|
1583
|
+
throw new Error('No segmentation found');
|
|
1584
|
+
}
|
|
1585
|
+
const {
|
|
1586
|
+
label
|
|
1587
|
+
} = segmentation;
|
|
1588
|
+
const SeriesDescription = promptResult.value || label || 'Research Derived Series';
|
|
1589
|
+
const generatedData = actions.generateSegmentation({
|
|
1590
|
+
segmentationId,
|
|
1591
|
+
options: {
|
|
1592
|
+
SeriesDescription
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
if (!generatedData || !generatedData.dataset) {
|
|
1596
|
+
throw new Error('Error during segmentation generation');
|
|
1597
|
+
}
|
|
1598
|
+
const {
|
|
1599
|
+
dataset: naturalizedReport
|
|
1600
|
+
} = generatedData;
|
|
1601
|
+
await dataSource.store.dicom(naturalizedReport);
|
|
1602
|
+
|
|
1603
|
+
// The "Mode" route listens for DicomMetadataStore changes
|
|
1604
|
+
// When a new instance is added, it listens and
|
|
1605
|
+
// automatically calls makeDisplaySets
|
|
1606
|
+
|
|
1607
|
+
// add the information for where we stored it to the instance as well
|
|
1608
|
+
naturalizedReport.wadoRoot = dataSource.getConfig().wadoRoot;
|
|
1609
|
+
src.DicomMetadataStore.addInstances([naturalizedReport], true);
|
|
1610
|
+
return naturalizedReport;
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
const definitions = {
|
|
1614
|
+
getUpdatedViewportsForSegmentation: {
|
|
1615
|
+
commandFn: actions.getUpdatedViewportsForSegmentation
|
|
1616
|
+
},
|
|
1617
|
+
loadSegmentationDisplaySetsForViewport: {
|
|
1618
|
+
commandFn: actions.loadSegmentationDisplaySetsForViewport
|
|
1619
|
+
},
|
|
1620
|
+
loadSegmentationsForViewport: {
|
|
1621
|
+
commandFn: actions.loadSegmentationsForViewport
|
|
1622
|
+
},
|
|
1623
|
+
createEmptySegmentationForViewport: {
|
|
1624
|
+
commandFn: actions.createEmptySegmentationForViewport
|
|
1625
|
+
},
|
|
1626
|
+
generateSegmentation: {
|
|
1627
|
+
commandFn: actions.generateSegmentation
|
|
1628
|
+
},
|
|
1629
|
+
downloadSegmentation: {
|
|
1630
|
+
commandFn: actions.downloadSegmentation
|
|
1631
|
+
},
|
|
1632
|
+
storeSegmentation: {
|
|
1633
|
+
commandFn: actions.storeSegmentation
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
return {
|
|
1637
|
+
actions,
|
|
1638
|
+
definitions
|
|
1639
|
+
};
|
|
1640
|
+
};
|
|
1641
|
+
/* harmony default export */ const src_commandsModule = (commandsModule);
|
|
1642
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/init.ts
|
|
1643
|
+
|
|
1644
|
+
function init(_ref) {
|
|
1645
|
+
let {
|
|
1646
|
+
configuration = {}
|
|
1647
|
+
} = _ref;
|
|
1648
|
+
(0,dist_esm.addTool)(dist_esm.BrushTool);
|
|
1649
|
+
}
|
|
1650
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-seg/src/index.tsx
|
|
1651
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
const Component = /*#__PURE__*/react.lazy(() => {
|
|
1660
|
+
return __webpack_require__.e(/* import() */ 451).then(__webpack_require__.bind(__webpack_require__, 4451));
|
|
1661
|
+
});
|
|
1662
|
+
const OHIFCornerstoneSEGViewport = props => {
|
|
1663
|
+
return /*#__PURE__*/react.createElement(react.Suspense, {
|
|
1664
|
+
fallback: /*#__PURE__*/react.createElement("div", null, "Loading...")
|
|
1665
|
+
}, /*#__PURE__*/react.createElement(Component, props));
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
/**
|
|
1669
|
+
* You can remove any of the following modules if you don't need them.
|
|
1670
|
+
*/
|
|
1671
|
+
const extension = {
|
|
1672
|
+
/**
|
|
1673
|
+
* Only required property. Should be a unique value across all extensions.
|
|
1674
|
+
* You ID can be anything you want, but it should be unique.
|
|
1675
|
+
*/
|
|
1676
|
+
id: id,
|
|
1677
|
+
preRegistration: init,
|
|
1678
|
+
/**
|
|
1679
|
+
* PanelModule should provide a list of panels that will be available in OHIF
|
|
1680
|
+
* for Modes to consume and render. Each panel is defined by a {name,
|
|
1681
|
+
* iconName, iconLabel, label, component} object. Example of a panel module
|
|
1682
|
+
* is the StudyBrowserPanel that is provided by the default extension in OHIF.
|
|
1683
|
+
*/
|
|
1684
|
+
getPanelModule: src_getPanelModule,
|
|
1685
|
+
getCommandsModule: src_commandsModule,
|
|
1686
|
+
getViewportModule(_ref) {
|
|
1687
|
+
let {
|
|
1688
|
+
servicesManager,
|
|
1689
|
+
extensionManager
|
|
1690
|
+
} = _ref;
|
|
1691
|
+
const ExtendedOHIFCornerstoneSEGViewport = props => {
|
|
1692
|
+
return /*#__PURE__*/react.createElement(OHIFCornerstoneSEGViewport, _extends({
|
|
1693
|
+
servicesManager: servicesManager,
|
|
1694
|
+
extensionManager: extensionManager,
|
|
1695
|
+
commandsManager: commandsManager
|
|
1696
|
+
}, props));
|
|
1697
|
+
};
|
|
1698
|
+
return [{
|
|
1699
|
+
name: 'dicom-seg',
|
|
1700
|
+
component: ExtendedOHIFCornerstoneSEGViewport
|
|
1701
|
+
}];
|
|
1702
|
+
},
|
|
1703
|
+
/**
|
|
1704
|
+
* SopClassHandlerModule should provide a list of sop class handlers that will be
|
|
1705
|
+
* available in OHIF for Modes to consume and use to create displaySets from Series.
|
|
1706
|
+
* Each sop class handler is defined by a { name, sopClassUids, getDisplaySetsFromSeries}.
|
|
1707
|
+
* Examples include the default sop class handler provided by the default extension
|
|
1708
|
+
*/
|
|
1709
|
+
getSopClassHandlerModule: src_getSopClassHandlerModule,
|
|
1710
|
+
getHangingProtocolModule: src_getHangingProtocolModule
|
|
1711
|
+
};
|
|
1712
|
+
/* harmony default export */ const cornerstone_dicom_seg_src = (extension);
|
|
1713
|
+
|
|
1714
|
+
/***/ }),
|
|
1715
|
+
|
|
1716
|
+
/***/ 78753:
|
|
1717
|
+
/***/ (() => {
|
|
1718
|
+
|
|
1719
|
+
/* (ignored) */
|
|
1720
|
+
|
|
1721
|
+
/***/ })
|
|
1722
|
+
|
|
1723
|
+
}]);
|