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:
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`)
|
||||
}
|
||||
Reference in new issue
Block a user