@cornerstonejs/dicom-image-loader 5.4.11 → 5.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ type Part10Input = ArrayBuffer | Uint8Array | (() => ArrayBuffer | Uint8Array | Promise<ArrayBuffer | Uint8Array>);
2
+ export declare function prefetchPart10Instance(baseImageId: string, part10: Part10Input): Promise<never>;
3
+ export default prefetchPart10Instance;
@@ -0,0 +1,6 @@
1
+ import { utilities } from '@cornerstonejs/metadata';
2
+ const { addDicomPart10Instance } = utilities;
3
+ export function prefetchPart10Instance(baseImageId, part10) {
4
+ return addDicomPart10Instance(baseImageId, part10);
5
+ }
6
+ export default prefetchPart10Instance;
@@ -2,6 +2,7 @@ import { Enums, imageRetrievalPoolManager, utilities, } from '@cornerstonejs/cor
2
2
  import { Enums as csCoreEnums } from '@cornerstonejs/core';
3
3
  import createImage from '../createImage.js';
4
4
  import getPixelData from './getPixelData.js';
5
+ import { loadImageFromCompressedFrameRegistry } from './loadImageFromRegistry.js';
5
6
  const { ProgressiveIterator } = utilities;
6
7
  const { ImageQualityStatus } = Enums;
