Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | /** * Decode a multiplexed Int16 buffer into per-channel arrays. * Layout: sample0ch0, sample0ch1 ... sample0chN, sample1ch0, … * Note: DICOM ECG data is canonically SS (signed short). The sampleInterpretation * field is forwarded to ECGViewport for its own use; the raw buffer is always * treated as Int16 because Cornerstone ECGViewport expects Int16Array[]. */ export function decodeInt16Multiplex( buffer: ArrayBuffer, numberOfChannels: number, numberOfSamples: number ): Int16Array[] { const src = new Int16Array(buffer); const channels: Int16Array[] = []; for (let ch = 0; ch < numberOfChannels; ch++) { const out = new Int16Array(numberOfSamples); for (let s = 0; s < numberOfSamples; s++) { out[s] = src[s * numberOfChannels + ch]; } channels.push(out); } return channels; } /** * Decode a base64 InlineBinary string into a raw ArrayBuffer. */ export function base64ToArrayBuffer(base64: string): ArrayBuffer { const binaryStr = atob(base64); const bytes = new Uint8Array(binaryStr.length); for (let i = 0; i < binaryStr.length; i++) { bytes[i] = binaryStr.charCodeAt(i); } return bytes.buffer; } export type EcgModule = { numberOfWaveformChannels: number; numberOfWaveformSamples: number; samplingFrequency: number; waveformBitsAllocated: number; waveformSampleInterpretation: string; multiplexGroupLabel: string; channelDefinitionSequence: Array<{ channelSourceSequence: { codeMeaning: string } }>; waveformData: { retrieveBulkData: () => Promise<Int16Array[]> }; }; /** * Parse the naturalized DICOM instance's WaveformSequence and build the ecgModule * that Cornerstone's ECGViewport.setEcg() expects via * metaData.get(MetadataModules.ECG, imageId). * * Returns null if the instance has no WaveformSequence. */ export function buildEcgModule( instance: any, userAuthenticationService?: any ): EcgModule | null { const waveformGroups = instance?.WaveformSequence; Iif (!waveformGroups?.length) { return null; } // Use the first (and typically only) multiplex group const group = waveformGroups[0]; const numberOfChannels = group.NumberOfWaveformChannels ?? 0; const numberOfSamples = group.NumberOfWaveformSamples ?? 0; const samplingFrequency = group.SamplingFrequency ?? 1; const bitsAllocated = group.WaveformBitsAllocated ?? 16; const sampleInterpretation = group.WaveformSampleInterpretation ?? 'SS'; const multiplexGroupLabel = group.MultiplexGroupLabel ?? ''; const channelDefinitionSequence = (group.ChannelDefinitionSequence ?? []).map(ch => ({ channelSourceSequence: { codeMeaning: ch?.ChannelSourceSequence?.[0]?.CodeMeaning ?? ch?.ChannelSourceSequence?.[0]?.codeMeaning ?? '', }, })); const retrieveBulkData = async (): Promise<Int16Array[]> => { const waveformData = group.WaveformData; Iif (!waveformData) { console.warn('[ECGViewport] No WaveformData found on instance'); return []; } let buffer: ArrayBuffer; if (waveformData.InlineBinary) { buffer = base64ToArrayBuffer(waveformData.InlineBinary); } else if (waveformData.BulkDataURI) { const headers: Record<string, string> = { Accept: 'application/octet-stream', }; const authHeader = userAuthenticationService?.getAuthorizationHeader?.(); Iif (authHeader) { Object.assign(headers, authHeader); } const response = await fetch(waveformData.BulkDataURI, { headers }); Iif (!response.ok) { throw new Error( `[ECGViewport] Failed to fetch waveform BulkDataURI: ${response.status}` ); } buffer = await response.arrayBuffer(); } else { console.warn('[ECGViewport] WaveformData has no InlineBinary or BulkDataURI'); return []; } return decodeInt16Multiplex(buffer, numberOfChannels, numberOfSamples); }; return { numberOfWaveformChannels: numberOfChannels, numberOfWaveformSamples: numberOfSamples, samplingFrequency, waveformBitsAllocated: bitsAllocated, waveformSampleInterpretation: sampleInterpretation, multiplexGroupLabel, channelDefinitionSequence, waveformData: { retrieveBulkData }, }; } |