@radicalimaging/static-wado-creator 0.6.4 → 1.1.0
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 +67 -0
- package/README.md +3 -1
- package/lib/DeleteStudy.js +3 -3
- package/lib/StaticWado.js +24 -2
- package/lib/index.js +1 -0
- package/lib/indexMain.js +10 -0
- package/lib/mkdicomwebConfig.js +19 -4
- package/lib/model/TagLists.js +7 -7
- package/lib/model/uids.js +9 -10
- package/lib/operation/InstanceDeduplicate.js +8 -8
- package/lib/operation/StudyData.js +24 -23
- package/lib/operation/ThumbnailService.js +1 -1
- package/lib/operation/adapter/transcodeImage.js +33 -16
- package/lib/operation/getDataSet.js +1 -1
- package/lib/operation/getVR.js +1 -1
- package/lib/util/adaptProgramOpts.js +2 -0
- package/lib/util/compressionOptionParser.js +3 -3
- package/lib/writer/CompleteStudyWriter.js +4 -6
- package/lib/writer/DeduplicateWriter.js +2 -2
- package/lib/writer/HashDataWriter.js +2 -2
- package/lib/writer/ImageFrameWriter.js +27 -8
- package/lib/writer/RawDicomWriter.js +36 -0
- package/lib/writer/VideoWriter.js +1 -1
- package/package.json +8 -7
- package/lib/dictionary/Tags.js +0 -111
- package/lib/dictionary/dataDictionary.js +0 -22268
- package/lib/operation/adapter/asyncIterableToBuffer.js +0 -112
- package/lib/writer/JSONWriter.js +0 -18
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.6.4](https://github.com/OHIF/static-wado/compare/@radicalimaging/static-wado-creator@0.6.3...@radicalimaging/static-wado-creator@0.6.4) (2022-04-04)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @radicalimaging/static-wado-creator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.6.3](https://github.com/OHIF/static-wado/compare/@radicalimaging/static-wado-creator@0.6.2...@radicalimaging/static-wado-creator@0.6.3) (2022-03-14)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @radicalimaging/static-wado-creator
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [0.6.2](https://github.com/OHIF/static-wado/compare/@radicalimaging/static-wado-creator@0.6.1...@radicalimaging/static-wado-creator@0.6.2) (2022-03-04)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package @radicalimaging/static-wado-creator
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [0.6.1](https://github.com/OHIF/static-wado/compare/@radicalimaging/static-wado-creator@0.6.0...@radicalimaging/static-wado-creator@0.6.1) (2022-03-04)
|
|
31
|
+
|
|
32
|
+
**Note:** Version bump only for package @radicalimaging/static-wado-creator
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# [0.6.0](https://github.com/OHIF/static-wado/compare/@radicalimaging/static-wado-creator@0.5.1...@radicalimaging/static-wado-creator@0.6.0) (2022-03-03)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Bug Fixes
|
|
42
|
+
|
|
43
|
+
* **static-wado:** make the various constructors use a straight configuration without using the program data ([1997d9f](https://github.com/OHIF/static-wado/commit/1997d9f0fe2e0a084d31edeb475494bcec78fd77))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Features
|
|
47
|
+
|
|
48
|
+
* **static-wado:** qido series fallback to retrieve from proxyAe ([#8](https://github.com/OHIF/static-wado/issues/8)) ([47b5550](https://github.com/OHIF/static-wado/commit/47b55503732e25be08b215bdc201593f64de52e6))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## 0.5.1 (2022-02-16)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Bug Fixes
|
|
58
|
+
|
|
59
|
+
* **pencil:** add more files to lint, increase test config and README ([89b667c](https://github.com/OHIF/static-wado/commit/89b667c83d324ab9fa540cda0c037af8fe088f72))
|
|
60
|
+
* **pencil:** adding must package and minor test changes ([9194f3f](https://github.com/OHIF/static-wado/commit/9194f3f1bb52da57e20bb8bb9f07262bcebdffbf))
|
|
61
|
+
* **pencil:** code review.lint ([fa1a58a](https://github.com/OHIF/static-wado/commit/fa1a58a0c62015503575fbeb172b71810c6833de))
|
|
62
|
+
* **pencil:** fix format ([68fbc9b](https://github.com/OHIF/static-wado/commit/68fbc9bf5a3e9bf85e3fbcddfb3e0759e79b769d))
|
|
63
|
+
* **pencil:** fix lint and add pre-commit restr ([deec345](https://github.com/OHIF/static-wado/commit/deec34524531d5a8595a775bac414f63f60e9f23))
|
|
64
|
+
* **pencil:** lint/prettier ([d6ecbc6](https://github.com/OHIF/static-wado/commit/d6ecbc6b5961e03e8a557f4fcc78af53549132cd))
|
|
65
|
+
* **pencil:** moving from wado-js to wado repo ([5916aec](https://github.com/OHIF/static-wado/commit/5916aecd7c77dbc4882681877e2b51210976427f))
|
|
66
|
+
* **pencil:** readme link name ([40408ab](https://github.com/OHIF/static-wado/commit/40408ab6b4e97c8656e30b1dd2c30b92440b9f90))
|
|
67
|
+
* **pencil:** unit tests migration ([d9d56dd](https://github.com/OHIF/static-wado/commit/d9d56dd619fb1f41ed1d5ad85f7f804e013c6527))
|
package/README.md
CHANGED
|
@@ -158,11 +158,13 @@ There is currently no notification of what studies have been updated between sta
|
|
|
158
158
|
### To Serve Instances As a Web Server
|
|
159
159
|
```
|
|
160
160
|
cd ~/dicomweb
|
|
161
|
-
npx http-server -p 5000 -g --cors
|
|
161
|
+
npx http-server -p 5000 -g --cors -e json
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
The -g option serves up compressed files ending in .gz as compressed http streams.
|
|
165
165
|
|
|
166
|
+
The -e option will mark json files as the default extension, so when requesting `GET dicomweb/studies/` the `./studies/index.json.gz` file will be served.
|
|
167
|
+
|
|
166
168
|
### To Create DICOM part 10 from DICOMweb files
|
|
167
169
|
TODO
|
|
168
170
|
|
package/lib/DeleteStudy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { JSONReader } = require("@radicalimaging/static-wado-util");
|
|
2
|
-
const JSONWriter = require("
|
|
3
|
-
const Tags = require("
|
|
2
|
+
const { JSONWriter } = require("@radicalimaging/static-wado-util");
|
|
3
|
+
const { Tags } = require("@radicalimaging/static-wado-util");
|
|
4
4
|
|
|
5
5
|
module.exports = (options) =>
|
|
6
6
|
async function (studyInstanceUid) {
|
|
@@ -8,7 +8,7 @@ module.exports = (options) =>
|
|
|
8
8
|
const studyData = await this.scanStudy("studies", studyInstanceUid);
|
|
9
9
|
studyData.delete();
|
|
10
10
|
const allStudies = await JSONReader(options.directoryName, "studies/index.json.gz", []);
|
|
11
|
-
const studiesWithoutDeleted = allStudies.filter((study) => studyInstanceUid != Tags.getValue(study,Tags.StudyInstanceUID));
|
|
11
|
+
const studiesWithoutDeleted = allStudies.filter((study) => studyInstanceUid != Tags.getValue(study, Tags.StudyInstanceUID));
|
|
12
12
|
await JSONWriter(options.directoryName, "studies", studiesWithoutDeleted);
|
|
13
13
|
delete this.studyData;
|
|
14
14
|
};
|
package/lib/StaticWado.js
CHANGED
|
@@ -3,8 +3,7 @@ const { Stats, handleHomeRelative } = require("@radicalimaging/static-wado-util"
|
|
|
3
3
|
const dicomParser = require("dicom-parser");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const { dirScanner } = require("@radicalimaging/static-wado-util");
|
|
7
|
-
const asyncIterableToBuffer = require("./operation/adapter/asyncIterableToBuffer");
|
|
6
|
+
const { dirScanner, JSONReader, JSONWriter, asyncIterableToBuffer } = require("@radicalimaging/static-wado-util");
|
|
8
7
|
const getDataSet = require("./operation/getDataSet");
|
|
9
8
|
const InstanceDeduplicate = require("./operation/InstanceDeduplicate");
|
|
10
9
|
const DeduplicateWriter = require("./writer/DeduplicateWriter");
|
|
@@ -19,6 +18,7 @@ const ThumbnailWriter = require("./writer/ThumbnailWriter");
|
|
|
19
18
|
const ThumbnailService = require("./operation/ThumbnailService");
|
|
20
19
|
const DeleteStudy = require("./DeleteStudy");
|
|
21
20
|
const RejectInstance = require("./RejectInstance");
|
|
21
|
+
const RawDicomWriter = require("./writer/RawDicomWriter");
|
|
22
22
|
|
|
23
23
|
function setStudyData(studyData) {
|
|
24
24
|
this.studyData = studyData;
|
|
@@ -51,6 +51,7 @@ class StaticWado {
|
|
|
51
51
|
reject: RejectInstance(this.options),
|
|
52
52
|
delete: DeleteStudy(this.options),
|
|
53
53
|
setStudyData,
|
|
54
|
+
rawDicomWriter: RawDicomWriter(this.options),
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -174,6 +175,8 @@ class StaticWado {
|
|
|
174
175
|
// convert to DICOMweb MetaData and BulkData
|
|
175
176
|
const result = await getDataSet(dataSet, generator, this.options);
|
|
176
177
|
|
|
178
|
+
await this.callback.rawDicomWriter?.(id, result, buffer);
|
|
179
|
+
|
|
177
180
|
const transcodedMeta = transcodeMetadata(result.metadata, id, this.options);
|
|
178
181
|
thumbnailService.generateThumbnails(id, dataSet, transcodedMeta, this.callback);
|
|
179
182
|
|
|
@@ -202,5 +205,24 @@ class StaticWado {
|
|
|
202
205
|
await this.callback.completeStudy();
|
|
203
206
|
Stats.OverallStats.summarize("Completed Study Processing");
|
|
204
207
|
}
|
|
208
|
+
|
|
209
|
+
async reindex() {
|
|
210
|
+
const { directoryName } = this.options;
|
|
211
|
+
const studiesDir = `${directoryName}/studies/`;
|
|
212
|
+
|
|
213
|
+
console.log("Re-indexing", studiesDir);
|
|
214
|
+
const dirs = await fs.promises.readdir(studiesDir);
|
|
215
|
+
const studies = [];
|
|
216
|
+
for (const dir of dirs) {
|
|
217
|
+
const study = await JSONReader(`${studiesDir}/${dir}`, "index.json.gz", null);
|
|
218
|
+
if (study === null) {
|
|
219
|
+
console.log("No study found in", dir);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
console.log("Adding study", dir);
|
|
223
|
+
studies.push(...study);
|
|
224
|
+
}
|
|
225
|
+
return JSONWriter(directoryName, "studies", studies);
|
|
226
|
+
}
|
|
205
227
|
}
|
|
206
228
|
module.exports = StaticWado;
|
package/lib/index.js
CHANGED
|
@@ -4,5 +4,6 @@ const StaticWado = require("./StaticWado");
|
|
|
4
4
|
StaticWado.mkdicomwebConfig = mkdicomwebConfig;
|
|
5
5
|
StaticWado.createMain = require("./createMain");
|
|
6
6
|
StaticWado.deleteMain = require("./deleteMain");
|
|
7
|
+
StaticWado.adaptProgramOpts = require("./util/adaptProgramOpts");
|
|
7
8
|
|
|
8
9
|
module.exports = StaticWado;
|
package/lib/indexMain.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const StaticWado = require("./StaticWado");
|
|
2
|
+
const adaptProgramOpts = require("./util/adaptProgramOpts");
|
|
3
|
+
|
|
4
|
+
module.exports = function createMain(options) {
|
|
5
|
+
const finalOptions = adaptProgramOpts(options, {
|
|
6
|
+
...this,
|
|
7
|
+
});
|
|
8
|
+
const importer = new StaticWado(finalOptions);
|
|
9
|
+
return importer.reindex();
|
|
10
|
+
};
|
package/lib/mkdicomwebConfig.js
CHANGED
|
@@ -4,6 +4,7 @@ const createMain = require("./createMain");
|
|
|
4
4
|
const deleteMain = require("./deleteMain");
|
|
5
5
|
const rejectMain = require("./rejectMain");
|
|
6
6
|
const instanceMain = require("./instanceMain");
|
|
7
|
+
const indexMain = require("./indexMain");
|
|
7
8
|
const groupMain = require("./groupMain");
|
|
8
9
|
const metadataMain = require("./metadataMain");
|
|
9
10
|
const compressionOptionParser = require("./util/compressionOptionParser");
|
|
@@ -24,6 +25,11 @@ const { mkdicomwebConfig } = ConfigPoint.register({
|
|
|
24
25
|
description: "Clean the outputs before generating/starting to write new values.",
|
|
25
26
|
defaultValue: false,
|
|
26
27
|
},
|
|
28
|
+
{
|
|
29
|
+
key: "-d, --deployments <listvalue...>",
|
|
30
|
+
description: "List of deployments from configuration to deploy to. Separated by space.",
|
|
31
|
+
defaultValue: undefined,
|
|
32
|
+
},
|
|
27
33
|
{
|
|
28
34
|
key: "-v, --verbose",
|
|
29
35
|
description: "Write verbose output",
|
|
@@ -31,10 +37,14 @@ const { mkdicomwebConfig } = ConfigPoint.register({
|
|
|
31
37
|
},
|
|
32
38
|
{
|
|
33
39
|
key: "-t, --content-type <type>",
|
|
34
|
-
description: 'Destination type to compress to (choices: "jls", "lei", "jls-lossy" or DICOM Transfer Syntax UID - default: "jls")',
|
|
35
|
-
defaultValue: "
|
|
40
|
+
description: 'Destination type to compress to (choices: "jls", "lei", "jls-lossy", "jhc", "jxl" or DICOM Transfer Syntax UID - default: "jls")',
|
|
41
|
+
defaultValue: "jhc",
|
|
36
42
|
customParser: compressionOptionParser,
|
|
37
43
|
},
|
|
44
|
+
{
|
|
45
|
+
key: "-e, --no-encapsulated-image-frame",
|
|
46
|
+
description: "Avoid encapsulating the image frame. Writes with the extension and without multipart",
|
|
47
|
+
},
|
|
38
48
|
{
|
|
39
49
|
key: "-m, --maximum-inline-public-length <value>",
|
|
40
50
|
description: "Maximum length of public binary data",
|
|
@@ -48,8 +58,8 @@ const { mkdicomwebConfig } = ConfigPoint.register({
|
|
|
48
58
|
{
|
|
49
59
|
key: "-r, --recompress <listvalue...>",
|
|
50
60
|
description: "List of types to recompress separated by space",
|
|
51
|
-
defaultValue: ["uncompressed", "jp2"],
|
|
52
|
-
choices: ["uncompressed", "jp2", "jpeglossless", "rle", "none"],
|
|
61
|
+
defaultValue: ["uncompressed", "jp2", "jls", "jll"],
|
|
62
|
+
choices: ["uncompressed", "jp2", "jpeglossless", "rle", "jph", "jls", "true", "none"],
|
|
53
63
|
},
|
|
54
64
|
{
|
|
55
65
|
key: "--recompress-thumb <listvalue...>",
|
|
@@ -114,6 +124,11 @@ const { mkdicomwebConfig } = ConfigPoint.register({
|
|
|
114
124
|
"deduplicated files each time a study instance UID is found, and only updates those studies\n" +
|
|
115
125
|
"having at least one ",
|
|
116
126
|
},
|
|
127
|
+
{
|
|
128
|
+
command: "index",
|
|
129
|
+
main: indexMain,
|
|
130
|
+
helpDescription: "Recreate the index for the DICOMweb data.",
|
|
131
|
+
},
|
|
117
132
|
{
|
|
118
133
|
command: "instance",
|
|
119
134
|
arguments: ["input"],
|
package/lib/model/TagLists.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const hashFactory = require("node-object-hash");
|
|
2
|
-
const Tags = require("
|
|
2
|
+
const { Tags } = require("@radicalimaging/static-wado-util");
|
|
3
3
|
|
|
4
4
|
/* eslint "no-param-reassign": "off" */
|
|
5
5
|
|
|
@@ -9,9 +9,9 @@ const { PatientID, PatientName, IssuerOfPatientID } = Tags;
|
|
|
9
9
|
const { StudyDescription, AccessionNumber, StudyInstanceUID, StudyDate, StudyTime } = Tags;
|
|
10
10
|
const { SeriesDescription, SeriesNumber, SeriesInstanceUID, SeriesDate, SeriesTime } = Tags;
|
|
11
11
|
|
|
12
|
-
const { DeduppedHash, DeduppedRef, DeduppedType
|
|
12
|
+
const { DeduppedHash, DeduppedRef, DeduppedType } = Tags;
|
|
13
13
|
|
|
14
|
-
const { pushList,
|
|
14
|
+
const { pushList, setValue, getValue } = Tags;
|
|
15
15
|
|
|
16
16
|
const PatientQuery = [
|
|
17
17
|
PatientID,
|
|
@@ -106,14 +106,14 @@ const ImageExtract = [
|
|
|
106
106
|
];
|
|
107
107
|
|
|
108
108
|
const addHash = (data, type) => {
|
|
109
|
-
const existing = getValue(data,DeduppedHash);
|
|
109
|
+
const existing = getValue(data, DeduppedHash);
|
|
110
110
|
if (existing) {
|
|
111
111
|
return existing;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const hashValue = hasher.hash(data);
|
|
115
|
-
setValue(data, DeduppedHash, hashValue
|
|
116
|
-
setValue(data, DeduppedType, type
|
|
115
|
+
setValue(data, DeduppedHash, hashValue);
|
|
116
|
+
setValue(data, DeduppedType, type);
|
|
117
117
|
return hashValue;
|
|
118
118
|
};
|
|
119
119
|
|
|
@@ -141,7 +141,7 @@ const TagSets = {
|
|
|
141
141
|
});
|
|
142
142
|
const hashValue = addHash(ret, type);
|
|
143
143
|
if (hashRef) {
|
|
144
|
-
pushList(data,DeduppedRef,hashValue);
|
|
144
|
+
pushList(data, DeduppedRef, hashValue);
|
|
145
145
|
}
|
|
146
146
|
return ret;
|
|
147
147
|
},
|
package/lib/model/uids.js
CHANGED
|
@@ -12,7 +12,7 @@ const jpeg = "image/jpeg";
|
|
|
12
12
|
const jls = "image/jls";
|
|
13
13
|
const jll = "image/jll";
|
|
14
14
|
const jxl = "image/x-jxl";
|
|
15
|
-
const htj2k = "image/
|
|
15
|
+
const htj2k = "image/jphc";
|
|
16
16
|
const jp2 = "image/jp2";
|
|
17
17
|
const rle = "image/dicom-rle";
|
|
18
18
|
|
|
@@ -21,20 +21,19 @@ 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 },
|
|
24
|
+
"1.2.840.10008.1.2.4.50": { contentType: jpeg, lossy: true, extension: ".jpeg" },
|
|
25
25
|
"1.2.840.10008.1.2.4.51": { contentType: jpeg, lossy: true },
|
|
26
26
|
"1.2.840.10008.1.2.4.57": { contentType: jpeg },
|
|
27
|
-
"1.2.840.10008.1.2.4.70": { contentType: jll },
|
|
28
|
-
"1.2.840.10008.1.2.4.140": { contentType: jxl },
|
|
27
|
+
"1.2.840.10008.1.2.4.70": { contentType: jll, extension: ".jll" },
|
|
28
|
+
"1.2.840.10008.1.2.4.140": { contentType: jxl, extension: ".jxl" },
|
|
29
|
+
// Lossy, original JPEG reconstruction mode
|
|
29
30
|
"1.2.840.10008.1.2.4.141": { contentType: jxl, lossy: true },
|
|
30
|
-
"1.2.840.10008.1.2.4.
|
|
31
|
+
"1.2.840.10008.1.2.4.142": { contentType: jxl, lossy: true },
|
|
32
|
+
"1.2.840.10008.1.2.4.80": { contentType: jls, extension: ".jls" },
|
|
31
33
|
"1.2.840.10008.1.2.4.81": { contentType: jls, lossy: true },
|
|
32
|
-
"1.2.840.10008.1.2.4.90": { contentType: jp2 },
|
|
34
|
+
"1.2.840.10008.1.2.4.90": { contentType: jp2, extension: ".jp2" },
|
|
33
35
|
"1.2.840.10008.1.2.4.91": { contentType: jp2 },
|
|
34
|
-
|
|
35
|
-
"1.2.840.10008.1.2.4.142": { contentType: jxl, lossy: true },
|
|
36
|
-
"1.2.840.10008.1.2.4.150": { contentType: htj2k },
|
|
37
|
-
// **
|
|
36
|
+
"3.2.840.10008.1.2.4.96": { contentType: htj2k, extension: ".jhc" },
|
|
38
37
|
"1.2.840.10008.1.2.5": { contentType: rle },
|
|
39
38
|
default: {},
|
|
40
39
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// TODO review returning type of some methods
|
|
2
|
-
const JSONWriter = require("
|
|
2
|
+
const { JSONWriter } = require("@radicalimaging/static-wado-util");
|
|
3
|
+
const { Tags } = require("@radicalimaging/static-wado-util");
|
|
3
4
|
const TagLists = require("../model/TagLists");
|
|
4
|
-
const Tags = require("../dictionary/Tags");
|
|
5
5
|
|
|
6
|
-
const {getValue, setValue} = Tags;
|
|
6
|
+
const { getValue, setValue } = Tags;
|
|
7
7
|
|
|
8
8
|
const extractors = {
|
|
9
9
|
patient: TagLists.PatientQuery,
|
|
@@ -21,26 +21,26 @@ const extractors = {
|
|
|
21
21
|
async function deduplicateSingleInstance(id, imageFrame) {
|
|
22
22
|
if (!imageFrame) return {};
|
|
23
23
|
const studyData = await this.completeStudy.getCurrentStudyData(this, id);
|
|
24
|
-
const seriesUID = getValue(imageFrame,Tags.SeriesInstanceUID);
|
|
25
|
-
const sopUID = getValue(imageFrame,Tags.SOPInstanceUID);
|
|
24
|
+
const seriesUID = getValue(imageFrame, Tags.SeriesInstanceUID);
|
|
25
|
+
const sopUID = getValue(imageFrame, Tags.SOPInstanceUID);
|
|
26
26
|
if (!sopUID) {
|
|
27
27
|
console.warn("No sop instance UID in", imageFrame);
|
|
28
28
|
return {};
|
|
29
29
|
}
|
|
30
30
|
if (studyData.sopExists(sopUID)) {
|
|
31
|
-
if(
|
|
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(
|
|
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
42
|
const extracted = TagLists.extract(deduplicated, key, this.extractors[key], TagLists.RemoveExtract);
|
|
43
|
-
const hashKey = getValue(extracted,Tags.DeduppedHash);
|
|
43
|
+
const hashKey = getValue(extracted, Tags.DeduppedHash);
|
|
44
44
|
await studyData.addExtracted(this, hashKey, extracted);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const hashFactory = require("node-object-hash");
|
|
4
|
-
const { JSONReader } = require("@radicalimaging/static-wado-util");
|
|
5
|
-
const Tags = require("
|
|
4
|
+
const { JSONReader, JSONWriter } = require("@radicalimaging/static-wado-util");
|
|
5
|
+
const { Tags } = require("@radicalimaging/static-wado-util");
|
|
6
6
|
const TagLists = require("../model/TagLists");
|
|
7
|
-
const JSONWriter = require("../writer/JSONWriter");
|
|
8
7
|
|
|
9
|
-
const {getValue, setValue, getList, setList} = Tags;
|
|
8
|
+
const { getValue, setValue, getList, setList } = Tags;
|
|
10
9
|
const hasher = hashFactory();
|
|
11
10
|
|
|
12
|
-
const getSeriesInstanceUid = (seriesInstance) =>
|
|
13
|
-
getValue(seriesInstance,Tags.SeriesInstanceUID);
|
|
11
|
+
const getSeriesInstanceUid = (seriesInstance) => getValue(seriesInstance, Tags.SeriesInstanceUID);
|
|
14
12
|
|
|
15
13
|
/**
|
|
16
14
|
* StudyData contains information about the grouped study data. It is used to create
|
|
@@ -88,10 +86,10 @@ class StudyData {
|
|
|
88
86
|
return true;
|
|
89
87
|
}
|
|
90
88
|
const info = deduplicatedTopFile[0];
|
|
91
|
-
if (!info || getValue(info,Tags.DeduppedHash) || getValue(info,Tags.DeduppedType) != "info") {
|
|
89
|
+
if (!info || getValue(info, Tags.DeduppedHash) || getValue(info, Tags.DeduppedType) != "info") {
|
|
92
90
|
return true;
|
|
93
91
|
}
|
|
94
|
-
const hashValue = getValue(info,Tags.DeduppedHash);
|
|
92
|
+
const hashValue = getValue(info, Tags.DeduppedHash);
|
|
95
93
|
return this.existingFiles[0].indexOf(hashValue) == -1;
|
|
96
94
|
} catch (e) {
|
|
97
95
|
console.log("Assume study metadata is dirty", e);
|
|
@@ -105,12 +103,12 @@ class StudyData {
|
|
|
105
103
|
this.newInstancesAdded += 1;
|
|
106
104
|
for (let i = 0; i < this.deduplicated.length; i++) {
|
|
107
105
|
const recombined = await this.recombine(i);
|
|
108
|
-
const reSeriesUid = getValue(recombined,Tags.SeriesInstanceUID);
|
|
106
|
+
const reSeriesUid = getValue(recombined, Tags.SeriesInstanceUID);
|
|
109
107
|
if (reSeriesUid !== seriesInstanceUid) continue;
|
|
110
|
-
const reSop = getValue(recombined,Tags.SOPInstanceUID);
|
|
108
|
+
const reSop = getValue(recombined, Tags.SOPInstanceUID);
|
|
111
109
|
if (!reSop) continue;
|
|
112
110
|
if (reSop !== sopInstanceUid) continue;
|
|
113
|
-
setValue(this.deduplicated[i], Tags.DeduppedType,"deleted");
|
|
111
|
+
setValue(this.deduplicated[i], Tags.DeduppedType, "deleted");
|
|
114
112
|
}
|
|
115
113
|
}
|
|
116
114
|
|
|
@@ -150,7 +148,7 @@ class StudyData {
|
|
|
150
148
|
if (index < 0 || index >= this.deduplicated.length) {
|
|
151
149
|
throw new Error(`Can't read index ${index}, out of bounds [0..${this.deduplicated.length})`);
|
|
152
150
|
}
|
|
153
|
-
const refs = getList(deduplicated,Tags.DeduppedRef);
|
|
151
|
+
const refs = getList(deduplicated, Tags.DeduppedRef);
|
|
154
152
|
if (!refs) {
|
|
155
153
|
console.log("No refs for", deduplicated);
|
|
156
154
|
return deduplicated;
|
|
@@ -165,7 +163,7 @@ class StudyData {
|
|
|
165
163
|
|
|
166
164
|
async addExtracted(callback, hashKey, item) {
|
|
167
165
|
if (this.extractData[hashKey]) {
|
|
168
|
-
if(
|
|
166
|
+
if (this.verbose) console.log("Already have extracted", hashKey, getValue(item, Tags.DeduppedType));
|
|
169
167
|
return;
|
|
170
168
|
}
|
|
171
169
|
await callback.bulkdata(this, hashKey, item);
|
|
@@ -192,7 +190,7 @@ class StudyData {
|
|
|
192
190
|
// console.log('Not adding', hashValue, 'because the hash exists');
|
|
193
191
|
return false;
|
|
194
192
|
}
|
|
195
|
-
const sopValue = getValue(data,Tags.SOPInstanceUID);
|
|
193
|
+
const sopValue = getValue(data, Tags.SOPInstanceUID);
|
|
196
194
|
const sopIndex = this.sopInstances[sopValue];
|
|
197
195
|
if (!sopValue) {
|
|
198
196
|
console.warn("No sop value in", filename, "reading", data);
|
|
@@ -275,14 +273,14 @@ class StudyData {
|
|
|
275
273
|
if (type == Tags.InstanceType) {
|
|
276
274
|
this.internalAddDeduplicated(item, name);
|
|
277
275
|
} else if (type == "info") {
|
|
278
|
-
const refs = getList(item,Tags.DeduppedRef);
|
|
276
|
+
const refs = getList(item, Tags.DeduppedRef);
|
|
279
277
|
if (refs) {
|
|
280
278
|
refs.forEach((hashValue) => {
|
|
281
279
|
this.readHashes[hashValue] = `${hashValue}.gz`;
|
|
282
280
|
});
|
|
283
281
|
}
|
|
284
282
|
} else {
|
|
285
|
-
const hashValue = getValue(item,Tags.DeduppedHash);
|
|
283
|
+
const hashValue = getValue(item, Tags.DeduppedHash);
|
|
286
284
|
if (hashValue) {
|
|
287
285
|
this.extractData[hashValue] = item;
|
|
288
286
|
}
|
|
@@ -299,7 +297,7 @@ class StudyData {
|
|
|
299
297
|
|
|
300
298
|
for (let i = 0; i < this.numberOfInstances; i++) {
|
|
301
299
|
const seriesInstance = await this.recombine(i);
|
|
302
|
-
const type = getValue(seriesInstance,Tags.DeduppedType);
|
|
300
|
+
const type = getValue(seriesInstance, Tags.DeduppedType);
|
|
303
301
|
if (type == "deleted") {
|
|
304
302
|
console.log("Skipping", type);
|
|
305
303
|
continue;
|
|
@@ -337,7 +335,7 @@ class StudyData {
|
|
|
337
335
|
numberOfInstances += instances.length;
|
|
338
336
|
numberOfSeries += 1;
|
|
339
337
|
seriesList.push(seriesQuery);
|
|
340
|
-
const modality = getValue(seriesQuery,Tags.Modality);
|
|
338
|
+
const modality = getValue(seriesQuery, Tags.Modality);
|
|
341
339
|
if (modalitiesInStudy.indexOf(modality) == -1) modalitiesInStudy.push(modality);
|
|
342
340
|
await JSONWriter(seriesPath, "metadata", instances, {
|
|
343
341
|
gzip: true,
|
|
@@ -379,8 +377,8 @@ class StudyData {
|
|
|
379
377
|
createInfo() {
|
|
380
378
|
const data = {};
|
|
381
379
|
const hashValue = hasher.hash(this.deduplicated);
|
|
382
|
-
setValue(data,Tags.DeduppedHash,hashValue);
|
|
383
|
-
setValue(data,Tags.DeduppedType,"info");
|
|
380
|
+
setValue(data, Tags.DeduppedHash, hashValue);
|
|
381
|
+
setValue(data, Tags.DeduppedType, "info");
|
|
384
382
|
return data;
|
|
385
383
|
}
|
|
386
384
|
|
|
@@ -393,7 +391,7 @@ class StudyData {
|
|
|
393
391
|
/** Writes the deduplicated group */
|
|
394
392
|
async writeDeduplicatedGroup() {
|
|
395
393
|
const data = this.createInfo();
|
|
396
|
-
const hashValue = getValue(data,Tags.DeduppedHash);
|
|
394
|
+
const hashValue = getValue(data, Tags.DeduppedHash);
|
|
397
395
|
console.log(
|
|
398
396
|
"Writing deduplicated hash data set data with",
|
|
399
397
|
Object.values(this.extractData).length,
|
|
@@ -401,8 +399,11 @@ class StudyData {
|
|
|
401
399
|
this.deduplicated.length,
|
|
402
400
|
"instance items"
|
|
403
401
|
);
|
|
404
|
-
setList(
|
|
405
|
-
|
|
402
|
+
setList(
|
|
403
|
+
data,
|
|
404
|
+
Tags.DeduppedRef,
|
|
405
|
+
Object.keys(this.readHashes).filter(() => this.deduplicatedHashes[hashValue] == undefined)
|
|
406
|
+
);
|
|
406
407
|
const deduplicatedList = [data, ...Object.values(this.extractData), ...this.deduplicated];
|
|
407
408
|
// const naturalList = deduplicatedList.map(Tags.naturalizeDataset);
|
|
408
409
|
// // console.log("naturalList=", JSON.stringify(naturalList,null,2));
|
|
@@ -4,10 +4,10 @@ const dicomCodec = require("@cornerstonejs/dicom-codec");
|
|
|
4
4
|
const staticCS = require("@radicalimaging/static-cs-lite");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const { exec } = require("child_process");
|
|
7
|
+
const { Tags } = require("@radicalimaging/static-wado-util");
|
|
7
8
|
const decodeImage = require("./adapter/decodeImage");
|
|
8
9
|
const { shouldThumbUseTranscoded } = require("./adapter/transcodeImage");
|
|
9
10
|
const { isVideo } = require("../writer/VideoWriter");
|
|
10
|
-
const Tags = require("../dictionary/Tags");
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Return the middle index of given list
|