@cornerstonejs/dicom-image-loader 4.17.5 → 4.18.1
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/esm/__tests__/ECGHelpers.spec.d.ts +1 -0
- package/dist/esm/__tests__/ECGHelpers.spec.js +191 -0
- package/dist/esm/imageLoader/wadors/metaData/ECGHelpers.d.ts +17 -0
- package/dist/esm/imageLoader/wadors/metaData/ECGHelpers.js +179 -0
- package/dist/esm/imageLoader/wadors/metaData/metaDataProvider.js +52 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { getECGModule } from '../imageLoader/wadors/metaData/ECGHelpers';
|
|
2
|
+
describe('ECGHelpers', () => {
|
|
3
|
+
describe('getECGModule', () => {
|
|
4
|
+
it('should return null when no WaveformSequence is found', () => {
|
|
5
|
+
const metadata = {};
|
|
6
|
+
const result = getECGModule(metadata);
|
|
7
|
+
expect(result).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
it('should parse ECG module from DICOM metadata with InlineBinary data', async () => {
|
|
10
|
+
const numberOfChannels = 2;
|
|
11
|
+
const numberOfSamples = 10;
|
|
12
|
+
const buffer = new ArrayBuffer(numberOfChannels * numberOfSamples * 2);
|
|
13
|
+
const view = new Int16Array(buffer);
|
|
14
|
+
for (let i = 0; i < numberOfSamples; i++) {
|
|
15
|
+
view[i * numberOfChannels + 0] = i * 100;
|
|
16
|
+
view[i * numberOfChannels + 1] = -i * 100;
|
|
17
|
+
}
|
|
18
|
+
const base64Data = btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
19
|
+
const metadata = {
|
|
20
|
+
'54000100': {
|
|
21
|
+
Value: [
|
|
22
|
+
{
|
|
23
|
+
'003A0005': { Value: [numberOfChannels] },
|
|
24
|
+
'003A0010': { Value: [numberOfSamples] },
|
|
25
|
+
'003A001A': { Value: [500] },
|
|
26
|
+
'54001004': { Value: [16] },
|
|
27
|
+
'54001006': { Value: ['SS'] },
|
|
28
|
+
'003A0020': { Value: ['ECG'] },
|
|
29
|
+
'003A0200': {
|
|
30
|
+
Value: [
|
|
31
|
+
{
|
|
32
|
+
'003A0208': {
|
|
33
|
+
Value: [
|
|
34
|
+
{
|
|
35
|
+
'00080104': { Value: ['Lead I'] },
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
'003A0208': {
|
|
42
|
+
Value: [
|
|
43
|
+
{
|
|
44
|
+
'00080104': { Value: ['Lead II'] },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
'54001010': {
|
|
52
|
+
InlineBinary: base64Data,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const result = getECGModule(metadata);
|
|
59
|
+
expect(result).not.toBeNull();
|
|
60
|
+
expect(result?.numberOfWaveformChannels).toBe(numberOfChannels);
|
|
61
|
+
expect(result?.numberOfWaveformSamples).toBe(numberOfSamples);
|
|
62
|
+
expect(result?.samplingFrequency).toBe(500);
|
|
63
|
+
expect(result?.waveformBitsAllocated).toBe(16);
|
|
64
|
+
expect(result?.waveformSampleInterpretation).toBe('SS');
|
|
65
|
+
expect(result?.multiplexGroupLabel).toBe('ECG');
|
|
66
|
+
expect(result?.channelDefinitionSequence.length).toBe(2);
|
|
67
|
+
expect(result?.channelDefinitionSequence[0].channelSourceSequence?.codeMeaning).toBe('Lead I');
|
|
68
|
+
expect(result?.channelDefinitionSequence[1].channelSourceSequence?.codeMeaning).toBe('Lead II');
|
|
69
|
+
});
|
|
70
|
+
it('should have a retrieveBulkData function that decodes InlineBinary data', async () => {
|
|
71
|
+
const numberOfChannels = 2;
|
|
72
|
+
const numberOfSamples = 5;
|
|
73
|
+
const buffer = new ArrayBuffer(numberOfChannels * numberOfSamples * 2);
|
|
74
|
+
const view = new Int16Array(buffer);
|
|
75
|
+
for (let i = 0; i < numberOfSamples; i++) {
|
|
76
|
+
view[i * numberOfChannels + 0] = i * 100;
|
|
77
|
+
view[i * numberOfChannels + 1] = -i * 100;
|
|
78
|
+
}
|
|
79
|
+
const base64Data = btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
80
|
+
const metadata = {
|
|
81
|
+
'54000100': {
|
|
82
|
+
Value: [
|
|
83
|
+
{
|
|
84
|
+
'003A0005': { Value: [numberOfChannels] },
|
|
85
|
+
'003A0010': { Value: [numberOfSamples] },
|
|
86
|
+
'003A001A': { Value: [500] },
|
|
87
|
+
'54001004': { Value: [16] },
|
|
88
|
+
'54001006': { Value: ['SS'] },
|
|
89
|
+
'003A0020': { Value: ['ECG'] },
|
|
90
|
+
'003A0200': { Value: [] },
|
|
91
|
+
'54001010': {
|
|
92
|
+
InlineBinary: base64Data,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const ecgModule = getECGModule(metadata);
|
|
99
|
+
expect(ecgModule).not.toBeNull();
|
|
100
|
+
expect(ecgModule?.waveformData.retrieveBulkData).toBeDefined();
|
|
101
|
+
const channelArrays = await ecgModule.waveformData.retrieveBulkData();
|
|
102
|
+
expect(channelArrays.length).toBe(numberOfChannels);
|
|
103
|
+
expect(channelArrays[0].length).toBe(numberOfSamples);
|
|
104
|
+
expect(channelArrays[1].length).toBe(numberOfSamples);
|
|
105
|
+
expect(channelArrays[0][0]).toBe(0);
|
|
106
|
+
expect(channelArrays[0][1]).toBe(100);
|
|
107
|
+
expect(channelArrays[1][0]).toBe(0);
|
|
108
|
+
expect(channelArrays[1][1]).toBe(-100);
|
|
109
|
+
});
|
|
110
|
+
it('should throw error when no data source is found', async () => {
|
|
111
|
+
const metadata = {
|
|
112
|
+
'54000100': {
|
|
113
|
+
Value: [
|
|
114
|
+
{
|
|
115
|
+
'003A0005': { Value: [2] },
|
|
116
|
+
'003A0010': { Value: [10] },
|
|
117
|
+
'003A001A': { Value: [500] },
|
|
118
|
+
'003A0200': { Value: [] },
|
|
119
|
+
'54001010': {},
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
const ecgModule = getECGModule(metadata);
|
|
125
|
+
expect(ecgModule).not.toBeNull();
|
|
126
|
+
try {
|
|
127
|
+
await ecgModule.waveformData.retrieveBulkData();
|
|
128
|
+
throw new Error('Expected retrieveBulkData to throw');
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
expect(error.message).toContain('[ECGHelpers] No data source found in WaveformData');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
it('should handle metadata with default values', () => {
|
|
135
|
+
const numberOfChannels = 1;
|
|
136
|
+
const numberOfSamples = 20;
|
|
137
|
+
const metadata = {
|
|
138
|
+
'54000100': {
|
|
139
|
+
Value: [
|
|
140
|
+
{
|
|
141
|
+
'003A0005': { Value: [numberOfChannels] },
|
|
142
|
+
'003A0010': { Value: [numberOfSamples] },
|
|
143
|
+
'003A001A': { Value: [250] },
|
|
144
|
+
'003A0200': { Value: [] },
|
|
145
|
+
'54001010': { Value: [] },
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
const result = getECGModule(metadata);
|
|
151
|
+
expect(result).not.toBeNull();
|
|
152
|
+
expect(result?.waveformBitsAllocated).toBe(16);
|
|
153
|
+
expect(result?.waveformSampleInterpretation).toBe('SS');
|
|
154
|
+
expect(result?.multiplexGroupLabel).toBe('ECG');
|
|
155
|
+
});
|
|
156
|
+
it('should parse channel definitions correctly', () => {
|
|
157
|
+
const metadata = {
|
|
158
|
+
'54000100': {
|
|
159
|
+
Value: [
|
|
160
|
+
{
|
|
161
|
+
'003A0005': { Value: [3] },
|
|
162
|
+
'003A0010': { Value: [100] },
|
|
163
|
+
'003A001A': { Value: [500] },
|
|
164
|
+
'003A0200': {
|
|
165
|
+
Value: [
|
|
166
|
+
{
|
|
167
|
+
'003A0208': {
|
|
168
|
+
Value: [{ '00080104': { Value: ['Channel A'] } }],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
'003A0208': {
|
|
173
|
+
Value: [{ '00080104': { Value: ['Channel B'] } }],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
'54001010': { Value: [] },
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const result = getECGModule(metadata);
|
|
185
|
+
expect(result?.channelDefinitionSequence.length).toBe(3);
|
|
186
|
+
expect(result?.channelDefinitionSequence[0].channelSourceSequence?.codeMeaning).toBe('Channel A');
|
|
187
|
+
expect(result?.channelDefinitionSequence[1].channelSourceSequence?.codeMeaning).toBe('Channel B');
|
|
188
|
+
expect(result?.channelDefinitionSequence[2].channelSourceSequence?.codeMeaning).toBe('Unknown');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface EcgModule {
|
|
2
|
+
numberOfWaveformChannels: number;
|
|
3
|
+
numberOfWaveformSamples: number;
|
|
4
|
+
samplingFrequency: number;
|
|
5
|
+
waveformBitsAllocated: number;
|
|
6
|
+
waveformSampleInterpretation: string;
|
|
7
|
+
multiplexGroupLabel: string;
|
|
8
|
+
channelDefinitionSequence: Array<{
|
|
9
|
+
channelSourceSequence?: {
|
|
10
|
+
codeMeaning?: string;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
waveformData: {
|
|
14
|
+
retrieveBulkData: () => Promise<Int16Array[]>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function getECGModule(metadata: Record<string, unknown>, wadoRsRoot?: string, studyUID?: string): EcgModule | null;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import getValue from './getValue';
|
|
2
|
+
import getNumberValue from './getNumberValue';
|
|
3
|
+
import getSequenceItems from './getSequenceItems';
|
|
4
|
+
const TAG = {
|
|
5
|
+
WaveformSequence: '54000100',
|
|
6
|
+
NumberOfWaveformChannels: '003A0005',
|
|
7
|
+
NumberOfWaveformSamples: '003A0010',
|
|
8
|
+
SamplingFrequency: '003A001A',
|
|
9
|
+
MultiplexGroupLabel: '003A0020',
|
|
10
|
+
ChannelDefinitionSequence: '003A0200',
|
|
11
|
+
ChannelSourceSequence: '003A0208',
|
|
12
|
+
CodeMeaning: '00080104',
|
|
13
|
+
WaveformBitsAllocated: '54001004',
|
|
14
|
+
WaveformSampleInterpretation: '54001006',
|
|
15
|
+
WaveformData: '54001010',
|
|
16
|
+
};
|
|
17
|
+
function base64ToUint8Array(base64) {
|
|
18
|
+
const binaryString = atob(base64);
|
|
19
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
20
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
21
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
22
|
+
}
|
|
23
|
+
return bytes;
|
|
24
|
+
}
|
|
25
|
+
function convertBuffer(dataSrc, numberOfChannels, numberOfSamples, bits, type) {
|
|
26
|
+
const data = new Uint8Array(dataSrc);
|
|
27
|
+
if (bits === 16 && type === 'SS') {
|
|
28
|
+
const ret = [];
|
|
29
|
+
const bytesPerSample = 2;
|
|
30
|
+
const totalBytes = bytesPerSample * numberOfChannels * numberOfSamples;
|
|
31
|
+
const length = Math.min(data.length, totalBytes);
|
|
32
|
+
for (let channel = 0; channel < numberOfChannels; channel++) {
|
|
33
|
+
const buffer = new Int16Array(numberOfSamples);
|
|
34
|
+
ret.push(buffer);
|
|
35
|
+
let sampleI = 0;
|
|
36
|
+
for (let sample = 2 * channel; sample < length; sample += 2 * numberOfChannels) {
|
|
37
|
+
const highByte = data[sample + 1];
|
|
38
|
+
const lowByte = data[sample];
|
|
39
|
+
const sign = highByte & 0x80;
|
|
40
|
+
buffer[sampleI++] = sign
|
|
41
|
+
? 0xffff0000 | (highByte << 8) | lowByte
|
|
42
|
+
: (highByte << 8) | lowByte;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return ret;
|
|
46
|
+
}
|
|
47
|
+
console.warn(`[ECGHelpers] Unsupported waveform format: ${bits}-bit ${type}. Only 16-bit SS is supported.`);
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
function multipartDecode(response) {
|
|
51
|
+
const message = new Uint8Array(response);
|
|
52
|
+
const separator = new TextEncoder().encode('\r\n\r\n');
|
|
53
|
+
const headerIndex = findToken(message, separator, 0, 1000);
|
|
54
|
+
if (headerIndex === -1) {
|
|
55
|
+
return [response];
|
|
56
|
+
}
|
|
57
|
+
const headerStr = new TextDecoder().decode(message.slice(0, headerIndex));
|
|
58
|
+
const boundaryString = identifyBoundary(headerStr);
|
|
59
|
+
if (!boundaryString) {
|
|
60
|
+
return [response];
|
|
61
|
+
}
|
|
62
|
+
const boundary = new TextEncoder().encode(boundaryString);
|
|
63
|
+
const components = [];
|
|
64
|
+
let offset = headerIndex + separator.length;
|
|
65
|
+
let boundaryIndex = findToken(message, boundary, offset);
|
|
66
|
+
while (boundaryIndex !== -1) {
|
|
67
|
+
const contentStart = findToken(message, separator, boundaryIndex, 1000);
|
|
68
|
+
if (contentStart === -1) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
const dataStart = contentStart + separator.length;
|
|
72
|
+
const nextBoundary = findToken(message, boundary, dataStart);
|
|
73
|
+
const dataEnd = nextBoundary === -1 ? message.length : nextBoundary - 2;
|
|
74
|
+
components.push(response.slice(dataStart, dataEnd));
|
|
75
|
+
if (nextBoundary === -1) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
offset = nextBoundary;
|
|
79
|
+
boundaryIndex = findToken(message, boundary, offset + boundary.length);
|
|
80
|
+
if (boundaryIndex === -1) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return components.length > 0 ? components : [response];
|
|
85
|
+
}
|
|
86
|
+
function findToken(message, token, startIndex, maxLength) {
|
|
87
|
+
const end = maxLength
|
|
88
|
+
? Math.min(message.length, startIndex + maxLength)
|
|
89
|
+
: message.length;
|
|
90
|
+
for (let i = startIndex; i < end - token.length + 1; i++) {
|
|
91
|
+
let found = true;
|
|
92
|
+
for (let j = 0; j < token.length; j++) {
|
|
93
|
+
if (message[i + j] !== token[j]) {
|
|
94
|
+
found = false;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (found) {
|
|
99
|
+
return i;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return -1;
|
|
103
|
+
}
|
|
104
|
+
function identifyBoundary(header) {
|
|
105
|
+
const match = header.match(/boundary=([^\s;]+)/i);
|
|
106
|
+
if (match) {
|
|
107
|
+
return `--${match[1].replace(/"/g, '')}`;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
function makeRetrieveBulkData(waveformData, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation, wadoRsRoot, studyUID) {
|
|
112
|
+
return async () => {
|
|
113
|
+
if (waveformData.Value) {
|
|
114
|
+
return waveformData.Value;
|
|
115
|
+
}
|
|
116
|
+
if (waveformData.InlineBinary) {
|
|
117
|
+
const raw = base64ToUint8Array(waveformData.InlineBinary);
|
|
118
|
+
return convertBuffer(raw, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
|
|
119
|
+
}
|
|
120
|
+
if (typeof waveformData.retrieveBulkData === 'function') {
|
|
121
|
+
const bulkdata = await waveformData.retrieveBulkData();
|
|
122
|
+
return convertBuffer(bulkdata, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
|
|
123
|
+
}
|
|
124
|
+
if (waveformData.BulkDataURI) {
|
|
125
|
+
let url = waveformData.BulkDataURI;
|
|
126
|
+
if (url.indexOf(':') === -1 && wadoRsRoot) {
|
|
127
|
+
url = studyUID
|
|
128
|
+
? `${wadoRsRoot}/studies/${studyUID}/${url}`
|
|
129
|
+
: `${wadoRsRoot}/${url}`;
|
|
130
|
+
}
|
|
131
|
+
const response = await fetch(url);
|
|
132
|
+
const buffer = await response.arrayBuffer();
|
|
133
|
+
const contentType = response.headers.get('content-type') || '';
|
|
134
|
+
const decoded = contentType.includes('multipart')
|
|
135
|
+
? multipartDecode(buffer)[0]
|
|
136
|
+
: buffer;
|
|
137
|
+
return convertBuffer(decoded, numberOfChannels, numberOfSamples, bitsAllocated, sampleInterpretation);
|
|
138
|
+
}
|
|
139
|
+
throw new Error('[ECGHelpers] No data source found in WaveformData');
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function getECGModule(metadata, wadoRsRoot, studyUID) {
|
|
143
|
+
const waveformSeqItems = getSequenceItems(metadata[TAG.WaveformSequence]);
|
|
144
|
+
if (!waveformSeqItems || waveformSeqItems.length === 0) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const waveform = waveformSeqItems[0];
|
|
148
|
+
const numberOfChannels = getNumberValue(waveform[TAG.NumberOfWaveformChannels]);
|
|
149
|
+
const numberOfSamples = getNumberValue(waveform[TAG.NumberOfWaveformSamples]);
|
|
150
|
+
const samplingFrequency = getNumberValue(waveform[TAG.SamplingFrequency]);
|
|
151
|
+
const waveformBitsAllocated = getNumberValue(waveform[TAG.WaveformBitsAllocated]) ?? 16;
|
|
152
|
+
const waveformSampleInterpretation = getValue(waveform[TAG.WaveformSampleInterpretation]) ?? 'SS';
|
|
153
|
+
const multiplexGroupLabel = getValue(waveform[TAG.MultiplexGroupLabel]) ?? 'ECG';
|
|
154
|
+
const channelDefItems = getSequenceItems(waveform[TAG.ChannelDefinitionSequence]);
|
|
155
|
+
const channelDefinitionSequence = (channelDefItems || []).map((channelDef) => {
|
|
156
|
+
const channelDefRecord = channelDef;
|
|
157
|
+
const sourceSeqItems = getSequenceItems(channelDefRecord[TAG.ChannelSourceSequence]);
|
|
158
|
+
const sourceSeq = sourceSeqItems?.[0] || {};
|
|
159
|
+
const codeMeaning = getValue(sourceSeq[TAG.CodeMeaning]);
|
|
160
|
+
return {
|
|
161
|
+
channelSourceSequence: {
|
|
162
|
+
codeMeaning: codeMeaning || 'Unknown',
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
const waveformDataObj = waveform[TAG.WaveformData] || {};
|
|
167
|
+
return {
|
|
168
|
+
numberOfWaveformChannels: numberOfChannels,
|
|
169
|
+
numberOfWaveformSamples: numberOfSamples,
|
|
170
|
+
samplingFrequency,
|
|
171
|
+
waveformBitsAllocated,
|
|
172
|
+
waveformSampleInterpretation,
|
|
173
|
+
multiplexGroupLabel,
|
|
174
|
+
channelDefinitionSequence,
|
|
175
|
+
waveformData: {
|
|
176
|
+
retrieveBulkData: makeRetrieveBulkData(waveformDataObj, numberOfChannels, numberOfSamples, waveformBitsAllocated, waveformSampleInterpretation, wadoRsRoot, studyUID),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as dicomParser from 'dicom-parser';
|
|
2
1
|
import { Enums, utilities, metaData as coreMetaData, } from '@cornerstonejs/core';
|
|
3
2
|
import getNumberValues from './getNumberValues';
|
|
4
3
|
import getNumberValue from './getNumberValue';
|
|
@@ -9,8 +8,9 @@ import { getMultiframeInformation, getFrameInformation, } from '../combineFrameI
|
|
|
9
8
|
import { extractOrientationFromMetadata, extractPositionFromMetadata, } from './extractPositioningFromMetadata';
|
|
10
9
|
import { getImageTypeSubItemFromMetadata } from './NMHelpers';
|
|
11
10
|
import isNMReconstructable from '../../isNMReconstructable';
|
|
12
|
-
import {
|
|
11
|
+
import { instanceModuleNames } from '../../getInstanceModule';
|
|
13
12
|
import { getUSEnhancedRegions } from './USHelpers';
|
|
13
|
+
import { getECGModule } from './ECGHelpers';
|
|
14
14
|
function metaDataProvider(type, imageId) {
|
|
15
15
|
const { MetadataModules } = Enums;
|
|
16
16
|
if (type === MetadataModules.MULTIFRAME) {
|
|
@@ -154,6 +154,20 @@ function metaDataProvider(type, imageId) {
|
|
|
154
154
|
if (type === MetadataModules.ULTRASOUND_ENHANCED_REGION) {
|
|
155
155
|
return getUSEnhancedRegions(metaData);
|
|
156
156
|
}
|
|
157
|
+
if (type === MetadataModules.ECG) {
|
|
158
|
+
const imageUri = imageId.replace('wadors:', '');
|
|
159
|
+
const studiesIndex = imageUri.indexOf('/studies/');
|
|
160
|
+
let wadoRsRoot;
|
|
161
|
+
let studyUID;
|
|
162
|
+
if (studiesIndex !== -1) {
|
|
163
|
+
wadoRsRoot = imageUri.substring(0, studiesIndex);
|
|
164
|
+
const afterStudies = imageUri.substring(studiesIndex + 9);
|
|
165
|
+
const nextSlash = afterStudies.indexOf('/');
|
|
166
|
+
studyUID =
|
|
167
|
+
nextSlash !== -1 ? afterStudies.substring(0, nextSlash) : afterStudies;
|
|
168
|
+
}
|
|
169
|
+
return getECGModule(metaData, wadoRsRoot, studyUID);
|
|
170
|
+
}
|
|
157
171
|
if (type === MetadataModules.CALIBRATION) {
|
|
158
172
|
const modality = getValue(metaData['00080060']);
|
|
159
173
|
if (modality === 'US') {
|
|
@@ -162,6 +176,42 @@ function metaDataProvider(type, imageId) {
|
|
|
162
176
|
sequenceOfUltrasoundRegions: enhancedRegion,
|
|
163
177
|
};
|
|
164
178
|
}
|
|
179
|
+
const imageUri = imageId.replace('wadors:', '');
|
|
180
|
+
const studiesIndex = imageUri.indexOf('/studies/');
|
|
181
|
+
let wadoRsRoot;
|
|
182
|
+
let studyUID;
|
|
183
|
+
if (studiesIndex !== -1) {
|
|
184
|
+
wadoRsRoot = imageUri.substring(0, studiesIndex);
|
|
185
|
+
const afterStudies = imageUri.substring(studiesIndex + 9);
|
|
186
|
+
const nextSlash = afterStudies.indexOf('/');
|
|
187
|
+
studyUID =
|
|
188
|
+
nextSlash !== -1 ? afterStudies.substring(0, nextSlash) : afterStudies;
|
|
189
|
+
}
|
|
190
|
+
const ecgModule = getECGModule(metaData, wadoRsRoot, studyUID);
|
|
191
|
+
if (ecgModule) {
|
|
192
|
+
const { numberOfWaveformSamples, samplingFrequency } = ecgModule;
|
|
193
|
+
const physicalDeltaX = 1 / (samplingFrequency || 1);
|
|
194
|
+
const physicalDeltaY = 0.001;
|
|
195
|
+
const ECG_AMPLITUDE_INDEX_SIZE = 65536;
|
|
196
|
+
const ECG_AMPLITUDE_OFFSET = 32768;
|
|
197
|
+
return {
|
|
198
|
+
sequenceOfUltrasoundRegions: [
|
|
199
|
+
{
|
|
200
|
+
regionLocationMinX0: 0,
|
|
201
|
+
regionLocationMaxX1: numberOfWaveformSamples,
|
|
202
|
+
regionLocationMinY0: 0,
|
|
203
|
+
regionLocationMaxY1: ECG_AMPLITUDE_INDEX_SIZE - 1,
|
|
204
|
+
referencePixelX0: 0,
|
|
205
|
+
referencePixelY0: ECG_AMPLITUDE_OFFSET,
|
|
206
|
+
physicalDeltaX,
|
|
207
|
+
physicalDeltaY,
|
|
208
|
+
physicalUnitsXDirection: 4,
|
|
209
|
+
physicalUnitsYDirection: -1,
|
|
210
|
+
regionDataType: 1,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
165
215
|
}
|
|
166
216
|
if (type === MetadataModules.IMAGE_URL) {
|
|
167
217
|
return getImageUrlModule(imageId, metaData);
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.
|
|
1
|
+
export declare const version = "4.18.1";
|
package/dist/esm/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '4.
|
|
1
|
+
export const version = '4.18.1';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/dicom-image-loader",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.18.1",
|
|
4
4
|
"description": "Cornerstone Image Loader for DICOM WADO-URI and WADO-RS and Local file",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"DICOM",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"uuid": "9.0.1"
|
|
113
113
|
},
|
|
114
114
|
"peerDependencies": {
|
|
115
|
-
"@cornerstonejs/core": "4.
|
|
115
|
+
"@cornerstonejs/core": "4.18.1",
|
|
116
116
|
"dicom-parser": "1.8.21"
|
|
117
117
|
},
|
|
118
118
|
"config": {
|
|
@@ -120,5 +120,5 @@
|
|
|
120
120
|
"path": "./node_modules/cz-conventional-changelog"
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
|
-
"gitHead": "
|
|
123
|
+
"gitHead": "1fc5684032269b1b1e3e3733cd891d48c27303f7"
|
|
124
124
|
}
|