dcmjs 0.23.1 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcmjs",
3
- "version": "0.23.1",
3
+ "version": "0.24.2",
4
4
  "description": "Javascript implementation of DICOM manipulation",
5
5
  "main": "build/dcmjs.js",
6
6
  "module": "build/dcmjs.es.js",
@@ -0,0 +1,237 @@
1
+ import "regenerator-runtime/runtime.js";
2
+
3
+ import dcmjs from "../src/index.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import os from "os";
7
+ import followRedirects from "follow-redirects";
8
+ const { https } = followRedirects;
9
+ import { promisify } from "util";
10
+ import unzipper from "unzipper";
11
+ import fsPromises from "fs/promises";
12
+
13
+ const {
14
+ DicomMetaDictionary,
15
+ DicomDict,
16
+ DicomMessage,
17
+ ReadBufferStream
18
+ } = dcmjs.data;
19
+
20
+ function downloadToFile(url, filePath) {
21
+ return new Promise((resolve, reject) => {
22
+ const fileStream = fs.createWriteStream(filePath);
23
+ https
24
+ .get(url, response => {
25
+ response.pipe(fileStream);
26
+ fileStream.on("finish", () => {
27
+ resolve(filePath);
28
+ });
29
+ })
30
+ .on("error", reject);
31
+ });
32
+ }
33
+
34
+ const areEqual = (first, second) =>
35
+ first.byteLength === second.byteLength &&
36
+ first.every((value, index) => value === second[index]);
37
+
38
+ it("test_untilTag", () => {
39
+ const buffer = fs.readFileSync("test/sample-dicom.dcm");
40
+ console.time("readFile");
41
+ const fullData = DicomMessage.readFile(buffer.buffer);
42
+ console.timeEnd("readFile");
43
+
44
+ console.time("readFile without untilTag");
45
+ const dicomData = DicomMessage.readFile(buffer.buffer, {
46
+ untilTag: "7FE00010",
47
+ includeUntilTagValue: false
48
+ });
49
+ console.timeEnd("readFile without untilTag");
50
+
51
+ console.time("readFile with untilTag");
52
+ const dicomData2 = DicomMessage.readFile(buffer.buffer, {
53
+ untilTag: "7FE00010",
54
+ includeUntilTagValue: true
55
+ });
56
+ console.timeEnd("readFile with untilTag");
57
+
58
+ const full_dataset = DicomMetaDictionary.naturalizeDataset(fullData.dict);
59
+ full_dataset._meta = DicomMetaDictionary.namifyDataset(fullData.meta);
60
+
61
+ const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
62
+ dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
63
+
64
+ const dataset2 = DicomMetaDictionary.naturalizeDataset(dicomData2.dict);
65
+ dataset2._meta = DicomMetaDictionary.namifyDataset(dicomData2.meta);
66
+
67
+ expect(full_dataset.PixelData).toEqual(dataset2.PixelData);
68
+ expect(dataset.PixelData).toEqual(0);
69
+ });
70
+
71
+ it("noCopy multiframe DICOM which has trailing padding", async () => {
72
+ const dicomUrl =
73
+ "https://github.com/dcmjs-org/data/releases/download/binary-parsing-stressors/multiframe-ultrasound.dcm";
74
+ const dicomPath = path.join(os.tmpdir(), "multiframe-ultrasound.dcm");
75
+
76
+ await downloadToFile(dicomUrl, dicomPath);
77
+
78
+ const dicomDictNoCopy = DicomMessage.readFile(
79
+ fs.readFileSync(dicomPath).buffer,
80
+ {
81
+ noCopy: true
82
+ }
83
+ );
84
+
85
+ const dicomDict = DicomMessage.readFile(fs.readFileSync(dicomPath).buffer, {
86
+ noCopy: false
87
+ });
88
+
89
+ Object.keys(dicomDict.dict).map(key => {
90
+ const value = dicomDict.dict[key].Value;
91
+ if (value[0] instanceof ArrayBuffer) {
92
+ value.map((e, idx) => {
93
+ const noCopyValue = dicomDictNoCopy.dict[key].Value[idx];
94
+ const copyValue = new Uint8Array(e);
95
+ expect(areEqual(noCopyValue, copyValue)).toEqual(true);
96
+ });
97
+ }
98
+ });
99
+ });
100
+
101
+ it("noCopy multiframe DICOM with large private tags before and after the image data", async () => {
102
+ const dicomUrl =
103
+ "https://github.com/dcmjs-org/data/releases/download/binary-parsing-stressors/large-private-tags.dcm";
104
+ const dicomPath = path.join(os.tmpdir(), "large-private-tags.dcm");
105
+
106
+ await downloadToFile(dicomUrl, dicomPath);
107
+
108
+ const dicomDictNoCopy = DicomMessage.readFile(
109
+ fs.readFileSync(dicomPath).buffer,
110
+ {
111
+ noCopy: true
112
+ }
113
+ );
114
+
115
+ const dicomDict = DicomMessage.readFile(fs.readFileSync(dicomPath).buffer, {
116
+ noCopy: false
117
+ });
118
+
119
+ Object.keys(dicomDict.dict).map(key => {
120
+ const value = dicomDict.dict[key].Value;
121
+ if (value[0] instanceof ArrayBuffer) {
122
+ value.map((e, idx) => {
123
+ const noCopyValue = dicomDictNoCopy.dict[key].Value[idx];
124
+ const copyValue = new Uint8Array(e);
125
+ expect(areEqual(noCopyValue, copyValue)).toEqual(true);
126
+ });
127
+ }
128
+ });
129
+ });
130
+
131
+ it("noCopy binary data into an ArrayBuffer", async () => {
132
+ const dicomUrl =
133
+ "https://github.com/dcmjs-org/data/releases/download/binary-tag/binary-tag.dcm";
134
+ const dicomPath = path.join(os.tmpdir(), "binary-tag.dcm");
135
+
136
+ await downloadToFile(dicomUrl, dicomPath);
137
+ const fileData = await promisify(fs.readFile)(dicomPath);
138
+
139
+ const dicomDictNoCopy = DicomMessage.readFile(fileData.buffer, {
140
+ noCopy: true
141
+ });
142
+
143
+ const dicomDict = DicomMessage.readFile(fileData.buffer, {
144
+ noCopy: false
145
+ });
146
+
147
+ Object.keys(dicomDict.dict).map(key => {
148
+ const value = dicomDict.dict[key].Value;
149
+ if (value[0] instanceof ArrayBuffer) {
150
+ value.map((e, idx) => {
151
+ const noCopyValue = dicomDictNoCopy.dict[key].Value[idx];
152
+ const copyValue = new Uint8Array(e);
153
+ expect(areEqual(noCopyValue, copyValue)).toEqual(true);
154
+ });
155
+ }
156
+ });
157
+ });
158
+
159
+ it("noCopy test_multiframe_1", async () => {
160
+ const url =
161
+ "https://github.com/dcmjs-org/data/releases/download/MRHead/MRHead.zip";
162
+ const zipFilePath = path.join(os.tmpdir(), "MRHead.zip");
163
+ const unzipPath = path.join(os.tmpdir(), "test_multiframe_1");
164
+
165
+ await downloadToFile(url, zipFilePath);
166
+
167
+ await new Promise(resolve => {
168
+ fs.createReadStream(zipFilePath).pipe(
169
+ unzipper.Extract({ path: unzipPath }).on("close", resolve)
170
+ );
171
+ });
172
+
173
+ const mrHeadPath = path.join(unzipPath, "MRHead");
174
+ const fileNames = await fsPromises.readdir(mrHeadPath);
175
+
176
+ const datasets = [];
177
+ fileNames.forEach(fileName => {
178
+ const arrayBuffer = fs.readFileSync(path.join(mrHeadPath, fileName))
179
+ .buffer;
180
+ const dicomDictNoCopy = DicomMessage.readFile(arrayBuffer, {
181
+ noCopy: true
182
+ });
183
+ const dicomDict = DicomMessage.readFile(arrayBuffer, {
184
+ noCopy: false
185
+ });
186
+
187
+ Object.keys(dicomDict.dict).map(key => {
188
+ const value = dicomDict.dict[key].Value;
189
+ if (value[0] instanceof ArrayBuffer) {
190
+ value.map((e, idx) => {
191
+ const noCopyValue = dicomDictNoCopy.dict[key].Value[idx];
192
+ const copyValue = new Uint8Array(e);
193
+ expect(areEqual(noCopyValue, copyValue)).toEqual(true);
194
+ });
195
+ }
196
+ });
197
+ });
198
+ });
199
+
200
+ it("noCopy test_fragment_multiframe", async () => {
201
+ const url =
202
+ "https://github.com/dcmjs-org/data/releases/download/encapsulation/encapsulation-fragment-multiframe.dcm";
203
+ const dcmPath = path.join(
204
+ os.tmpdir(),
205
+ "encapsulation-fragment-multiframe.dcm"
206
+ );
207
+
208
+ await downloadToFile(url, dcmPath);
209
+ const file = fs.readFileSync(dcmPath);
210
+
211
+ const dicomDict = dcmjs.data.DicomMessage.readFile(file.buffer, {
212
+ // ignoreErrors: true,
213
+ });
214
+
215
+ const dicomDictNoCopy = DicomMessage.readFile(file.buffer, {
216
+ noCopy: true
217
+ });
218
+
219
+ Object.keys(dicomDict.dict).map(key => {
220
+ const value = dicomDict.dict[key].Value;
221
+ if (value[0] instanceof ArrayBuffer) {
222
+ value.map((e, idx) => {
223
+ const noCopyValue = dicomDictNoCopy.dict[key].Value[idx];
224
+ const copyValue = new Uint8Array(e);
225
+ const areEqual = (first, second) =>
226
+ first.every((value, index) => value === second[index]);
227
+
228
+ const totalSize = noCopyValue.reduce(
229
+ (sum, arr) => sum + arr.byteLength,
230
+ 0
231
+ );
232
+ expect(totalSize).toEqual(copyValue.length);
233
+ expect(areEqual(noCopyValue[0], copyValue)).toEqual(true);
234
+ });
235
+ }
236
+ });
237
+ });
package/test/data.test.js CHANGED
@@ -8,6 +8,7 @@ import os from "os";
8
8
  import path from "path";
