@radicalimaging/static-wado-creator 1.5.0 → 1.6.11
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/CHANGELOG.md +234 -0
- package/README.md +1 -1
- package/bin/mkdicomweb.mjs +8 -0
- package/dist/canvas-xaqc8v6y.node +0 -0
- package/dist/index.js +511433 -0
- package/lib/DeleteStudy.js +11 -9
- package/lib/RejectInstance.js +6 -5
- package/lib/StaticWado.js +222 -73
- package/lib/createMain.js +3 -3
- package/lib/createPart10.js +19 -19
- package/lib/createThumbnail.js +163 -0
- package/lib/deleteMain.js +2 -2
- package/lib/groupMain.js +2 -2
- package/lib/index.js +7 -5
- package/lib/indexMain.js +2 -2
- package/lib/instanceMain.js +3 -2
- package/lib/metadataMain.js +2 -2
- package/lib/mkdicomwebConfig.js +210 -115
- package/lib/model/TagLists.js +13 -4
- package/lib/model/uids.js +32 -28
- package/lib/operation/InstanceDeduplicate.js +18 -13
- package/lib/operation/ScanStudy.js +33 -7
- package/lib/operation/StudyData.js +122 -71
- package/lib/operation/ThumbnailService.js +44 -36
- package/lib/operation/adapter/decodeImage.js +7 -4
- package/lib/operation/adapter/getImageInfo.js +23 -6
- package/lib/operation/adapter/transcodeImage.js +157 -90
- package/lib/operation/extractImageFrames.js +25 -14
- package/lib/operation/getDataSet.js +4 -4
- package/lib/operation/getEncapsulatedImageFrame.js +16 -4
- package/lib/operation/getNumberOfFrames.js +2 -2
- package/lib/operation/getVR.js +12 -4
- package/lib/operation/getValue.js +42 -65
- package/lib/operation/validateMetadata.js +40 -0
- package/lib/program/index.js +2 -3
- package/lib/rejectMain.js +2 -2
- package/lib/serverMain.js +21 -0
- package/lib/util/IdCreator.js +25 -8
- package/lib/util/adaptProgramOpts.js +1 -1
- package/lib/util/compressionOptionParser.js +4 -4
- package/lib/util/resultsReport.ts +87 -0
- package/lib/writer/CompleteStudyWriter.js +42 -19
- package/lib/writer/DeduplicateWriter.js +7 -7
- package/lib/writer/ExpandUriPath.js +4 -3
- package/lib/writer/HashDataWriter.js +21 -17
- package/lib/writer/ImageFrameWriter.js +22 -10
- package/lib/writer/MultipartHeader.js +1 -1
- package/lib/writer/RawDicomWriter.js +18 -10
- package/lib/writer/ThumbnailWriter.js +2 -2
- package/lib/writer/VideoWriter.js +23 -34
- package/lib/writer/WriteMultipart.js +3 -3
- package/lib/writer/WriteStream.js +14 -12
- package/package.json +22 -18
- package/bin/mkdicomweb.js +0 -9
package/lib/mkdicomwebConfig.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
const ConfigPoint = require(
|
|
2
|
-
const { staticWadoConfig } = require(
|
|
3
|
-
const createMain = require(
|
|
4
|
-
const createPart10 = require(
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
1
|
+
const ConfigPoint = require('config-point');
|
|
2
|
+
const { staticWadoConfig } = require('@radicalimaging/static-wado-util');
|
|
3
|
+
const createMain = require('./createMain');
|
|
4
|
+
const createPart10 = require('./createPart10');
|
|
5
|
+
const createThumbnail = require('./createThumbnail');
|
|
6
|
+
const deleteMain = require('./deleteMain');
|
|
7
|
+
const rejectMain = require('./rejectMain');
|
|
8
|
+
const serverMain = require('./serverMain');
|
|
9
|
+
const instanceMain = require('./instanceMain');
|
|
10
|
+
const indexMain = require('./indexMain');
|
|
11
|
+
const groupMain = require('./groupMain');
|
|
12
|
+
const metadataMain = require('./metadataMain');
|
|
13
|
+
const compressionOptionParser = require('./util/compressionOptionParser');
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Defines the basic configuration values for the dicomwebserver component. See the README for more details.
|
|
@@ -22,217 +24,310 @@ const { mkdicomwebConfig } = ConfigPoint.register({
|
|
|
22
24
|
isGroup: true,
|
|
23
25
|
options: [
|
|
24
26
|
{
|
|
25
|
-
key:
|
|
26
|
-
description:
|
|
27
|
-
defaultValue:
|
|
27
|
+
key: '-c <configFile.json5>',
|
|
28
|
+
description: 'Use an alternate configuration file',
|
|
29
|
+
defaultValue: '~/static-wado.json5',
|
|
28
30
|
},
|
|
29
31
|
{
|
|
30
|
-
key:
|
|
31
|
-
description:
|
|
32
|
+
key: '--clean',
|
|
33
|
+
description: 'Clean the outputs before generating/starting to write new values.',
|
|
32
34
|
defaultValue: false,
|
|
33
35
|
},
|
|
34
36
|
{
|
|
35
|
-
key:
|
|
36
|
-
description:
|
|
37
|
+
key: '-d, --deployments <listvalue...>',
|
|
38
|
+
description: 'List of deployments from configuration to deploy to. Separated by space.',
|
|
37
39
|
defaultValue: undefined,
|
|
38
40
|
},
|
|
39
41
|
{
|
|
40
|
-
key:
|
|
41
|
-
description:
|
|
42
|
+
key: '--no-notifications',
|
|
43
|
+
description: 'Turns off notifications',
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
|
-
key:
|
|
45
|
-
description:
|
|
46
|
+
key: '--delete',
|
|
47
|
+
description: 'Delete the imported instances',
|
|
46
48
|
defaultValue: false,
|
|
47
49
|
},
|
|
48
50
|
{
|
|
49
|
-
key:
|
|
50
|
-
description:
|
|
51
|
+
key: '--lossy',
|
|
52
|
+
description: 'Generate lossy instances instead of lossless',
|
|
51
53
|
defaultValue: false,
|
|
52
54
|
},
|
|
53
55
|
{
|
|
54
|
-
key:
|
|
55
|
-
description:
|
|
56
|
-
|
|
56
|
+
key: '--alternate <type>',
|
|
57
|
+
description:
|
|
58
|
+
'Generates an alternate representaton of the image generally in the /lossy sub-directory',
|
|
59
|
+
choices: ['jhc', 'jls', 'jhcLossless', 'jlsLossless'],
|
|
57
60
|
},
|
|
58
61
|
{
|
|
59
|
-
key:
|
|
60
|
-
description:
|
|
62
|
+
key: '--alternate-thumbnail',
|
|
63
|
+
description: 'Generates a thumbnail for the alternate representation',
|
|
61
64
|
},
|
|
62
65
|
{
|
|
63
|
-
key:
|
|
64
|
-
description:
|
|
65
|
-
defaultValue:
|
|
66
|
+
key: '--alternate-name <dir>',
|
|
67
|
+
description: 'Uses a given sub directory name',
|
|
68
|
+
defaultValue: 'lossy',
|
|
66
69
|
},
|
|
67
70
|
{
|
|
68
|
-
key:
|
|
69
|
-
description:
|
|
71
|
+
key: '-v, --verbose',
|
|
72
|
+
description: 'Write verbose output',
|
|
70
73
|
defaultValue: false,
|
|
71
74
|
},
|
|
72
75
|
{
|
|
73
|
-
key:
|
|
74
|
-
description: '
|
|
75
|
-
|
|
76
|
+
key: '--show-progress',
|
|
77
|
+
description: 'Show progress during DICOM file processing',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: '-t, --content-type <type>',
|
|
81
|
+
description:
|
|
82
|
+
'Destination type to compress to (choices: "jpeg", "jls", "lei", "jls-lossy", "jhc", "jxl" or DICOM Transfer Syntax UID - default: "jls")',
|
|
83
|
+
defaultValue: 'jls',
|
|
76
84
|
customParser: compressionOptionParser,
|
|
77
85
|
},
|
|
78
86
|
{
|
|
79
|
-
key:
|
|
80
|
-
description:
|
|
87
|
+
key: '--encapsulated-image',
|
|
88
|
+
description:
|
|
89
|
+
'Avoid encapsulating the image frame. Writes with the extension and without multipart',
|
|
81
90
|
},
|
|
82
91
|
{
|
|
83
|
-
key:
|
|
84
|
-
description:
|
|
92
|
+
key: '-e, --no-encapsulated-image',
|
|
93
|
+
description:
|
|
94
|
+
'Avoid encapsulating the image frame. Writes with the extension and without multipart',
|
|
85
95
|
},
|
|
86
96
|
{
|
|
87
|
-
key:
|
|
88
|
-
description:
|
|
97
|
+
key: '--single-part-image',
|
|
98
|
+
description: 'Writes single part image data',
|
|
89
99
|
},
|
|
90
100
|
{
|
|
91
|
-
key:
|
|
92
|
-
description:
|
|
101
|
+
key: '--no-single-part-image',
|
|
102
|
+
description: 'Writes single part image data',
|
|
93
103
|
},
|
|
94
104
|
{
|
|
95
|
-
key:
|
|
96
|
-
description:
|
|
105
|
+
key: '-m, --maximum-inline-public-length <value>',
|
|
106
|
+
description: 'Maximum length of public binary data',
|
|
97
107
|
defaultValue: 128 * 1024 + 2,
|
|
98
108
|
},
|
|
99
109
|
{
|
|
100
|
-
key:
|
|
101
|
-
description:
|
|
110
|
+
key: '-M, --maximum-inline-private-length <value>',
|
|
111
|
+
description: 'Maximum length of private binary data',
|
|
102
112
|
defaultValue: 64,
|
|
103
113
|
},
|
|
104
114
|
{
|
|
105
|
-
key:
|
|
106
|
-
description:
|
|
107
|
-
defaultValue: [
|
|
108
|
-
choices: [
|
|
115
|
+
key: '-r, --recompress <listvalue...>',
|
|
116
|
+
description: 'List of types to recompress separated by space',
|
|
117
|
+
defaultValue: ['uncompressed', 'jp2', 'jls', 'jll'],
|
|
118
|
+
choices: [
|
|
119
|
+
'uncompressed',
|
|
120
|
+
'jp2',
|
|
121
|
+
'jpeg',
|
|
122
|
+
'jpeglossless',
|
|
123
|
+
'rle',
|
|
124
|
+
'jph',
|
|
125
|
+
'jls',
|
|
126
|
+
'true',
|
|
127
|
+
'none',
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
key: '--recompress-color <listvalue...>',
|
|
132
|
+
description: 'List of types to recompress for color images, separated by space',
|
|
133
|
+
defaultValue: ['uncompressed'],
|
|
134
|
+
choices: [
|
|
135
|
+
'uncompressed',
|
|
136
|
+
'jpeg',
|
|
137
|
+
'jp2',
|
|
138
|
+
'jpeglossless',
|
|
139
|
+
'rle',
|
|
140
|
+
'jph',
|
|
141
|
+
'jls',
|
|
142
|
+
'true',
|
|
143
|
+
'none',
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
key: '-f, --force',
|
|
148
|
+
description: 'Force the update even if the SOP exists',
|
|
149
|
+
defaultValue: false,
|
|
109
150
|
},
|
|
110
151
|
{
|
|
111
|
-
key:
|
|
112
|
-
description:
|
|
113
|
-
defaultValue:
|
|
114
|
-
choices: ["uncompressed", "jpeg", "jp2", "jpeglossless", "rle", "jph", "jls", "true", "none"],
|
|
152
|
+
key: '--delete-input',
|
|
153
|
+
description: 'Deletes successful input files',
|
|
154
|
+
defaultValue: false,
|
|
115
155
|
},
|
|
116
156
|
{
|
|
117
|
-
key:
|
|
118
|
-
description:
|
|
157
|
+
key: '--delete-failed',
|
|
158
|
+
description: 'Deletes unsuccessful input files',
|
|
119
159
|
defaultValue: false,
|
|
120
160
|
},
|
|
121
161
|
{
|
|
122
|
-
key:
|
|
123
|
-
description:
|
|
124
|
-
defaultValue: [
|
|
125
|
-
choices: [
|
|
162
|
+
key: '--recompress-thumb <listvalue...>',
|
|
163
|
+
description: 'List of types to recompress thumb separated by space',
|
|
164
|
+
defaultValue: ['uncompressed', 'jp2'],
|
|
165
|
+
choices: ['uncompressed', 'jp2', 'jpeglossless', 'rle', 'none'],
|
|
126
166
|
},
|
|
127
167
|
{
|
|
128
|
-
key:
|
|
129
|
-
description:
|
|
168
|
+
key: '--no-recompress',
|
|
169
|
+
description: 'Force no recompression',
|
|
130
170
|
defaultValue: false,
|
|
131
171
|
},
|
|
132
172
|
{
|
|
133
|
-
key:
|
|
134
|
-
description:
|
|
173
|
+
key: '--no-recompress-thumb',
|
|
174
|
+
description: 'Force no recompression thumbnail',
|
|
135
175
|
defaultValue: false,
|
|
136
176
|
},
|
|
137
177
|
{
|
|
138
|
-
key:
|
|
139
|
-
description:
|
|
178
|
+
key: '--no-thumb',
|
|
179
|
+
description: 'Skip generating thumbnails using CS3D internal render',
|
|
180
|
+
defaultValue: true,
|
|
140
181
|
},
|
|
141
182
|
{
|
|
142
|
-
key:
|
|
143
|
-
description:
|
|
183
|
+
key: '--dcm2jpg',
|
|
184
|
+
description: 'Use dcm2jpg for thumbnail image generation',
|
|
144
185
|
defaultValue: false,
|
|
145
186
|
},
|
|
146
187
|
{
|
|
147
|
-
key:
|
|
148
|
-
description:
|
|
188
|
+
key: '--rendered',
|
|
189
|
+
description: 'Use dcm2jpg to generate a rendered PNG image',
|
|
149
190
|
defaultValue: false,
|
|
150
191
|
},
|
|
151
192
|
{
|
|
152
|
-
key:
|
|
153
|
-
description:
|
|
154
|
-
defaultValue:
|
|
193
|
+
key: '-T, --color-content-type <value>',
|
|
194
|
+
description: 'Colour content type',
|
|
195
|
+
defaultValue: 'jpeg',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
key: '--path-deduplicated <path>',
|
|
199
|
+
description: 'Set the deduplicate data directory path (relative to dir)',
|
|
200
|
+
defaultValue: 'deduplicated',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
key: '--path-instances <path>',
|
|
204
|
+
description: 'Set the instances directory path (relative to dir)',
|
|
205
|
+
defaultValue: 'instances',
|
|
155
206
|
},
|
|
156
207
|
{
|
|
157
|
-
key:
|
|
158
|
-
description:
|
|
159
|
-
defaultValue:
|
|
208
|
+
key: '--store-multipart-bulk-data',
|
|
209
|
+
description: 'Use multipart encoding for non image frame bulkdata',
|
|
210
|
+
defaultValue: false,
|
|
160
211
|
},
|
|
161
212
|
{
|
|
162
|
-
key:
|
|
163
|
-
description:
|
|
164
|
-
|
|
213
|
+
key: '--prepend-bulk-data-uri <value>',
|
|
214
|
+
description:
|
|
215
|
+
'Prepend bulkdata uri (ex. to use absolute Uri like http://host:3000/dicomweb)',
|
|
216
|
+
defaultValue: '',
|
|
165
217
|
},
|
|
166
218
|
{
|
|
167
|
-
key:
|
|
168
|
-
description:
|
|
219
|
+
key: '--expand-bulk-data-uri',
|
|
220
|
+
description:
|
|
221
|
+
'expand bulkdata relative uri to use full relative path (should also be set when using --prepend-bulk-data-uri)',
|
|
169
222
|
defaultValue: false,
|
|
170
223
|
},
|
|
171
224
|
{
|
|
172
|
-
key:
|
|
173
|
-
description:
|
|
174
|
-
defaultValue:
|
|
225
|
+
key: '--quiet',
|
|
226
|
+
description: 'Run quietly, minimizing extraneous output',
|
|
227
|
+
defaultValue: false,
|
|
175
228
|
},
|
|
176
229
|
{
|
|
177
|
-
key:
|
|
178
|
-
description:
|
|
230
|
+
key: '--multipart',
|
|
231
|
+
description: 'Add a multipart result output',
|
|
179
232
|
defaultValue: false,
|
|
180
233
|
},
|
|
181
234
|
{
|
|
182
|
-
key:
|
|
183
|
-
description:
|
|
184
|
-
defaultValue:
|
|
235
|
+
key: '--no-verify',
|
|
236
|
+
description: 'Verify',
|
|
237
|
+
defaultValue: true,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
key: '-o, --dir <value>',
|
|
241
|
+
description: 'Set output directory',
|
|
242
|
+
defaultValue: {
|
|
243
|
+
configOperation: 'reference',
|
|
244
|
+
source: 'staticWadoConfig',
|
|
245
|
+
reference: 'rootDir',
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
key: '--sop-instance-uid <value>',
|
|
250
|
+
description: 'Choose sop instances to run against (only for instance runs)',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: '--series-instance-uid <value>',
|
|
254
|
+
description: 'Choose series instances to run against (only for instance runs)',
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
{
|
|
258
|
+
key: '--hash-study-uid-path',
|
|
259
|
+
description: 'Enable hashing of studyUID folder structure',
|
|
260
|
+
defaultValue: false,
|
|
185
261
|
},
|
|
186
262
|
],
|
|
187
263
|
programs: [
|
|
188
264
|
{
|
|
189
|
-
command:
|
|
265
|
+
command: 'create',
|
|
190
266
|
isDefault: true,
|
|
191
|
-
arguments: [
|
|
267
|
+
arguments: ['input'],
|
|
192
268
|
main: createMain,
|
|
193
269
|
helpDescription:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
270
|
+
'Make DICOMweb query and metadata from binary Part 10 DICOM files. Does a full read\n' +
|
|
271
|
+
'of deduplicated files each time a study instance UID is found, and only updates\n' +
|
|
272
|
+
'those studies having at least one ',
|
|
197
273
|
},
|
|
198
274
|
{
|
|
199
|
-
command:
|
|
200
|
-
|
|
275
|
+
command: 'server',
|
|
276
|
+
main: serverMain,
|
|
277
|
+
helpDescription: 'Run server deploy instances single threaded/separated exec process',
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
command: 'part10',
|
|
281
|
+
arguments: ['studyUID'],
|
|
201
282
|
main: createPart10,
|
|
202
|
-
helpDescription:
|
|
283
|
+
helpDescription: 'Store the specified study as part 10 data',
|
|
203
284
|
},
|
|
204
285
|
{
|
|
205
|
-
command:
|
|
286
|
+
command: 'index',
|
|
206
287
|
main: indexMain,
|
|
207
|
-
helpDescription:
|
|
288
|
+
helpDescription: 'Recreate the index for the DICOMweb data.',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
command: 'thumbnail',
|
|
292
|
+
main: createThumbnail,
|
|
293
|
+
helpDescription: 'Create a thumbnail for the specified image',
|
|
294
|
+
options: [
|
|
295
|
+
{
|
|
296
|
+
key: '--series-thumbnail',
|
|
297
|
+
description: 'Generate a series thumbnail instead of instance',
|
|
298
|
+
},
|
|
299
|
+
],
|
|
208
300
|
},
|
|
209
301
|
{
|
|
210
|
-
command:
|
|
211
|
-
arguments: [
|
|
302
|
+
command: 'instance',
|
|
303
|
+
arguments: ['input'],
|
|
212
304
|
main: instanceMain,
|
|
213
|
-
helpDescription:
|
|
305
|
+
helpDescription:
|
|
306
|
+
"Make instance level DICOMweb metadata and bulkdata, but don't group or write series metadata",
|
|
214
307
|
},
|
|
215
308
|
{
|
|
216
|
-
command:
|
|
217
|
-
arguments: [
|
|
309
|
+
command: 'group',
|
|
310
|
+
arguments: ['input'],
|
|
218
311
|
main: groupMain,
|
|
219
|
-
helpDescription:
|
|
312
|
+
helpDescription:
|
|
313
|
+
'Group instance level metadata into deduplicated data.\nDeletes instance level deduplicated information once it is confirmed written.',
|
|
220
314
|
},
|
|
221
315
|
{
|
|
222
|
-
command:
|
|
223
|
-
arguments: [
|
|
316
|
+
command: 'metadata',
|
|
317
|
+
arguments: ['input'],
|
|
224
318
|
main: metadataMain,
|
|
225
|
-
helpDescription:
|
|
319
|
+
helpDescription:
|
|
320
|
+
'Write the metadata object (series and study details) from the grouped deduplicated data.',
|
|
226
321
|
},
|
|
227
322
|
{
|
|
228
|
-
command:
|
|
323
|
+
command: 'delete',
|
|
229
324
|
main: deleteMain,
|
|
230
|
-
helpDescription:
|
|
325
|
+
helpDescription: 'Delete the given study, series or instance (not yet implemented)',
|
|
231
326
|
},
|
|
232
327
|
{
|
|
233
|
-
command:
|
|
328
|
+
command: 'reject <studyUID...>',
|
|
234
329
|
main: rejectMain,
|
|
235
|
-
helpDescription:
|
|
330
|
+
helpDescription: 'Reject the specified series',
|
|
236
331
|
},
|
|
237
332
|
],
|
|
238
333
|
},
|
package/lib/model/TagLists.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const hashFactory = require(
|
|
2
|
-
const { Tags } = require(
|
|
1
|
+
const hashFactory = require('node-object-hash');
|
|
2
|
+
const { Tags } = require('@radicalimaging/static-wado-util');
|
|
3
3
|
|
|
4
4
|
/* eslint "no-param-reassign": "off" */
|
|
5
5
|
|
|
@@ -24,7 +24,16 @@ const PatientQuery = [
|
|
|
24
24
|
Tags.PatientIdentityRemoved,
|
|
25
25
|
Tags.DeidentificationMethodCodeSequence,
|
|
26
26
|
];
|
|
27
|
-
const StudyQuery = [
|
|
27
|
+
const StudyQuery = [
|
|
28
|
+
StudyDescription,
|
|
29
|
+
AccessionNumber,
|
|
30
|
+
StudyInstanceUID,
|
|
31
|
+
StudyDate,
|
|
32
|
+
StudyTime,
|
|
33
|
+
Tags.StudyStatusID,
|
|
34
|
+
Tags.StudyPriorityID,
|
|
35
|
+
Tags.StudyID,
|
|
36
|
+
];
|
|
28
37
|
|
|
29
38
|
const PatientStudyQuery = [...PatientQuery, ...StudyQuery];
|
|
30
39
|
|
|
@@ -132,7 +141,7 @@ const TagSets = {
|
|
|
132
141
|
extract: (data, type, tagSet, options) => {
|
|
133
142
|
const ret = {};
|
|
134
143
|
const { remove, hashRef } = options || {};
|
|
135
|
-
tagSet.forEach(
|
|
144
|
+
tagSet.forEach(tag => {
|
|
136
145
|
const value = data[tag];
|
|
137
146
|
if (value) {
|
|
138
147
|
ret[tag] = value;
|
package/lib/model/uids.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const octetStream =
|
|
1
|
+
const octetStream = 'application/octet-stream';
|
|
2
2
|
const uncompressed = {
|
|
3
3
|
uncompressed: true,
|
|
4
4
|
gzip: true,
|
|
@@ -8,37 +8,41 @@ const uncompressed = {
|
|
|
8
8
|
/**
|
|
9
9
|
* @see {@link https://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_8.7.3.3.2.html}
|
|
10
10
|
*/
|
|
11
|
-
const jpeg =
|
|
12
|
-
const jls =
|
|
13
|
-
const jll =
|
|
14
|
-
const jxl =
|
|
15
|
-
const htj2k =
|
|
16
|
-
const jp2 =
|
|
17
|
-
const rle =
|
|
11
|
+
const jpeg = 'image/jpeg';
|
|
12
|
+
const jls = 'image/jls';
|
|
13
|
+
const jll = 'image/jll';
|
|
14
|
+
const jxl = 'image/x-jxl';
|
|
15
|
+
const htj2k = 'image/jphc';
|
|
16
|
+
const jp2 = 'image/jp2';
|
|
17
|
+
const rle = 'image/dicom-rle';
|
|
18
18
|
|
|
19
19
|
const uids = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
'1.2.840.10008.1.2': uncompressed,
|
|
21
|
+
'1.2.840.10008.1.2.1': uncompressed,
|
|
22
|
+
'1.2.840.10008.1.2.1.99': uncompressed,
|
|
23
|
+
'1.2.840.10008.1.2.2': uncompressed,
|
|
24
|
+
'1.2.840.10008.1.2.4.50': {
|
|
25
|
+
contentType: jpeg,
|
|
26
|
+
lossy: true,
|
|
27
|
+
extension: '.jpeg',
|
|
28
|
+
},
|
|
29
|
+
'1.2.840.10008.1.2.4.51': { contentType: jpeg, lossy: true },
|
|
30
|
+
'1.2.840.10008.1.2.4.57': { contentType: jpeg },
|
|
31
|
+
'1.2.840.10008.1.2.4.70': { contentType: jll, extension: '.jll' },
|
|
32
|
+
'1.2.840.10008.1.2.4.140': { contentType: jxl, extension: '.jxl' },
|
|
29
33
|
// Lossy, original JPEG reconstruction mode
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
'1.2.840.10008.1.2.4.141': { contentType: jxl, lossy: true },
|
|
35
|
+
'1.2.840.10008.1.2.4.142': { contentType: jxl, lossy: true },
|
|
36
|
+
'1.2.840.10008.1.2.4.80': { contentType: jls, extension: '.jls' },
|
|
37
|
+
'1.2.840.10008.1.2.4.81': { contentType: jls, lossy: true },
|
|
38
|
+
'1.2.840.10008.1.2.4.90': { contentType: jp2, extension: '.jp2' },
|
|
39
|
+
'1.2.840.10008.1.2.4.91': { contentType: jp2 },
|
|
36
40
|
// Private/test transfer syntax for HTJ2K
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
'3.2.840.10008.1.2.4.96': { contentType: htj2k },
|
|
42
|
+
'1.2.840.10008.1.2.4.201': { contentType: htj2k },
|
|
43
|
+
'1.2.840.10008.1.2.4.202': { contentType: htj2k, extension: '.jhc' },
|
|
44
|
+
'1.2.840.10008.1.2.4.203': { contentType: htj2k, lossy: true },
|
|
45
|
+
'1.2.840.10008.1.2.5': { contentType: rle },
|
|
42
46
|
default: {},
|
|
43
47
|
};
|
|
44
48
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// TODO review returning type of some methods
|
|
2
|
-
const { JSONWriter } = require(
|
|
3
|
-
const { Tags } = require(
|
|
4
|
-
const TagLists = require(
|
|
2
|
+
const { JSONWriter } = require('@radicalimaging/static-wado-util');
|
|
3
|
+
const { Tags } = require('@radicalimaging/static-wado-util');
|
|
4
|
+
const TagLists = require('../model/TagLists');
|
|
5
5
|
|
|
6
6
|
const { getValue, setValue } = Tags;
|
|
7
7
|
|
|
@@ -24,22 +24,27 @@ async function deduplicateSingleInstance(id, imageFrame, { force }) {
|
|
|
24
24
|
const seriesUID = getValue(imageFrame, Tags.SeriesInstanceUID);
|
|
25
25
|
const sopUID = getValue(imageFrame, Tags.SOPInstanceUID);
|
|
26
26
|
if (!sopUID) {
|
|
27
|
-
console.warn(
|
|
27
|
+
console.warn('No sop instance UID in', imageFrame);
|
|
28
28
|
return {};
|
|
29
29
|
}
|
|
30
30
|
if (!force && studyData.sopExists(sopUID)) {
|
|
31
|
-
if (this.verbose) console.log(
|
|
31
|
+
if (this.verbose) console.log('SOP Instance UID', sopUID, 'already exists, skipping');
|
|
32
32
|
// TODO - allow replace as an option
|
|
33
33
|
// Null value means skip writing this instance
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
if (this.verbose) console.log(
|
|
37
|
+
if (this.verbose) console.log('SOP Instance UID', sopUID, 'being added');
|
|
38
38
|
const deduplicated = { ...imageFrame };
|
|
39
39
|
|
|
40
40
|
if (!this.extractors) this.extractors = extractors;
|
|
41
41
|
for (const key of Object.keys(this.extractors)) {
|
|
42
|
-
const extracted = TagLists.extract(
|
|
42
|
+
const extracted = TagLists.extract(
|
|
43
|
+
deduplicated,
|
|
44
|
+
key,
|
|
45
|
+
this.extractors[key],
|
|
46
|
+
TagLists.RemoveExtract
|
|
47
|
+
);
|
|
43
48
|
const hashKey = getValue(extracted, Tags.DeduppedHash);
|
|
44
49
|
await studyData.addExtracted(this, hashKey, extracted);
|
|
45
50
|
}
|
|
@@ -53,13 +58,13 @@ async function deduplicateSingleInstance(id, imageFrame, { force }) {
|
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
/** Canonicalize the JSON data, making Values always arrays, remote "undefined" from the tags etc */
|
|
56
|
-
const canonicalize =
|
|
61
|
+
const canonicalize = json => {
|
|
57
62
|
if (!json) return undefined;
|
|
58
63
|
if (Array.isArray(json)) {
|
|
59
64
|
return json.map(canonicalize);
|
|
60
65
|
}
|
|
61
|
-
Object.keys(json).forEach(
|
|
62
|
-
if (!tag || tag ===
|
|
66
|
+
Object.keys(json).forEach(tag => {
|
|
67
|
+
if (!tag || tag === 'undefined') {
|
|
63
68
|
// console.error('Got an undefined tag:', tag, typeof(tag));
|
|
64
69
|
// TODO check returning value
|
|
65
70
|
return;
|
|
@@ -73,13 +78,13 @@ const canonicalize = (json) => {
|
|
|
73
78
|
return json;
|
|
74
79
|
};
|
|
75
80
|
|
|
76
|
-
const InstanceDeduplicate =
|
|
81
|
+
const InstanceDeduplicate = options =>
|
|
77
82
|
async function run(id, sourceImageFrame) {
|
|
78
83
|
// Notify the existing listeners, if any
|
|
79
84
|
const imageFrame = canonicalize(sourceImageFrame);
|
|
80
85
|
if (options.isInstance) {
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
await JSONWriter(id.sopInstanceRootPath, 'metadata', imageFrame);
|
|
87
|
+
console.verbose('Wrote instance metadata', id.sopInstanceRootPath, 'metadata');
|
|
83
88
|
}
|
|
84
89
|
if (!options.isDeduplicate && !options.isGroup) {
|
|
85
90
|
return;
|