@radicalimaging/static-wado-creator 1.3.2 → 1.6.10

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/README.md +1 -1
  3. package/bin/mkdicomweb.mjs +8 -0
  4. package/dist/canvas-xaqc8v6y.node +0 -0
  5. package/dist/index.js +481518 -0
  6. package/lib/DeleteStudy.js +9 -2
  7. package/lib/RejectInstance.js +15 -3
  8. package/lib/StaticWado.js +245 -41
  9. package/lib/createPart10.js +8 -4
  10. package/lib/createThumbnail.js +181 -0
  11. package/lib/index.js +2 -0
  12. package/lib/instanceMain.js +1 -0
  13. package/lib/mkdicomwebConfig.js +122 -19
  14. package/lib/model/TagLists.js +24 -3
  15. package/lib/model/uids.js +5 -1
  16. package/lib/operation/InstanceDeduplicate.js +14 -4
  17. package/lib/operation/ScanStudy.js +38 -5
  18. package/lib/operation/StudyData.js +158 -38
  19. package/lib/operation/ThumbnailService.js +65 -31
  20. package/lib/operation/adapter/decodeImage.js +5 -2
  21. package/lib/operation/adapter/getImageInfo.js +19 -1
  22. package/lib/operation/adapter/transcodeImage.js +174 -52
  23. package/lib/operation/extractImageFrames.js +19 -4
  24. package/lib/operation/getDataSet.js +41 -7
  25. package/lib/operation/getEncapsulatedImageFrame.js +30 -6
  26. package/lib/operation/getUncompressedImageFrame.js +10 -2
  27. package/lib/operation/getVR.js +10 -2
  28. package/lib/operation/getValue.js +40 -12
  29. package/lib/operation/validateMetadata.js +40 -0
  30. package/lib/program/index.js +5 -3
  31. package/lib/serverMain.js +27 -0
  32. package/lib/util/IdCreator.js +32 -6
  33. package/lib/util/adaptProgramOpts.js +1 -1
  34. package/lib/util/compressionOptionParser.js +12 -3
  35. package/lib/util/resultsReport.ts +88 -0
  36. package/lib/writer/CompleteStudyWriter.js +49 -13
  37. package/lib/writer/DeduplicateWriter.js +2 -1
  38. package/lib/writer/ExpandUriPath.js +4 -1
  39. package/lib/writer/HashDataWriter.js +5 -1
  40. package/lib/writer/ImageFrameWriter.js +38 -13
  41. package/lib/writer/RawDicomWriter.js +11 -2
  42. package/lib/writer/VideoWriter.js +19 -7
  43. package/lib/writer/WriteMultipart.js +3 -1
  44. package/lib/writer/WriteStream.js +12 -4
  45. package/lib/writer/static-dicomweb.code-workspace +8 -0
  46. package/package.json +23 -19
  47. package/bin/mkdicomweb.js +0 -9
  48. package/license.md +0 -21
@@ -9,6 +9,7 @@ module.exports = function createMain(options, program) {
9
9
  isDeduplicate: true,
10
10
  isGroup: false,
11
11
  isStudyData: false,
12
+ isSkipStudyScan: true,
12
13
  });
13
14
  const importer = new StaticWado(finalOptions);
14
15
  return importer.executeCommand(program.args);
@@ -2,8 +2,10 @@ const ConfigPoint = require("config-point");
2
2
  const { staticWadoConfig } = require("@radicalimaging/static-wado-util");
3
3
  const createMain = require("./createMain");
4
4
  const createPart10 = require("./createPart10");
5
+ const createThumbnail = require("./createThumbnail");
5
6
  const deleteMain = require("./deleteMain");
6
7
  const rejectMain = require("./rejectMain");
8
+ const serverMain = require("./serverMain");
7
9
  const instanceMain = require("./instanceMain");
8
10
  const indexMain = require("./indexMain");
9
11
  const groupMain = require("./groupMain");