9
9
  import unzipper from "unzipper";
10
10
  import followRedirects from "follow-redirects";
11
+ import { promisify } from "util";
11
12
 
12
13
  const { https } = followRedirects;
13
14
 
@@ -16,8 +17,12 @@ import minimalDataset from "./mocks/minimal_fields_dataset.json";
16
17
  import arrayItem from "./arrayItem.json";
17
18
  import { rawTags } from "./rawTags";
18
19
 
19
- const { DicomMetaDictionary, DicomDict, DicomMessage, ReadBufferStream } =
20
- dcmjs.data;
20
+ const {
21
+ DicomMetaDictionary,
22
+ DicomDict,
23
+ DicomMessage,
24
+ ReadBufferStream
25
+ } = dcmjs.data;
21
26
 
22
27
  const EXPLICIT_LITTLE_ENDIAN = "1.2.840.10008.1.2.1";
23
28
 
@@ -170,8 +175,9 @@ it("test_json_1", () => {
170
175
  //
171
176
  // make a natural version of a dataset with sequence tags and confirm it has correct values
172
177
  //
173
- const naturalSequence =
174
- DicomMetaDictionary.naturalizeDataset(sequenceMetadata);
178
+ const naturalSequence = DicomMetaDictionary.naturalizeDataset(
179
+ sequenceMetadata
180
+ );
175
181
 
176
182
  // The match object needs to be done on the actual element, not the proxied value
177
183
  expect(naturalSequence.ProcedureCodeSequence[0]).toMatchObject({
@@ -233,17 +239,17 @@ it("test_multiframe_1", async () => {
233
239
 
234
240
  const datasets = [];
235
241
  fileNames.forEach(fileName => {
236
- const arrayBuffer = fs.readFileSync(
237
- path.join(mrHeadPath, fileName)
238
- ).buffer;
242
+ const arrayBuffer = fs.readFileSync(path.join(mrHeadPath, fileName))
243
+ .buffer;
239
244
  const dicomDict = DicomMessage.readFile(arrayBuffer);
240
245
  const dataset = DicomMetaDictionary.naturalizeDataset(dicomDict.dict);
241
246
 
242
247
  datasets.push(dataset);
243
248
  });
244
249
 
245
- const multiframe =
246
- dcmjs.normalizers.Normalizer.normalizeToDataset(datasets);
250
+ const multiframe = dcmjs.normalizers.Normalizer.normalizeToDataset(
251
+ datasets
252
+ );
247
253
  const spacing =
248
254
  multiframe.SharedFunctionalGroupsSequence.PixelMeasuresSequence
249
255
  .SpacingBetweenSlices;
@@ -279,9 +285,8 @@ it("test_oneslice_seg", async () => {
279
285
 
280
286
  const datasets = [];
281
287
  fileNames.forEach(fileName => {
282
- const arrayBuffer = fs.readFileSync(
283
- path.join(ctPelvisPath, fileName)
284
- ).buffer;
288
+ const arrayBuffer = fs.readFileSync(path.join(ctPelvisPath, fileName))
289
+ .buffer;
285
290
  const dicomDict = DicomMessage.readFile(arrayBuffer);
286
291
  const dataset = DicomMetaDictionary.naturalizeDataset(dicomDict.dict);
287
292
  datasets.push(dataset);
@@ -307,8 +312,9 @@ it("test_oneslice_seg", async () => {
307
312
  });
308
313
 
309
314
  it("test_normalizer_smaller", () => {
310
- const naturalizedTags =
311
- dcmjs.data.DicomMetaDictionary.naturalizeDataset(rawTags);
315
+ const naturalizedTags = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
316
+ rawTags
317
+ );
312
318
 
313
319
  const rawTagsLen = JSON.stringify(rawTags).length;
314
320
  const naturalizedTagsLen = JSON.stringify(naturalizedTags).length;
@@ -451,39 +457,6 @@ it("test_invalid_vr_length", () => {
451
457
  }
452
458
  });
453
459
 
454
- it("test_untiltag", () => {
455
- const buffer = fs.readFileSync("test/sample-dicom.dcm");
456
- console.time("readFile");
457
- const fullData = DicomMessage.readFile(buffer.buffer);
458
- console.timeEnd("readFile");
459
-
460
- console.time("readFile without untilTag");
461
- const dicomData = DicomMessage.readFile(buffer.buffer, {
462
- untilTag: "7FE00010",
463
- includeUntilTagValue: false
464
- });
465
- console.timeEnd("readFile without untilTag");
466
-
467
- console.time("readFile with untilTag");
468
- const dicomData2 = DicomMessage.readFile(buffer.buffer, {
469
- untilTag: "7FE00010",
470
- includeUntilTagValue: true
471
- });
472
- console.timeEnd("readFile with untilTag");
473
-
474
- const full_dataset = DicomMetaDictionary.naturalizeDataset(fullData.dict);
475
- full_dataset._meta = DicomMetaDictionary.namifyDataset(fullData.meta);
476
-
477
- const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
478
- dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
479
-
480
- const dataset2 = DicomMetaDictionary.naturalizeDataset(dicomData2.dict);
481
- dataset2._meta = DicomMetaDictionary.namifyDataset(dicomData2.meta);
482
-
483
- expect(full_dataset.PixelData).toEqual(dataset2.PixelData);
484
- expect(dataset.PixelData).toEqual(0);
485
- });
486
-
487
460
  it("test_encapsulation", async () => {
488
461
  const url =
489
462
  "https://github.com/dcmjs-org/data/releases/download/encapsulation/encapsulation.dcm";
@@ -530,10 +503,10 @@ it("test_encapsulation", async () => {
530
503
  throw new Error("Invalid a dicom file");
531
504
  }
532
505
 
533
- const el = DicomMessage.readTag(stream, useSyntax),
506
+ const el = DicomMessage._readTag(stream, useSyntax),
534
507
  metaLength = el.values[0]; //read header buffer
535
508
  const metaStream = stream.more(metaLength);
536
- const metaHeader = DicomMessage.read(metaStream, useSyntax, false); //get the syntax
509
+ const metaHeader = DicomMessage._read(metaStream, useSyntax); //get the syntax
537
510
  let mainSyntax = metaHeader["00020010"].Value[0];
538
511
 
539
512
  mainSyntax = DicomMessage._normalizeSyntax(mainSyntax);
@@ -564,7 +537,7 @@ it("test_encapsulation", async () => {
564
537
  lengths.push(length);
565
538
  }
566
539
 
567
- DicomMessage.readTag(stream, mainSyntax, null, false);
540
+ DicomMessage._readTag(stream, mainSyntax);
568
541
  }
569
542
 
570
543
  // then
@@ -595,3 +568,76 @@ it("test_custom_dictionary", () => {
595
568
  //check that all other fields were preserved, 15 original + 1 for _vr and +1 for "TrialName"
596
569
  expect(Object.keys(dataset).length).toEqual(17);
597
570
  });
571
+
572
+ it("Reads DICOM with multiplicity", async () => {
573
+ const url =
574
+ "https://github.com/dcmjs-org/data/releases/download/multiplicity/multiplicity.dcm";
575
+ const dcmPath = path.join(os.tmpdir(), "multiplicity.dcm");
576
+
577
+ await downloadToFile(url, dcmPath);
578
+
579
+ const file = await promisify(fs.readFile)(dcmPath);
580
+ const dicomDict = DicomMessage.readFile(file.buffer);
581
+
582
+ expect(dicomDict.dict["00101020"].Value).toEqual([1, 2]);
583
+ expect(dicomDict.dict["0018100B"].Value).toEqual(["1.2", "3.4"]);
584
+ });
585
+
586
+ it("Reads binary data into an ArrayBuffer", async () => {
587
+ const dicomUrl =
588
+ "https://github.com/dcmjs-org/data/releases/download/binary-tag/binary-tag.dcm";
589
+ const dicomPath = path.join(os.tmpdir(), "binary-tag.dcm");
590
+
591
+ await downloadToFile(dicomUrl, dicomPath);
592
+
593
+ const fileData = await promisify(fs.readFile)(dicomPath);
594
+ const dicomDict = DicomMessage.readFile(fileData.buffer);
595
+ const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
596
+ dicomDict.dict
597
+ );
598
+
599
+ expect(dataset.PixelData).toBeInstanceOf(Array);
600
+ expect(dataset.PixelData[0]).toBeInstanceOf(ArrayBuffer);
601
+ expect([...new Uint8Array(dataset.PixelData[0])]).toEqual([2, 3, 4, 5, 6]);
602
+ });
603
+
604
+ it("Reads a multiframe DICOM which has trailing padding", async () => {
605
+ const dicomUrl =
606
+ "https://github.com/dcmjs-org/data/releases/download/binary-parsing-stressors/multiframe-ultrasound.dcm";
607
+ const dicomPath = path.join(os.tmpdir(), "multiframe-ultrasound.dcm");
608
+
609
+ await downloadToFile(dicomUrl, dicomPath);
610
+
611
+ const dicomDict = DicomMessage.readFile(fs.readFileSync(dicomPath).buffer);
612
+ const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
613
+ dicomDict.dict
614
+ );
615
+
616
+ expect(dataset.PixelData.length).toEqual(29);
617
+ expect(dataset.PixelData[0]).toBeInstanceOf(ArrayBuffer);
618
+ expect(dataset.PixelData[0].byteLength).toEqual(104976);
619
+ expect(dataset.PixelData[1].byteLength).toEqual(104920);
620
+ expect(dataset.PixelData[27].byteLength).toEqual(103168);
621
+ expect(dataset.PixelData[28].byteLength).toEqual(103194);
622
+ });
623
+
624
+ it("Reads a multiframe DICOM with large private tags before and after the image data", async () => {
625
+ const dicomUrl =
626
+ "https://github.com/dcmjs-org/data/releases/download/binary-parsing-stressors/large-private-tags.dcm";
627
+ const dicomPath = path.join(os.tmpdir(), "large-private-tags.dcm");
628
+
629
+ await downloadToFile(dicomUrl, dicomPath);
630
+
631
+ const dicomDict = DicomMessage.readFile(fs.readFileSync(dicomPath).buffer);
632
+ const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
633
+ dicomDict.dict
634
+ );
635
+
636
+ expect(dataset.PixelData).toBeInstanceOf(Array);
637
+ expect(dataset.PixelData.length).toEqual(130);
638
+ expect(dataset.PixelData[0]).toBeInstanceOf(ArrayBuffer);
639
+ expect(dataset.PixelData[0].byteLength).toEqual(61518);
640
+ expect(dataset.PixelData[1].byteLength).toEqual(61482);
641
+ expect(dataset.PixelData[128].byteLength).toEqual(62144);
642
+ expect(dataset.PixelData[129].byteLength).toEqual(62148);
643
+ });
@@ -1,44 +0,0 @@
1
- import dcmjs from "../src/index.js";
2
- import fs from "fs";
3
-
4
- it("test_untilTag", () => {
5
- const buffer = fs.readFileSync("test/sample-dicom.dcm");
6
-
7
- const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
8
-
9
- console.time("readFile");
10
- const fullData = DicomMessage.readFile(buffer.buffer);
11
- console.timeEnd("readFile");
12
-
13
- console.time("readFile without untilTag");
14
- const dicomData = DicomMessage.readFile(buffer.buffer, {
15
- untilTag: "7FE00010",
16
- includeUntilTagValue: false
17
- });
18
- console.timeEnd("readFile without untilTag");
19
-
20
- console.time("readFile with untilTag");
21
- const dicomData2 = DicomMessage.readFile(buffer.buffer, {
22
- untilTag: "7FE00010",
23
- includeUntilTagValue: true
24
- });
25
- console.timeEnd("readFile with untilTag");
26
-
27
- const full_dataset = DicomMetaDictionary.naturalizeDataset(fullData.dict);
28
- full_dataset._meta = DicomMetaDictionary.namifyDataset(fullData.meta);
29
-
30
- // console.log(full_dataset.PixelData);
31
-
32
- const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
33
- dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
34
-
35
- // console.log(dataset.PixelData);
36
-
37
- const dataset2 = DicomMetaDictionary.naturalizeDataset(dicomData2.dict);
38
- dataset2._meta = DicomMetaDictionary.namifyDataset(dicomData2.meta);
39
-
40
- // console.log(dataset2.PixelData);
41
-
42
- expect(full_dataset.PixelData).toEqual(dataset2.PixelData);
43
- expect(dataset.PixelData).toEqual(0);
44
- });