@cornerstonejs/metadata 5.0.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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/enums/CalibrationTypes.d.ts +12 -0
  3. package/dist/esm/enums/CalibrationTypes.js +13 -0
  4. package/dist/esm/enums/MetadataModules.d.ts +54 -0
  5. package/dist/esm/enums/MetadataModules.js +57 -0
  6. package/dist/esm/enums/index.d.ts +4 -0
  7. package/dist/esm/enums/index.js +4 -0
  8. package/dist/esm/index.d.ts +10 -0
  9. package/dist/esm/index.js +9 -0
  10. package/dist/esm/metaData.d.ts +33 -0
  11. package/dist/esm/metaData.js +181 -0
  12. package/dist/esm/registerDefaultProviders.d.ts +1 -0
  13. package/dist/esm/registerDefaultProviders.js +38 -0
  14. package/dist/esm/types/DicomStreamTypes.d.ts +13 -0
  15. package/dist/esm/types/DicomStreamTypes.js +0 -0
  16. package/dist/esm/types/IImageCalibration.d.ts +11 -0
  17. package/dist/esm/types/IImageCalibration.js +0 -0
  18. package/dist/esm/types/MetadataModuleTypes.d.ts +100 -0
  19. package/dist/esm/types/MetadataModuleTypes.js +0 -0
  20. package/dist/esm/types/index.d.ts +3 -0
  21. package/dist/esm/types/index.js +0 -0
  22. package/dist/esm/utilities/Tags.d.ts +25 -0
  23. package/dist/esm/utilities/Tags.js +112 -0
  24. package/dist/esm/utilities/bulkDataFromArray.d.ts +1 -0
  25. package/dist/esm/utilities/bulkDataFromArray.js +38 -0
  26. package/dist/esm/utilities/calibratedPixelSpacingMetadataProvider.d.ts +7 -0
  27. package/dist/esm/utilities/calibratedPixelSpacingMetadataProvider.js +21 -0
  28. package/dist/esm/utilities/dicomStream/MetaDataIterator.d.ts +11 -0
  29. package/dist/esm/utilities/dicomStream/MetaDataIterator.js +58 -0
  30. package/dist/esm/utilities/dicomStream/NaturalTagListener.d.ts +22 -0
  31. package/dist/esm/utilities/dicomStream/NaturalTagListener.js +106 -0
  32. package/dist/esm/utilities/dicomStream/index.d.ts +3 -0
  33. package/dist/esm/utilities/dicomStream/index.js +2 -0
  34. package/dist/esm/utilities/getNaturalizedField.d.ts +3 -0
  35. package/dist/esm/utilities/getNaturalizedField.js +34 -0
  36. package/dist/esm/utilities/getPixelSpacingInformation.d.ts +13 -0
  37. package/dist/esm/utilities/getPixelSpacingInformation.js +93 -0
  38. package/dist/esm/utilities/imageIdToURI.d.ts +1 -0
  39. package/dist/esm/utilities/imageIdToURI.js +15 -0
  40. package/dist/esm/utilities/index.d.ts +15 -0
  41. package/dist/esm/utilities/index.js +15 -0
  42. package/dist/esm/utilities/isEqual.d.ts +2 -0
  43. package/dist/esm/utilities/isEqual.js +2 -0
  44. package/dist/esm/utilities/isVideoTransferSyntax.d.ts +2 -0
  45. package/dist/esm/utilities/isVideoTransferSyntax.js +25 -0
  46. package/dist/esm/utilities/logging.d.ts +3 -0
  47. package/dist/esm/utilities/logging.js +2 -0
  48. package/dist/esm/utilities/metadataProvider/addDicomPart10Instance.d.ts +2 -0
  49. package/dist/esm/utilities/metadataProvider/addDicomPart10Instance.js +12 -0
  50. package/dist/esm/utilities/metadataProvider/cacheData.d.ts +39 -0
  51. package/dist/esm/utilities/metadataProvider/cacheData.js +235 -0
  52. package/dist/esm/utilities/metadataProvider/calibrationModule.d.ts +2 -0
  53. package/dist/esm/utilities/metadataProvider/calibrationModule.js +25 -0
  54. package/dist/esm/utilities/metadataProvider/combineFrameInstance.d.ts +5 -0
  55. package/dist/esm/utilities/metadataProvider/combineFrameInstance.js +158 -0
  56. package/dist/esm/utilities/metadataProvider/compressedFrameData.d.ts +1 -0
  57. package/dist/esm/utilities/metadataProvider/compressedFrameData.js +133 -0
  58. package/dist/esm/utilities/metadataProvider/dataLookup.d.ts +7 -0
  59. package/dist/esm/utilities/metadataProvider/dataLookup.js +18 -0
  60. package/dist/esm/utilities/metadataProvider/dicomSplit.d.ts +1 -0
  61. package/dist/esm/utilities/metadataProvider/dicomSplit.js +5 -0
  62. package/dist/esm/utilities/metadataProvider/ecgFromInstance.d.ts +18 -0
  63. package/dist/esm/utilities/metadataProvider/ecgFromInstance.js +228 -0
  64. package/dist/esm/utilities/metadataProvider/ecgModule.d.ts +1 -0
  65. package/dist/esm/utilities/metadataProvider/ecgModule.js +0 -0
  66. package/dist/esm/utilities/metadataProvider/imageIdsProviders.d.ts +11 -0
  67. package/dist/esm/utilities/metadataProvider/imageIdsProviders.js +117 -0
  68. package/dist/esm/utilities/metadataProvider/imagePlaneCalibrated.d.ts +4 -0
  69. package/dist/esm/utilities/metadataProvider/imagePlaneCalibrated.js +89 -0
  70. package/dist/esm/utilities/metadataProvider/index.d.ts +18 -0
  71. package/dist/esm/utilities/metadataProvider/index.js +17 -0
  72. package/dist/esm/utilities/metadataProvider/instanceFromListener.d.ts +2 -0
  73. package/dist/esm/utilities/metadataProvider/instanceFromListener.js +9 -0
  74. package/dist/esm/utilities/metadataProvider/makeArrayLike.d.ts +1 -0
  75. package/dist/esm/utilities/metadataProvider/makeArrayLike.js +25 -0
  76. package/dist/esm/utilities/metadataProvider/naturalizedHandlers.d.ts +5 -0
  77. package/dist/esm/utilities/metadataProvider/naturalizedHandlers.js +67 -0
  78. package/dist/esm/utilities/metadataProvider/pixelDataUpdate.d.ts +2 -0
  79. package/dist/esm/utilities/metadataProvider/pixelDataUpdate.js +133 -0
  80. package/dist/esm/utilities/metadataProvider/scalingFromInstance.d.ts +1 -0
  81. package/dist/esm/utilities/metadataProvider/scalingFromInstance.js +148 -0
  82. package/dist/esm/utilities/metadataProvider/tagModules.d.ts +7 -0
  83. package/dist/esm/utilities/metadataProvider/tagModules.js +62 -0
  84. package/dist/esm/utilities/metadataProvider/transferSyntaxProvider.d.ts +2 -0
  85. package/dist/esm/utilities/metadataProvider/transferSyntaxProvider.js +20 -0
  86. package/dist/esm/utilities/metadataProvider/uriModule.d.ts +19 -0
  87. package/dist/esm/utilities/metadataProvider/uriModule.js +26 -0
  88. package/dist/esm/utilities/modules/cine.d.ts +2 -0
  89. package/dist/esm/utilities/modules/cine.js +4 -0
  90. package/dist/esm/utilities/modules/clinicalTrial.d.ts +2 -0
  91. package/dist/esm/utilities/modules/clinicalTrial.js +6 -0
  92. package/dist/esm/utilities/modules/ecg.d.ts +2 -0
  93. package/dist/esm/utilities/modules/ecg.js +11 -0
  94. package/dist/esm/utilities/modules/generalImage.d.ts +2 -0
  95. package/dist/esm/utilities/modules/generalImage.js +14 -0
  96. package/dist/esm/utilities/modules/generalSeries.d.ts +2 -0
  97. package/dist/esm/utilities/modules/generalSeries.js +11 -0
  98. package/dist/esm/utilities/modules/generalStudy.d.ts +2 -0
  99. package/dist/esm/utilities/modules/generalStudy.js +7 -0
  100. package/dist/esm/utilities/modules/imagePixel.d.ts +2 -0
  101. package/dist/esm/utilities/modules/imagePixel.js +31 -0
  102. package/dist/esm/utilities/modules/imagePlane.d.ts +2 -0
  103. package/dist/esm/utilities/modules/imagePlane.js +8 -0
  104. package/dist/esm/utilities/modules/index.d.ts +5 -0
  105. package/dist/esm/utilities/modules/index.js +48 -0
  106. package/dist/esm/utilities/modules/modalityLut.d.ts +2 -0
  107. package/dist/esm/utilities/modules/modalityLut.js +5 -0
  108. package/dist/esm/utilities/modules/patient.d.ts +2 -0
  109. package/dist/esm/utilities/modules/patient.js +6 -0
  110. package/dist/esm/utilities/modules/patientStudy.d.ts +2 -0
  111. package/dist/esm/utilities/modules/patientStudy.js +6 -0
  112. package/dist/esm/utilities/modules/ptImage.d.ts +2 -0
  113. package/dist/esm/utilities/modules/ptImage.js +4 -0
  114. package/dist/esm/utilities/modules/ptIsotope.d.ts +2 -0
  115. package/dist/esm/utilities/modules/ptIsotope.js +1 -0
  116. package/dist/esm/utilities/modules/ptSeries.d.ts +2 -0
  117. package/dist/esm/utilities/modules/ptSeries.js +5 -0
  118. package/dist/esm/utilities/modules/radiopharmaceuticalInfo.d.ts +2 -0
  119. package/dist/esm/utilities/modules/radiopharmaceuticalInfo.js +6 -0
  120. package/dist/esm/utilities/modules/sopCommon.d.ts +2 -0
  121. package/dist/esm/utilities/modules/sopCommon.js +1 -0
  122. package/dist/esm/utilities/modules/transferSyntax.d.ts +2 -0
  123. package/dist/esm/utilities/modules/transferSyntax.js +4 -0
  124. package/dist/esm/utilities/modules/ultrasoundEnhancedRegion.d.ts +2 -0
  125. package/dist/esm/utilities/modules/ultrasoundEnhancedRegion.js +1 -0
  126. package/dist/esm/utilities/modules/unassigned.d.ts +2 -0
  127. package/dist/esm/utilities/modules/unassigned.js +15 -0
  128. package/dist/esm/utilities/modules/usRegionChild.d.ts +2 -0
  129. package/dist/esm/utilities/modules/usRegionChild.js +18 -0
  130. package/dist/esm/utilities/modules/voiLut.d.ts +2 -0
  131. package/dist/esm/utilities/modules/voiLut.js +6 -0
  132. package/dist/esm/utilities/splitImageIdsBy4DTags.d.ts +12 -0
  133. package/dist/esm/utilities/splitImageIdsBy4DTags.js +314 -0
  134. package/dist/esm/utilities/toNumber.d.ts +2 -0
  135. package/dist/esm/utilities/toNumber.js +2 -0
  136. package/dist/esm/version.d.ts +1 -0
  137. package/dist/esm/version.js +1 -0
  138. package/package.json +99 -0
