UNPKG

dicomweb-proxy

Version:

A proxy to translate between dicomweb and dimse

143 lines (132 loc) 7.03 kB
import { LoggerSingleton } from '../utils/logger'; import { ConfParams, config } from '../utils/config'; import { fileExists, waitForCachedInstancePath } from '../utils/fileHelper'; import { get_element } from '@iwharris/dicom-data-dictionary'; import dicomParser from 'dicom-parser'; import fs from 'fs'; interface ValueType { Value?: string[] | number[] | unknown[]; BulkDataURI?: string; vr: string; } type ElementType = Record<string, ValueType>; function parseFile(filename: string): Promise<ElementType> { const logger = LoggerSingleton.Instance; return new Promise<ElementType>((resolve, reject) => { fileExists(filename).then((success: boolean) => { if (!success) { logger.error(`file does not exist: ${filename}`); return reject(); } fs.promises.readFile(filename).then((data: Uint8Array) => { const dataset = dicomParser.parseDicom(data); // parse additional needed attributes const patientName = dataset.string('x00100010'); const patentID = dataset.string('x00100020'); const studyInstanceUID = dataset.string('x0020000d'); const studyDate = dataset.string('x00080020'); const studyTime = dataset.string('x00080030'); const seriesInstanceUID = dataset.string('x0020000e'); const seriesNumber = dataset.string('x00200011'); const sopInstanceUID = dataset.string('x00080018'); const sopClassUID = dataset.string('x00080016'); const bitsAllocated = dataset.uint16('x00280100'); const bitsStored = dataset.uint16('x00280101'); const highBit = dataset.uint16('x00280102'); const rows = dataset.uint16('x00280010'); const cols = dataset.uint16('x00280011'); const pixelSpacingString = dataset.string('x00280030'); const pixelSpacing = pixelSpacingString ? pixelSpacingString.split('\\').map((e: string) => parseFloat(e)) : [1, 1]; const modality = dataset.string('x00080060'); const samplesPerPixel = dataset.uint16('x00280002'); const photometricInterpretation = dataset.string('x00280004'); const pixelRepresentation = dataset.uint16('x00280103'); const windowCenter = dataset.string('x00281050'); const wc = windowCenter ? parseFloat(windowCenter.split('\\')[0]) : 40; const windowWidth = dataset.string('x00281051'); const ww = windowWidth ? parseFloat(windowWidth.split('\\')[0]) : 80; const rescaleIntercept = parseFloat(dataset.string('x00281052') || '1'); const rescaleSlope = parseFloat(dataset.string('x00281053') || '1'); const iopString = dataset.string('x00200037'); const iop = iopString ? iopString.split('\\').map((e: string) => parseFloat(e)) : null; const ippString = dataset.string('x00200032'); const ipp = ippString ? ippString.split('\\').map((e: string) => parseFloat(e)) : null; const instanceNumber = dataset.string('x00200013'); const sliceThickness = dataset.string('x00180050'); const sliceLocation = dataset.string('x00201041'); const encDocumentValue = dataset.elements.x00420011; const encapsulatedDocument = encDocumentValue ? Buffer.from(dataset.byteArray.buffer, encDocumentValue.dataOffset, encDocumentValue.length).toString('base64') : null; // append to all results const resultStatic: ElementType = { '00100010': { Value: [{ Alphabetic: patientName }], vr: 'PN' }, '00100020': { Value: [patentID], vr: 'LO' }, '0020000D': { Value: [studyInstanceUID], vr: 'UI' }, ...(studyDate && { '00080020': { Value: [studyDate], vr: 'DA' } }), ...(studyTime && { '00080030': { Value: [studyTime], vr: 'TM' } }), '0020000E': { Value: [seriesInstanceUID], vr: 'UI' }, ...(seriesNumber && { '00200011': { Value: [seriesNumber], vr: 'IS' } }), '00080018': { Value: [sopInstanceUID], vr: 'UI' }, '00080016': { Value: [sopClassUID], vr: 'UI' }, ...(modality && { '00080060': { Value: [modality], vr: 'CS' } }), '00280002': { Value: [samplesPerPixel], vr: 'US' }, ...(photometricInterpretation && { '00280004': { Value: [photometricInterpretation], vr: 'CS' } }), '00280010': { Value: [rows], vr: 'US' }, '00280011': { Value: [cols], vr: 'US' }, '00280030': { Value: pixelSpacing, vr: 'DS' }, '00280100': { Value: [bitsAllocated], vr: 'US' }, '00280101': { Value: [bitsStored], vr: 'US' }, '00280102': { Value: [highBit], vr: 'US' }, '00280103': { Value: [pixelRepresentation], vr: 'US' }, '00281050': { Value: [wc], vr: 'DS' }, '00281051': { Value: [ww], vr: 'DS' }, '00281052': { Value: [rescaleIntercept], vr: 'DS' }, '00281053': { Value: [rescaleSlope], vr: 'DS' }, ...(iop && { '00200037': { Value: iop, vr: 'DS' } }), ...(ipp && { '00200032': { Value: ipp, vr: 'DS' } }), ...(instanceNumber && { '00200013': { Value: [instanceNumber], vr: 'IS' } }), ...(sliceThickness && { '00180050': { Value: [sliceThickness], vr: 'DS' } }), ...(sliceLocation && { '00201041': { Value: [sliceLocation], vr: 'DS' } }), //...( encapsulatedDocument && {'00420011': { Value: [encapsulatedDocument], vr: 'OB' }}), // unfortunately this does not work ...(encapsulatedDocument && { '00420011': { BulkDataURI: `series/${seriesInstanceUID}/bulkdata/${sopInstanceUID}`, vr: 'OB' } }), }; const keys: string[] = []; for (const propertyName in dataset.elements) { keys.push(propertyName); } keys.sort(); let resultDynamic: ElementType = {}; for (let k = 0; k < keys.length; k++) { const propertyName = keys[k]; const element = dataset.elements[propertyName]; const tag = element.tag.substring(1).toUpperCase(); const vr = element.vr || get_element(tag)?.vr; const tagValue = dataset.string(element.tag); if (tagValue) { resultDynamic = Object.assign(resultDynamic, { [tag]: { Value: [tagValue], vr, }, }); } } resolve({ ...resultDynamic, ...resultStatic }); }); }); }); } export function parseMeta(json: object, studyInstanceUID: string, seriesInstanceUID: string): Promise<unknown> { const logger = LoggerSingleton.Instance; logger.info(`parsing series ${seriesInstanceUID}`); const parsing = new Array<Promise<ElementType>>(); const storagePath = config.get(ConfParams.STORAGE_PATH) as string; for (const [key] of Object.entries(json)) { const sopInstanceUid = json[key]['00080018'].Value[0]; parsing.push( waitForCachedInstancePath(storagePath, studyInstanceUID, sopInstanceUid).then((pathname) => parseFile(pathname)) ); } return Promise.all(parsing); }