@abasb75/dicom-pixel-decoder
Version:
A lightweight DICOM image decoder.
960 lines (937 loc) • 32.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/Utilities/getMinMax.ts
function getMinMax(arrayPixel) {
let min = arrayPixel[0];
let max = arrayPixel[0];
for (let i = 1; i < arrayPixel.length; i++) {
const pixel = arrayPixel[i];
if (pixel < min) min = pixel;
if (pixel > max) max = pixel;
}
return { min, max };
}
var getMinMax_default = getMinMax;
// src/Utilities/PaletteColor.ts
var PaletteColor = class _PaletteColor {
static applyPaletteColor(pixelData, paletteColorData) {
if (!paletteColorData || !paletteColorData.red || !paletteColorData.blue || !paletteColorData.green) {
return pixelData;
}
if (!paletteColorData.red.littleEndian && paletteColorData.red.bitsPerEntry === 16 && pixelData instanceof Uint8Array) {
for (let i = 0; i < pixelData.byteLength; i += 2) {
const pixel1 = pixelData[i];
const pixel2 = pixelData[i + 1];
pixelData[i] = pixel2;
pixelData[i + 1] = pixel1;
}
}
const redPixels = _PaletteColor.paletteDataToPixel(pixelData, paletteColorData.red);
const greenPixels = _PaletteColor.paletteDataToPixel(pixelData, paletteColorData.green);
const bluePixels = _PaletteColor.paletteDataToPixel(pixelData, paletteColorData.blue);
if (!redPixels || !greenPixels || !bluePixels) {
return pixelData;
}
let _pixelData = null;
if (paletteColorData.red.bitsPerEntry === 16) {
_pixelData = new Uint16Array(pixelData.length * 3);
} else {
_pixelData = new Uint8Array(pixelData.length * 3);
}
for (let i = 0; i < pixelData.length; i++) {
_pixelData[i * 3] = redPixels[i];
_pixelData[i * 3 + 1] = greenPixels[i];
_pixelData[i * 3 + 2] = bluePixels[i];
}
return _pixelData;
}
static paletteDataToPixel(pixelData, colorData) {
if (!colorData.data) {
return null;
}
let _pixelData = new Array(pixelData.length);
for (let i = 0; i < pixelData.length; i++) {
let pixel = pixelData[i];
if (pixel < colorData.firstInputValueMapped) {
pixel = 0;
} else if (pixel > colorData.firstInputValueMapped + colorData.lutEntries - 1) {
pixel = colorData.lutEntries - 1;
} else {
pixel = pixel - colorData.firstInputValueMapped;
}
_pixelData[i] = colorData.data[pixel];
}
return _pixelData;
}
};
var PaletteColor_default = PaletteColor;
// src/Utilities/PixelSpacing.ts
var PixelSpacing = class _PixelSpacing {
static apply(pixelData, pixelSpacing, width, hieght, samplePerPixel) {
if (!pixelSpacing || !Array.isArray(pixelSpacing)) {
return {
pixelData,
pixelSpacing,
width,
hieght
};
}
if (pixelSpacing.length > 2 || pixelSpacing[0] === pixelSpacing[1]) {
return {
pixelData,
pixelSpacing,
width,
hieght
};
}
const xPixelSpacing = pixelSpacing[1];
const yPixelSpacing = pixelSpacing[0];
if (xPixelSpacing > yPixelSpacing) {
return _PixelSpacing.xScaling(
pixelData,
xPixelSpacing,
yPixelSpacing,
width,
hieght,
samplePerPixel
);
}
return _PixelSpacing.yScaling(
pixelData,
xPixelSpacing,
yPixelSpacing,
width,
hieght,
samplePerPixel
);
}
// untested
static yScaling(pixelData, xPixelSpacing, yPixelSpacing, width, hieght, samplePerPixel) {
const scaledX = yPixelSpacing / xPixelSpacing;
const newHieght = Math.floor(hieght * scaledX);
const _pixelData = new pixelData.constructor(width * newHieght * samplePerPixel);
for (let i = 0; i < width; i++) {
for (let j = 0; j < newHieght; j++) {
const base = i * width * j;
if (j === 0) {
for (let k = 0; k < samplePerPixel; k++) {
_pixelData[i + k] = pixelData[i + k];
}
continue;
}
const targetPixelIndex = Math.round(i * hieght / newHieght);
for (let k = 0; k < samplePerPixel; k++) {
_pixelData[base + j + k] = pixelData[i * width * targetPixelIndex + k];
}
}
}
return {
pixelData: _pixelData,
width,
hieght: newHieght
};
}
static xScaling(pixelData, xPixelSpacing, yPixelSpacing, width, hieght, samplePerPixel) {
const scaledX = xPixelSpacing / yPixelSpacing;
const newWidth = Math.floor(width * scaledX);
const _pixelData = new pixelData.constructor(newWidth * hieght * samplePerPixel);
for (let j = 0; j < hieght; j++) {
for (let i = 0; i < newWidth; i++) {
const baseTarget = j * newWidth;
const baseSource = j * width;
if (i === 0) {
for (let k = 0; k < samplePerPixel; k++) {
_pixelData[baseTarget + i + k] = pixelData[baseSource + i + k];
}
continue;
}
const targetPixelIndex = Math.round(i * width / newWidth);
for (let k = 0; k < samplePerPixel; k++) {
_pixelData[baseTarget + i + k] = pixelData[baseSource + targetPixelIndex + k];
}
}
}
return {
pixelData: _pixelData,
width: newWidth,
hieght
};
}
};
var PixelSpacing_default = PixelSpacing;
// src/DecodedImage.ts
var DecodedImage = class {
constructor(transferSyntax, width, height, pixelData) {
__publicField(this, "transferSyntax");
__publicField(this, "width");
__publicField(this, "height");
__publicField(this, "min");
__publicField(this, "max");
__publicField(this, "windowWidth");
__publicField(this, "windowCenter");
__publicField(this, "pixelData");
__publicField(this, "photometricInterpretation");
__publicField(this, "pixelModule");
__publicField(this, "scalingModule");
__publicField(this, "voiLUTModule");
__publicField(this, "arrayType");
__publicField(this, "littleEndian", true);
__publicField(this, "pixelTypes", "Integer");
this.transferSyntax = transferSyntax;
this.width = width;
this.height = height;
this.pixelData = pixelData;
}
getMinMax() {
return getMinMax_default(this.pixelData);
}
getLUT() {
return {
...this.getMinMax(),
windowWidth: this.windowWidth,
windowCenter: this.windowCenter
};
}
applyPaletteColor(paleteData) {
if (paleteData) {
this.pixelData = PaletteColor_default.applyPaletteColor(
this.pixelData,
paleteData
);
Decoder_default._setLUT(this, {
...this.pixelModule,
...this.scalingModule,
...this.voiLUTModule,
littleEndian: this.littleEndian,
transferSyntaxUID: this.transferSyntax,
isFloat: this.pixelTypes === "Float"
});
}
}
applySpacing() {
const scaled = PixelSpacing_default.apply(
this.pixelData,
this.pixelModule?.pixelSpacing,
this.width,
this.height,
this.pixelModule?.samplesPerPixel || 1
);
this.pixelData = scaled.pixelData;
this.width = scaled.width;
this.height = scaled.hieght;
}
};
var DecodedImage_default = DecodedImage;
// src/Decoders/JPEG2000.ts
import { decode } from "@abasb75/jpeg2000-decoder";
var JPEG2000 = class {
static async decode(pixelData) {
const buffer = new Uint8Array(pixelData.buffer, pixelData.byteOffset, pixelData.byteLength).buffer;
const decoded = await decode(buffer);
return decoded.decodedBuffer;
}
};
var JPEG2000_default = JPEG2000;
// src/Decoders/JPEGBaselineLossyProcess1_8bit.ts
import turboJPEGDecoder from "@abasb75/turbojpeg-codec/decode";
import JpegImage from "@abasb75/jpegjs";
var turboJPEG = null;
var JPEGBaselineLossyProcess1_8bit = class _JPEGBaselineLossyProcess1_8bit {
static async decode(pixelData, options) {
try {
if (options.bitsAllocated === 8 && [3, 4].includes(options.samplesPerPixel)) {
return _JPEGBaselineLossyProcess1_8bit.browser(pixelData);
} else {
return _JPEGBaselineLossyProcess1_8bit.jpegJS(pixelData, options);
}
} catch {
const data = await _JPEGBaselineLossyProcess1_8bit.turboJpeg(pixelData);
return data;
}
}
static async jpegJS(pixelData, options) {
const jpeg2 = new JpegImage();
jpeg2.parse(new Uint8Array(pixelData.buffer));
if (options.bitsAllocated > 8) {
return jpeg2.getData16(options.columns, options.rows);
}
return jpeg2.getData(options.columns, options.rows);
}
static async browser(pixelData) {
if (typeof document === "undefined") {
if (typeof createImageBitmap !== "undefined" && typeof OffscreenCanvas !== "undefined") {
return await this.browserV2(pixelData);
}
return await _JPEGBaselineLossyProcess1_8bit.turboJpeg(pixelData);
}
const createImage = (imageData2) => new Promise((resolve, reject) => {
var img2 = document.createElement("img");
img2.src = imageData2;
img2.onload = () => {
resolve(img2);
};
img2.onerror = () => {
reject();
};
});
const buffer = new Uint8Array(pixelData.buffer, pixelData.byteOffset, pixelData.byteLength).slice().buffer;
var blob = new Blob([buffer], { type: "image/jpeg" });
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL(blob);
const img = await createImage(imageUrl);
const canvas = document.createElement("canvas");
canvas.height = img.height;
canvas.width = img.width;
const context = canvas.getContext("2d");
context?.drawImage(img, 0, 0);
const imageData = context?.getImageData(0, 0, img.width, img.height);
return new Uint8Array(imageData?.data?.buffer);
}
static async browserV2(pixelData) {
const buffer = new Uint8Array(pixelData.buffer, pixelData.byteOffset, pixelData.byteLength).slice().buffer;
const blob = new Blob([buffer], { type: "image/jpeg" });
const imageBitmap = await createImageBitmap(blob);
const canvas = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("Could not get OffscreenCanvasRenderingContext2D");
}
ctx.drawImage(imageBitmap, 0, 0);
const imageData = ctx.getImageData(0, 0, imageBitmap.width, imageBitmap.height);
return new Uint8Array(imageData.data.buffer);
}
static async turboJpeg(pixelData) {
if (!turboJPEG) {
turboJPEG = await turboJPEGDecoder();
}
const iterations = 1;
const decoder3 = new turboJPEG.JPEGDecoder();
const encodedBitStream = new Uint8Array(pixelData.buffer);
const encodedBuffer = decoder3.getEncodedBuffer(encodedBitStream.length);
encodedBuffer.set(encodedBitStream);
for (let i = 0; i < iterations; i++) {
decoder3.decode();
}
const frameInfo = decoder3.getFrameInfo();
var decodedBuffer = decoder3.getDecodedBuffer();
return decodedBuffer;
}
};
var JPEGBaselineLossyProcess1_8bit_default = JPEGBaselineLossyProcess1_8bit;
// src/Decoders/JPEGBaselineLossyProcess2_12bit.ts
var JPEGBaselineLossyProcess2_12bit = class extends JPEGBaselineLossyProcess1_8bit_default {
static async decode(pixelData, options) {
return this.jpegJS(pixelData, options);
}
};
var JPEGBaselineLossyProcess2_12bit_default = JPEGBaselineLossyProcess2_12bit;
// src/Decoders/JPEGLS.ts
import CharLSDecoder from "@abasb75/charls/decode";
var decoder = null;
var decode2 = null;
var JPEGLS = class {
static async decode(pixelData) {
if (!decoder || !decode2) {
decoder = await CharLSDecoder();
decode2 = decoder.decode;
}
const buffer = new Uint8Array(pixelData.buffer, pixelData.byteOffset, pixelData.byteLength).slice();
const decoded = await decode2(buffer);
return decoded?.data;
}
};
var JPEGLS_default = JPEGLS;
// src/Decoders/JPEGLossLess.ts
import * as jpeg from "jpeg-lossless-decoder-js";
var decoder2 = null;
var JPEGLossLess = class {
static async decode(pixelData, options) {
if (!decoder2) {
const Decoder3 = jpeg.Decoder;
decoder2 = new Decoder3();
}
const decoded = decoder2.decode(
pixelData.buffer,
pixelData.byteOffset,
pixelData.byteLength,
options.bitsAllocated === 8 ? 1 : 2
);
return decoded;
}
};
var JPEGLossLess_default = JPEGLossLess;
// src/Decoders/Uncompressed.ts
var UncompressedDecoder = class {
// only convert dataview to uint8 now
static decode(pixelData) {
let arrayBuffer = pixelData.buffer;
return new Uint8Array(arrayBuffer);
}
};
var Uncompressed_default = UncompressedDecoder;
// src/Utilities/changeTypedArray.ts
function changeTypedArray(pixelArray, minAfterScale, maxAfterScale) {
if (Number.isInteger(minAfterScale) && Number.isInteger(maxAfterScale)) {
if (minAfterScale >= 0 && minAfterScale <= 255) {
return new Uint8Array(pixelArray);
} else if (minAfterScale >= 0 && minAfterScale <= 65535) {
return new Uint16Array(pixelArray);
} else if (minAfterScale >= 0 && maxAfterScale <= 4294967295) {
return new Uint32Array(pixelArray);
} else if (minAfterScale >= -128 && minAfterScale <= 127) {
return new Int8Array(pixelArray);
} else if (minAfterScale >= -32768 && minAfterScale <= 32767) {
return new Int16Array(pixelArray);
} else if (minAfterScale >= -2147483648 && maxAfterScale <= 2147483647) {
return new Int32Array(pixelArray);
}
}
return new Float32Array(pixelArray);
}
var changeTypedArray_default = changeTypedArray;
// src/Utilities/getIsArrayPixelHasValidType.ts
function getIsArrayPixelHasValidType(arrayPixel, min, max) {
if (arrayPixel instanceof Uint8Array) {
if (min < 0 || max > 255) {
return false;
}
} else if (arrayPixel instanceof Int8Array) {
if (min < -128 || max > 127) {
return false;
}
} else if (arrayPixel instanceof Uint16Array) {
if (min < 0 || max > 65535) {
return false;
}
} else if (arrayPixel instanceof Int16Array) {
if (min < -32768 || max > 32767) {
return false;
}
}
return true;
}
var getIsArrayPixelHasValidType_default = getIsArrayPixelHasValidType;
// src/Utilities/getMinMaxAfterScale.ts
function getMinMaxAfterScale(min, max, rescaleSlope, rescaleIntercept) {
if (typeof rescaleSlope !== "number" || typeof rescaleIntercept !== "number") {
return { min, max };
}
return {
minAfterScale: min * rescaleSlope + rescaleIntercept,
maxAfterScale: max * rescaleSlope + rescaleIntercept
};
}
var getMinMaxAfterScale_default = getMinMaxAfterScale;
// src/Decoders/RLE.ts
import * as dicomRle from "dicom-rle";
var RLE = class {
static decode(pixelData, options) {
const {
rows,
columns,
samplesPerPixel,
bitsAllocated,
planarConfiguration
} = options;
if (!rows || !columns || !samplesPerPixel) {
return null;
}
const decoder3 = new dicomRle.RleDecoder();
const decoded = decoder3.decode(new Uint8Array(pixelData.buffer), {
height: rows,
width: columns,
samplesPerPixel,
bitsAllocated,
planarConfiguration
});
return decoded;
}
};
var RLE_default = RLE;
// src/Decoders/HTJ2K.ts
import OpenJPHDecoder from "@abasb75/openjph/decode";
var openjphDecoder = null;
var HTJ2K = class {
static async decode(pixelData) {
if (!openjphDecoder) {
openjphDecoder = await OpenJPHDecoder();
}
const uint8Array = new Uint8Array(pixelData.buffer);
const iterations = 1;
const decodeLevel = 0;
const decoder3 = new openjphDecoder.HTJ2KDecoder();
const encodedBuffer = decoder3.getEncodedBuffer(uint8Array.length);
encodedBuffer.set(uint8Array);
decoder3.readHeader();
for (let i = 0; i < iterations; i++) {
decoder3.decodeSubResolution(decodeLevel);
}
const frameInfo = decoder3.getFrameInfo();
const decodedBuffer = decoder3.getDecodedBuffer();
console.log({ frameInfo });
return decodedBuffer;
}
};
var HTJ2K_default = HTJ2K;
// src/Utilities/ybrFull.ts
function yrbFullToRgba(pixelData, pixelModule) {
const { columns, rows, planarConfiguration } = pixelModule;
if (!columns || !rows) {
return pixelData;
}
if (pixelData.length !== columns * rows * 3) {
return pixelData;
}
const pixelCounts = rows * columns;
const _pixelData = new Uint8ClampedArray(pixelCounts * 4);
if (planarConfiguration === 1) {
for (let i = 0; i < pixelCounts; i++) {
const y = pixelData[i];
const cb = pixelData[pixelCounts + i];
const cr = pixelData[pixelCounts * 2 + i];
_pixelData[i * 4] = y + 1.402 * (cr - 128);
_pixelData[i * 4 + 1] = y - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
_pixelData[i * 4 + 2] = y + 1.772 * (cb - 128);
_pixelData[i * 4 + 3] = 255;
}
} else {
for (let i = 0; i < pixelCounts; i++) {
const y = pixelData[i * 3];
const cb = pixelData[i * 3 + 1];
const cr = pixelData[i * 3 + 2];
_pixelData[i * 4] = y + 1.402 * (cr - 128);
_pixelData[i * 4 + 1] = y - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
_pixelData[i * 4 + 2] = y + 1.772 * (cb - 128);
_pixelData[i * 4 + 3] = 255;
}
}
return new Uint8Array(_pixelData);
}
var ybrFull_default = yrbFullToRgba;
// src/Utilities/ybrFull422.ts
function ybrFull422ToRgba(pixelData, pixelModule) {
const { columns, rows } = pixelModule;
if (!columns || !rows) {
return pixelData;
}
const pixelCounts = rows * columns;
if (pixelData.length !== 2 * pixelCounts) {
return pixelData;
}
const _pixelData = new Uint8ClampedArray(pixelCounts * 4);
let ybrIndex = 0;
for (let i = 0; i < pixelCounts; i += 2) {
const y1 = pixelData[ybrIndex++];
const y2 = pixelData[ybrIndex++];
const cb = pixelData[ybrIndex++];
const cr = pixelData[ybrIndex++];
_pixelData[i * 4] = y1 + 1.402 * (cr - 128);
_pixelData[i * 4 + 1] = y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
_pixelData[i * 4 + 2] = y1 + 1.772 * (cb - 128);
_pixelData[i * 4 + 3] = 255;
_pixelData[i * 4 + 4] = y2 + 1.402 * (cr - 128);
_pixelData[i * 4 + 5] = y2 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
_pixelData[i * 4 + 6] = y2 + 1.772 * (cb - 128);
_pixelData[i * 4 + 7] = 255;
}
return new Uint16Array(_pixelData);
}
var ybrFull422_default = ybrFull422ToRgba;
// src/Utilities/planerConfiguration.ts
function applyPlanerConfiguration(pixelData) {
if (pixelData.length % 3 !== 0) {
return pixelData;
}
const pixelCounts = pixelData.length / 3;
const _pixelData = new Uint8ClampedArray(pixelCounts * 4);
for (let i = 0; i < pixelCounts; i++) {
_pixelData[i * 4] = pixelData[i];
_pixelData[i * 4 + 1] = pixelData[pixelCounts + i];
_pixelData[i * 4 + 2] = pixelData[2 * pixelCounts + i];
_pixelData[i * 4 + 3] = 255;
}
return new Uint8Array(_pixelData);
}
var planerConfiguration_default = applyPlanerConfiguration;
// src/Decoders/UnSyntaxed.ts
var UnSyntaxed = class {
// for unsupported transfer syntax
// TODO: need to logic for identifying image type
static async decode(pixelData, options) {
console.warn("image has no transfersyntax or supported syntax.");
const { columns, rows } = options;
const bitsAllocated = options.bitsAllocated ? options.bitsAllocated : 8;
if (!columns || !rows) {
throw new Error("image has not sepefected width & row!");
}
if (pixelData.byteLength) {
options.bitsAllocated = bitsAllocated || 8;
return Uncompressed_default.decode(pixelData);
}
}
};
var UnSyntaxed_default = UnSyntaxed;
// src/Utilities/invertMonoChrome1.ts
function invertMonochrome1(image) {
const { min, max } = image.getMinMax();
for (let i = 0; i < image.pixelData.length; i++) {
image.pixelData[i] = max - (image.pixelData[i] + min);
}
return image.pixelData;
}
var invertMonoChrome1_default = invertMonochrome1;
// src/Decoder.ts
var Decoder2 = class _Decoder {
static async decode(pixelData, options) {
const transferSyntaxUID = options.transferSyntaxUID;
let decodedPixelData = null;
switch (transferSyntaxUID) {
case "1.2.840.10008.1.2":
case "1.2.840.10008.1.2.1":
case "1.2.840.10008.1.2.1.99":
case "1.2.840.10008.1.2.2":
decodedPixelData = Uncompressed_default.decode(pixelData);
break;
case "1.2.840.10008.1.2.4.50":
decodedPixelData = await JPEGBaselineLossyProcess1_8bit_default.decode(pixelData, options);
break;
case "1.2.840.10008.1.2.4.51":
case "1.2.840.10008.1.2.4.52":
/**untested */
case "1.2.840.10008.1.2.4.53":
case "1.2.840.10008.1.2.4.54":
/**untested */
case "1.2.840.10008.1.2.4.55":
case "1.2.840.10008.1.2.4.56":
/**untested */
case "1.2.840.10008.1.2.4.58":
/**untested */
case "1.2.840.10008.1.2.4.59":
/**untested */
case "1.2.840.10008.1.2.4.60":
/**untested */
case "1.2.840.10008.1.2.4.61":
/**untested */
case "1.2.840.10008.1.2.4.62":
/**untested */
case "1.2.840.10008.1.2.4.63":
/**untested */
case "1.2.840.10008.1.2.4.64":
decodedPixelData = await JPEGBaselineLossyProcess2_12bit_default.decode(pixelData, options);
break;
case "1.2.840.10008.1.2.4.57":
case "1.2.840.10008.1.2.4.65":
/**untested */
case "1.2.840.10008.1.2.4.66":
/**untested */
case "1.2.840.10008.1.2.4.70":
decodedPixelData = await JPEGLossLess_default.decode(pixelData, options);
break;
case "1.2.840.10008.1.2.4.80":
case "1.2.840.10008.1.2.4.81":
decodedPixelData = await JPEGLS_default.decode(pixelData);
break;
case "1.2.840.10008.1.2.4.90":
case "1.2.840.10008.1.2.4.91":
case "1.2.840.10008.1.2.4.92":
/**untested */
case "1.2.840.10008.1.2.4.93":
decodedPixelData = await JPEG2000_default.decode(pixelData);
break;
case "3.2.840.10008.1.2.4.96":
/**untested */
case "1.2.840.10008.1.2.4.201":
case "1.2.840.10008.1.2.4.202":
/**untested */
case "1.2.840.10008.1.2.4.203":
decodedPixelData = await HTJ2K_default.decode(pixelData);
break;
case "1.2.840.10008.1.2.5":
decodedPixelData = await RLE_default.decode(pixelData, options);
break;
default:
decodedPixelData = await UnSyntaxed_default.decode(pixelData, options);
}
decodedPixelData = _Decoder._toSutibleTypedArray(decodedPixelData, options);
if (!decodedPixelData) return null;
if (!decodedPixelData) {
return null;
}
const image = new DecodedImage_default(
transferSyntaxUID,
options.columns || 0,
options.rows || 0,
decodedPixelData
);
if (options.pixelRepresentation === 1 && options.bitsStored) {
for (let i = 0; i < image.pixelData.length; i++) {
image.pixelData[i] = image.pixelData[i] << 32 - options.bitsStored >> 32 - options.bitsStored;
}
}
image.photometricInterpretation = options.photometricInterpretation;
_Decoder._applyColorSpace(image, options);
image.pixelData = _Decoder._applyScaling(image, options);
_Decoder._setLUT(image, options);
image.pixelData = _Decoder._fixSize(image.pixelData, options);
return image;
}
static _setLUT(image, options) {
if (options.windowCenter && options.windowWidth) {
let windowWidth = options.windowWidth;
let windowCenter = options.windowCenter;
if (Array.isArray(windowWidth)) {
windowWidth = windowWidth.length ? windowWidth[0] : 0;
}
options;
if (Array.isArray(windowCenter)) {
windowCenter = windowCenter.length ? windowCenter[0] : 0;
}
image.windowWidth = windowWidth;
image.windowCenter = windowCenter;
image.max = windowCenter - 0.5 + windowWidth / 2;
image.min = windowCenter - 0.5 - windowWidth / 2;
return;
}
const { min, max } = image.getMinMax();
image.windowWidth = max - min;
image.windowCenter = min + image.windowWidth / 2;
}
static _applyScaling(image, options) {
const { min, max } = image.getMinMax();
image.min = min;
image.max = max;
const { rescaleSlope, rescaleIntercept } = options;
if (typeof rescaleSlope !== "number" || typeof rescaleIntercept !== "number") {
return image.pixelData;
}
const { minAfterScale, maxAfterScale } = getMinMaxAfterScale_default(
min,
max,
rescaleSlope,
rescaleIntercept
);
if (min === minAfterScale && max === maxAfterScale) {
return image.pixelData;
}
const isValidType = getIsArrayPixelHasValidType_default(
image.pixelData,
minAfterScale || 0,
maxAfterScale || 0
);
image.min = minAfterScale;
image.max = maxAfterScale;
const _decodedPixelData = isValidType ? image.pixelData : changeTypedArray_default(
image.pixelData,
image.min || 0,
image.max || 0
);
for (let i = 0; i < _decodedPixelData.length; i++) {
_decodedPixelData[i] = _decodedPixelData[i] * rescaleSlope + rescaleIntercept;
}
return _decodedPixelData;
}
static _applyColorSpace(image, options) {
if (image.photometricInterpretation === "MONOCHROME1") {
image.pixelData = invertMonoChrome1_default(
image
);
}
if (["RGB", "YBR"].includes(options.photometricInterpretation) && options.planarConfiguration) {
image.pixelData = planerConfiguration_default(image.pixelData);
}
if (options.photometricInterpretation === "YBR_FULL") {
image.pixelData = ybrFull_default(image.pixelData, options);
} else if (options.photometricInterpretation === "YBR_FULL_422") {
image.pixelData = ybrFull422_default(image.pixelData, options);
}
}
static _toSutibleTypedArray(pixelData, options) {
const { bitsAllocated } = options;
const offset = pixelData.byteOffset;
const length = pixelData.byteLength;
if (bitsAllocated > 32) {
return new Float64Array(
pixelData.buffer,
offset,
length / 8
);
} else if (bitsAllocated > 16) {
if (options.isFloat) {
return _Decoder._endianFixer(
new Float32Array(
pixelData.buffer,
offset,
length / 4
),
!options.littleEndian
);
}
if (options.pixelRepresentation === 0) {
return _Decoder._endianFixer(
new Uint32Array(
pixelData.buffer,
offset,
length / 4
),
!options.littleEndian
);
}
return _Decoder._endianFixer(
new Float32Array(
pixelData.buffer,
offset,
length / 4
),
!options.littleEndian
);
} else if (bitsAllocated > 8) {
if (options.pixelRepresentation === 0) {
return _Decoder._endianFixer(
new Uint16Array(
pixelData.buffer,
offset,
length / 2
),
!options.littleEndian
);
}
return _Decoder._endianFixer(
new Int16Array(
pixelData.buffer,
offset,
length / 2
),
!options.littleEndian
);
} else if (bitsAllocated === 8) {
if (options.pixelRepresentation === 0) {
return _Decoder._endianFixer(
new Uint8Array(pixelData),
!options.littleEndian
);
}
return _Decoder._endianFixer(
new Int8Array(pixelData),
!options.littleEndian
);
} else if (bitsAllocated === 1) {
const buffer8 = new Uint8Array(pixelData);
const bits = new Uint8Array((options.rows || 0) * (options.columns || 0));
for (let i = 0; i < buffer8.length; i++) {
for (let j = 0; j < 8; j++) {
bits[i * 8 + j] = buffer8[i] >> j & 1;
}
}
return bits;
}
}
static _endianFixer(data, bigEndian = false) {
if (!bigEndian) {
return data;
}
if (data instanceof Uint16Array || data instanceof Int16Array) {
for (let i = 0; i < data.byteLength; i++) {
data[i] = (data[i] & 255) << 8 | data[i] >> 8 & 255;
}
}
return data;
}
static _fixSize(pixelData, options) {
const rows = options?.rows || 0;
const columns = options?.columns || 0;
if (rows * columns === pixelData.length) {
return pixelData;
} else if (3 * rows * columns === pixelData.length) {
return pixelData;
} else if (4 * rows * columns === pixelData.length) {
return pixelData;
}
let newLen = null;
if (pixelData.length < rows * columns) {
newLen = columns * rows;
} else if (pixelData.length < 3 * rows * columns) {
newLen = 3 * columns * rows;
} else {
newLen = 4 * columns * rows;
}
let newPixelsArray = null;
let minimum = 0;
if (pixelData instanceof Int8Array) {
newPixelsArray = new Int8Array(newLen);
minimum = -128;
} else if (pixelData instanceof Uint8Array) {
newPixelsArray = new Uint8Array(newLen);
} else if (pixelData instanceof Int16Array) {
newPixelsArray = new Int16Array(newLen);
minimum = -32768;
} else if (pixelData instanceof Uint16Array) {
newPixelsArray = new Uint16Array(newLen);
} else if (pixelData instanceof Int32Array) {
newPixelsArray = new Int32Array(newLen);
minimum = -2147483648;
} else if (pixelData instanceof Uint32Array) {
newPixelsArray = new Uint32Array(newLen);
} else if (pixelData instanceof Float32Array) {
newPixelsArray = new Float32Array(newLen);
minimum = -34e37;
} else if (pixelData instanceof Float64Array) {
newPixelsArray = new Float64Array(newLen);
minimum = -Infinity;
}
if (!newPixelsArray) return pixelData;
for (let i = 0; i < newLen; i++) {
newPixelsArray[i] = i < pixelData.length ? pixelData[i] : minimum;
}
return newPixelsArray;
}
};
var Decoder_default = Decoder2;
// src/decode.ts
async function decode3(pixelData, options) {
let pixelDataView;
if (pixelData instanceof ArrayBuffer) {
pixelDataView = new DataView(pixelData);
} else {
pixelDataView = pixelData;
}
const image = await Decoder_default.decode(pixelDataView, options);
image.pixelModule = {
pixelMeasuresSequence: options.pixelMeasuresSequence,
photometricInterpretation: options.photometricInterpretation,
numberOfFrames: options.numberOfFrames,
pixelRepresentation: options.pixelRepresentation,
pixelSpacing: options.pixelSpacing,
spacingBetweenSlices: options.spacingBetweenSlices,
rows: options.rows,
columns: options.columns,
bitsAllocated: options.bitsAllocated,
highBit: options.highBit,
bitsStored: options.bitsStored,
samplesPerPixel: options.samplesPerPixel,
pixelDataProviderURL: options.pixelDataProviderURL,
pixelPaddingRangeLimit: options.pixelPaddingRangeLimit,
extendedOffsetTable: options.extendedOffsetTable,
extendedOffsetTableLengths: options.extendedOffsetTableLengths,
pixelAspectRatio: options.pixelAspectRatio,
planarConfiguration: options.planarConfiguration,
redPaletteColorLookupTableDescriptor: options.redPaletteColorLookupTableDescriptor,
greenPaletteColorLookupTableDescriptor: options.greenPaletteColorLookupTableDescriptor,
bluePaletteColorLookupTableDescriptor: options.bluePaletteColorLookupTableDescriptor,
alphaPaletteColorLookupTableDescriptor: options.alphaPaletteColorLookupTableDescriptor,
redPaletteColorLookupTableData: options.redPaletteColorLookupTableData,
greenPaletteColorLookupTableData: options.greenPaletteColorLookupTableData,
bluePaletteColorLookupTableData: options.bluePaletteColorLookupTableData,
alphaPaletteColorLookupTableData: options.alphaPaletteColorLookupTableData,
segmentedRedPaletteColorLookupTableData: options.segmentedRedPaletteColorLookupTableData,
segmentedGreenPaletteColorLookupTableData: options.segmentedGreenPaletteColorLookupTableData,
segmentedBluePaletteColorLookupTableData: options.segmentedBluePaletteColorLookupTableData,
segmentedAlphaPaletteColorLookupTableData: options.segmentedAlphaPaletteColorLookupTableData
};
image.pixelTypes = options.isFloat ? "Float" : "Integer";
return image;
}
var decode_default = decode3;
export {
decode_default as decode
};