@@ -0,0 +1,228 @@
1
+ import { addTypedProvider } from '../../metaData';
2
+ import { MetadataModules } from '../../enums';
3
+ function base64ToUint8Array(base64) {
4
+ const binaryString = atob(base64);
5
+ const bytes = new Uint8Array(binaryString.length);
6
+ for (let i = 0; i < binaryString.length; i++) {
7
+ bytes[i] = binaryString.charCodeAt(i);
8
+ }
9
+ return bytes;
10
+ }
11
+ function convertBuffer(dataSrc, numberOfChannels, numberOfSamples, bits, type) {
12
+ const data = new Uint8Array(dataSrc);
13
+ if (bits === 16 && type === 'SS') {
14
+ const ret = [];
15
+ const bytesPerSample = 2;
16
+ const totalBytes = bytesPerSample * numberOfChannels * numberOfSamples;
17
+ const length = Math.min(data.length, totalBytes);
18
+ for (let channel = 0; channel < numberOfChannels; channel++) {
19
+ const buffer = new Int16Array(numberOfSamples);
20
+ ret.push(buffer);
21
+ let sampleI = 0;
22
+ for (let sample = 2 * channel; sample < length; sample += 2 * numberOfChannels) {
23
+ const highByte = data[sample + 1];
24
+ const lowByte = data[sample];
25
+ const sign = highByte & 0x80;
26
+ buffer[sampleI++] = sign
27
+ ? 0xffff0000 | (highByte << 8) | lowByte
28
+ : (highByte << 8) | lowByte;
29
+ }
30
+ }
31
+ return ret;
32
+ }
33
+ return [];
34
+ }
35
+ function multipartDecode(response) {
36
+ const message = new Uint8Array(response);
37
+ const separator = new TextEncoder().encode('\r\n\r\n');
38
+ let offset = 0;
39
+ const maxHeader = 1000;
40
+ let headerEnd = -1;
41
+ for (let i = 0; i < Math.min(message.length - separator.length, offset + maxHeader); i++) {
42
+ let found = true;
43
+ for (let j = 0; j < separator.length; j++) {
44
+ if (message[i + j] !== separator[j]) {
45
+ found = false;
46
+ break;
47
+ }
48
+ }
49
+ if (found) {
50
+ headerEnd = i;
51
+ break;
52
+ }
53
+ }
54
+ if (headerEnd === -1)
55
+ return [response];
56
+ const headerStr = new TextDecoder().decode(message.slice(0, headerEnd));
57
+ const boundaryMatch = headerStr.match(/boundary=([^\s;]+)/i);
58
+ const boundary = boundaryMatch
59
+ ? new TextEncoder().encode(`--${boundaryMatch[1].replace(/"/g, '').trim()}`)
60
+ : null;
61
+ if (!boundary)
62
+ return [response];
63
+ const dataStart = headerEnd + separator.length;
64
+ const components = [];
65
+ let idx = message.indexOf(boundary[0], dataStart);
66
+ while (idx !== -1) {
67
+ const nextSep = message.indexOf(separator[0], idx);
68
+ if (nextSep === -1)
69
+ break;
70
+ const partStart = nextSep + separator.length;
71
+ const nextBound = message.indexOf(boundary[0], partStart);
72
+ const partEnd = nextBound === -1 ? message.length : nextBound - 2;
73
+ if (partEnd > partStart) {
74
+ components.push(response.slice(partStart, partEnd));
75
+ }
76
+ if (nextBound === -1)
77
+ break;
78
+ idx = message.indexOf(boundary[0], nextBound + boundary.length);
79
+ }
80
+ return components.length > 0 ? components : [response];
81
+ }
82
+ function parseWadoRsImageId(imageId) {
83
+ const uri = imageId.replace(/^wadors:/i, '');
84
+ const studiesIndex = uri.indexOf('/studies/');
85
+ if (studiesIndex === -1)
86
+ return {};
87
+ const wadoRsRoot = uri.substring(0, studiesIndex);
88
+ const afterStudies = uri.substring(studiesIndex + 9);
89
+ const nextSlash = afterStudies.indexOf('/');
90
+ const studyUID = nextSlash !== -1 ? afterStudies.substring(0, nextSlash) : afterStudies;
91
+ return { wadoRsRoot, studyUID };
92
+ }
93
+ function toArray(seq) {
94
+ if (!seq)
95
+ return [];
96
+ if (Array.isArray(seq))
97
+ return seq;
98
+ if (typeof seq.length === 'number') {
99
+ return Array.from(seq);
100
+ }
101
+ return [seq];
102
+ }
103
+ export function buildEcgModuleFromInstance(instance, imageId) {
104
+ const raw = instance.WaveformSequence;
105
+ const groups = toArray(raw);
106
+ if (!groups.length)
107
+ return null;
108
+ const group = groups[0];
109
+ const numberOfChannels = group.NumberOfWaveformChannels ?? 0;
110
+ const numberOfSamples = group.NumberOfWaveformSamples ?? 0;
111
+ const samplingFrequency = group.SamplingFrequency ?? 1;
112
+ const bitsAllocated = group.WaveformBitsAllocated ?? 16;
113
+ const sampleInterpretation = group.WaveformSampleInterpretation ?? 'SS';
114
+ const multiplexGroupLabel = group.MultiplexGroupLabel ?? '';
115
+ const channelDefSeq = toArray(group.ChannelDefinitionSequence);
116
+ const channelDefinitionSequence = channelDefSeq.map((ch) => {
117
+ const srcSeqArr = toArray(ch.ChannelSourceSequence);
118
+ const srcSeq = srcSeqArr[0];
119
+ const codeMeaning = srcSeq?.CodeMeaning ?? '';
120
+ return {
121
+ channelSourceSequence: { codeMeaning },
122
+ };
123
+ });
124
+ let waveformDataRaw = (group.WaveformData ?? group.waveformData);
125
+ if (waveformDataRaw &&
126
+ typeof waveformDataRaw.length === 'number' &&
127
+ waveformDataRaw.length > 0) {
128
+ waveformDataRaw = waveformDataRaw[0];
129
+ }
130
+ const waveformData = waveformDataRaw ?? {};
131
+ const { wadoRsRoot = undefined, studyUID = undefined } = imageId
132
+ ? parseWadoRsImageId(imageId)
133
+ : {};
134
+ const retrieveBulkData = async () => {
135
+ const wd = waveformData;
136
+ if (wd instanceof ArrayBuffer ||
137
+ (typeof ArrayBuffer !== 'undefined' &&
138
+ ArrayBuffer.isView &&
139
+ ArrayBuffer.isView(wd))) {
140
+ return convertBuffer(wd, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
141
+ }
142
+ if (waveformData.Value)
143
+ return waveformData.Value;
144
+ if (waveformData.InlineBinary) {
145
+ const raw = base64ToUint8Array(waveformData.InlineBinary);
146
+ return convertBuffer(raw, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
147
+ }
148
+ if (typeof waveformData.retrieveBulkData === 'function') {
149
+ return waveformData.retrieveBulkData();
150
+ }
151
+ if (waveformData.BulkDataURI) {
152
+ let url = waveformData.BulkDataURI;
153
+ if (url.indexOf(':') === -1 && wadoRsRoot) {
154
+ url = studyUID
155
+ ? `${wadoRsRoot}/studies/${studyUID}/${url}`
156
+ : `${wadoRsRoot}/${url}`;
157
+ }
158
+ const response = await fetch(url);
159
+ const buffer = await response.arrayBuffer();
160
+ const contentType = response.headers.get('content-type') || '';
161
+ const decoded = contentType.includes('multipart')
162
+ ? multipartDecode(buffer)[0]
163
+ : buffer;
164
+ return convertBuffer(decoded, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
165
+ }
166
+ console.warn('[ecgFromInstance] No waveform data source found. group keys:', Object.keys(group), 'waveformData keys:', Object.keys(waveformData));
167
+ throw new Error('[ecgFromInstance] No waveform data source found');
168
+ };
169
+ return {
170
+ numberOfWaveformChannels: numberOfChannels,
171
+ numberOfWaveformSamples: numberOfSamples,
172
+ samplingFrequency,
173
+ waveformBitsAllocated: bitsAllocated,
174
+ waveformSampleInterpretation: sampleInterpretation,
175
+ multiplexGroupLabel,
176
+ channelDefinitionSequence,
177
+ waveformData: { retrieveBulkData },
178
+ };
179
+ }
180
+ const ECG_FROM_INSTANCE_PRIORITY = 4_000;
181
+ const ecgFromInstanceProvider = (next, query, data, options) => {
182
+ const instance = data;
183
+ const hasWaveform = instance && instance.WaveformSequence;
184
+ if (!hasWaveform) {
185
+ return next(query, data, options);
186
+ }
187
+ const result = buildEcgModuleFromInstance(instance, query);
188
+ return result ?? next(query, data, options);
189
+ };
190
+ const ECG_AMPLITUDE_INDEX_SIZE = 65536;
191
+ const ECG_AMPLITUDE_OFFSET = 32768;
192
+ const ecgCalibrationProvider = (next, query, data, options) => {
193
+ const instance = data;
194
+ const raw = instance?.WaveformSequence;
195
+ const groups = toArray(raw);
196
+ if (!groups.length)
197
+ return next(query, data, options);
198
+ const group = groups[0];
199
+ const numberOfWaveformSamples = group.NumberOfWaveformSamples ?? 0;
200
+ const samplingFrequency = group.SamplingFrequency ?? 1;
201
+ const physicalDeltaX = 1 / (samplingFrequency || 1);
202
+ const physicalDeltaY = 0.001;
203
+ return {
204
+ sequenceOfUltrasoundRegions: [
205
+ {
206
+ regionLocationMinX0: 0,
207
+ regionLocationMaxX1: numberOfWaveformSamples,
208
+ regionLocationMinY0: 0,
209
+ regionLocationMaxY1: ECG_AMPLITUDE_INDEX_SIZE - 1,
210
+ referencePixelX0: 0,
211
+ referencePixelY0: ECG_AMPLITUDE_OFFSET,
212
+ physicalDeltaX,
213
+ physicalDeltaY,
214
+ physicalUnitsXDirection: 4,
215
+ physicalUnitsYDirection: -1,
216
+ regionDataType: 1,
217
+ },
218
+ ],
219
+ };
220
+ };
221
+ export function registerEcgFromInstanceProvider() {
222
+ addTypedProvider(MetadataModules.ECG, ecgFromInstanceProvider, {
223
+ priority: ECG_FROM_INSTANCE_PRIORITY,
224
+ });
225
+ addTypedProvider(MetadataModules.CALIBRATION, ecgCalibrationProvider, {
226
+ priority: ECG_FROM_INSTANCE_PRIORITY,
227
+ });
228
+ }
@@ -0,0 +1 @@
1
+ export type { EcgModuleMetadata } from '../../types';
@@ -0,0 +1,11 @@
1
+ import { MetadataModules } from '../../enums';
2
+ import { type TypedProvider } from '../../metaData';
3
+ export declare const FRAME_IMAGE_IDS = MetadataModules.FRAME_IMAGE_IDS;
4
+ export declare const BASE_IMAGE_ID = MetadataModules.BASE_IMAGE_ID;
5
+ export type ImageIdTransformPlugin = TypedProvider;
6
+ export declare function generateFrameImageIdsFromNaturalized(baseImageId: string, naturalized: Record<string, unknown> | null | undefined): Set<string> | undefined;
7
+ export declare function registerFrameImageIdsProvider(provider: ImageIdTransformPlugin, priority?: number): void;
8
+ export declare function baseImageIdQueryFilter(next: any, query: string, data: any, options: any): any;
9
+ export declare function registerImageIdTransformFilter(filter: (imageId: string, frameImageIds: Set<string>) => Iterable<string> | void, priority?: number): void;
10
+ export declare function registerImageIdProviders(): void;
11
+ export declare function bulkUpdateImageIds(imageIds: Iterable<string>, updater: (imageId: string) => void): Set<string>;
@@ -0,0 +1,117 @@
1
+ import { MetadataModules } from '../../enums';
2
+ import { addTypedProvider, getTyped, metadataModuleProvider, } from '../../metaData';
3
+ import { getNaturalizedNumber, getNaturalizedString, } from '../getNaturalizedField';
4
+ export const FRAME_IMAGE_IDS = MetadataModules.FRAME_IMAGE_IDS;
5
+ export const BASE_IMAGE_ID = MetadataModules.BASE_IMAGE_ID;
6
+ const BASE_IMAGE_ID_PATH_FILTER_PRIORITY = 9_000;
7
+ const BASE_IMAGE_ID_QUERY_FILTER_PRIORITY = 8_000;
8
+ const DEFAULT_FRAME_IMAGE_IDS_PRIORITY = 9_000;
9
+ function defaultBaseImageIdProvider(_next, imageId) {
10
+ return imageId;
11
+ }
12
+ function framePathToBaseFilter(next, imageId, data, options) {
13
+ const baseImageId = next(imageId, data, options) ?? imageId;
14
+ return baseImageId.replace(/\/frames\/\d+(?=(\?|#|$))/i, '');
15
+ }
16
+ function frameQueryToBaseFilter(next, imageId, data, options) {
17
+ const baseImageId = next(imageId, data, options) ?? imageId;
18
+ return baseImageId
19
+ .replace(/([?&])frame=\d+(&?)/i, (_match, separator, hasTrailingAmp) => {
20
+ if (separator === '?' && hasTrailingAmp) {
21
+ return '?';
22
+ }
23
+ return '';
24
+ })
25
+ .replace(/\?$/, '')
26
+ .replace(/\?&/, '?')
27
+ .replace(/&&+/g, '&');
28
+ }
29
+ function hasPhotometricInterpretation(naturalized) {
30
+ return !!getNaturalizedString(naturalized, 'PhotometricInterpretation');
31
+ }
32
+ function addFrameQueryParameter(imageId, frameNumber) {
33
+ const separator = imageId.includes('?') ? '&' : '?';
34
+ return `${imageId}${separator}frame=${frameNumber}`;
35
+ }
36
+ function addDicomwebFramePath(imageId, frameNumber) {
37
+ const instancesMatch = /\/instances\/[^/]+/i.exec(imageId);
38
+ if (!instancesMatch) {
39
+ return;
40
+ }
41
+ const insertIndex = instancesMatch.index + instancesMatch[0].length;
42
+ return `${imageId.slice(0, insertIndex)}/frames/${frameNumber}${imageId.slice(insertIndex)}`;
43
+ }
44
+ export function generateFrameImageIdsFromNaturalized(baseImageId, naturalized) {
45
+ if (!naturalized) {
46
+ return;
47
+ }
48
+ if (!hasPhotometricInterpretation(naturalized)) {
49
+ return new Set([baseImageId]);
50
+ }
51
+ const frameImageIds = new Set();
52
+ const numberOfFrames = Math.floor(getNaturalizedNumber(naturalized, 'NumberOfFrames', 1));
53
+ if (!Number.isFinite(numberOfFrames) || numberOfFrames < 1) {
54
+ return;
55
+ }
56
+ for (let frameNumber = 1; frameNumber <= numberOfFrames; frameNumber++) {
57
+ const framePath = addDicomwebFramePath(baseImageId, frameNumber);
58
+ if (framePath) {
59
+ frameImageIds.add(framePath);
60
+ continue;
61
+ }
62
+ frameImageIds.add(addFrameQueryParameter(baseImageId, frameNumber));
63
+ }
64
+ return frameImageIds;
65
+ }
66
+ function defaultFrameImageIdsProvider(next, imageId, data, options) {
67
+ const naturalized = metadataModuleProvider(MetadataModules.NATURALIZED, imageId, options);
68
+ return (generateFrameImageIdsFromNaturalized(imageId, naturalized) ||
69
+ next(imageId, data, options));
70
+ }
71
+ export function registerFrameImageIdsProvider(provider, priority = 0) {
72
+ addTypedProvider(FRAME_IMAGE_IDS, provider, { priority });
73
+ }
74
+ export function baseImageIdQueryFilter(next, query, data, options) {
75
+ const baseImageId = getTyped(MetadataModules.BASE_IMAGE_ID, query, options);
76
+ return next(baseImageId, data, options);
77
+ }
78
+ export function registerImageIdTransformFilter(filter, priority = 0) {
79
+ const provider = (next, imageId, data, options) => {
80
+ const frameImageIds = next(imageId, data, options) ??
81
+ new Set([imageId]);
82
+ const produced = filter(imageId, frameImageIds);
83
+ if (produced) {
84
+ for (const transformedImageId of produced) {
85
+ frameImageIds.add(transformedImageId);
86
+ }
87
+ }
88
+ return frameImageIds;
89
+ };
90
+ registerFrameImageIdsProvider(provider, priority);
91
+ }
92
+ export function registerImageIdProviders() {
93
+ addTypedProvider(BASE_IMAGE_ID, defaultBaseImageIdProvider, { priority: 0 });
94
+ addTypedProvider(BASE_IMAGE_ID, framePathToBaseFilter, {
95
+ priority: BASE_IMAGE_ID_PATH_FILTER_PRIORITY,
96
+ });
97
+ addTypedProvider(BASE_IMAGE_ID, frameQueryToBaseFilter, {
98
+ priority: BASE_IMAGE_ID_QUERY_FILTER_PRIORITY,
99
+ });
100
+ addTypedProvider(FRAME_IMAGE_IDS, defaultFrameImageIdsProvider, {
101
+ priority: DEFAULT_FRAME_IMAGE_IDS_PRIORITY,
102
+ });
103
+ }
104
+ export function bulkUpdateImageIds(imageIds, updater) {
105
+ const updatedImageIds = new Set();
106
+ for (const imageId of imageIds) {
107
+ const frameImageIds = getTyped(MetadataModules.FRAME_IMAGE_IDS, imageId);
108
+ if (!frameImageIds) {
109
+ continue;
110
+ }
111
+ for (const frameImageId of frameImageIds) {
112
+ updater(frameImageId);
113
+ updatedImageIds.add(frameImageId);
114
+ }
115
+ }
116
+ return updatedImageIds;
117
+ }
@@ -0,0 +1,4 @@
1
+ import type { ImagePlaneModuleMetadata } from '../../types';
2
+ export declare const getImagePlaneCalibrated: (next: any, imageId: string, instance: any, options: any) => ImagePlaneModuleMetadata;
3
+ export declare function registerImagePlaneCalibrated(): void;
4
+ export default getImagePlaneCalibrated;
@@ -0,0 +1,89 @@
1
+ import { MetadataModules } from '../../enums';
2
+ import { toFiniteNumber } from '../toNumber';
3
+ import { getNaturalizedNumber } from '../getNaturalizedField';
4
+ import calibratedPixelSpacingMetadataProvider from '../calibratedPixelSpacingMetadataProvider';
5
+ import getPixelSpacingInformation from '../getPixelSpacingInformation';
6
+ import { addTypedProvider } from '../../metaData';
7
+ export const getImagePlaneCalibrated = (next, imageId, instance, options) => {
8
+ if (!instance) {
9
+ return next(imageId, instance, options);
10
+ }
11
+ const { ImageOrientationPatient, ImagePositionPatient } = instance;
12
+ const { PixelSpacing, type } = getPixelSpacingInformation(instance);
13
+ let rowPixelSpacing;
14
+ let columnPixelSpacing;
15
+ let rowCosines;
16
+ let columnCosines;
17
+ let usingDefaultValues = false;
18
+ let isDefaultValueSetForRowCosine = false;
19
+ let isDefaultValueSetForColumnCosine = false;
20
+ let imageOrientationPatient;
21
+ if (PixelSpacing) {
22
+ [rowPixelSpacing, columnPixelSpacing] = PixelSpacing;
23
+ const calibratedPixelSpacing = calibratedPixelSpacingMetadataProvider.get('calibratedPixelSpacing', imageId);
24
+ if (!calibratedPixelSpacing) {
25
+ calibratedPixelSpacingMetadataProvider.add(imageId, {
26
+ rowPixelSpacing: parseFloat(PixelSpacing[0]),
27
+ columnPixelSpacing: parseFloat(PixelSpacing[1]),
28
+ type,
29
+ });
30
+ }
31
+ }
32
+ else {
33
+ rowPixelSpacing = columnPixelSpacing = 1;
34
+ usingDefaultValues = true;
35
+ }
36
+ if (ImageOrientationPatient) {
37
+ imageOrientationPatient = toFiniteNumber(ImageOrientationPatient) || [1, 0, 0, 0, 1, 0];
38
+ rowCosines = imageOrientationPatient.slice(0, 3);
39
+ columnCosines = imageOrientationPatient.slice(3, 6);
40
+ }
41
+ else {
42
+ rowCosines = [1, 0, 0];
43
+ columnCosines = [0, 1, 0];
44
+ imageOrientationPatient = [1, 0, 0, 0, 1, 0];
45
+ usingDefaultValues = true;
46
+ isDefaultValueSetForRowCosine = true;
47
+ isDefaultValueSetForColumnCosine = true;
48
+ }
49
+ const imagePositionPatient = toFiniteNumber(ImagePositionPatient) || [0, 0, 0];
50
+ if (!ImagePositionPatient) {
51
+ usingDefaultValues = true;
52
+ }
53
+ const rowPixelSpacingNumber = toFiniteNumber(rowPixelSpacing) ?? null;
54
+ const columnPixelSpacingNumber = toFiniteNumber(columnPixelSpacing) ?? null;
55
+ const pixelSpacing = rowPixelSpacingNumber !== null && columnPixelSpacingNumber !== null
56
+ ? [rowPixelSpacingNumber, columnPixelSpacingNumber]
57
+ : Array.isArray(PixelSpacing)
58
+ ? PixelSpacing.map((value) => Number(value)).filter(Number.isFinite)
59
+ : [];
60
+ const result = {
61
+ frameOfReferenceUID: instance.FrameOfReferenceUID,
62
+ rows: getNaturalizedNumber(instance, 'Rows', 0),
63
+ columns: getNaturalizedNumber(instance, 'Columns', 0),
64
+ imageOrientationPatient,
65
+ rowCosines,
66
+ columnCosines,
67
+ imagePositionPatient,
68
+ sliceLocation: getNaturalizedNumber(instance, 'SliceLocation', 0),
69
+ pixelSpacing,
70
+ rowPixelSpacing: rowPixelSpacingNumber,
71
+ sliceThickness: getNaturalizedNumber(instance, 'SliceThickness'),
72
+ spacingBetweenSlices: getNaturalizedNumber(instance, 'SpacingBetweenSlices'),
73
+ columnPixelSpacing: columnPixelSpacingNumber,
74
+ };
75
+ Object.defineProperty(result, 'usingDefaultValues', {
76
+ value: usingDefaultValues,
77
+ });
78
+ Object.defineProperty(result, 'isDefaultValueSetForRowCosine', {
79
+ value: isDefaultValueSetForRowCosine,
80
+ });
81
+ Object.defineProperty(result, 'isDefaultValueSetForColumnCosine', {
82
+ value: isDefaultValueSetForColumnCosine,
83
+ });
84
+ return result;
85
+ };
86
+ export function registerImagePlaneCalibrated() {
87
+ addTypedProvider(MetadataModules.IMAGE_PLANE, getImagePlaneCalibrated);
88
+ }
89
+ export default getImagePlaneCalibrated;
@@ -0,0 +1,18 @@
1
+ export { dicomSplit } from './dicomSplit';
2
+ export * from './imagePlaneCalibrated';
3
+ export * from './cacheData';
4
+ export * from './imageIdsProviders';
5
+ export * from './dataLookup';
6
+ export * from './naturalizedHandlers';
7
+ export * from './tagModules';
8
+ export * from './instanceFromListener';
9
+ export * from './calibrationModule';
10
+ export * from './combineFrameInstance';
11
+ export * from './uriModule';
12
+ export * from './pixelDataUpdate';
13
+ export * from './transferSyntaxProvider';
14
+ export * from './addDicomPart10Instance';
15
+ export type { CompressedFrameDataMetadata } from '../../types';
16
+ export * from './ecgModule';
17
+ export * from './ecgFromInstance';
18
+ export * from './scalingFromInstance';
@@ -0,0 +1,17 @@
1
+ export { dicomSplit } from './dicomSplit';
2
+ export * from './imagePlaneCalibrated';
3
+ export * from './cacheData';
4
+ export * from './imageIdsProviders';
5
+ export * from './dataLookup';
6
+ export * from './naturalizedHandlers';
7
+ export * from './tagModules';
8
+ export * from './instanceFromListener';
9
+ export * from './calibrationModule';
10
+ export * from './combineFrameInstance';
11
+ export * from './uriModule';
12
+ export * from './pixelDataUpdate';
13
+ export * from './transferSyntaxProvider';
14
+ export * from './addDicomPart10Instance';
15
+ export * from './ecgModule';
16
+ export * from './ecgFromInstance';
17
+ export * from './scalingFromInstance';
@@ -0,0 +1,2 @@
1
+ export declare const instanceOrigToInstanceProvider: (next: any, query: any, data: any, options: any) => any;
2
+ export declare function registerInstanceFromListener(): void;
@@ -0,0 +1,9 @@
1
+ import { MetadataModules } from '../../enums';
2
+ import { addTypedProvider, metadataModuleProvider } from '../../metaData';
3
+ export const instanceOrigToInstanceProvider = (next, query, data, options) => {
4
+ return (metadataModuleProvider(MetadataModules.NATURALIZED, query, options) ||
5
+ next(query, data, options));
6
+ };
7
+ export function registerInstanceFromListener() {
8
+ addTypedProvider(MetadataModules.INSTANCE, instanceOrigToInstanceProvider);
9
+ }
@@ -0,0 +1 @@
1
+ export declare function makeArrayLike(obj: any): any;
@@ -0,0 +1,25 @@
1
+ export function makeArrayLike(obj) {
2
+ if (obj === null || obj === undefined) {
3
+ return obj;
4
+ }
5
+ if (typeof obj !== 'object' || Array.isArray(obj)) {
6
+ return obj;
7
+ }
8
+ Object.defineProperty(obj, 'length', {
9
+ value: 1,
10
+ configurable: true,
11
+ });
12
+ Object.defineProperty(obj, 0, {
13
+ value: obj,
14
+ writable: true,
15
+ configurable: true,
16
+ enumerable: false,
17
+ });
18
+ Object.defineProperty(obj, Symbol.iterator, {
19
+ value: function* () {
20
+ yield this;
21
+ },
22
+ configurable: true,
23
+ });
24
+ return obj;
25
+ }
@@ -0,0 +1,5 @@
1
+ type Part10Input = ArrayBuffer | Uint8Array | (() => ArrayBuffer | Uint8Array | Promise<ArrayBuffer | Uint8Array>);
2
+ export declare function naturalizeDicomwebMetadata(metadata: Record<string, unknown>): any;
3
+ export declare function naturalizePart10Buffer(part10: Part10Input): Promise<any>;
4
+ export declare function registerNaturalizedHandlers(): void;
5
+ export {};
@@ -0,0 +1,67 @@
1
+ import dcmjs from 'dcmjs';
2
+ import { MetadataModules } from '../../enums';
3
+ import { addAddProvider, addTypedProvider } from '../../metaData';
4
+ import { MetaDataIterator, NaturalTagListener } from '../dicomStream';
5
+ import { baseImageIdQueryFilter } from './imageIdsProviders';
6
+ const { AsyncDicomReader } = dcmjs.async;
7
+ const NATURAL_BASE_IMAGE_ID_FILTER_PRIORITY = 60_000;
8
+ const NATURALIZED_ADD_HANDLER_PRIORITY = 30_000;
9
+ function toArrayBuffer(part10Value) {
10
+ if (part10Value instanceof ArrayBuffer) {
11
+ return part10Value;
12
+ }
13
+ return part10Value.buffer.slice(part10Value.byteOffset, part10Value.byteOffset + part10Value.byteLength);
14
+ }
15
+ async function resolvePart10Input(part10) {
16
+ const resolvedValue = typeof part10 === 'function' ? await part10() : part10;
17
+ if (!(resolvedValue instanceof ArrayBuffer) &&
18
+ !(resolvedValue instanceof Uint8Array)) {
19
+ throw new Error('part10 must resolve to ArrayBuffer or Uint8Array');
20
+ }
21
+ return toArrayBuffer(resolvedValue);
22
+ }
23
+ export function naturalizeDicomwebMetadata(metadata) {
24
+ const iterator = new MetaDataIterator(metadata);
25
+ const listener = NaturalTagListener.createMetadataListener();
26
+ listener.startObject();
27
+ iterator.syncIterator(listener);
28
+ return listener.pop();
29
+ }
30
+ export async function naturalizePart10Buffer(part10) {
31
+ const arrayBuffer = await resolvePart10Input(part10);
32
+ const reader = new AsyncDicomReader();
33
+ const listener = NaturalTagListener.createMetadataListener();
34
+ reader.stream.addBuffer(arrayBuffer);
35
+ reader.stream.setComplete();
36
+ await reader.readFile({ listener });
37
+ const naturalized = reader.dict;
38
+ const transferSyntaxUid = reader.syntax;
39
+ if (transferSyntaxUid) {
40
+ naturalized.TransferSyntaxUID = Array.isArray(transferSyntaxUid)
41
+ ? transferSyntaxUid[0]
42
+ : transferSyntaxUid;
43
+ }
44
+ return naturalized;
45
+ }
46
+ function naturalizedAddProvider(next, query, data, options) {
47
+ const dicomwebJson = options?.dicomwebJson ?? options?.metadata;
48
+ if (dicomwebJson && typeof dicomwebJson === 'object') {
49
+ return naturalizeDicomwebMetadata(dicomwebJson);
50
+ }
51
+ const part10Buffer = options?.part10Buffer ?? options?.part10;
52
+ if (part10Buffer) {
53
+ return naturalizePart10Buffer(part10Buffer);
54
+ }
55
+ return next(query, data, options);
56
+ }
57
+ export function registerNaturalizedHandlers() {
58
+ addTypedProvider(MetadataModules.NATURALIZED, baseImageIdQueryFilter, {
59
+ priority: NATURAL_BASE_IMAGE_ID_FILTER_PRIORITY,
60
+ });
61
+ addAddProvider(MetadataModules.NATURALIZED, baseImageIdQueryFilter, {
62
+ priority: NATURAL_BASE_IMAGE_ID_FILTER_PRIORITY,
63
+ });
64
+ addAddProvider(MetadataModules.NATURALIZED, naturalizedAddProvider, {
65
+ priority: NATURALIZED_ADD_HANDLER_PRIORITY,
66
+ });
67
+ }
@@ -0,0 +1,2 @@
1
+ export declare function pixelDataUpdate(next: any, query: any, data: any, options: any): any;
2
+ export declare function registerPixelDataUpdate(): void;