7
8
  const streamableTransferSyntaxes = new Set([
@@ -56,6 +57,19 @@ function getImageRetrievalPool() {
56
57
  }
57
58
  const mediaType = 'multipart/related; type=application/octet-stream; transfer-syntax=*';
58
59
  function loadImage(imageId, options = {}) {
60
+ const registryPromise = loadImageFromCompressedFrameRegistry(imageId, options);
61
+ if (registryPromise) {
62
+ return {
63
+ promise: registryPromise.catch((error) => {
64
+ console.warn(`Failed to decode registry frame data for ${imageId}; falling back to network retrieval`, error);
65
+ return loadImageFromNetwork(imageId, options).promise;
66
+ }),
67
+ cancelFn: undefined,
68
+ };
69
+ }
70
+ return loadImageFromNetwork(imageId, options);
71
+ }
72
+ function loadImageFromNetwork(imageId, options = {}) {
59
73
  const imageRetrievalPool = getImageRetrievalPool();
60
74
  const start = new Date().getTime();
61
75
  const uncompressedIterator = new ProgressiveIterator('decompress');
@@ -0,0 +1,3 @@
1
+ import type { DICOMLoaderIImage, DICOMLoaderImageOptions } from '../../types/index.js';
2
+ export declare function loadImageFromCompressedFrameRegistry(imageId: string, options?: DICOMLoaderImageOptions): Promise<DICOMLoaderIImage> | undefined;
3
+ export default loadImageFromCompressedFrameRegistry;
@@ -0,0 +1,62 @@
1
+ import { Enums, metaData } from '@cornerstonejs/core';
2
+ import { Enums as MetadataEnums } from '@cornerstonejs/metadata';
3
+ import createImage from '../createImage.js';
4
+ const { ImageQualityStatus } = Enums;
5
+ const COMPRESSED_FRAME_DATA = MetadataEnums.MetadataModules.COMPRESSED_FRAME_DATA;
6
+ function getWadorsFrameIndex(imageId) {
7
+ const match = imageId.match(/\/frames\/(\d+)/);
8
+ return match ? Math.max(0, Number(match[1]) - 1) : 0;
9
+ }
10
+ const asByteArray = (data) => data instanceof ArrayBuffer ? new Uint8Array(data) : data;
11
+ function concatPixelData(pixelData) {
12
+ if (!Array.isArray(pixelData)) {
13
+ return asByteArray(pixelData);
14
+ }
15
+ if (pixelData.length === 0) {
16
+ return undefined;
17
+ }
18
+ if (pixelData.length === 1) {
19
+ return asByteArray(pixelData[0]);
20
+ }
21
+ let totalLength = 0;
22
+ for (const fragment of pixelData) {
23
+ totalLength += asByteArray(fragment).length;
24
+ }
25
+ const result = new Uint8Array(totalLength);
26
+ let offset = 0;
27
+ for (const fragment of pixelData) {
28
+ const view = asByteArray(fragment);
29
+ result.set(view, offset);
30
+ offset += view.length;
31
+ }
32
+ return result;
33
+ }
34
+ export function loadImageFromCompressedFrameRegistry(imageId, options = {}) {
35
+ const frameIndex = getWadorsFrameIndex(imageId);
36
+ let frameData;
37
+ try {
38
+ frameData = metaData.getTyped(COMPRESSED_FRAME_DATA, imageId, {
39
+ frameIndex,
40
+ });
41
+ }
42
+ catch (error) {
43
+ return undefined;
44
+ }
45
+ if (!frameData?.pixelData) {
46
+ return undefined;
47
+ }
48
+ const { pixelData, transferSyntaxUid } = frameData;
49
+ const concatenated = concatPixelData(pixelData);
50
+ if (!concatenated?.length || !transferSyntaxUid) {
51
+ return undefined;
52
+ }
53
+ const start = Date.now();
54
+ return createImage(imageId, concatenated, transferSyntaxUid, options).then((image) => {
55
+ const out = image;
56
+ out.imageQualityStatus = ImageQualityStatus.FULL_RESOLUTION;
57
+ out.transferSyntaxUID = transferSyntaxUid;
58
+ out.loadTimeInMS = Date.now() - start;
59
+ return out;
60
+ });
61
+ }
62
+ export default loadImageFromCompressedFrameRegistry;
@@ -10,6 +10,7 @@ import { default as getMinMax } from './shared/getMinMax.js';
10
10
  import { default as isColorImage } from './shared/isColorImage.js';
11
11
  import { default as isJPEGBaseline8BitColor } from './imageLoader/isJPEGBaseline8BitColor.js';
12
12
  import { default as getPixelData } from './imageLoader/wadors/getPixelData.js';
13
+ import { default as prefetchPart10Instance } from './imageLoader/prefetchPart10Instance.js';
13
14
  import { internal } from './imageLoader/internal/index.js';
14
15
  import * as constants from './constants/index.js';
15
16
  import type * as Types from './types/index.js';
@@ -84,6 +85,7 @@ declare const cornerstoneDICOMImageLoader: {
84
85
  decodeJPEGBaseline8BitColor: typeof decodeJPEGBaseline8BitColor;
85
86
  getImageFrame: typeof getImageFrame;
86
87
  getPixelData: typeof getPixelData;
88
+ prefetchPart10Instance: typeof prefetchPart10Instance;
87
89
  getMinMax: typeof getMinMax;
88
90
  isColorImage: typeof isColorImage;
89
91
  isJPEGBaseline8BitColor: typeof isJPEGBaseline8BitColor;
@@ -115,6 +117,6 @@ declare const cornerstoneDICOMImageLoader: {
115
117
  RLE: typeof import("./shared/decoders/decodeRLE.js").default;
116
118
  };
117
119
  };
118
- export { constants, convertRGBColorByPixel, convertRGBColorByPlane, convertYBRFullByPixel, convertYBRFullByPlane, convertPaletteColor, convertPaletteColorWithFetch, wadouri, wadors, init, convertColorSpace, createImage, decodeJPEGBaseline8BitColor, getImageFrame, getPixelData, getMinMax, isColorImage, isJPEGBaseline8BitColor, internal, decodeImageFrame, postProcessDecodedPixels, initializers, decoders, };
120
+ export { constants, convertRGBColorByPixel, convertRGBColorByPlane, convertYBRFullByPixel, convertYBRFullByPlane, convertPaletteColor, convertPaletteColorWithFetch, wadouri, wadors, init, convertColorSpace, createImage, decodeJPEGBaseline8BitColor, getImageFrame, getPixelData, prefetchPart10Instance, getMinMax, isColorImage, isJPEGBaseline8BitColor, internal, decodeImageFrame, postProcessDecodedPixels, initializers, decoders, };
119
121
  export type { Types };
120
122
  export default cornerstoneDICOMImageLoader;
package/dist/esm/index.js CHANGED
@@ -10,6 +10,7 @@ import { default as getMinMax } from './shared/getMinMax.js';
10
10
  import { default as isColorImage } from './shared/isColorImage.js';
11
11
  import { default as isJPEGBaseline8BitColor } from './imageLoader/isJPEGBaseline8BitColor.js';
12
12
  import { default as getPixelData } from './imageLoader/wadors/getPixelData.js';
13
+ import { default as prefetchPart10Instance } from './imageLoader/prefetchPart10Instance.js';
13
14
  import { internal } from './imageLoader/internal/index.js';
14
15
  import * as constants from './constants/index.js';
15
16
  import { decodeImageFrame, postProcessDecodedPixels, } from './decodeImageFrameWorker.js';
@@ -30,6 +31,7 @@ const cornerstoneDICOMImageLoader = {
30
31
  decodeJPEGBaseline8BitColor,
31
32
  getImageFrame,
32
33
  getPixelData,
34
+ prefetchPart10Instance,
33
35
  getMinMax,
34
36
  isColorImage,
35
37
  isJPEGBaseline8BitColor,
@@ -39,5 +41,5 @@ const cornerstoneDICOMImageLoader = {
39
41
  initializers,
40
42
  decoders,
41
43
  };
42
- export { constants, convertRGBColorByPixel, convertRGBColorByPlane, convertYBRFullByPixel, convertYBRFullByPlane, convertPaletteColor, convertPaletteColorWithFetch, wadouri, wadors, init, convertColorSpace, createImage, decodeJPEGBaseline8BitColor, getImageFrame, getPixelData, getMinMax, isColorImage, isJPEGBaseline8BitColor, internal, decodeImageFrame, postProcessDecodedPixels, initializers, decoders, };
44
+ export { constants, convertRGBColorByPixel, convertRGBColorByPlane, convertYBRFullByPixel, convertYBRFullByPlane, convertPaletteColor, convertPaletteColorWithFetch, wadouri, wadors, init, convertColorSpace, createImage, decodeJPEGBaseline8BitColor, getImageFrame, getPixelData, prefetchPart10Instance, getMinMax, isColorImage, isJPEGBaseline8BitColor, internal, decodeImageFrame, postProcessDecodedPixels, initializers, decoders, };
43
45
  export default cornerstoneDICOMImageLoader;
@@ -1,4 +1,5 @@
1
1
  import type { ByteArray } from 'dicom-parser';
2
2
  import type { Types } from '@cornerstonejs/core';
3
3
  declare function decodeRLE(imageFrame: Types.IImageFrame, pixelData: ByteArray): Promise<Types.IImageFrame>;
4
+ export declare function unpackOneBitPlanar(packed: Uint8Array, imageFrame: Types.IImageFrame, frameSize: number): Uint8Array;
4
5
  export default decodeRLE;
@@ -8,11 +8,32 @@ async function decodeRLE(imageFrame, pixelData) {
8
8
  else if (imageFrame.bitsAllocated === 16) {
9
9
  return decode16(imageFrame, pixelData);
10
10
  }
11
+ else if (imageFrame.bitsAllocated === 1) {
12
+ return decode8Planar(imageFrame, pixelData, true);
13
+ }
11
14
  throw new Error('unsupported pixel format for RLE');
12
15
  }
13
- function decode8(imageFrame, pixelData) {
16
+ function getFrameSize(imageFrame, isOneBit = false) {
17
+ const pixelCount = imageFrame.rows * imageFrame.columns;
18
+ return isOneBit ? Math.ceil(pixelCount / 8) : pixelCount;
19
+ }
20
+ export function unpackOneBitPlanar(packed, imageFrame, frameSize) {
21
+ const pixelsPerPlane = imageFrame.rows * imageFrame.columns;
22
+ const unpacked = new Uint8Array(pixelsPerPlane * imageFrame.samplesPerPixel);
23
+ for (let s = 0; s < imageFrame.samplesPerPixel; s++) {
24
+ const planeOffset = s * frameSize;
25
+ for (let i = 0; i < pixelsPerPlane; i++) {
26
+ const bytePos = planeOffset + Math.floor(i / 8);
27
+ const bitPos = i % 8;
28
+ unpacked[s * pixelsPerPlane + i] =
29
+ packed[bytePos] & (1 << bitPos) ? 1 : 0;
30
+ }
31
+ }
32
+ return unpacked;
33
+ }
34
+ function decode8(imageFrame, pixelData, isOneBit = false) {
14
35
  const frameData = pixelData;
15
- const frameSize = imageFrame.rows * imageFrame.columns;
36
+ const frameSize = getFrameSize(imageFrame, isOneBit);
16
37
  const outFrame = new ArrayBuffer(frameSize * imageFrame.samplesPerPixel);
17
38
  const header = new DataView(frameData.buffer, frameData.byteOffset);
18
39
  const data = new Int8Array(frameData.buffer, frameData.byteOffset);
@@ -47,9 +68,9 @@ function decode8(imageFrame, pixelData) {
47
68
  imageFrame.pixelData = new Uint8Array(outFrame);
48
69
  return imageFrame;
49
70
  }
50
- function decode8Planar(imageFrame, pixelData) {
71
+ function decode8Planar(imageFrame, pixelData, isOneBit = false) {
51
72
  const frameData = pixelData;
52
- const frameSize = imageFrame.rows * imageFrame.columns;
73
+ const frameSize = getFrameSize(imageFrame, isOneBit);
53
74
  const outFrame = new ArrayBuffer(frameSize * imageFrame.samplesPerPixel);
54
75
  const header = new DataView(frameData.buffer, frameData.byteOffset);
55
76
  const data = new Int8Array(frameData.buffer, frameData.byteOffset);
@@ -82,6 +103,9 @@ function decode8Planar(imageFrame, pixelData) {
82
103
  }
83
104
  }
84
105
  imageFrame.pixelData = new Uint8Array(outFrame);
106
+ if (isOneBit) {
107
+ imageFrame.pixelData = unpackOneBitPlanar(imageFrame.pixelData, imageFrame, frameSize);
108
+ }
85
109
  return imageFrame;
86
110
  }
87
111
  function decode16(imageFrame, pixelData) {
@@ -1 +1 @@
1
- export declare const version = "5.4.11";
1
+ export declare const version = "5.4.13";
@@ -1 +1 @@
1
- export const version = '5.4.11';
1
+ export const version = '5.4.13';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/dicom-image-loader",
3
- "version": "5.4.11",
3
+ "version": "5.4.13",
4
4
  "description": "Cornerstone Image Loader for DICOM WADO-URI and WADO-RS and Local file",
5
5
  "keywords": [
6
6
  "DICOM",
@@ -91,7 +91,7 @@
91
91
  "clean:coverage": "shx rm -rf coverage",
92
92
  "doc": "npm run doc:generate && opn documentation/index.html",
93
93
  "doc:generate": "npm run clean:docs && jsdoc -c .jsdocrc",
94
- "dev": "tsc --project ./tsconfig.json --watch",
94
+ "dev": "tsc --project ./tsconfig.json --watch --sourceMap --inlineSources",
95
95
  "lint": "oxlint .",
96
96
  "start": "npm run webpack:dev",
97
97
  "start:dev": "webpack-dev-server --config .webpack/webpack-dev",
@@ -122,12 +122,12 @@
122
122
  "uuid": "9.0.1"
123
123
  },
124
124
  "devDependencies": {
125
- "@cornerstonejs/core": "5.4.11",
126
- "@cornerstonejs/metadata": "5.4.11"
125
+ "@cornerstonejs/core": "5.4.13",
126
+ "@cornerstonejs/metadata": "5.4.13"
127
127
  },
128
128
  "peerDependencies": {
129
- "@cornerstonejs/core": "5.4.11",
130
- "@cornerstonejs/metadata": "5.4.11",
129
+ "@cornerstonejs/core": "5.4.13",
130
+ "@cornerstonejs/metadata": "5.4.13",
131
131
  "dicom-parser": "1.8.21"
132
132
  },
133
133
  "config": {
@@ -135,5 +135,5 @@
135
135
  "path": "./node_modules/cz-conventional-changelog"
136
136
  }
137
137
  },
138
- "gitHead": "b6139a8e5a81a2f5a1bf5e1d1e238605d85c6d65"
138
+ "gitHead": "9f0b0f4813368e68bc0c04f02132e06f15b6a0f8"
139
139
  }