Fix palette colour display in OHIF v3 (#2731)

* fix(OHIFv3):Fix palette colour data display

* PR comments - added params description
This commit is contained in:
Bill Wallace authored and GitHub committed 2022-03-11 11:18:56 -05:00
1 parent 3184e5572f
commit f3822f98a3
4 files changed
+67 -231

No files matched your search

@@ -1,165 +1,72 @@
import { api } from 'dicomweb-client';
import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import errorHandler from '../../errorHandler';
export default async function fetchPaletteColorLookupTableData(
instance,
server
/**
* Gets the palette color data for the specified tag - red/green/blue,
* either from the given UID or from the tag itself.
* Returns an array if the data is immediately available, or a promise
* which resolves to the data if the data needs to be loaded.
* Returns undefined if the palette isn't specified.
*
* @param {*} item containing the palette colour data and description
* @param {*} tag is the tag for the palette data
* @param {*} descriptorTag is the tag for the descriptor
* @returns Array view containing the palette data, or a promise to return one.
* Returns undefined if the palette data is absent.
*/
export default function fetchPaletteColorLookupTableData(
item, tag, descriptorTag
) {
const {
PaletteColorLookupTableUID,
RedPaletteColorLookupTableDescriptor,
GreenPaletteColorLookupTableDescriptor,
BluePaletteColorLookupTableDescriptor,
RedPaletteColorLookupTableData,
GreenPaletteColorLookupTableData,
BluePaletteColorLookupTableData,
} = instance;
return new Promise((resolve, reject) => {
let entry;
if (_paletteColorCache.isValidUID(PaletteColorLookupTableUID)) {
entry = _paletteColorCache.get(PaletteColorLookupTableUID);
if (entry) {
return resolve(entry);
}
}
// no entry in cache... Fetch remote data.
const promises = [
_getPaletteColor(
server,
RedPaletteColorLookupTableData,
RedPaletteColorLookupTableDescriptor
),
_getPaletteColor(
server,
GreenPaletteColorLookupTableData,
GreenPaletteColorLookupTableDescriptor
),
_getPaletteColor(
server,
BluePaletteColorLookupTableData,
BluePaletteColorLookupTableDescriptor
),
];
Promise.all(promises).then(
([
RedPaletteColorLookupTableData,
GreenPaletteColorLookupTableData,
BluePaletteColorLookupTableData,
]) => {
// when PaletteColorLookupTableUID is present, the entry can be cached...
_paletteColorCache.add({
RedPaletteColorLookupTableData,
GreenPaletteColorLookupTableData,
BluePaletteColorLookupTableData,
PaletteColorLookupTableUID,
});
instance.RedPaletteColorLookupTableData = RedPaletteColorLookupTableData;
instance.GreenPaletteColorLookupTableData = GreenPaletteColorLookupTableData;
instance.BluePaletteColorLookupTableData = BluePaletteColorLookupTableData;
resolve();
}
);
});
const { PaletteColorLookupTableUID } = item;
const paletteData = item[tag];
if (paletteData === undefined && PaletteColorLookupTableUID === undefined) return;
// performance optimization - read UID and cache by UID
return _getPaletteColor(
item[tag],
item[descriptorTag]
)
}
/**
* Simple cache schema for retrieved color palettes.
*/
const _paletteColorCache = {
count: 0,
maxAge: 24 * 60 * 60 * 1000, // 24h cache?
entries: {},
isValidUID: function(PaletteColorLookupTableUID) {
return (
typeof PaletteColorLookupTableUID === 'string' &&
PaletteColorLookupTableUID.length > 0
);
},
get: function(PaletteColorLookupTableUID) {
let entry = null;
if (this.entries.hasOwnProperty(PaletteColorLookupTableUID)) {
entry = this.entries[PaletteColorLookupTableUID];
// check how the entry is...
if (Date.now() - entry.time > this.maxAge) {
// entry is too old... remove entry.
delete this.entries[PaletteColorLookupTableUID];
this.count--;
entry = null;
}
}
return entry;
},
add: function(entry) {
if (this.isValidUID(entry.uid)) {
let PaletteColorLookupTableUID = entry.uid;
if (this.entries.hasOwnProperty(PaletteColorLookupTableUID) !== true) {
this.count++; // increment cache entry count...
}
entry.time = Date.now();
this.entries[PaletteColorLookupTableUID] = entry;
// @TODO: Add logic to get rid of old entries and reduce memory usage...
}
},
};
function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
function _getPaletteColor(paletteColorLookupTableData, lutDescriptor) {
const numLutEntries = lutDescriptor[0];
const bits = lutDescriptor[2];
if (!paletteColorLookupTableData) return undefined;
const arrayBufferToPaletteColorLUT = arraybuffer => {
const byteArray =
bits === 16 ? new Uint16Array(arraybuffer) : new Uint8Array(arraybuffer);
const lut = [];
for (let i = 0; i < numLutEntries; i++) {
lut[i] = byteArray[i];
}
if (bits === 16) {
let j = 0;
for (let i = 0; i < numLutEntries; i++) {
lut[i] = arraybuffer[j++] + arraybuffer[j++] << 8;
}
} else {
for (let i = 0; i < numLutEntries; i++) {
lut[i] = byteArray[i];
}
}
return lut;
};
if (paletteColorLookupTableData.BulkDataURI) {
let uri = paletteColorLookupTableData.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(arrayBufferToPaletteColorLUT);
} else if (paletteColorLookupTableData.InlineBinary) {
const inlineBinaryData = atob(paletteColorLookupTableData.InlineBinary);
const arraybuffer = str2ab(inlineBinaryData);
return new Promise(resolve => {
resolve(arrayBufferToPaletteColorLUT(arraybuffer));
});
} else {
return Promise.resolve(
arrayBufferToPaletteColorLUT(paletteColorLookupTableData)
);
if (paletteColorLookupTableData.palette) {
return paletteColorLookupTableData.palette;
}
if (paletteColorLookupTableData.InlineBinary) {
try {
const arraybuffer = Uint8Array.from(atob(paletteColorLookupTableData.InlineBinary), c =>
c.charCodeAt(0)
);
return (paletteColorLookupTableData.palette = arrayBufferToPaletteColorLUT(arraybuffer));
} catch (e) {
console.log("Couldn't decode", paletteColorLookupTableData.InlineBinary, e);
return undefined;
}
}
if (paletteColorLookupTableData.retrieveBulkData) {
return paletteColorLookupTableData.retrieveBulkData().then(val =>
(paletteColorLookupTableData.palette = arrayBufferToPaletteColorLUT(val)));
}
throw new Error(`No data found for ${paletteColorLookupTableData} palette`)
}