@fnndsc/salsa 3.15.0 → 3.17.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/dist/dicom/dictionary.d.ts +42 -0
- package/dist/dicom/dictionary.js +221 -0
- package/dist/dicom/dictionary.js.map +1 -0
- package/dist/dicom/index.d.ts +7 -0
- package/dist/dicom/index.js +24 -0
- package/dist/dicom/index.js.map +1 -0
- package/dist/dicom/series.d.ts +120 -0
- package/dist/dicom/series.js +209 -0
- package/dist/dicom/series.js.map +1 -0
- package/dist/dicom/tags.d.ts +80 -0
- package/dist/dicom/tags.js +234 -0
- package/dist/dicom/tags.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pacs/anonymise.d.ts +93 -0
- package/dist/pacs/anonymise.js +196 -0
- package/dist/pacs/anonymise.js.map +1 -0
- package/dist/pacs/index.d.ts +1 -0
- package/dist/pacs/index.js +15 -0
- package/dist/pacs/index.js.map +1 -1
- package/dist/retrieve/watch.d.ts +4 -0
- package/dist/retrieve/watch.js +29 -6
- package/dist/retrieve/watch.js.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The static knowledge a tag listing needs beyond the DICOM dictionary:
|
|
3
|
+
* which module a tag belongs to, which tags carry identifying information,
|
|
4
|
+
* and how a handful of coded values read in words.
|
|
5
|
+
*
|
|
6
|
+
* All three are surfaces' concerns as much as the kernel's, so they live
|
|
7
|
+
* once, here, and every surface renders from the same answer: grouping
|
|
8
|
+
* decides where a row sits, the PHI flag decides whether it is shown or
|
|
9
|
+
* redacted, decoding gives a human the meaning of `MR` or `HFS`. The kernel
|
|
10
|
+
* flags and decodes; it never strips.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/** Where a tag sits in a grouped listing. */
|
|
15
|
+
export type DicomTagGroup = 'meta' | 'patient' | 'study' | 'series' | 'image' | 'equipment' | 'private' | 'other';
|
|
16
|
+
/** The groups in the order a listing shows them. */
|
|
17
|
+
export declare const DICOM_TAG_GROUPS: readonly DicomTagGroup[];
|
|
18
|
+
/**
|
|
19
|
+
* Places a tag in its listing group.
|
|
20
|
+
*
|
|
21
|
+
* @param groupNumber - The tag's group number (the `gggg` of `(gggg,eeee)`).
|
|
22
|
+
* @param name - The dictionary name, when the tag has one.
|
|
23
|
+
* @returns The listing group.
|
|
24
|
+
*/
|
|
25
|
+
export declare function tagGroup_of(groupNumber: number, name: string | undefined): DicomTagGroup;
|
|
26
|
+
/**
|
|
27
|
+
* Tags that identify a person or an institution. Flagged on the model so a
|
|
28
|
+
* surface can redact; never stripped by the kernel.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DICOM_PHI_TAGS: ReadonlySet<string>;
|
|
31
|
+
/** Transfer syntaxes by UID. A renderer's decoder support is judged against these. */
|
|
32
|
+
export declare const TRANSFER_SYNTAX_NAMES: Readonly<Record<string, string>>;
|
|
33
|
+
/**
|
|
34
|
+
* Reads a coded value in words, for the short list of tags a reader meets
|
|
35
|
+
* first. SOP class UIDs are named by the caller through the dictionary.
|
|
36
|
+
*
|
|
37
|
+
* @param name - The tag's dictionary name.
|
|
38
|
+
* @param value - The tag's formatted value.
|
|
39
|
+
* @returns The decoded words, or undefined when the tag is not coded or the
|
|
40
|
+
* code is unknown.
|
|
41
|
+
*/
|
|
42
|
+
export declare function tagValue_decode(name: string, value: string): string | undefined;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file The static knowledge a tag listing needs beyond the DICOM dictionary:
|
|
4
|
+
* which module a tag belongs to, which tags carry identifying information,
|
|
5
|
+
* and how a handful of coded values read in words.
|
|
6
|
+
*
|
|
7
|
+
* All three are surfaces' concerns as much as the kernel's, so they live
|
|
8
|
+
* once, here, and every surface renders from the same answer: grouping
|
|
9
|
+
* decides where a row sits, the PHI flag decides whether it is shown or
|
|
10
|
+
* redacted, decoding gives a human the meaning of `MR` or `HFS`. The kernel
|
|
11
|
+
* flags and decodes; it never strips.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.TRANSFER_SYNTAX_NAMES = exports.DICOM_PHI_TAGS = exports.DICOM_TAG_GROUPS = void 0;
|
|
17
|
+
exports.tagGroup_of = tagGroup_of;
|
|
18
|
+
exports.tagValue_decode = tagValue_decode;
|
|
19
|
+
/** The groups in the order a listing shows them. */
|
|
20
|
+
exports.DICOM_TAG_GROUPS = [
|
|
21
|
+
'patient',
|
|
22
|
+
'study',
|
|
23
|
+
'series',
|
|
24
|
+
'image',
|
|
25
|
+
'equipment',
|
|
26
|
+
'meta',
|
|
27
|
+
'private',
|
|
28
|
+
'other',
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Named tags whose module the group number alone gets wrong. Group 0008 mixes
|
|
32
|
+
* study, series and image identity; 0018 mixes acquisition and equipment;
|
|
33
|
+
* 0020 mixes study, series and image position.
|
|
34
|
+
*/
|
|
35
|
+
const GROUP_BY_NAME = {
|
|
36
|
+
// 0008: series identity and content
|
|
37
|
+
Modality: 'series',
|
|
38
|
+
SeriesDate: 'series',
|
|
39
|
+
SeriesTime: 'series',
|
|
40
|
+
SeriesDescription: 'series',
|
|
41
|
+
BodyPartExamined: 'series',
|
|
42
|
+
ProtocolName: 'series',
|
|
43
|
+
// 0008: image identity
|
|
44
|
+
SOPClassUID: 'image',
|
|
45
|
+
SOPInstanceUID: 'image',
|
|
46
|
+
ImageType: 'image',
|
|
47
|
+
ContentDate: 'image',
|
|
48
|
+
ContentTime: 'image',
|
|
49
|
+
AcquisitionDate: 'image',
|
|
50
|
+
AcquisitionTime: 'image',
|
|
51
|
+
AcquisitionNumber: 'image',
|
|
52
|
+
// 0008: equipment
|
|
53
|
+
Manufacturer: 'equipment',
|
|
54
|
+
ManufacturerModelName: 'equipment',
|
|
55
|
+
StationName: 'equipment',
|
|
56
|
+
InstitutionName: 'equipment',
|
|
57
|
+
InstitutionAddress: 'equipment',
|
|
58
|
+
InstitutionalDepartmentName: 'equipment',
|
|
59
|
+
// 0018: equipment
|
|
60
|
+
DeviceSerialNumber: 'equipment',
|
|
61
|
+
SoftwareVersions: 'equipment',
|
|
62
|
+
MagneticFieldStrength: 'equipment',
|
|
63
|
+
// 0020: study
|
|
64
|
+
StudyInstanceUID: 'study',
|
|
65
|
+
StudyID: 'study',
|
|
66
|
+
// 0020: series
|
|
67
|
+
SeriesInstanceUID: 'series',
|
|
68
|
+
SeriesNumber: 'series',
|
|
69
|
+
FrameOfReferenceUID: 'series',
|
|
70
|
+
PositionReferenceIndicator: 'series',
|
|
71
|
+
PatientPosition: 'series',
|
|
72
|
+
// 0020: image
|
|
73
|
+
InstanceNumber: 'image',
|
|
74
|
+
ImagePositionPatient: 'image',
|
|
75
|
+
ImageOrientationPatient: 'image',
|
|
76
|
+
SliceLocation: 'image',
|
|
77
|
+
ImageComments: 'image',
|
|
78
|
+
NumberOfFrames: 'image',
|
|
79
|
+
};
|
|
80
|
+
/** Module by tag group number, for tags the name table does not place. */
|
|
81
|
+
const GROUP_BY_NUMBER = {
|
|
82
|
+
0x0002: 'meta',
|
|
83
|
+
0x0008: 'study',
|
|
84
|
+
0x0010: 'patient',
|
|
85
|
+
0x0018: 'series',
|
|
86
|
+
0x0020: 'image',
|
|
87
|
+
0x0028: 'image',
|
|
88
|
+
0x0032: 'study',
|
|
89
|
+
0x0038: 'patient',
|
|
90
|
+
0x0040: 'study',
|
|
91
|
+
0x0054: 'series',
|
|
92
|
+
0x0070: 'image',
|
|
93
|
+
0x0088: 'series',
|
|
94
|
+
0x3006: 'image',
|
|
95
|
+
0x7fe0: 'image',
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Places a tag in its listing group.
|
|
99
|
+
*
|
|
100
|
+
* @param groupNumber - The tag's group number (the `gggg` of `(gggg,eeee)`).
|
|
101
|
+
* @param name - The dictionary name, when the tag has one.
|
|
102
|
+
* @returns The listing group.
|
|
103
|
+
*/
|
|
104
|
+
function tagGroup_of(groupNumber, name) {
|
|
105
|
+
if (groupNumber % 2 === 1)
|
|
106
|
+
return 'private';
|
|
107
|
+
if (name !== undefined && GROUP_BY_NAME[name] !== undefined)
|
|
108
|
+
return GROUP_BY_NAME[name];
|
|
109
|
+
return GROUP_BY_NUMBER[groupNumber] ?? 'other';
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Tags that identify a person or an institution. Flagged on the model so a
|
|
113
|
+
* surface can redact; never stripped by the kernel.
|
|
114
|
+
*/
|
|
115
|
+
exports.DICOM_PHI_TAGS = new Set([
|
|
116
|
+
'PatientName',
|
|
117
|
+
'PatientID',
|
|
118
|
+
'PatientBirthDate',
|
|
119
|
+
'PatientBirthTime',
|
|
120
|
+
'PatientAddress',
|
|
121
|
+
'PatientTelephoneNumbers',
|
|
122
|
+
'OtherPatientIDs',
|
|
123
|
+
'OtherPatientNames',
|
|
124
|
+
'PatientMotherBirthName',
|
|
125
|
+
'InstitutionName',
|
|
126
|
+
'InstitutionAddress',
|
|
127
|
+
'ReferringPhysicianName',
|
|
128
|
+
'PerformingPhysicianName',
|
|
129
|
+
'OperatorsName',
|
|
130
|
+
'PhysiciansOfRecord',
|
|
131
|
+
'NameOfPhysiciansReadingStudy',
|
|
132
|
+
'AccessionNumber',
|
|
133
|
+
]);
|
|
134
|
+
/** Words for the coded values a reader meets first. */
|
|
135
|
+
const MODALITY_NAMES = {
|
|
136
|
+
MR: 'Magnetic Resonance',
|
|
137
|
+
CT: 'Computed Tomography',
|
|
138
|
+
PT: 'Positron Emission Tomography',
|
|
139
|
+
NM: 'Nuclear Medicine',
|
|
140
|
+
US: 'Ultrasound',
|
|
141
|
+
CR: 'Computed Radiography',
|
|
142
|
+
DX: 'Digital Radiography',
|
|
143
|
+
MG: 'Mammography',
|
|
144
|
+
RF: 'Radio Fluoroscopy',
|
|
145
|
+
XA: 'X-Ray Angiography',
|
|
146
|
+
OT: 'Other',
|
|
147
|
+
SR: 'Structured Report',
|
|
148
|
+
SEG: 'Segmentation',
|
|
149
|
+
RTSTRUCT: 'RT Structure Set',
|
|
150
|
+
RTDOSE: 'RT Dose',
|
|
151
|
+
RTPLAN: 'RT Plan',
|
|
152
|
+
PR: 'Presentation State',
|
|
153
|
+
KO: 'Key Object Selection',
|
|
154
|
+
DOC: 'Document',
|
|
155
|
+
};
|
|
156
|
+
const PATIENT_SEX_NAMES = {
|
|
157
|
+
M: 'Male',
|
|
158
|
+
F: 'Female',
|
|
159
|
+
O: 'Other',
|
|
160
|
+
};
|
|
161
|
+
const PATIENT_POSITION_NAMES = {
|
|
162
|
+
HFS: 'Head First Supine',
|
|
163
|
+
HFP: 'Head First Prone',
|
|
164
|
+
HFDR: 'Head First Decubitus Right',
|
|
165
|
+
HFDL: 'Head First Decubitus Left',
|
|
166
|
+
FFS: 'Feet First Supine',
|
|
167
|
+
FFP: 'Feet First Prone',
|
|
168
|
+
FFDR: 'Feet First Decubitus Right',
|
|
169
|
+
FFDL: 'Feet First Decubitus Left',
|
|
170
|
+
};
|
|
171
|
+
const PHOTOMETRIC_NAMES = {
|
|
172
|
+
MONOCHROME1: 'Greyscale, minimum is white',
|
|
173
|
+
MONOCHROME2: 'Greyscale, minimum is black',
|
|
174
|
+
RGB: 'Colour, RGB',
|
|
175
|
+
'PALETTE COLOR': 'Colour, palette',
|
|
176
|
+
YBR_FULL: 'Colour, YBR full',
|
|
177
|
+
YBR_FULL_422: 'Colour, YBR 4:2:2',
|
|
178
|
+
YBR_RCT: 'Colour, YBR reversible',
|
|
179
|
+
YBR_ICT: 'Colour, YBR irreversible',
|
|
180
|
+
};
|
|
181
|
+
/** Transfer syntaxes by UID. A renderer's decoder support is judged against these. */
|
|
182
|
+
exports.TRANSFER_SYNTAX_NAMES = {
|
|
183
|
+
'1.2.840.10008.1.2': 'Implicit VR Little Endian',
|
|
184
|
+
'1.2.840.10008.1.2.1': 'Explicit VR Little Endian',
|
|
185
|
+
'1.2.840.10008.1.2.1.99': 'Deflated Explicit VR Little Endian',
|
|
186
|
+
'1.2.840.10008.1.2.2': 'Explicit VR Big Endian',
|
|
187
|
+
'1.2.840.10008.1.2.4.50': 'JPEG Baseline (8-bit)',
|
|
188
|
+
'1.2.840.10008.1.2.4.51': 'JPEG Extended (12-bit)',
|
|
189
|
+
'1.2.840.10008.1.2.4.57': 'JPEG Lossless',
|
|
190
|
+
'1.2.840.10008.1.2.4.70': 'JPEG Lossless SV1',
|
|
191
|
+
'1.2.840.10008.1.2.4.80': 'JPEG-LS Lossless',
|
|
192
|
+
'1.2.840.10008.1.2.4.81': 'JPEG-LS Near-Lossless',
|
|
193
|
+
'1.2.840.10008.1.2.4.90': 'JPEG 2000 Lossless',
|
|
194
|
+
'1.2.840.10008.1.2.4.91': 'JPEG 2000',
|
|
195
|
+
'1.2.840.10008.1.2.4.201': 'High-Throughput JPEG 2000 Lossless',
|
|
196
|
+
'1.2.840.10008.1.2.4.202': 'High-Throughput JPEG 2000 Lossless RPCL',
|
|
197
|
+
'1.2.840.10008.1.2.4.203': 'High-Throughput JPEG 2000',
|
|
198
|
+
'1.2.840.10008.1.2.5': 'RLE Lossless',
|
|
199
|
+
};
|
|
200
|
+
/** Decoders by tag name; each answers with words or nothing. */
|
|
201
|
+
const DECODERS = {
|
|
202
|
+
Modality: (value) => MODALITY_NAMES[value],
|
|
203
|
+
PatientSex: (value) => PATIENT_SEX_NAMES[value],
|
|
204
|
+
PatientPosition: (value) => PATIENT_POSITION_NAMES[value],
|
|
205
|
+
PhotometricInterpretation: (value) => PHOTOMETRIC_NAMES[value],
|
|
206
|
+
TransferSyntaxUID: (value) => exports.TRANSFER_SYNTAX_NAMES[value],
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Reads a coded value in words, for the short list of tags a reader meets
|
|
210
|
+
* first. SOP class UIDs are named by the caller through the dictionary.
|
|
211
|
+
*
|
|
212
|
+
* @param name - The tag's dictionary name.
|
|
213
|
+
* @param value - The tag's formatted value.
|
|
214
|
+
* @returns The decoded words, or undefined when the tag is not coded or the
|
|
215
|
+
* code is unknown.
|
|
216
|
+
*/
|
|
217
|
+
function tagValue_decode(name, value) {
|
|
218
|
+
const decoder = DECODERS[name];
|
|
219
|
+
return decoder === undefined ? undefined : decoder(value);
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=dictionary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary.js","sourceRoot":"","sources":["../../src/dicom/dictionary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* @file DICOM in the kernel: tag reading and the series as a folder.
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
__exportStar(require("./dictionary.js"), exports);
|
|
22
|
+
__exportStar(require("./tags.js"), exports);
|
|
23
|
+
__exportStar(require("./series.js"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dicom/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;GAGG;AACH,kDAAgC;AAChC,4CAA0B;AAC1B,8CAA4B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The series as the kernel knows it.
|
|
3
|
+
*
|
|
4
|
+
* A DICOM series in ChRIS is a folder of files. Files are the truth; a
|
|
5
|
+
* viewer hides them. This module answers "what is this folder as a series"
|
|
6
|
+
* without walking every file: the listing gives the count, the bytes and
|
|
7
|
+
* the order (oxidicom names each file by its InstanceNumber, so the name
|
|
8
|
+
* sorts the stack), and one header read gives identity, modality and
|
|
9
|
+
* geometry. A full positional sort is a volume renderer's business and is
|
|
10
|
+
* deferred to it.
|
|
11
|
+
*
|
|
12
|
+
* Headers read once are kept, keyed by path, so a tag listing that follows
|
|
13
|
+
* a scroll costs nothing on the second visit.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { type Result } from '@fnndsc/cumin';
|
|
18
|
+
import type { VFSItem } from '../vfs/provider.js';
|
|
19
|
+
import { type DicomTagSet } from './tags.js';
|
|
20
|
+
/** How a folder is read; injected so the summary is testable without a daemon. */
|
|
21
|
+
export interface DicomFolderIO {
|
|
22
|
+
list(path: string): Promise<Result<VFSItem[]>>;
|
|
23
|
+
readBinary(path: string): Promise<Result<Buffer>>;
|
|
24
|
+
}
|
|
25
|
+
/** The real folder reader: the VFS for listings, CFS for bytes. */
|
|
26
|
+
export declare const dicomFolderIO_default: DicomFolderIO;
|
|
27
|
+
/** How a folder's files are ordered into a stack. */
|
|
28
|
+
export type DicomSeriesOrder = 'filename' | 'unknown';
|
|
29
|
+
/** The voxel grid, from one header. */
|
|
30
|
+
export interface DicomGeometry {
|
|
31
|
+
rows: number;
|
|
32
|
+
columns: number;
|
|
33
|
+
/** Row and column spacing in mm. */
|
|
34
|
+
pixelSpacing?: [number, number];
|
|
35
|
+
sliceThickness?: number;
|
|
36
|
+
spacingBetweenSlices?: number;
|
|
37
|
+
}
|
|
38
|
+
/** What one folder is as a series. */
|
|
39
|
+
export interface DicomSeriesSummary {
|
|
40
|
+
path: string;
|
|
41
|
+
seriesInstanceUID?: string;
|
|
42
|
+
studyInstanceUID?: string;
|
|
43
|
+
modality: string;
|
|
44
|
+
seriesDescription: string;
|
|
45
|
+
seriesNumber?: number;
|
|
46
|
+
/** DICOM files in the folder. */
|
|
47
|
+
instances: number;
|
|
48
|
+
/** Frames in the header read; one for a classic single-frame file. */
|
|
49
|
+
frames: number;
|
|
50
|
+
order: DicomSeriesOrder;
|
|
51
|
+
geometry?: DicomGeometry;
|
|
52
|
+
transferSyntax?: {
|
|
53
|
+
uid: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
};
|
|
56
|
+
/** Sum of the folder's DICOM file sizes. */
|
|
57
|
+
bytes: number;
|
|
58
|
+
/** The file whose header answered, first in stack order. */
|
|
59
|
+
header: string;
|
|
60
|
+
/** Files ordered for a stack, first to last. */
|
|
61
|
+
files: string[];
|
|
62
|
+
/** Annotation files found for the series' UID. */
|
|
63
|
+
annotations: string[];
|
|
64
|
+
}
|
|
65
|
+
/** Options for a series summary. */
|
|
66
|
+
export interface DicomSeriesOptions {
|
|
67
|
+
/** Where annotation folders live; `<root>/<SeriesInstanceUID>` is listed when given. */
|
|
68
|
+
annotationRoot?: string;
|
|
69
|
+
}
|
|
70
|
+
/** A folder's DICOM files, ordered. */
|
|
71
|
+
export interface DicomFolderListing {
|
|
72
|
+
files: Array<{
|
|
73
|
+
path: string;
|
|
74
|
+
size: number;
|
|
75
|
+
}>;
|
|
76
|
+
order: DicomSeriesOrder;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reads one file's header through the cache.
|
|
80
|
+
*
|
|
81
|
+
* @param path - The file's VFS path.
|
|
82
|
+
* @param io - The folder reader.
|
|
83
|
+
* @returns The tag set, or an error when the file could not be read or parsed.
|
|
84
|
+
*/
|
|
85
|
+
export declare function dicomHeader_get(path: string, io?: DicomFolderIO): Promise<Result<DicomTagSet>>;
|
|
86
|
+
/**
|
|
87
|
+
* Forgets every cached header.
|
|
88
|
+
*/
|
|
89
|
+
export declare function dicomHeaderCache_clear(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Lists a folder's DICOM files in stack order.
|
|
92
|
+
*
|
|
93
|
+
* Order is `filename` when every file starts with a number and a dash
|
|
94
|
+
* (oxidicom's `<InstanceNumber>-<SOPInstanceUID>.dcm`), sorted by that number;
|
|
95
|
+
* otherwise `unknown`, sorted by name.
|
|
96
|
+
*
|
|
97
|
+
* @param folder - The folder's VFS path.
|
|
98
|
+
* @param io - The folder reader.
|
|
99
|
+
* @returns The ordered files with sizes, or an error when the folder cannot be listed.
|
|
100
|
+
*/
|
|
101
|
+
export declare function dicomFolder_list(folder: string, io?: DicomFolderIO): Promise<Result<DicomFolderListing>>;
|
|
102
|
+
/**
|
|
103
|
+
* Picks at most `cap` files spread evenly across the stack, always keeping
|
|
104
|
+
* the first and the last.
|
|
105
|
+
*
|
|
106
|
+
* @param files - The ordered files.
|
|
107
|
+
* @param cap - The most to keep.
|
|
108
|
+
* @returns The chosen files, in order.
|
|
109
|
+
*/
|
|
110
|
+
export declare function dicomFiles_sample<T>(files: T[], cap: number): T[];
|
|
111
|
+
/**
|
|
112
|
+
* Summarizes a folder as a series from its listing and one header.
|
|
113
|
+
*
|
|
114
|
+
* @param folder - The folder's VFS path.
|
|
115
|
+
* @param options - Where to look for annotations.
|
|
116
|
+
* @param io - The folder reader.
|
|
117
|
+
* @returns The summary; an error when the folder cannot be listed, holds no
|
|
118
|
+
* DICOM files, or its first file cannot be read.
|
|
119
|
+
*/
|
|
120
|
+
export declare function dicomSeries_summarize(folder: string, options?: DicomSeriesOptions, io?: DicomFolderIO): Promise<Result<DicomSeriesSummary>>;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dicomFolderIO_default = void 0;
|
|
4
|
+
exports.dicomHeader_get = dicomHeader_get;
|
|
5
|
+
exports.dicomHeaderCache_clear = dicomHeaderCache_clear;
|
|
6
|
+
exports.dicomFolder_list = dicomFolder_list;
|
|
7
|
+
exports.dicomFiles_sample = dicomFiles_sample;
|
|
8
|
+
exports.dicomSeries_summarize = dicomSeries_summarize;
|
|
9
|
+
/**
|
|
10
|
+
* @file The series as the kernel knows it.
|
|
11
|
+
*
|
|
12
|
+
* A DICOM series in ChRIS is a folder of files. Files are the truth; a
|
|
13
|
+
* viewer hides them. This module answers "what is this folder as a series"
|
|
14
|
+
* without walking every file: the listing gives the count, the bytes and
|
|
15
|
+
* the order (oxidicom names each file by its InstanceNumber, so the name
|
|
16
|
+
* sorts the stack), and one header read gives identity, modality and
|
|
17
|
+
* geometry. A full positional sort is a volume renderer's business and is
|
|
18
|
+
* deferred to it.
|
|
19
|
+
*
|
|
20
|
+
* Headers read once are kept, keyed by path, so a tag listing that follows
|
|
21
|
+
* a scroll costs nothing on the second visit.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
const cumin_1 = require("@fnndsc/cumin");
|
|
26
|
+
const dispatcher_js_1 = require("../vfs/dispatcher.js");
|
|
27
|
+
const index_js_1 = require("../files/index.js");
|
|
28
|
+
const dictionary_js_1 = require("./dictionary.js");
|
|
29
|
+
const tags_js_1 = require("./tags.js");
|
|
30
|
+
/** The real folder reader: the VFS for listings, CFS for bytes. */
|
|
31
|
+
exports.dicomFolderIO_default = {
|
|
32
|
+
list: (path) => dispatcher_js_1.vfsDispatcher.list(path),
|
|
33
|
+
readBinary: (path) => (0, index_js_1.fileContent_getBinary)(path),
|
|
34
|
+
};
|
|
35
|
+
/** Headers kept by path. Bounded; the oldest entry leaves first. */
|
|
36
|
+
const HEADER_CACHE_LIMIT = 4096;
|
|
37
|
+
const headerCache = new Map();
|
|
38
|
+
/**
|
|
39
|
+
* Reads one file's header through the cache.
|
|
40
|
+
*
|
|
41
|
+
* @param path - The file's VFS path.
|
|
42
|
+
* @param io - The folder reader.
|
|
43
|
+
* @returns The tag set, or an error when the file could not be read or parsed.
|
|
44
|
+
*/
|
|
45
|
+
async function dicomHeader_get(path, io = exports.dicomFolderIO_default) {
|
|
46
|
+
const cached = headerCache.get(path);
|
|
47
|
+
if (cached !== undefined)
|
|
48
|
+
return (0, cumin_1.Ok)(cached);
|
|
49
|
+
const bytes = await io.readBinary(path);
|
|
50
|
+
if (!bytes.ok)
|
|
51
|
+
return (0, cumin_1.Err)();
|
|
52
|
+
const parsed = (0, tags_js_1.dicomTags_read)(bytes.value);
|
|
53
|
+
if (!parsed.ok)
|
|
54
|
+
return (0, cumin_1.Err)();
|
|
55
|
+
if (headerCache.size >= HEADER_CACHE_LIMIT) {
|
|
56
|
+
const oldest = headerCache.keys().next().value;
|
|
57
|
+
if (oldest !== undefined)
|
|
58
|
+
headerCache.delete(oldest);
|
|
59
|
+
}
|
|
60
|
+
headerCache.set(path, parsed.value);
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Forgets every cached header.
|
|
65
|
+
*/
|
|
66
|
+
function dicomHeaderCache_clear() {
|
|
67
|
+
headerCache.clear();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Lists a folder's DICOM files in stack order.
|
|
71
|
+
*
|
|
72
|
+
* Order is `filename` when every file starts with a number and a dash
|
|
73
|
+
* (oxidicom's `<InstanceNumber>-<SOPInstanceUID>.dcm`), sorted by that number;
|
|
74
|
+
* otherwise `unknown`, sorted by name.
|
|
75
|
+
*
|
|
76
|
+
* @param folder - The folder's VFS path.
|
|
77
|
+
* @param io - The folder reader.
|
|
78
|
+
* @returns The ordered files with sizes, or an error when the folder cannot be listed.
|
|
79
|
+
*/
|
|
80
|
+
async function dicomFolder_list(folder, io = exports.dicomFolderIO_default) {
|
|
81
|
+
const listing = await io.list(folder);
|
|
82
|
+
if (!listing.ok)
|
|
83
|
+
return (0, cumin_1.Err)();
|
|
84
|
+
const base = folder.endsWith('/') ? folder.slice(0, -1) : folder;
|
|
85
|
+
const entries = listing.value.filter((item) => item.type === 'file' && /\.dcm$/i.test(item.name));
|
|
86
|
+
const numbered = entries.length > 0 && entries.every((item) => /^\d+-/.test(item.name));
|
|
87
|
+
const sorted = [...entries].sort((a, b) => numbered ? parseInt(a.name, 10) - parseInt(b.name, 10) || a.name.localeCompare(b.name) : a.name.localeCompare(b.name));
|
|
88
|
+
return (0, cumin_1.Ok)({
|
|
89
|
+
files: sorted.map((item) => ({ path: `${base}/${item.name}`, size: item.size })),
|
|
90
|
+
order: numbered ? 'filename' : 'unknown',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Picks at most `cap` files spread evenly across the stack, always keeping
|
|
95
|
+
* the first and the last.
|
|
96
|
+
*
|
|
97
|
+
* @param files - The ordered files.
|
|
98
|
+
* @param cap - The most to keep.
|
|
99
|
+
* @returns The chosen files, in order.
|
|
100
|
+
*/
|
|
101
|
+
function dicomFiles_sample(files, cap) {
|
|
102
|
+
if (cap <= 0)
|
|
103
|
+
return [];
|
|
104
|
+
if (files.length <= cap)
|
|
105
|
+
return files;
|
|
106
|
+
if (cap === 1)
|
|
107
|
+
return [files[0]];
|
|
108
|
+
const chosen = [];
|
|
109
|
+
const step = (files.length - 1) / (cap - 1);
|
|
110
|
+
for (let i = 0; i < cap; i++)
|
|
111
|
+
chosen.push(files[Math.round(i * step)]);
|
|
112
|
+
return chosen;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Summarizes a folder as a series from its listing and one header.
|
|
116
|
+
*
|
|
117
|
+
* @param folder - The folder's VFS path.
|
|
118
|
+
* @param options - Where to look for annotations.
|
|
119
|
+
* @param io - The folder reader.
|
|
120
|
+
* @returns The summary; an error when the folder cannot be listed, holds no
|
|
121
|
+
* DICOM files, or its first file cannot be read.
|
|
122
|
+
*/
|
|
123
|
+
async function dicomSeries_summarize(folder, options = {}, io = exports.dicomFolderIO_default) {
|
|
124
|
+
const listed = await dicomFolder_list(folder, io);
|
|
125
|
+
if (!listed.ok || listed.value.files.length === 0)
|
|
126
|
+
return (0, cumin_1.Err)();
|
|
127
|
+
const files = listed.value.files;
|
|
128
|
+
const header = await dicomHeader_get(files[0].path, io);
|
|
129
|
+
if (!header.ok)
|
|
130
|
+
return (0, cumin_1.Err)();
|
|
131
|
+
const tags = header.value.tags;
|
|
132
|
+
const seriesInstanceUID = tagText_of(tags, 'SeriesInstanceUID');
|
|
133
|
+
const summary = {
|
|
134
|
+
path: folder,
|
|
135
|
+
...(seriesInstanceUID !== undefined ? { seriesInstanceUID } : {}),
|
|
136
|
+
...(tagText_of(tags, 'StudyInstanceUID') !== undefined ? { studyInstanceUID: tagText_of(tags, 'StudyInstanceUID') } : {}),
|
|
137
|
+
modality: tagText_of(tags, 'Modality') ?? 'OT',
|
|
138
|
+
seriesDescription: tagText_of(tags, 'SeriesDescription') ?? '',
|
|
139
|
+
...(tagNumber_of(tags, 'SeriesNumber') !== undefined ? { seriesNumber: tagNumber_of(tags, 'SeriesNumber') } : {}),
|
|
140
|
+
instances: files.length,
|
|
141
|
+
frames: tagNumber_of(tags, 'NumberOfFrames') ?? 1,
|
|
142
|
+
order: listed.value.order,
|
|
143
|
+
...(geometry_of(tags) !== undefined ? { geometry: geometry_of(tags) } : {}),
|
|
144
|
+
...(header.value.transferSyntaxUID !== undefined
|
|
145
|
+
? {
|
|
146
|
+
transferSyntax: {
|
|
147
|
+
uid: header.value.transferSyntaxUID,
|
|
148
|
+
...(dictionary_js_1.TRANSFER_SYNTAX_NAMES[header.value.transferSyntaxUID] !== undefined ? { name: dictionary_js_1.TRANSFER_SYNTAX_NAMES[header.value.transferSyntaxUID] } : {}),
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
: {}),
|
|
152
|
+
bytes: files.reduce((total, file) => total + file.size, 0),
|
|
153
|
+
header: files[0].path,
|
|
154
|
+
files: files.map((file) => file.path),
|
|
155
|
+
annotations: [],
|
|
156
|
+
};
|
|
157
|
+
if (options.annotationRoot !== undefined && seriesInstanceUID !== undefined) {
|
|
158
|
+
const root = options.annotationRoot.endsWith('/') ? options.annotationRoot.slice(0, -1) : options.annotationRoot;
|
|
159
|
+
const found = await io.list(`${root}/${seriesInstanceUID}`);
|
|
160
|
+
if (found.ok) {
|
|
161
|
+
summary.annotations = found.value
|
|
162
|
+
.filter((item) => item.type === 'file')
|
|
163
|
+
.map((item) => `${root}/${seriesInstanceUID}/${item.name}`)
|
|
164
|
+
.sort();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return (0, cumin_1.Ok)(summary);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* A tag's text, or nothing when absent or empty.
|
|
171
|
+
*/
|
|
172
|
+
function tagText_of(tags, name) {
|
|
173
|
+
const tag = (0, tags_js_1.dicomTag_find)(tags, name);
|
|
174
|
+
return tag === undefined || tag.value === '' ? undefined : tag.value;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A tag's first value as a number, or nothing when absent or not numeric.
|
|
178
|
+
*/
|
|
179
|
+
function tagNumber_of(tags, name) {
|
|
180
|
+
const text = tagText_of(tags, name);
|
|
181
|
+
if (text === undefined)
|
|
182
|
+
return undefined;
|
|
183
|
+
const parsed = Number(text.split('\\')[0]);
|
|
184
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The voxel grid from a header, when it has one.
|
|
188
|
+
*/
|
|
189
|
+
function geometry_of(tags) {
|
|
190
|
+
const rows = tagNumber_of(tags, 'Rows');
|
|
191
|
+
const columns = tagNumber_of(tags, 'Columns');
|
|
192
|
+
if (rows === undefined || columns === undefined)
|
|
193
|
+
return undefined;
|
|
194
|
+
const geometry = { rows, columns };
|
|
195
|
+
const spacing = tagText_of(tags, 'PixelSpacing');
|
|
196
|
+
if (spacing !== undefined) {
|
|
197
|
+
const parts = spacing.split('\\').map(Number);
|
|
198
|
+
if (parts.length === 2 && parts.every(Number.isFinite))
|
|
199
|
+
geometry.pixelSpacing = [parts[0], parts[1]];
|
|
200
|
+
}
|
|
201
|
+
const thickness = tagNumber_of(tags, 'SliceThickness');
|
|
202
|
+
if (thickness !== undefined)
|
|
203
|
+
geometry.sliceThickness = thickness;
|
|
204
|
+
const between = tagNumber_of(tags, 'SpacingBetweenSlices');
|
|
205
|
+
if (between !== undefined)
|
|
206
|
+
geometry.spacingBetweenSlices = between;
|
|
207
|
+
return geometry;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=series.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"series.js","sourceRoot":"","sources":["../../src/dicom/series.ts"],"names":[],"mappings":"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"}
|