larvitar 1.5.2 → 1.5.6

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.
@@ -38,6 +38,12 @@ jobs:
38
38
  repository: clenemt/docdash
39
39
  path: ./template/docdash/
40
40
 
41
+ # Install docdash node modules
42
+ - name: Install node modules
43
+ working-directory: ./template/docdash/
44
+ run: |
45
+ yarn install
46
+
41
47
  # Generate docs
42
48
  - name: Generate docs
43
49
  run: |
@@ -50,6 +56,4 @@ jobs:
50
56
  github_token: ${{ secrets.GITHUB_TOKEN }}
51
57
  message: "Update docs"
52
58
  branch: ${{ github.head_ref }}
53
- empty: true
54
-
55
-
59
+ empty: true
package/README.md CHANGED
@@ -6,15 +6,16 @@
6
6
 
7
7
  ## Dicom Image Toolkit for CornerstoneJS
8
8
 
9
- ### Current version: 1.5.2
9
+ ### Current version: 1.5.6
10
10
 
11
- ### Latest Published Release: 1.5.2
11
+ ### Latest Published Release: 1.5.6
12
12
 
13
13
  This library provides common DICOM functionalities to be used in web-applications: it's wrapper that simplifies the use of cornerstone-js environment.
14
14
  Orthogonal multiplanar reformat is included as well as custom loader/exporter for nrrd files and [Vuex](https://vuex.vuejs.org/) custom integration.
15
15
 
16
16
  - `index` main file
17
17
  - `dataDictionary` json file for dicom tags
18
+ - `imageAnonymization` provides anonymization functionalities
18
19
  - `imageColormaps` provides color maps functionalities
19
20
  - `imageContours` using to populate cornerstone tool for segmentation contours on 2D images
20
21
  - `imageIo` import a dicom image in .nrrd format and build contiguous array for exporting data as volume
@@ -4,19 +4,61 @@
4
4
  */
5
5
 
6
6
  // external libraries
7
- import aes from "crypto-js/aes";
8
7
  import sha256 from "crypto-js/sha256";
9
8
  import Hex from "crypto-js/enc-hex";
10
9
  import { forEach } from "lodash";
11
10
 
11
+ const SH = [
12
+ "x00080050" // Accession Number,
13
+ ];
14
+
15
+ const OPTIONAL = [
16
+ "x00100030", // Patient's Birth Date
17
+ "x00080090", // Referring Physician's Name,
18
+ "x00100020", // Patient ID
19
+ "x00100040", // Patient's Sex
20
+ "x00200010" // Study ID
21
+ ];
22
+
23
+ const REMOVE = [
24
+ "x00080014", // Instance Creator UID
25
+ "x00080080", // Institution Name
26
+ "x00080081", // Institution Address
27
+ "x00080092", // Referring Physician's Address
28
+ "x00080094", // Referring Physician's Telephone numbers
29
+ "x00081010", // Station Name
30
+ "x00081030", // Study Description
31
+ "x0008103e", // Series Description
32
+ "x00081040", // Institutional Department name
33
+ "x00081048", // Physician(s) of Record
34
+ "x00081050", // Performing Physicians' Name
35
+ "x00081060", // Name of Physician(s) Reading study
36
+ "x00081070", // Operator's Name
37
+ "x00081080", // Admitting Diagnoses Description
38
+ "x00082111", // Derivation Description
39
+ "x00100032", // Patient's Birth Time
40
+ "x00101000", // Other Patient Ids
41
+ "x00101001", // Other Patient Names
42
+ "x00101010", // Patient's Age
43
+ "x00101020", // Patient's Size
44
+ "x00101030", // Patient's Weight
45
+ "x00101090", // Medical Record Locator
46
+ "x00102160", // Ethnic Group
47
+ "x00102180", // Occupation
48
+ "x001021b0", // Additional Patient's History
49
+ "x00104000", // Patient Comments
50
+ "x00181000", // Device Serial Number
51
+ "x00181030", // Protocol Name
52
+ "x00204000", // Image Comments
53
+ "x00400275" // Request Attributes Sequence
54
+ ];
55
+
12
56
  // global vars
13
57
  const TAGS = [
14
58
  "x00080014", // Instance Creator UID
15
- "x00080018", // SOP Instance UID
16
59
  "x00080050", // Accession Number
17
60
  "x00080080", // Institution Name
18
61
  "x00080081", // Institution Address
19
- "x00080081", // Institution Address
20
62
  "x00080090", // Referring Physician's Name
21
63
  "x00080092", // Referring Physician's Address
22
64
  "x00080094", // Referring Physician's Telephone numbers
@@ -29,7 +71,6 @@ const TAGS = [
29
71
  "x00081060", // Name of Physician(s) Reading study
30
72
  "x00081070", // Operator's Name
31
73
  "x00081080", // Admitting Diagnoses Description
32
- "x00081155", // Referenced SOP Instance UID
33
74
  "x00082111", // Derivation Description
34
75
  "x00100010", // Patient's Name
35
76
  "x00100020", // Patient ID
@@ -48,15 +89,12 @@ const TAGS = [
48
89
  "x00104000", // Patient Comments
49
90
  "x00181000", // Device Serial Number
50
91
  "x00181030", // Protocol Name
51
- "x0020000d", // Study Instance UID
52
- "x0020000e", // Series Instance UID
53
92
  "x00200010", // Study ID
54
93
  "x00200052", // Frame of Reference UID
55
94
  "x00200200", // Synchronization Frame of Reference UID
56
95
  "x00204000", // Image Comments
57
96
  "x00400275", // Request Attributes Sequence
58
97
  "x0040a124", // UID
59
- "x0040a730", // Content Sequence
60
98
  "x00880140", // Storage Media File-set UID
61
99
  "x30060024", // Referenced Frame of Reference UID
62
100
  "x300600c2" // Related Frame of Reference UID
@@ -65,7 +103,6 @@ const TAGS = [
65
103
  /*
66
104
  * This module provides the following functions to be exported:
67
105
  * anonymize(series)
68
- * encrypt(series, passphrase)
69
106
  */
70
107
 
71
108
  /**
@@ -79,9 +116,30 @@ export const anonymize = function (series) {
79
116
  forEach(TAGS, function (tag) {
80
117
  if (tag in instance.metadata) {
81
118
  let anonymized_value = sha256(instance.metadata[tag]).toString(Hex);
82
- instance.metadata[tag] = anonymized_value;
119
+ // Patient Tag Anonymization
120
+ if (tag === "x00100010") {
121
+ instance.metadata[tag] =
122
+ "Anonymized^" + anonymized_value.substring(0, 6);
123
+ }
124
+ // Short string
125
+ else if (SH.includes(tag) === true) {
126
+ instance.metadata[tag] = anonymized_value.substring(0, 16);
127
+ }
128
+ // Required, empty if unknown
129
+ else if (OPTIONAL.includes(tag) === true) {
130
+ instance.metadata[tag] = "";
131
+ }
132
+ // Optional
133
+ else if (REMOVE.includes(tag) === true) {
134
+ delete instance.metadata[tag];
135
+ }
136
+ // Default sha256
137
+ else {
138
+ instance.metadata[tag] = anonymized_value;
139
+ }
83
140
  }
84
141
  });
142
+ instance.metadata["x00120062"] = "YES"; // Patient Identity Removed Attribute
85
143
  instance.metadata.seriesUID = instance.metadata["x0020000e"];
86
144
  instance.metadata.instanceUID = instance.metadata["x00080018"];
87
145
  instance.metadata.studyUID = instance.metadata["x0020000d"];
@@ -90,72 +148,11 @@ export const anonymize = function (series) {
90
148
  instance.metadata.patientName = instance.metadata["x00100010"];
91
149
  instance.metadata.patientBirthdate = instance.metadata["x00100030"];
92
150
  instance.metadata.seriesDescription = instance.metadata["x0008103e"];
151
+ instance.metadata.anonymized = true;
93
152
  });
94
153
 
95
- delete series["instanceUIDs"];
96
- series.instanceUIDs = {};
97
-
98
- forEach(series.imageIds, function (imageId) {
99
- series.instanceUIDs[series.instances[imageId].metadata.instanceUID] =
100
- imageId;
101
- });
102
-
103
- series.larvitarSeriesInstanceUID = sha256(
104
- series.larvitarSeriesInstanceUID
105
- ).toString(Hex);
106
- series.seriesUID = sha256(series.seriesUID).toString(Hex);
107
- series.seriesDescription = sha256(series.seriesDescription).toString(Hex);
108
- series.studyUID = sha256(series.studyUID).toString(Hex);
109
-
110
- return series;
111
- };
112
-
113
- /**
114
- * Encrypt DICOM series' metadata using AES
115
- * @function encrypt
116
- * @param {Object} series - Cornerstone series object
117
- * @param {String} passphrase - AES passphrase
118
- * @returns {Object} anonymized_series: Cornerstone encrypted series object
119
- */
120
- export const encrypt = function (series, passphrase) {
121
- if (!passphrase) {
122
- return "Error, provide a valid passphrase";
123
- }
124
- forEach(series.instances, function (instance) {
125
- forEach(TAGS, function (tag) {
126
- if (tag in instance.metadata) {
127
- let anonymized_value = aes
128
- .encrypt(instance.metadata[tag], passphrase)
129
- .toString();
130
- instance.metadata[tag] = anonymized_value;
131
- }
132
- });
133
- instance.metadata.seriesUID = instance.metadata["x0020000e"];
134
- instance.metadata.instanceUID = instance.metadata["x00080018"];
135
- instance.metadata.studyUID = instance.metadata["x0020000d"];
136
- instance.metadata.accessionNumber = instance.metadata["x00080050"];
137
- instance.metadata.studyDescription = instance.metadata["x00081030"];
138
- instance.metadata.patientName = instance.metadata["x00100010"];
139
- instance.metadata.patientBirthdate = instance.metadata["x00100030"];
140
- instance.metadata.seriesDescription = instance.metadata["x0008103e"];
141
- });
142
-
143
- delete series["instanceUIDs"];
144
- series.instanceUIDs = {};
145
-
146
- forEach(series.imageIds, function (imageId) {
147
- series.instanceUIDs[series.instances[imageId].metadata.instanceUID] =
148
- imageId;
149
- });
150
-
151
- series.larvitarSeriesInstanceUID = aes
152
- .encrypt(series.larvitarSeriesInstanceUID, passphrase)
153
- .toString();
154
- series.seriesUID = aes.encrypt(series.seriesUID, passphrase).toString();
155
- series.seriesDescription = aes
156
- .encrypt(series.seriesDescription, passphrase)
157
- .toString();
158
- series.studyUID = aes.encrypt(series.studyUID, passphrase).toString();
154
+ series.seriesDescription = undefined;
155
+ series.anonymized = true;
159
156
 
160
157
  return series;
161
158
  };
@@ -34,10 +34,6 @@ const globalConfig = {
34
34
  1
35
35
  ),
36
36
  startWebWorkersOnDemand: true,
37
- webWorkerTaskPaths: [
38
- "https://unpkg.com/cornerstone-wado-image-loader@4.1.0/dist/610.bundle.min.worker.js",
39
- "https://unpkg.com/cornerstone-wado-image-loader@4.1.0/dist/888.bundle.min.worker.js"
40
- ],
41
37
  taskConfiguration: {
42
38
  decodeTask: {
43
39
  loadCodecsOnStartup: true,
@@ -150,6 +146,7 @@ export const updateLoadedStack = function (
150
146
  let is4D = seriesData.metadata.is4D;
151
147
  let SOPUID = seriesData.metadata["x00080016"];
152
148
  let isPDF = SOPUID == "1.2.840.10008.5.1.4.1.1.104.1" ? true : false;
149
+ let anonymized = seriesData.metadata.anonymized;
153
150
 
154
151
  let color = cornerstoneWADOImageLoader.isColorImage(
155
152
  seriesData.metadata["x00280004"]
@@ -173,6 +170,7 @@ export const updateLoadedStack = function (
173
170
  isMultiframe: isMultiframe,
174
171
  is4D: is4D,
175
172
  isPDF: isPDF,
173
+ anonymized: anonymized,
176
174
  modality: modality,
177
175
  color: color,
178
176
  bytes: 0
@@ -280,6 +280,7 @@ let parseFile = function (file) {
280
280
  dataSet: dataSet
281
281
  };
282
282
  imageObject.metadata = metadata;
283
+ imageObject.metadata.anonymized = false;
283
284
  imageObject.metadata.seriesUID = seriesInstanceUID;
284
285
  imageObject.metadata.instanceUID = instanceUID;
285
286
  imageObject.metadata.studyUID = metadata["x0020000d"];
@@ -9,6 +9,8 @@ import {
9
9
  sortBy,
10
10
  clone,
11
11
  find,
12
+ filter,
13
+ keys,
12
14
  has,
13
15
  max,
14
16
  map,
@@ -24,6 +26,7 @@ import { convertBytes } from "dicom-character-set";
24
26
  // internal libraries
25
27
  import { getDicomImageId } from "./loaders/dicomLoader";
26
28
  import TAG_DICT from "./dataDictionary.json";
29
+ import { getSeriesDataFromLarvitarManager } from "./loaders/commonLoader";
27
30
 
28
31
  // global module variables
29
32
  // variables used to manage the reslice functionality
@@ -51,6 +54,7 @@ const resliceTable = {
51
54
  * getDistanceBetweenSlices(seriesData, sliceIndex1, sliceIndex2)
52
55
  * parseTag(dataSet, propertyName, element)
53
56
  * isElement(o)
57
+ * getImageMetadata(dataSet, imageId)
54
58
  */
55
59
 
56
60
  /**
@@ -848,6 +852,43 @@ export const parseTag = function (dataSet, propertyName, element) {
848
852
  return value;
849
853
  };
850
854
 
855
+ /**
856
+ * @instance
857
+ * @function getImageMetadata
858
+ * @param {String} seriesId - The seriesUID
859
+ * @param {String} instanceUID - The SOPInstanceUID
860
+ * @return {Array} - List of metadata objects: tag, name and value
861
+ */
862
+ export const getImageMetadata = function (seriesId, instanceUID) {
863
+ const seriesData = getSeriesDataFromLarvitarManager(seriesId);
864
+ const imageId = seriesData.instanceUIDs[instanceUID];
865
+ let metadata = seriesData.instances[imageId].metadata;
866
+ // get elements from metadata where the key starts with x and is length 7
867
+ let metadata_keys = filter(keys(metadata), function (key) {
868
+ return key.length === 9 && key[0] === "x";
869
+ });
870
+ // loop metadata using metadata_keys and return list of key value pairs
871
+ let metadata_list = map(metadata_keys, function (key) {
872
+ // if value is a dictionary return empty string
873
+ const value = metadata[key].constructor == Object ? "" : metadata[key];
874
+ // convert key removing x and adding comma at position 4
875
+ const tagKey = (
876
+ "(" +
877
+ key.slice(1, 5) +
878
+ "," +
879
+ key.slice(5) +
880
+ ")"
881
+ ).toUpperCase();
882
+ const name = TAG_DICT[tagKey] ? TAG_DICT[tagKey].name : "";
883
+ return {
884
+ tag: tagKey,
885
+ name: name,
886
+ value: value
887
+ };
888
+ });
889
+ return metadata_list;
890
+ };
891
+
851
892
  /* Internal module functions */
852
893
 
853
894
  /**
@@ -175,6 +175,15 @@ export const clearMultiFrameCache = function (seriesId) {
175
175
  * @returns {Object} custom image object
176
176
  */
177
177
  let createCustomImage = function (id, imageId, frameIndex, metadata) {
178
+ let options = {};
179
+ // always preScale the pixel array unless it is asked not to
180
+ options.preScale = {
181
+ enabled:
182
+ options.preScale && options.preScale.enabled !== undefined
183
+ ? options.preScale.enabled
184
+ : false
185
+ };
186
+
178
187
  let dataSet = multiframeDatasetCache[id].dataSet;
179
188
  let pixelDataElement = dataSet.elements.x7fe00010;
180
189
  // Extract pixelData of the required frame
@@ -203,11 +212,27 @@ let createCustomImage = function (id, imageId, frameIndex, metadata) {
203
212
  ? window.document.getElementsByTagName("canvas")[0]
204
213
  : window.document.createElement("canvas");
205
214
 
215
+ // Get the scaling parameters from the metadata
216
+ if (options.preScale.enabled) {
217
+ const scalingParameters = cornerstoneWADOImageLoader.getScalingParameters(
218
+ cornerstone.metaData,
219
+ imageId
220
+ );
221
+
222
+ if (scalingParameters) {
223
+ options.preScale = {
224
+ ...options.preScale,
225
+ scalingParameters
226
+ };
227
+ }
228
+ }
229
+
206
230
  const decodePromise = cornerstoneWADOImageLoader.decodeImageFrame(
207
231
  imageFrame,
208
232
  transferSyntax,
209
233
  pixelData,
210
- canvas
234
+ canvas,
235
+ options
211
236
  );
212
237
 
213
238
  let promise = new Promise((resolve, reject) => {
package/index.js CHANGED
@@ -37,7 +37,8 @@ import {
37
37
  getMeanValue,
38
38
  getReslicedMetadata,
39
39
  getReslicedPixeldata,
40
- getDistanceBetweenSlices
40
+ getDistanceBetweenSlices,
41
+ getImageMetadata
41
42
  } from "./imaging/imageUtils";
42
43
 
43
44
  import {
@@ -48,7 +49,7 @@ import {
48
49
  importNRRDImage
49
50
  } from "./imaging/imageIo";
50
51
 
51
- import { anonymize, encrypt } from "./imaging/imageAnonymization";
52
+ import { anonymize } from "./imaging/imageAnonymization";
52
53
 
53
54
  import {
54
55
  buildLayer,
@@ -243,6 +244,7 @@ export {
243
244
  getReslicedMetadata,
244
245
  getReslicedPixeldata,
245
246
  getDistanceBetweenSlices,
247
+ getImageMetadata,
246
248
  // imageIo
247
249
  buildHeader,
248
250
  getCachedPixelData,
@@ -251,7 +253,6 @@ export {
251
253
  importNRRDImage,
252
254
  // imageAnonymization
253
255
  anonymize,
254
- encrypt,
255
256
  // imageLayers
256
257
  buildLayer,
257
258
  updateLayer,
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "medical",
7
7
  "cornerstone"
8
8
  ],
9
- "version": "1.5.2",
9
+ "version": "1.5.6",
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",
@@ -29,7 +29,7 @@
29
29
  "cornerstone-core": "^2.6.1",
30
30
  "cornerstone-file-image-loader": "^0.3.0",
31
31
  "cornerstone-tools": "^6.0.7",
32
- "cornerstone-wado-image-loader": "^4.1.3",
32
+ "cornerstone-wado-image-loader": "^4.10.2",
33
33
  "cornerstone-web-image-loader": "^2.1.1",
34
34
  "crypto-js": "^4.1.1",
35
35
  "dicom-character-set": "^1.0.3",