From 277c3bf4193a994b219ebb9e3d10aa2305923bf9 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Thu, 23 Dec 2021 11:28:46 -0500 Subject: [PATCH] fix: Overlays were broken. (#2614) * fix(OHIFv3):Overlays were broken. Adds a retrieveBulkData to elements containing BulkDataURI so that anyone display the data can retrieve the required information. * fix(OHIFv3):Added a call in the wrong spot, removed it. * docs(OHIFv3):Added some documentation as requested --- .../default/src/DicomWebDataSource/index.js | 42 +++++++++- platform/core/src/classes/MetadataProvider.js | 11 --- .../metadataProvider/fetchOverlayData.js | 82 ------------------- platform/core/src/utils/str2ab.js | 11 +-- 4 files changed, 41 insertions(+), 105 deletions(-) delete mode 100644 platform/core/src/utils/metadataProvider/fetchOverlayData.js diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index 07f72b9e7..152ed5e21 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -162,9 +162,9 @@ function createDicomWebApi(dicomWebConfig, UserAuthenticationService) { ); } + const storeInstances = instances => { const naturalizedInstances = instances.map(naturalizeDataset); - DicomMetadataStore.addInstances(naturalizedInstances); callback(naturalizedInstances); }; @@ -252,9 +252,47 @@ function createDicomWebApi(dicomWebConfig, UserAuthenticationService) { sortFunction ); + /** + * naturalizes the dataset, and adds a retrieve bulkdata method + * to any values containing BulkDataURI. + * @param {*} instance + * @returns naturalized dataset, with retrieveBulkData methods + */ + const addRetrieveBulkData = instance => { + const naturalized = naturalizeDataset(instance); + Object.keys(naturalized).forEach(key => { + const value = naturalized[key]; + // The value.Value will be set with the bulkdata read value + // in which case it isn't necessary to re-read this. + if (value && value.BulkDataURI && !value.Value) { + // Provide a method to fetch bulkdata + value.retrieveBulkData = () => { + const options = { + // The bulkdata fetches work with either multipart or + // singlepart, so set multipart to false to let the server + // decide which type to respond with. + multipart: false, + BulkDataURI: value.BulkDataURI, + // The study instance UID is required if the bulkdata uri + // is relative - that isn't disallowed by DICOMweb, but + // isn't well specified in the standard, but is needed in + // any implementation that stores static copies of the metadata + StudyInstanceUID: naturalized.StudyInstanceUID, + }; + return qidoDicomWebClient.retrieveBulkData(options).then(val => { + const ret = val && val[0] || undefined; + value.Value = ret; + return ret; + }); + }; + } + }); + return naturalized + }; + // Async load series, store as retrieved function storeInstances(instances) { - const naturalizedInstances = instances.map(naturalizeDataset); + const naturalizedInstances = instances.map(addRetrieveBulkData); DicomMetadataStore.addInstances(naturalizedInstances, madeInClient); } diff --git a/platform/core/src/classes/MetadataProvider.js b/platform/core/src/classes/MetadataProvider.js index f214d1918..b1e377a3d 100644 --- a/platform/core/src/classes/MetadataProvider.js +++ b/platform/core/src/classes/MetadataProvider.js @@ -1,8 +1,6 @@ import queryString from 'query-string'; import dicomParser from 'dicom-parser'; import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation'; -import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData'; -import fetchOverlayData from '../utils/metadataProvider/fetchOverlayData'; import DicomMetadataStore from '../services/DicomMetadataStore'; class MetadataProvider { @@ -105,14 +103,6 @@ class MetadataProvider { // return instance; // } - // async _checkBulkDataAndInlineBinaries(instance, server) { - // await fetchOverlayData(instance, server); - - // if (instance.PhotometricInterpretation === 'PALETTE COLOR') { - // await fetchPaletteColorLookupTableData(instance, server); - // } - // } - _getInstance(imageId) { const uids = this._getUIDsFromImageID(imageId); @@ -508,4 +498,3 @@ const WADO_IMAGE_LOADER_TAGS = { }; const INSTANCE = 'instance'; -const DICOMWEB = 'dicomweb'; diff --git a/platform/core/src/utils/metadataProvider/fetchOverlayData.js b/platform/core/src/utils/metadataProvider/fetchOverlayData.js deleted file mode 100644 index 11e4d99cd..000000000 --- a/platform/core/src/utils/metadataProvider/fetchOverlayData.js +++ /dev/null @@ -1,82 +0,0 @@ -import { api } from 'dicomweb-client'; -import DICOMWeb from '../../DICOMWeb'; -import str2ab from '../str2ab'; -import unpackOverlay from './unpackOverlay'; - -import errorHandler from '../../errorHandler'; - -export default async function fetchOverlayData(instance, server) { - const OverlayDataPromises = []; - const OverlayDataTags = []; - - return new Promise((resolve, reject) => { - for (let overlayGroup = 0x00; overlayGroup <= 0x1e; overlayGroup += 0x02) { - let groupStr = `60${overlayGroup.toString(16)}`; - - if (groupStr.length === 3) { - groupStr = `600${overlayGroup.toString(16)}`; - } - - const OverlayDataTag = `${groupStr}3000`; - - if (instance[OverlayDataTag] && instance[OverlayDataTag].InlineBinary) { - const inlineBinaryData = atob(instance[OverlayDataTag].InlineBinary); - const arraybuffer = str2ab(inlineBinaryData); - - instance[OverlayDataTag] = unpackOverlay(arraybuffer); - } else if ( - instance[OverlayDataTag] && - instance[OverlayDataTag].BulkDataURI - ) { - OverlayDataPromises.push( - _getOverlayData(instance[OverlayDataTag], server) - ); - OverlayDataTags.push(OverlayDataTag); - } else if ( - instance[OverlayDataTag] && - instance[OverlayDataTag] instanceof ArrayBuffer - ) { - instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag]); - } - } - - if (OverlayDataPromises.length) { - Promise.all(OverlayDataPromises).then(results => { - for (let i = 0; i < results.length; i++) { - instance[OverlayDataTags[i]] = results[i]; - } - - resolve(); - }); - } else { - resolve(); - } - }); -} - -async function _getOverlayData(tag, server) { - const { BulkDataURI } = tag; - - let uri = BulkDataURI; - - // TODO: Workaround for dcm4chee behind SSL-terminating proxy returning - // incorrect bulk data URIs - if (server.wadoRoot.indexOf('https') === 0 && !uri.includes('https')) { - uri = uri.replace('http', 'https'); - } - - const config = { - url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used - headers: DICOMWeb.getAuthorizationHeader(server), - errorInterceptor: errorHandler.getHTTPErrorHandler(), - }; - const dicomWeb = new api.DICOMwebClient(config); - const options = { - BulkDataURI: uri, - }; - - return dicomWeb - .retrieveBulkData(options) - .then(result => result[0]) - .then(unpackOverlay); -} diff --git a/platform/core/src/utils/str2ab.js b/platform/core/src/utils/str2ab.js index 3125e0bd8..f804b551b 100644 --- a/platform/core/src/utils/str2ab.js +++ b/platform/core/src/utils/str2ab.js @@ -4,13 +4,4 @@ * @param {String} str Input String * @return {ArrayBuffer} Output converted ArrayBuffer */ -export default function str2ab(str) { - const strLen = str.length; - const bytes = new Uint8Array(strLen); - - for (let i = 0; i < strLen; i++) { - bytes[i] = str.charCodeAt(i); - } - - return bytes.buffer; -} +export default str => Uint8Array.from(atob(str), c => c.charCodeAt(0));