@@ -28,12 +30,14 @@ const { mkdicomwebConfig } = ConfigPoint.register({
28
30
  },
29
31
  {
30
32
  key: "--clean",
31
- description: "Clean the outputs before generating/starting to write new values.",
33
+ description:
34
+ "Clean the outputs before generating/starting to write new values.",
32
35
  defaultValue: false,
33
36
  },
34
37
  {
35
38
  key: "-d, --deployments <listvalue...>",
36
- description: "List of deployments from configuration to deploy to. Separated by space.",
39
+ description:
40
+ "List of deployments from configuration to deploy to. Separated by space.",
37
41
  defaultValue: undefined,
38
42
  },
39
43
  {
@@ -52,7 +56,8 @@ const { mkdicomwebConfig } = ConfigPoint.register({
52
56
  },
53
57
  {
54
58
  key: "--alternate <type>",
55
- description: "Generates an alternate representaton of the image generally in the /lossy sub-directory",
59
+ description:
60
+ "Generates an alternate representaton of the image generally in the /lossy sub-directory",
56
61
  choices: ["jhc", "jls", "jhcLossless", "jlsLossless"],
57
62
  },
58
63
  {
@@ -69,19 +74,26 @@ const { mkdicomwebConfig } = ConfigPoint.register({
69
74
  description: "Write verbose output",
70
75
  defaultValue: false,
71
76
  },
77
+ {
78
+ key: "--show-progress",
79
+ description: "Show progress during DICOM file processing",
80
+ },
72
81
  {
73
82
  key: "-t, --content-type <type>",
74
- description: 'Destination type to compress to (choices: "jpeg", "jls", "lei", "jls-lossy", "jhc", "jxl" or DICOM Transfer Syntax UID - default: "jls")',
83
+ description:
84
+ 'Destination type to compress to (choices: "jpeg", "jls", "lei", "jls-lossy", "jhc", "jxl" or DICOM Transfer Syntax UID - default: "jls")',
75
85
  defaultValue: "jls",
76
86
  customParser: compressionOptionParser,
77
87
  },
78
88
  {
79
89
  key: "--encapsulated-image",
80
- description: "Avoid encapsulating the image frame. Writes with the extension and without multipart",
90
+ description:
91
+ "Avoid encapsulating the image frame. Writes with the extension and without multipart",
81
92
  },
82
93
  {
83
94
  key: "-e, --no-encapsulated-image",
84
- description: "Avoid encapsulating the image frame. Writes with the extension and without multipart",
95
+ description:
96
+ "Avoid encapsulating the image frame. Writes with the extension and without multipart",
85
97
  },
86
98
  {
87
99
  key: "--single-part-image",
@@ -105,19 +117,50 @@ const { mkdicomwebConfig } = ConfigPoint.register({
105
117
  key: "-r, --recompress <listvalue...>",
106
118
  description: "List of types to recompress separated by space",
107
119
  defaultValue: ["uncompressed", "jp2", "jls", "jll"],
108
- choices: ["uncompressed", "jp2", "jpeg", "jpeglossless", "rle", "jph", "jls", "true", "none"],
120
+ choices: [
121
+ "uncompressed",
122
+ "jp2",
123
+ "jpeg",
124
+ "jpeglossless",
125
+ "rle",
126
+ "jph",
127
+ "jls",
128
+ "true",
129
+ "none",
130
+ ],
109
131
  },
110
132
  {
111
133
  key: "--recompress-color <listvalue...>",
112
- description: "List of types to recompress for color images, separated by space",
134
+ description:
135
+ "List of types to recompress for color images, separated by space",
113
136
  defaultValue: ["uncompressed"],
114
- choices: ["uncompressed", "jpeg", "jp2", "jpeglossless", "rle", "jph", "jls", "true", "none"],
137
+ choices: [
138
+ "uncompressed",
139
+ "jpeg",
140
+ "jp2",
141
+ "jpeglossless",
142
+ "rle",
143
+ "jph",
144
+ "jls",
145
+ "true",
146
+ "none",
147
+ ],
115
148
  },
116
149
  {
117
150
  key: "-f, --force",
118
151
  description: "Force the update even if the SOP exists",
119
152
  defaultValue: false,
120
153
  },
154
+ {
155
+ key: "--delete-input",
156
+ description: "Deletes successful input files",
157
+ defaultValue: false,
158
+ },
159
+ {
160
+ key: "--delete-failed",
161
+ description: "Deletes unsuccessful input files",
162
+ defaultValue: false,
163
+ },
121
164
  {
122
165
  key: "--recompress-thumb <listvalue...>",
123
166
  description: "List of types to recompress thumb separated by space",
@@ -135,8 +178,9 @@ const { mkdicomwebConfig } = ConfigPoint.register({
135
178
  defaultValue: false,
136
179
  },
137
180
  {
138
- key: "--thumb",
139
- description: "Generate thumbnails",
181
+ key: "--no-thumb",
182
+ description: "Skip generating thumbnails using CS3D internal render",
183
+ defaultValue: true,
140
184
  },
141
185
  {
142
186
  key: "--dcm2jpg",
@@ -155,7 +199,8 @@ const { mkdicomwebConfig } = ConfigPoint.register({
155
199
  },
156
200
  {
157
201
  key: "--path-deduplicated <path>",
158
- description: "Set the deduplicate data directory path (relative to dir)",
202
+ description:
203
+ "Set the deduplicate data directory path (relative to dir)",
159
204
  defaultValue: "deduplicated",
160
205
  },
161
206
  {
@@ -170,18 +215,55 @@ const { mkdicomwebConfig } = ConfigPoint.register({
170
215
  },
171
216
  {
172
217
  key: "--prepend-bulk-data-uri <value>",
173
- description: "Prepend bulkdata uri (ex. to use absolute Uri like http://host:3000/dicomweb)",
218
+ description:
219
+ "Prepend bulkdata uri (ex. to use absolute Uri like http://host:3000/dicomweb)",
174
220
  defaultValue: "",
175
221
  },
176
222
  {
177
223
  key: "--expand-bulk-data-uri",
178
- description: "expand bulkdata relative uri to use full relative path (should also be set when using --prepend-bulk-data-uri)",
224
+ description:
225
+ "expand bulkdata relative uri to use full relative path (should also be set when using --prepend-bulk-data-uri)",
179
226
  defaultValue: false,
180
227
  },
228
+ {
229
+ key: "--quiet",
230
+ description: "Run quietly, minimizing extraneous output",
231
+ defaultValue: false,
232
+ },
233
+ {
234
+ key: "--multipart",
235
+ description: "Add a multipart result output",
236
+ defaultValue: false,
237
+ },
238
+ {
239
+ key: "--no-verify",
240
+ description: "Verify",
241
+ defaultValue: true,
242
+ },
181
243
  {
182
244
  key: "-o, --dir <value>",
183
245
  description: "Set output directory",
184
- defaultValue: { configOperation: "reference", source: "staticWadoConfig", reference: "rootDir" },
246
+ defaultValue: {
247
+ configOperation: "reference",
248
+ source: "staticWadoConfig",
249
+ reference: "rootDir",
250
+ },
251
+ },
252
+ {
253
+ key: "--sop-instance-uid <value>",
254
+ description:
255
+ "Choose sop instances to run against (only for instance runs)",
256
+ },
257
+ {
258
+ key: "--series-instance-uid <value>",
259
+ description:
260
+ "Choose series instances to run against (only for instance runs)",
261
+ },
262
+
263
+ {
264
+ key: "--hash-study-uid-path",
265
+ description: "Enable hashing of studyUID folder structure",
266
+ defaultValue: false,
185
267
  },
186
268
  ],
187
269
  programs: [
@@ -195,6 +277,12 @@ const { mkdicomwebConfig } = ConfigPoint.register({
195
277
  "of deduplicated files each time a study instance UID is found, and only updates\n" +
196
278
  "those studies having at least one ",
197
279
  },
280
+ {
281
+ command: "server",
282
+ main: serverMain,
283
+ helpDescription:
284
+ "Run server deploy instances single threaded/separated exec process",
285
+ },
198
286
  {
199
287
  command: "part10",
200
288
  arguments: ["studyUID"],
@@ -206,28 +294,43 @@ const { mkdicomwebConfig } = ConfigPoint.register({
206
294
  main: indexMain,
207
295
  helpDescription: "Recreate the index for the DICOMweb data.",
208
296
  },
297
+ {
298
+ command: "thumbnail",
299
+ main: createThumbnail,
300
+ helpDescription: "Create a thumbnail for the specified image",
301
+ options: [
302
+ {
303
+ key: "--series-thumbnail",
304
+ description: "Generate a series thumbnail instead of instance",
305
+ },
306
+ ],
307
+ },
209
308
  {
210
309
  command: "instance",
211
310
  arguments: ["input"],
212
311
  main: instanceMain,
213
- helpDescription: "Make instance level DICOMweb metadata and bulkdata, but don't group or write series metadata",
312
+ helpDescription:
313
+ "Make instance level DICOMweb metadata and bulkdata, but don't group or write series metadata",
214
314
  },
215
315
  {
216
316
  command: "group",
217
317
  arguments: ["input"],
218
318
  main: groupMain,
219
- helpDescription: "Group instance level metadata into deduplicated data.\nDeletes instance level deduplicated information once it is confirmed written.",
319
+ helpDescription:
320
+ "Group instance level metadata into deduplicated data.\nDeletes instance level deduplicated information once it is confirmed written.",
220
321
  },
221
322
  {
222
323
  command: "metadata",
223
324
  arguments: ["input"],
224
325
  main: metadataMain,
225
- helpDescription: "Write the metadata object (series and study details) from the grouped deduplicated data.",
326
+ helpDescription:
327
+ "Write the metadata object (series and study details) from the grouped deduplicated data.",
226
328
  },
227
329
  {
228
330
  command: "delete",
229
331
  main: deleteMain,
230
- helpDescription: "Delete the given study, series or instance (not yet implemented)",
332
+ helpDescription:
333
+ "Delete the given study, series or instance (not yet implemented)",
231
334
  },
232
335
  {
233
336
  command: "reject <studyUID...>",
@@ -6,8 +6,20 @@ const { Tags } = require("@radicalimaging/static-wado-util");
6
6
  const hasher = hashFactory.hasher();
7
7
 
8
8
  const { PatientID, PatientName, IssuerOfPatientID } = Tags;
9
- const { StudyDescription, AccessionNumber, StudyInstanceUID, StudyDate, StudyTime } = Tags;
10
- const { SeriesDescription, SeriesNumber, SeriesInstanceUID, SeriesDate, SeriesTime } = Tags;
9
+ const {
10
+ StudyDescription,
11
+ AccessionNumber,
12
+ StudyInstanceUID,
13
+ StudyDate,
14
+ StudyTime,
15
+ } = Tags;
16
+ const {
17
+ SeriesDescription,
18
+ SeriesNumber,
19
+ SeriesInstanceUID,
20
+ SeriesDate,
21
+ SeriesTime,
22
+ } = Tags;
11
23
 
12
24
  const { DeduppedHash, DeduppedRef, DeduppedType } = Tags;
13
25
 
@@ -24,7 +36,16 @@ const PatientQuery = [
24
36
  Tags.PatientIdentityRemoved,
25
37
  Tags.DeidentificationMethodCodeSequence,
26
38
  ];
27
- const StudyQuery = [StudyDescription, AccessionNumber, StudyInstanceUID, StudyDate, StudyTime, Tags.StudyStatusID, Tags.StudyPriorityID, Tags.StudyID];
39
+ const StudyQuery = [
40
+ StudyDescription,
41
+ AccessionNumber,
42
+ StudyInstanceUID,
43
+ StudyDate,
44
+ StudyTime,
45
+ Tags.StudyStatusID,
46
+ Tags.StudyPriorityID,
47
+ Tags.StudyID,
48
+ ];
28
49
 
29
50
  const PatientStudyQuery = [...PatientQuery, ...StudyQuery];
30
51
 
package/lib/model/uids.js CHANGED
@@ -21,7 +21,11 @@ const uids = {
21
21
  "1.2.840.10008.1.2.1": uncompressed,
22
22
  "1.2.840.10008.1.2.1.99": uncompressed,
23
23
  "1.2.840.10008.1.2.2": uncompressed,
24
- "1.2.840.10008.1.2.4.50": { contentType: jpeg, lossy: true, extension: ".jpeg" },
24
+ "1.2.840.10008.1.2.4.50": {
25
+ contentType: jpeg,
26
+ lossy: true,
27
+ extension: ".jpeg",
28
+ },
25
29
  "1.2.840.10008.1.2.4.51": { contentType: jpeg, lossy: true },
26
30
  "1.2.840.10008.1.2.4.57": { contentType: jpeg },
27
31
  "1.2.840.10008.1.2.4.70": { contentType: jll, extension: ".jll" },
@@ -28,7 +28,8 @@ async function deduplicateSingleInstance(id, imageFrame, { force }) {
28
28
  return {};
29
29
  }
30
30
  if (!force && studyData.sopExists(sopUID)) {
31
- if (this.verbose) console.log("SOP Instance UID", sopUID, "already exists, skipping");
31
+ if (this.verbose)
32
+ console.log("SOP Instance UID", sopUID, "already exists, skipping");
32
33
  // TODO - allow replace as an option
33
34
  // Null value means skip writing this instance
34
35
  return null;
@@ -39,7 +40,12 @@ async function deduplicateSingleInstance(id, imageFrame, { force }) {
39
40
 
40
41
  if (!this.extractors) this.extractors = extractors;
41
42
  for (const key of Object.keys(this.extractors)) {
42
- const extracted = TagLists.extract(deduplicated, key, this.extractors[key], TagLists.RemoveExtract);
43
+ const extracted = TagLists.extract(
44
+ deduplicated,
45
+ key,
46
+ this.extractors[key],
47
+ TagLists.RemoveExtract,
48
+ );
43
49
  const hashKey = getValue(extracted, Tags.DeduppedHash);
44
50
  await studyData.addExtracted(this, hashKey, extracted);
45
51
  }
@@ -78,7 +84,7 @@ const InstanceDeduplicate = (options) =>
78
84
  // Notify the existing listeners, if any
79
85
  const imageFrame = canonicalize(sourceImageFrame);
80
86
  if (options.isInstance) {
81
- if (options.verbose) console.log("Writing instance metadata");
87
+ console.verbose("Writing instance metadata");
82
88
  await JSONWriter(id.sopInstanceRootPath, "metadata", imageFrame);
83
89
  }
84
90
  if (!options.isDeduplicate && !options.isGroup) {
@@ -90,7 +96,11 @@ const InstanceDeduplicate = (options) =>
90
96
  this.verbose = options.verbose;
91
97
  }
92
98
 
93
- const deduppedInstance = await this.deduplicateSingleInstance(id, imageFrame, options);
99
+ const deduppedInstance = await this.deduplicateSingleInstance(
100
+ id,
101
+ imageFrame,
102
+ options,
103
+ );
94
104
  if (deduppedInstance) {
95
105
  // this refers to callee
96
106
  await this.deduplicated(id, deduppedInstance);
@@ -1,14 +1,47 @@
1
+ const {
2
+ getStudyUIDPathAndSubPath,
3
+ } = require("@radicalimaging/static-wado-util");
4
+
1
5
  const path = require("path");
2
6
 
3
7
  function ScanStudy(options) {
4
- const { directoryName, deduplicatedRoot, deduplicatedInstancesRoot } = options;
8
+ const {
9
+ directoryName,
10
+ deduplicatedRoot,
11
+ deduplicatedInstancesRoot,
12
+ hashStudyUidPath,
13
+ } = options;
5
14
 
6
15
  return function scanStudy(studyInstanceUid) {
16
+ const { path: hashPath = "", subpath: hashSubpath = "" } = hashStudyUidPath
17
+ ? getStudyUIDPathAndSubPath(studyInstanceUid)
18
+ : {};
19
+
7
20
  console.verbose("scanStudy", studyInstanceUid);
8
- const studyPath = path.join(directoryName, "studies", studyInstanceUid);
9
- const deduplicatedInstancesPath = path.join(deduplicatedInstancesRoot, studyInstanceUid);
10
- const deduplicatedPath = path.join(deduplicatedRoot, studyInstanceUid);
11
- console.verbose("Importing", studyInstanceUid, studyPath, deduplicatedInstancesPath, deduplicatedPath);
21
+
22
+ if (hashPath && hashSubpath) {
23
+ console.verbose("hashPath", hashPath);
24
+ console.verbose("hashSubpath", hashSubpath);
25
+ }
26
+
27
+ const studySubDir = hashStudyUidPath
28
+ ? path.join(hashPath, hashSubpath, studyInstanceUid)
29
+ : studyInstanceUid;
30
+
31
+ const studyPath = path.join(directoryName, "studies", studySubDir);
32
+ const deduplicatedInstancesPath = path.join(
33
+ deduplicatedInstancesRoot,
34
+ // studyInstanceUid,
35
+ studySubDir,
36
+ );
37
+ const deduplicatedPath = path.join(deduplicatedRoot, studySubDir);
38
+ console.verbose(
39
+ "Importing",
40
+ studyInstanceUid,
41
+ studyPath,
42
+ deduplicatedInstancesPath,
43
+ deduplicatedPath,
44
+ );
12
45
  return this.completeStudy.getCurrentStudyData(this, {
13
46
  studyPath,
14
47
  deduplicatedPath,