cornerstone-wado-image-loader
Version:
Cornerstone Image Loader for DICOM WADO-URI and WADO-RS
354 lines (283 loc) • 13.3 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>shared/decodeImageFrame.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav >
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#addTask">addTask</a></li><li><a href="global.html#arrayBufferToString">arrayBufferToString</a></li><li><a href="global.html#cacheSizeInBytes">cacheSizeInBytes</a></li><li><a href="global.html#cancelTask">cancelTask</a></li><li><a href="global.html#decodeAsync">decodeAsync</a></li><li><a href="global.html#decodeImageFrame">decodeImageFrame</a></li><li><a href="global.html#extractOrientationFromDataset">extractOrientationFromDataset</a></li><li><a href="global.html#extractOrientationFromMetadata">extractOrientationFromMetadata</a></li><li><a href="global.html#extractOrientationFromNMMultiframeDataset">extractOrientationFromNMMultiframeDataset</a></li><li><a href="global.html#extractOrientationFromNMMultiframeMetadata">extractOrientationFromNMMultiframeMetadata</a></li><li><a href="global.html#extractPositionFromDataset">extractPositionFromDataset</a></li><li><a href="global.html#extractPositionFromMetadata">extractPositionFromMetadata</a></li><li><a href="global.html#extractPositionFromNMMultiframeDataset">extractPositionFromNMMultiframeDataset</a></li><li><a href="global.html#extractPositionFromNMMultiframeMetadata">extractPositionFromNMMultiframeMetadata</a></li><li><a href="global.html#extractSliceThicknessFromDataset">extractSliceThicknessFromDataset</a></li><li><a href="global.html#extractSpacingFromDataset">extractSpacingFromDataset</a></li><li><a href="global.html#framesAreFragmented">framesAreFragmented</a></li><li><a href="global.html#getImageTypeSubItemFromDataset">getImageTypeSubItemFromDataset</a></li><li><a href="global.html#getImageTypeSubItemFromMetadata">getImageTypeSubItemFromMetadata</a></li><li><a href="global.html#getMinMax">getMinMax</a></li><li><a href="global.html#getNumberString">getNumberString</a></li><li><a href="global.html#getNumberValues">getNumberValues</a></li><li><a href="global.html#getStatistics">getStatistics</a></li><li><a href="global.html#getTransferSyntaxForContentType">getTransferSyntaxForContentType</a></li><li><a href="global.html#getUncompressedImageFrame">getUncompressedImageFrame</a></li><li><a href="global.html#getValue">getValue</a></li><li><a href="global.html#handleMessageFromWorker">handleMessageFromWorker</a></li><li><a href="global.html#handler">handler</a></li><li><a href="global.html#initialize">initialize</a></li><li><a href="global.html#loadCodecs">loadCodecs</a></li><li><a href="global.html#loadWebWorkerTask">loadWebWorkerTask</a></li><li><a href="global.html#registerLoaders">registerLoaders</a></li><li><a href="global.html#registerTaskHandler">registerTaskHandler</a></li><li><a href="global.html#removeAFromRGBA">removeAFromRGBA</a></li><li><a href="global.html#setPixelDataType">setPixelDataType</a></li><li><a href="global.html#setTaskPriority">setTaskPriority</a></li><li><a href="global.html#spawnWebWorker">spawnWebWorker</a></li><li><a href="global.html#startTaskOnWebWorker">startTaskOnWebWorker</a></li><li><a href="global.html#terminate">terminate</a></li><li><a href="global.html#unpackBinaryFrame">unpackBinaryFrame</a></li></ul>
</nav>
<div id="main">
<h1 class="page-title">shared/decodeImageFrame.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/* eslint-disable complexity */
import decodeLittleEndian from './decoders/decodeLittleEndian.js';
import decodeBigEndian from './decoders/decodeBigEndian.js';
import decodeRLE from './decoders/decodeRLE.js';
import decodeJPEGBaseline8Bit from './decoders/decodeJPEGBaseline8Bit.js';
// import decodeJPEGBaseline12Bit from './decoders/decodeJPEGBaseline12Bit.js';
import decodeJPEGBaseline12Bit from './decoders/decodeJPEGBaseline12Bit-js.js';
import decodeJPEGLossless from './decoders/decodeJPEGLossless.js';
import decodeJPEGLS from './decoders/decodeJPEGLS.js';
import decodeJPEG2000 from './decoders/decodeJPEG2000.js';
import decodeHTJ2K from './decoders/decodeHTJ2K.js';
import scaleArray from './scaling/scaleArray.js';
/**
* Decodes the provided image frame.
* This is an async function return the result, or you can provide an optional
* callbackFn that is called with the results.
*/
async function decodeImageFrame(
imageFrame,
transferSyntax,
pixelData,
decodeConfig,
options,
callbackFn
) {
const start = new Date().getTime();
let decodePromise = null;
let opts;
switch (transferSyntax) {
case '1.2.840.10008.1.2':
case '1.2.840.10008.1.2.1':
// Implicit or Explicit VR Little Endian
decodePromise = decodeLittleEndian(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.2':
// Explicit VR Big Endian (retired)
decodePromise = decodeBigEndian(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.1.99':
// Deflate transfer syntax (deflated by dicomParser)
decodePromise = decodeLittleEndian(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.5':
// RLE Lossless
decodePromise = decodeRLE(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.4.50':
// JPEG Baseline lossy process 1 (8 bit)
opts = {
...imageFrame,
};
decodePromise = decodeJPEGBaseline8Bit(pixelData, opts);
break;
case '1.2.840.10008.1.2.4.51':
// JPEG Baseline lossy process 2 & 4 (12 bit)
// opts = {
// ...imageFrame,
// };
// decodePromise = decodeJPEGBaseline12Bit(pixelData, opts);
//throw new Error('Currently unsupported: 1.2.840.10008.1.2.4.51');
decodePromise = decodeJPEGBaseline12Bit(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.4.57':
// JPEG Lossless, Nonhierarchical (Processes 14)
decodePromise = decodeJPEGLossless(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.4.70':
// JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1])
decodePromise = decodeJPEGLossless(imageFrame, pixelData);
break;
case '1.2.840.10008.1.2.4.80':
// JPEG-LS Lossless Image Compression
opts = {
signed: imageFrame.pixelRepresentation === 1, // imageFrame.signed,
// shouldn't need...
bytesPerPixel: imageFrame.bitsAllocated <= 8 ? 1 : 2,
...imageFrame,
};
decodePromise = decodeJPEGLS(pixelData, opts);
break;
case '1.2.840.10008.1.2.4.81':
// JPEG-LS Lossy (Near-Lossless) Image Compression
opts = {
signed: false, // imageFrame.signed,
// shouldn't need...
bytesPerPixel: imageFrame.bitsAllocated <= 8 ? 1 : 2,
...imageFrame,
};
decodePromise = decodeJPEGLS(pixelData, opts);
break;
case '1.2.840.10008.1.2.4.90':
opts = {
...imageFrame,
};
// JPEG 2000 Lossless
// imageFrame, pixelData, decodeConfig, options
decodePromise = decodeJPEG2000(pixelData, opts);
break;
case '1.2.840.10008.1.2.4.91':
// JPEG 2000 Lossy
opts = {
...imageFrame,
};
// JPEG 2000 Lossy
// imageFrame, pixelData, decodeConfig, options
decodePromise = decodeJPEG2000(pixelData, opts);
break;
case '3.2.840.10008.1.2.4.96':
// HTJ2K
opts = {
...imageFrame,
};
decodePromise = decodeHTJ2K(pixelData, opts);
break;
default:
throw new Error(`no decoder for transfer syntax ${transferSyntax}`);
}
/* Don't know if these work...
// JPEG 2000 Part 2 Multicomponent Image Compression (Lossless Only)
else if(transferSyntax === "1.2.840.10008.1.2.4.92")
{
return decodeJPEG2000(dataSet, frame);
}
// JPEG 2000 Part 2 Multicomponent Image Compression
else if(transferSyntax === "1.2.840.10008.1.2.4.93")
{
return decodeJPEG2000(dataSet, frame);
}
*/
if (!decodePromise) {
throw new Error('decodePromise not defined');
}
const decodedFrame = await decodePromise;
const postProcessed = postProcessDecodedPixels(
decodedFrame,
options,
start,
decodeConfig
);
// Call the callbackFn to agree with older arguments
callbackFn?.(postProcessed);
return postProcessed;
}
function postProcessDecodedPixels(imageFrame, options, start, decodeConfig) {
const { use16BitDataType } = decodeConfig || {};
const shouldShift =
imageFrame.pixelRepresentation !== undefined &&
imageFrame.pixelRepresentation === 1;
const shift =
shouldShift && imageFrame.bitsStored !== undefined
? 32 - imageFrame.bitsStored
: undefined;
if (shouldShift && shift !== undefined) {
for (let i = 0; i < imageFrame.pixelData.length; i++) {
// eslint-disable-next-line no-bitwise
imageFrame.pixelData[i] = (imageFrame.pixelData[i] << shift) >> shift;
}
}
// Cache the pixelData reference quickly incase we want to set a targetBuffer _and_ scale.
let pixelDataArray = imageFrame.pixelData;
imageFrame.pixelDataLength = imageFrame.pixelData.length;
if (options.targetBuffer) {
let offset, length;
// If we have a target buffer, write to that instead. This helps reduce memory duplication.
({ offset, length } = options.targetBuffer);
const { arrayBuffer, type } = options.targetBuffer;
let TypedArrayConstructor;
if (offset === null || offset === undefined) {
offset = 0;
}
if ((length === null || length === undefined) && offset !== 0) {
length = imageFrame.pixelDataLength - offset;
} else if (length === null || length === undefined) {
length = imageFrame.pixelDataLength;
}
switch (type) {
case 'Uint8Array':
TypedArrayConstructor = Uint8Array;
break;
case use16BitDataType && 'Uint16Array':
TypedArrayConstructor = Uint16Array;
break;
case use16BitDataType && 'Int16Array':
TypedArrayConstructor = Int16Array;
break;
case 'Float32Array':
TypedArrayConstructor = Float32Array;
break;
default:
throw new Error('target array for image does not have a valid type.');
}
const imageFramePixelData = imageFrame.pixelData;
if (length !== imageFramePixelData.length) {
throw new Error(
`target array for image does not have the same length (${length}) as the decoded image length (${imageFramePixelData.length}).`
);
}
// TypedArray.Set is api level and ~50x faster than copying elements even for
// Arrays of different types, which aren't simply memcpy ops.
let typedArray;
if (arrayBuffer) {
typedArray = new TypedArrayConstructor(arrayBuffer, offset, length);
} else {
typedArray = new TypedArrayConstructor(length);
}
typedArray.set(imageFramePixelData, 0);
// If need to scale, need to scale correct array.
pixelDataArray = typedArray;
}
if (options.preScale.enabled) {
const scalingParameters = options.preScale.scalingParameters;
if (!scalingParameters) {
throw new Error(
'options.preScale.scalingParameters must be defined if preScale.enabled is true, and scalingParameters cannot be derived from the metadata providers.'
);
}
const { rescaleSlope, rescaleIntercept } = scalingParameters;
if (
typeof rescaleSlope === 'number' &&
typeof rescaleIntercept === 'number'
) {
if (scaleArray(pixelDataArray, scalingParameters)) {
imageFrame.preScale = {
...options.preScale,
scaled: true,
};
}
}
}
// Handle cases where the targetBuffer is not backed by a SharedArrayBuffer
if (
options.targetBuffer &&
(!options.targetBuffer.arrayBuffer ||
options.targetBuffer.arrayBuffer instanceof ArrayBuffer)
) {
imageFrame.pixelData = pixelDataArray;
}
const end = new Date().getTime();
imageFrame.decodeTimeInMS = end - start;
return imageFrame;
}
export default decodeImageFrame;
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a> using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>
</body>
</html>