@cornerstonejs/dicom-image-loader 5.0.0-beta.1 → 5.0.0

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.
@@ -81,10 +81,6 @@ export function postProcessDecodedPixels(imageFrame, options, start, decodeConfi
81
81
  }
82
82
  }
83
83
  else if (disableScale) {
84
- imageFrame.preScale = {
85
- enabled: true,
86
- scaled: false,
87
- };
88
84
  minAfterScale = minBeforeScale;
89
85
  maxAfterScale = maxBeforeScale;
90
86
  }
@@ -21,7 +21,7 @@ export default function convertPaletteColor(imageFrame, colorBuffer, useRGBA) {
21
21
  let bufferIndex = 0;
22
22
  const start = imageFrame.redPaletteColorLookupTableDescriptor[1];
23
23
  const bitsStored = imageFrame.redPaletteColorLookupTableDescriptor[2];
24
- const shift = bitsStored > 8 || rData.some((num) => num > 255) ? 8 : 0;
24
+ const shift = bitsStored > 8 && rData.some((num) => num > 255) ? 8 : 0;
25
25
  const rDataCleaned = convertLUTto8Bit(rData, shift);
26
26
  const gDataCleaned = convertLUTto8Bit(gData, shift);
27
27
  const bDataCleaned = convertLUTto8Bit(bData, shift);
@@ -28,6 +28,13 @@ async function createImage(imageId, pixelData, transferSyntax, options = {}) {
28
28
  imageFrame.decodeLevel = options.decodeLevel;
29
29
  options.allowFloatRendering = canRenderFloatTextures();
30
30
  let redData, greenData, blueData;
31
+ const paletteDescriptors = imageFrame.photometricInterpretation === 'PALETTE COLOR'
32
+ ? {
33
+ red: imageFrame.redPaletteColorLookupTableDescriptor,
34
+ green: imageFrame.greenPaletteColorLookupTableDescriptor,
35
+ blue: imageFrame.bluePaletteColorLookupTableDescriptor,
36
+ }
37
+ : null;
31
38
  if (imageFrame.photometricInterpretation === 'PALETTE COLOR') {
32
39
  [redData, greenData, blueData] = await Promise.all([
33
40
  fetchPaletteData(imageFrame, 'red', null),
@@ -43,6 +50,9 @@ async function createImage(imageId, pixelData, transferSyntax, options = {}) {
43
50
  scalingParameters: scalingParameters,
44
51
  };
45
52
  }
53
+ else {
54
+ options.preScale.enabled = false;
55
+ }
46
56
  }
47
57
  const { decodeConfig } = getOptions();
48
58
  Object.keys(imageFrame).forEach((key) => {
@@ -94,7 +104,42 @@ async function createImage(imageId, pixelData, transferSyntax, options = {}) {
94
104
  const sopCommonModule = metaData.get(MetadataModules.SOP_COMMON, imageId) || {};
95
105
  const calibrationModule = metaData.get(MetadataModules.CALIBRATION, imageId) || {};
96
106
  const { rows, columns } = imageFrame;
97
- if (imageFrame.photometricInterpretation === 'PALETTE COLOR') {
107
+ if (imageFrame.photometricInterpretation === 'PALETTE COLOR' &&
108
+ paletteDescriptors) {
109
+ const normalizeLutIfBytes = (data, descriptor) => {
110
+ if (data == null || !descriptor || descriptor.length < 3)
111
+ return data;
112
+ const tableLen = descriptor[0];
113
+ const bits = descriptor[2];
114
+ if (bits !== 16 || tableLen <= 0)
115
+ return data;
116
+ const expectedBytes = tableLen * 2;
117
+ let view = null;
118
+ if (Array.isArray(data) && data.length > 0) {
119
+ const first = data[0];
120
+ if (first instanceof ArrayBuffer) {
121
+ view = new Uint8Array(first);
122
+ }
123
+ else if (ArrayBuffer.isView(first)) {
124
+ view = first;
125
+ }
126
+ }
127
+ else if (data instanceof ArrayBuffer) {
128
+ view = new Uint8Array(data);
129
+ }
130
+ else if (ArrayBuffer.isView(data)) {
131
+ view = data;
132
+ }
133
+ if (view && view.byteLength === expectedBytes) {
134
+ return new Uint16Array(view.buffer, view.byteOffset, tableLen);
135
+ }
136
+ return data;
137
+ };
138
+ imageFrame.redPaletteColorLookupTableData = normalizeLutIfBytes(redData, paletteDescriptors.red);
139
+ imageFrame.greenPaletteColorLookupTableData = normalizeLutIfBytes(greenData, paletteDescriptors.green);
140
+ imageFrame.bluePaletteColorLookupTableData = normalizeLutIfBytes(blueData, paletteDescriptors.blue);
141
+ }
142
+ else if (imageFrame.photometricInterpretation === 'PALETTE COLOR') {
98
143
  imageFrame.redPaletteColorLookupTableData = redData;
99
144
  imageFrame.greenPaletteColorLookupTableData = greenData;
100
145
  imageFrame.bluePaletteColorLookupTableData = blueData;
@@ -111,6 +156,55 @@ async function createImage(imageId, pixelData, transferSyntax, options = {}) {
111
156
  data: new Uint8ClampedArray(3 * imageFrame.columns * imageFrame.rows),
112
157
  };
113
158
  }
159
+ if (imageFrame.photometricInterpretation === 'PALETTE COLOR') {
160
+ const pd = imageFrame.pixelData;
161
+ const len = pd?.length ?? 0;
162
+ const sliceSize = Math.min(40, len);
163
+ const r = imageFrame.redPaletteColorLookupTableData;
164
+ const g = imageFrame.greenPaletteColorLookupTableData;
165
+ const b = imageFrame.bluePaletteColorLookupTableData;
166
+ const desc = imageFrame.redPaletteColorLookupTableDescriptor;
167
+ const lutLen = (x) => x != null &&
168
+ typeof x.length === 'number'
169
+ ? x.length
170
+ : null;
171
+ const lutByteLen = (x) => {
172
+ if (x == null)
173
+ return null;
174
+ if (x instanceof ArrayBuffer)
175
+ return x.byteLength;
176
+ if (ArrayBuffer.isView(x))
177
+ return x.byteLength;
178
+ return null;
179
+ };
180
+ const first10 = (x) => x != null &&
181
+ typeof x.length === 'number'
182
+ ? Array.from(x).slice(0, 10)
183
+ : null;
184
+ console.log('[createImage] PALETTE COLOR before convertColorSpace', {
185
+ imageId,
186
+ descriptor: desc,
187
+ pixelDataLength: len,
188
+ pixelDataSlice: sliceSize > 0 && pd
189
+ ? Array.from({ length: sliceSize }, (_, i) => pd[i])
190
+ : [],
191
+ redLUT: {
192
+ length: lutLen(r),
193
+ byteLength: lutByteLen(r),
194
+ first10: first10(r),
195
+ },
196
+ greenLUT: {
197
+ length: lutLen(g),
198
+ byteLength: lutByteLen(g),
199
+ first10: first10(g),
200
+ },
201
+ blueLUT: {
202
+ length: lutLen(b),
203
+ byteLength: lutByteLen(b),
204
+ first10: first10(b),
205
+ },
206
+ });
207
+ }
114
208
  convertColorSpace(imageFrame, imageData.data, useRGBA);
115
209
  imageFrame.imageData = imageData;
116
210
  imageFrame.pixelData = imageData.data;
@@ -165,9 +259,10 @@ async function createImage(imageId, pixelData, transferSyntax, options = {}) {
165
259
  windowWidth: voiLutModule.windowWidth
166
260
  ? voiLutModule.windowWidth[0]
167
261
  : undefined,
168
- voiLUTFunction: voiLutModule.voiLUTFunction
169
- ? voiLutModule.voiLUTFunction
170
- : undefined,
262
+ voiLUTFunction: (voiLutModule.voiLUTFunction?.length &&
263
+ voiLutModule.voiLUTFunction[0]) ||
264
+ voiLutModule.voiLutFunction ||
265
+ undefined,
171
266
  decodeTimeInMS: imageFrame.decodeTimeInMS,
172
267
  floatPixelData: undefined,
173
268
  imageFrame,
@@ -25,8 +25,6 @@ const instanceModuleNames = [
25
25
  'multiframeModule',
26
26
  'generalSeriesModule',
27
27
  'patientStudyModule',
28
- 'imagePlaneModule',
29
- 'nmMultiframeGeometryModule',
30
28
  'imagePixelModule',
31
29
  'modalityLutModule',
32
30
  'voiLutModule',
@@ -3,9 +3,15 @@ export default function getScalingParameters(metaData, imageId) {
3
3
  const generalSeriesModule = (metaData.get('generalSeriesModule', imageId) ||
4
4
  {});
5
5
  const { modality } = generalSeriesModule;
6
+ const rescaleSlope = modalityLutModule.rescaleSlope;
7
+ const rescaleIntercept = modalityLutModule.rescaleIntercept;
8
+ if (rescaleSlope === 1 &&
9
+ (rescaleIntercept === 0 || rescaleIntercept == null)) {
10
+ return undefined;
11
+ }
6
12
  const scalingParameters = {
7
- rescaleSlope: modalityLutModule.rescaleSlope,
8
- rescaleIntercept: modalityLutModule.rescaleIntercept,
13
+ rescaleSlope,
14
+ rescaleIntercept,
9
15
  modality,
10
16
  };
11
17
  const scalingModules = metaData.get('scalingModule', imageId) || {};
@@ -53,7 +53,8 @@ declare const cornerstoneDICOMImageLoader: {
53
53
  loadImageFromPromise: typeof import("./wadouri/loadImage").loadImageFromPromise;
54
54
  getLoaderForScheme: typeof import("./wadouri/loadImage").getLoaderForScheme;
55
55
  getPixelData: typeof import("./wadouri/getPixelData").default;
56
- loadImage: typeof import("./wadouri/loadImage").loadImage;
56
+ loadImage: (imageId: string, options?: import("../types").DICOMLoaderImageOptions) => import("packages/core/dist/esm/types").IImageLoadObject;
57
+ loadImageFromNaturalizedMetadata: typeof import("./wadouri/loadImage").loadImageFromNaturalizedMetadata;
57
58
  parseImageId: typeof import("./wadouri/parseImageId").default;
58
59
  unpackBinaryFrame: typeof import("./wadouri/unpackBinaryFrame").default;
59
60
  register: typeof import("./wadouri/register").default;
@@ -1,2 +1,4 @@
1
- declare function registerLoaders(): void;
1
+ declare function registerLoaders(options?: {
2
+ useLegacyMetadataProvider?: boolean;
3
+ }): void;
2
4
  export default registerLoaders;
@@ -1,7 +1,15 @@
1
+ import { cache } from '@cornerstonejs/core';
2
+ import { utilities } from '@cornerstonejs/metadata';
3
+ import dataSetCacheManager from './wadouri/dataSetCacheManager';
4
+ import wadorsMetaDataManager from './wadors/metaDataManager';
1
5
  import wadouriRegister from './wadouri/register';
2
6
  import wadorsRegister from './wadors/register';
3
- function registerLoaders() {
4
- wadorsRegister();
5
- wadouriRegister();
7
+ function registerLoaders(options) {
8
+ cache.purgeCache();
9
+ dataSetCacheManager.purge();
10
+ wadorsMetaDataManager.purge();
11
+ utilities.clearCacheData();
12
+ wadorsRegister(options);
13
+ wadouriRegister(options);
6
14
  }
7
15
  export default registerLoaders;
@@ -10,7 +10,6 @@ import { getImageTypeSubItemFromMetadata } from './NMHelpers';
10
10
  import isNMReconstructable from '../../isNMReconstructable';
11
11
  import { instanceModuleNames } from '../../getInstanceModule';
12
12
  import { getUSEnhancedRegions } from './USHelpers';
13
- import { getECGModule } from './ECGHelpers';
14
13
  function metaDataProvider(type, imageId) {
15
14
  const { MetadataModules } = Enums;
16
15
  if (type === MetadataModules.MULTIFRAME) {
@@ -154,20 +153,6 @@ function metaDataProvider(type, imageId) {
154
153
  if (type === MetadataModules.ULTRASOUND_ENHANCED_REGION) {
155
154
  return getUSEnhancedRegions(metaData);
156
155
  }
157
- if (type === MetadataModules.ECG) {
158
- const imageUri = imageId.replace('wadors:', '');
159
- const studiesIndex = imageUri.indexOf('/studies/');
160
- let wadoRsRoot;
161
- let studyUID;
162
- if (studiesIndex !== -1) {
163
- wadoRsRoot = imageUri.substring(0, studiesIndex);
164
- const afterStudies = imageUri.substring(studiesIndex + 9);
165
- const nextSlash = afterStudies.indexOf('/');
166
- studyUID =
167
- nextSlash !== -1 ? afterStudies.substring(0, nextSlash) : afterStudies;
168
- }
169
- return getECGModule(metaData, wadoRsRoot, studyUID);
170
- }
171
156
  if (type === MetadataModules.CALIBRATION) {
172
157
  const modality = getValue(metaData['00080060']);
173
158
  if (modality === 'US') {
@@ -176,42 +161,6 @@ function metaDataProvider(type, imageId) {
176
161
  sequenceOfUltrasoundRegions: enhancedRegion,
177
162
  };
178
163
  }
179
- const imageUri = imageId.replace('wadors:', '');
180
- const studiesIndex = imageUri.indexOf('/studies/');
181
- let wadoRsRoot;
182
- let studyUID;
183
- if (studiesIndex !== -1) {
184
- wadoRsRoot = imageUri.substring(0, studiesIndex);
185
- const afterStudies = imageUri.substring(studiesIndex + 9);
186
- const nextSlash = afterStudies.indexOf('/');
187
- studyUID =
188
- nextSlash !== -1 ? afterStudies.substring(0, nextSlash) : afterStudies;
189
- }
190
- const ecgModule = getECGModule(metaData, wadoRsRoot, studyUID);
191
- if (ecgModule) {
192
- const { numberOfWaveformSamples, samplingFrequency } = ecgModule;
193
- const physicalDeltaX = 1 / (samplingFrequency || 1);
194
- const physicalDeltaY = 0.001;
195
- const ECG_AMPLITUDE_INDEX_SIZE = 65536;
196
- const ECG_AMPLITUDE_OFFSET = 32768;
197
- return {
198
- sequenceOfUltrasoundRegions: [
199
- {
200
- regionLocationMinX0: 0,
201
- regionLocationMaxX1: numberOfWaveformSamples,
202
- regionLocationMinY0: 0,
203
- regionLocationMaxY1: ECG_AMPLITUDE_INDEX_SIZE - 1,
204
- referencePixelX0: 0,
205
- referencePixelY0: ECG_AMPLITUDE_OFFSET,
206
- physicalDeltaX,
207
- physicalDeltaY,
208
- physicalUnitsXDirection: 4,
209
- physicalUnitsYDirection: -1,
210
- regionDataType: 1,
211
- },
212
- ],
213
- };
214
- }
215
164
  }
216
165
  if (type === MetadataModules.IMAGE_URL) {
217
166
  return getImageUrlModule(imageId, metaData);
@@ -1 +1,3 @@
1
- export default function (): void;
1
+ export default function (options?: {
2
+ useLegacyMetadataProvider?: boolean;
3
+ }): void;
@@ -1,7 +1,14 @@
1
1
  import { metaData, registerImageLoader } from '@cornerstonejs/core';
2
2
  import loadImage from './loadImage';
3
3
  import { metaDataProvider } from './metaData';
4
- export default function () {
4
+ import { registerDefaultProviders } from '@cornerstonejs/metadata';
5
+ export default function (options) {
5
6
  registerImageLoader('wadors', loadImage);
7
+ if (options?.useLegacyMetadataProvider) {
8
+ console.warn('wadors metaDataProvider is deprecated. Use addDicomWebInstance from @cornerstonejs/metadata instead.');
9
+ metaData.addProvider(metaDataProvider);
10
+ return;
11
+ }
12
+ registerDefaultProviders();
6
13
  metaData.addProvider(metaDataProvider);
7
14
  }
@@ -1,5 +1,5 @@
1
- import * as dicomParser from 'dicom-parser';
2
1
  import { xhrRequest } from '../internal/index';
2
+ import { parseDicom } from './parseDicomWithInflater';
3
3
  import dataSetFromPartialContent from './dataset-from-partial-content';
4
4
  import { combineFrameInstanceDataset } from './combineFrameInstanceDataset';
5
5
  import multiframeDataset from './retrieveMultiframeDataset';
@@ -76,7 +76,7 @@ function load(uri, loadRequest = xhrRequest, imageId) {
76
76
  });
77
77
  }
78
78
  else {
79
- dataSet = dicomParser.parseDicom(byteArray);
79
+ dataSet = parseDicom(byteArray);
80
80
  }
81
81
  }
82
82
  catch (error) {
@@ -1,4 +1,4 @@
1
- import * as dicomParser from 'dicom-parser';
1
+ import { parseDicom } from './parseDicomWithInflater';
2
2
  function fixFragments(dataSet) {
3
3
  const fragments = dataSet.elements.x7fe00010.fragments;
4
4
  const totalLength = dataSet.byteArray.length;
@@ -12,7 +12,7 @@ function fixFragments(dataSet) {
12
12
  return dataSet;
13
13
  }
14
14
  function parsePartialByteArray(byteArray) {
15
- let dataSet = dicomParser.parseDicom(byteArray, {
15
+ let dataSet = parseDicom(byteArray, {
16
16
  untilTag: 'x7fe00010',
17
17
  });
18
18
  if (!dataSet.elements.x7fe00010) {
@@ -20,7 +20,7 @@ function parsePartialByteArray(byteArray) {
20
20
  }
21
21
  let pixelDataSet;
22
22
  try {
23
- pixelDataSet = dicomParser.parseDicom(byteArray);
23
+ pixelDataSet = parseDicom(byteArray);
24
24
  }
25
25
  catch (err) {
26
26
  console.error(err);
@@ -5,7 +5,7 @@ import getEncapsulatedImageFrame from './getEncapsulatedImageFrame';
5
5
  import getUncompressedImageFrame from './getUncompressedImageFrame';
6
6
  import loadFileRequest from './loadFileRequest';
7
7
  import getPixelData from './getPixelData';
8
- import { loadImageFromPromise, getLoaderForScheme, loadImage } from './loadImage';
8
+ import { loadImageFromPromise, getLoaderForScheme, loadImage, loadImageFromNaturalizedMetadata } from './loadImage';
9
9
  import parseImageId from './parseImageId';
10
10
  import unpackBinaryFrame from './unpackBinaryFrame';
11
11
  import register from './register';
@@ -47,10 +47,11 @@ declare const _default: {
47
47
  loadImageFromPromise: typeof loadImageFromPromise;
48
48
  getLoaderForScheme: typeof getLoaderForScheme;
49
49
  getPixelData: typeof getPixelData;
50
- loadImage: typeof loadImage;
50
+ loadImage: (imageId: string, options?: import("../../types").DICOMLoaderImageOptions) => import("packages/core/dist/esm/types").IImageLoadObject;
51
+ loadImageFromNaturalizedMetadata: typeof loadImageFromNaturalizedMetadata;
51
52
  parseImageId: typeof parseImageId;
52
53
  unpackBinaryFrame: typeof unpackBinaryFrame;
53
54
  register: typeof register;
54
55
  };
55
56
  export default _default;
56
- export { metaData, dataSetCacheManager, fileManager, getEncapsulatedImageFrame, getUncompressedImageFrame, loadFileRequest, loadImageFromPromise, getLoaderForScheme, getPixelData, loadImage, parseImageId, unpackBinaryFrame, register, };
57
+ export { metaData, dataSetCacheManager, fileManager, getEncapsulatedImageFrame, getUncompressedImageFrame, loadFileRequest, loadImageFromPromise, getLoaderForScheme, getPixelData, loadImage, loadImageFromNaturalizedMetadata, parseImageId, unpackBinaryFrame, register, };
@@ -5,7 +5,7 @@ import getEncapsulatedImageFrame from './getEncapsulatedImageFrame';
5
5
  import getUncompressedImageFrame from './getUncompressedImageFrame';
6
6
  import loadFileRequest from './loadFileRequest';
7
7
  import getPixelData from './getPixelData';
8
- import { loadImageFromPromise, getLoaderForScheme, loadImage, } from './loadImage';
8
+ import { loadImageFromPromise, getLoaderForScheme, loadImage, loadImageFromNaturalizedMetadata, } from './loadImage';
9
9
  import parseImageId from './parseImageId';
10
10
  import unpackBinaryFrame from './unpackBinaryFrame';
11
11
  import register from './register';
@@ -28,8 +28,9 @@ export default {
28
28
  getLoaderForScheme,
29
29
  getPixelData,
30
30
  loadImage,
31
+ loadImageFromNaturalizedMetadata,
31
32
  parseImageId,
32
33
  unpackBinaryFrame,
33
34
  register,
34
35
  };
35
- export { metaData, dataSetCacheManager, fileManager, getEncapsulatedImageFrame, getUncompressedImageFrame, loadFileRequest, loadImageFromPromise, getLoaderForScheme, getPixelData, loadImage, parseImageId, unpackBinaryFrame, register, };
36
+ export { metaData, dataSetCacheManager, fileManager, getEncapsulatedImageFrame, getUncompressedImageFrame, loadFileRequest, loadImageFromPromise, getLoaderForScheme, getPixelData, loadImage, loadImageFromNaturalizedMetadata, parseImageId, unpackBinaryFrame, register, };
@@ -4,6 +4,8 @@ import type { LoadRequestFunction, DICOMLoaderIImage, DICOMLoaderImageOptions }
4
4
  declare function loadImageFromPromise(dataSetPromise: Promise<DataSet>, imageId: string, frame: number, sharedCacheKey: string, options: DICOMLoaderImageOptions, callbacks?: {
5
5
  imageDoneCallback: (image: DICOMLoaderIImage) => void;
6
6
  }): Types.IImageLoadObject;
7
+ declare function loadImageFromDataSet(dataSet: any, imageId: string, frame: number, _sharedCacheKey: any, options: any): Types.IImageLoadObject;
7
8
  declare function getLoaderForScheme(scheme: string): LoadRequestFunction;
8
- declare function loadImage(imageId: string, options?: DICOMLoaderImageOptions): Types.IImageLoadObject;
9
- export { loadImageFromPromise, getLoaderForScheme, loadImage };
9
+ declare function loadImageFromNaturalizedMetadata(imageId: string, options?: DICOMLoaderImageOptions): Types.IImageLoadObject;
10
+ declare const loadImage: (imageId: string, options?: DICOMLoaderImageOptions) => Types.IImageLoadObject;
11
+ export { loadImageFromPromise, getLoaderForScheme, loadImage, loadImageFromNaturalizedMetadata, loadImageFromDataSet, };
@@ -1,4 +1,5 @@
1
- import { Enums } from '@cornerstonejs/core';
1
+ import { Enums, metaData } from '@cornerstonejs/core';
2
+ import { Enums as MetadataEnums, utilities } from '@cornerstonejs/metadata';
2
3
  import createImage from '../createImage';
3
4
  import { xhrRequest } from '../internal/index';
4
5
  import dataSetCacheManager from './dataSetCacheManager';
@@ -6,6 +7,8 @@ import getPixelData from './getPixelData';
6
7
  import loadFileRequest from './loadFileRequest';
7
8
  import parseImageId from './parseImageId';
8
9
  const { ImageQualityStatus } = Enums;
10
+ const { addDicomPart10Instance } = utilities;
11
+ const NATURALIZED = MetadataEnums.MetadataModules.NATURALIZED;
9
12
  function addDecache(imageLoadObject, imageId) {
10
13
  imageLoadObject.decache = function () {
11
14
  const parsedImageId = parseImageId(imageId);
@@ -28,7 +31,6 @@ function loadImageFromPromise(dataSetPromise, imageId, frame = 0, sharedCacheKey
28
31
  imagePromise.then((image) => {
29
32
  image = image;
30
33
  image.data = dataSet;
31
- image.sharedCacheKey = sharedCacheKey;
32
34
  const end = new Date().getTime();
33
35
  image.loadTimeInMS = loadEnd - start;
34
36
  image.totalTimeInMS = end - start;
@@ -52,7 +54,7 @@ function loadImageFromPromise(dataSetPromise, imageId, frame = 0, sharedCacheKey
52
54
  });
53
55
  return imageLoadObject;
54
56
  }
55
- function loadImageFromDataSet(dataSet, imageId, frame = 0, sharedCacheKey, options) {
57
+ function loadImageFromDataSet(dataSet, imageId, frame = 0, _sharedCacheKey, options) {
56
58
  const start = new Date().getTime();
57
59
  const promise = new Promise((resolve, reject) => {
58
60
  const loadEnd = new Date().getTime();
@@ -92,16 +94,88 @@ function getLoaderForScheme(scheme) {
92
94
  return loadFileRequest;
93
95
  }
94
96
  }
95
- function loadImage(imageId, options = {}) {
97
+ const asByteArray = (data) => data instanceof ArrayBuffer ? new Uint8Array(data) : data;
98
+ function concatPixelData(pixelData) {
99
+ if (!Array.isArray(pixelData)) {
100
+ return asByteArray(pixelData);
101
+ }
102
+ if (pixelData.length === 0) {
103
+ return undefined;
104
+ }
105
+ if (pixelData.length === 1) {
106
+ return asByteArray(pixelData[0]);
107
+ }
108
+ let totalLength = 0;
109
+ for (const frame of pixelData) {
110
+ totalLength += asByteArray(frame).length;
111
+ }
112
+ const result = new Uint8Array(totalLength);
113
+ let offset = 0;
114
+ for (const frame of pixelData) {
115
+ const view = asByteArray(frame);
116
+ result.set(view, offset);
117
+ offset += view.length;
118
+ }
119
+ return result;
120
+ }
121
+ function loadImageFromNaturalizedMetadata(imageId, options = {}) {
96
122
  const parsedImageId = parseImageId(imageId);
97
123
  options = Object.assign({}, options);
98
124
  delete options.loader;
99
125
  const schemeLoader = getLoaderForScheme(parsedImageId.scheme);
100
- if (dataSetCacheManager.isLoaded(parsedImageId.url)) {
101
- const dataSet = dataSetCacheManager.get(parsedImageId.url, schemeLoader, imageId);
102
- return loadImageFromDataSet(dataSet, imageId, parsedImageId.pixelDataFrame, parsedImageId.url, options);
126
+ const frameIndex = parsedImageId.pixelDataFrame !== undefined
127
+ ? parsedImageId.pixelDataFrame
128
+ : 0;
129
+ const promise = (async () => {
130
+ const start = Date.now();
131
+ console.log('[dicomImageLoader/wadouri] loadImageFromNaturalizedMetadata: start', {
132
+ imageId,
133
+ scheme: parsedImageId.scheme,
134
+ url: parsedImageId.url,
135
+ frameIndex,
136
+ });
137
+ let natural = metaData.get(NATURALIZED, imageId);
138
+ if (!natural) {
139
+ console.log('[dicomImageLoader/wadouri] loadImageFromNaturalizedMetadata: no NATURALIZED metadata, attempting to fetch and populate', { imageId });
140
+ if (!schemeLoader) {
141
+ throw new Error(`loadImageFromNaturalizedMetadata: no NATURALIZED cache and unknown scheme ${parsedImageId.scheme}`);
142
+ }
143
+ const result = (await schemeLoader(parsedImageId.url, imageId));
144
+ const arrayBuffer = result instanceof ArrayBuffer ? result : result.arrayBuffer;
145
+ const baseImageId = `${parsedImageId.scheme}:${parsedImageId.url}`;
146
+ await addDicomPart10Instance(baseImageId, arrayBuffer);
147
+ natural = metaData.get(NATURALIZED, imageId);
148
+ }
149
+ const loadEnd = Date.now();
150
+ const frameData = metaData.getTyped(MetadataEnums.MetadataModules.COMPRESSED_FRAME_DATA, imageId, { frameIndex });
151
+ if (!frameData) {
152
+ console.warn('[dicomImageLoader/wadouri] loadImageFromNaturalizedMetadata: no COMPRESSED_FRAME_DATA for imageId', { imageId, frameIndex });
153
+ throw new Error(`loadImageFromNaturalizedMetadata: no pixel data in NATURALIZED for imageId ${imageId}`);
154
+ }
155
+ const { pixelData, transferSyntaxUid } = frameData;
156
+ const concatenatedPixelData = concatPixelData(pixelData);
157
+ const image = await createImage(imageId, concatenatedPixelData, transferSyntaxUid, options);
158
+ const end = Date.now();
159
+ const out = image;
160
+ out.imageQualityStatus = ImageQualityStatus.FULL_RESOLUTION;
161
+ out.data = natural;
162
+ out.loadTimeInMS = loadEnd - start;
163
+ out.totalTimeInMS = end - start;
164
+ return out;
165
+ })();
166
+ return { promise };
167
+ }
168
+ const loadImage = (imageId, options = {}) => {
169
+ const parsedImageId = parseImageId(imageId);
170
+ const schemeLoader = getLoaderForScheme(parsedImageId.scheme);
171
+ if (!schemeLoader) {
172
+ throw new Error(`wadouri loadImage: no loader for scheme '${parsedImageId.scheme}'`);
103
173
  }
174
+ const frameIndex = parsedImageId.pixelDataFrame !== undefined
175
+ ? parsedImageId.pixelDataFrame
176
+ : 0;
177
+ const sharedCacheKey = parsedImageId.url;
104
178
  const dataSetPromise = dataSetCacheManager.load(parsedImageId.url, schemeLoader, imageId);
105
- return loadImageFromPromise(dataSetPromise, imageId, parsedImageId.pixelDataFrame, parsedImageId.url, options);
106
- }
107
- export { loadImageFromPromise, getLoaderForScheme, loadImage };
179
+ return loadImageFromPromise(dataSetPromise, imageId, frameIndex, sharedCacheKey, options);
180
+ };
181
+ export { loadImageFromPromise, getLoaderForScheme, loadImage, loadImageFromNaturalizedMetadata, loadImageFromDataSet, };
@@ -0,0 +1,7 @@
1
+ import * as dicomParser from 'dicom-parser';
2
+ declare function inflater(byteArray: Uint8Array, position: number): Uint8Array;
3
+ export declare const parseDicomOptions: {
4
+ inflater: typeof inflater;
5
+ };
6
+ export declare function parseDicom(byteArray: Uint8Array, options?: dicomParser.ParseDicomOptions): dicomParser.DataSet;
7
+ export {};
@@ -0,0 +1,17 @@
1
+ import * as dicomParser from 'dicom-parser';
2
+ import pako from 'pako';
3
+ function inflater(byteArray, position) {
4
+ const deflated = byteArray.slice(position);
5
+ const inflated = pako.inflateRaw(deflated);
6
+ const fullByteArray = new Uint8Array(inflated.length + position);
7
+ fullByteArray.set(byteArray.slice(0, position), 0);
8
+ fullByteArray.set(inflated, position);
9
+ return fullByteArray;
10
+ }
11
+ export const parseDicomOptions = { inflater };
12
+ export function parseDicom(byteArray, options) {
13
+ return dicomParser.parseDicom(byteArray, {
14
+ ...parseDicomOptions,
15
+ ...options,
16
+ });
17
+ }
@@ -1 +1,4 @@
1
- export default function (): void;
1
+ export default function (options?: {
2
+ useLegacyMetadataProvider?: boolean;
3
+ }): void;
4
+ export { loadImageFromNaturalizedMetadata as loadImage } from './loadImage';
@@ -1,9 +1,19 @@
1
1
  import { metaData, registerImageLoader } from '@cornerstonejs/core';
2
- import { loadImage } from './loadImage';
2
+ import { registerDefaultProviders } from '@cornerstonejs/metadata';
3
+ import { loadImage, loadImageFromNaturalizedMetadata } from './loadImage';
3
4
  import { metaDataProvider } from './metaData/index';
4
- export default function () {
5
- registerImageLoader('dicomweb', loadImage);
6
- registerImageLoader('wadouri', loadImage);
7
- registerImageLoader('dicomfile', loadImage);
8
- metaData.addProvider(metaDataProvider);
5
+ export default function (options) {
6
+ if (options?.useLegacyMetadataProvider === true) {
7
+ console.warn('wadouri metaDataProvider is deprecated. Use registerMetadataProvider module from @cornerstonejs/metadata instead.');
8
+ registerImageLoader('dicomweb', loadImage);
9
+ registerImageLoader('wadouri', loadImage);
10
+ registerImageLoader('dicomfile', loadImage);
11
+ metaData.addProvider(metaDataProvider);
12
+ return;
13
+ }
14
+ registerImageLoader('dicomweb', loadImageFromNaturalizedMetadata);
15
+ registerImageLoader('wadouri', loadImageFromNaturalizedMetadata);
16
+ registerImageLoader('dicomfile', loadImageFromNaturalizedMetadata);
17
+ registerDefaultProviders();
9
18
  }
19
+ export { loadImageFromNaturalizedMetadata as loadImage } from './loadImage';
@@ -53,7 +53,8 @@ declare const cornerstoneDICOMImageLoader: {
53
53
  loadImageFromPromise: typeof import("./imageLoader/wadouri/loadImage").loadImageFromPromise;
54
54
  getLoaderForScheme: typeof import("./imageLoader/wadouri/loadImage").getLoaderForScheme;
55
55
  getPixelData: typeof import("./imageLoader/wadouri/getPixelData").default;
56
- loadImage: typeof import("./imageLoader/wadouri/loadImage").loadImage;
56
+ loadImage: (imageId: string, options?: Types.DICOMLoaderImageOptions) => import("packages/core/dist/esm/types").IImageLoadObject;
57
+ loadImageFromNaturalizedMetadata: typeof import("./imageLoader/wadouri/loadImage").loadImageFromNaturalizedMetadata;
57
58
  parseImageId: typeof import("./imageLoader/wadouri/parseImageId").default;
58
59
  unpackBinaryFrame: typeof import("./imageLoader/wadouri/unpackBinaryFrame").default;
59
60
  register: typeof import("./imageLoader/wadouri/register").default;
package/dist/esm/init.js CHANGED
@@ -7,7 +7,7 @@ const workerFn = () => {
7
7
  };
8
8
  function init(options = {}) {
9
9
  setOptions(options);
10
- registerLoaders();
10
+ registerLoaders(options);
11
11
  const workerManager = getWebWorkerManager();
12
12
  const maxWorkers = options?.maxWebWorkers || getReasonableWorkerCount();
13
13
  workerManager.registerWorker('dicomImageLoader', workerFn, {
@@ -13,4 +13,5 @@ export interface LoaderOptions {
13
13
  errorInterceptor?: (error: LoaderXhrRequestError) => void;
14
14
  strict?: boolean;
15
15
  decodeConfig?: LoaderDecodeOptions;
16
+ useLegacyMetadataProvider?: boolean;
16
17
  }
@@ -1 +1 @@
1
- export declare const version = "5.0.0-beta.1";
1
+ export declare const version = "5.0.0";
@@ -1 +1 @@
1
- export const version = '5.0.0-beta.1';
1
+ export const version = '5.0.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/dicom-image-loader",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0",
4
4
  "description": "Cornerstone Image Loader for DICOM WADO-URI and WADO-RS and Local file",
5
5
  "keywords": [
6
6
  "DICOM",
@@ -65,15 +65,16 @@
65
65
  }
66
66
  },
67
67
  "scripts": {
68
- "build:loader": "yarn run build:all && yarn run copy-dts",
68
+ "build:loader": "pnpm run build:all && pnpm run copy-dts",
69
+ "build": "pnpm run build:esm",
69
70
  "build:esm": "tsc --project ./tsconfig.json",
70
71
  "build:esm:watch": "tsc --project ./tsconfig.json --watch",
71
72
  "build:umd:dynamic": "cross-env NODE_ENV=production webpack --config .webpack/webpack-dynamic-import.js",
72
73
  "build:umd:bundle": "cross-env NODE_ENV=production webpack --config .webpack/webpack-bundle.js",
73
- "build:all": "yarn run build:esm",
74
+ "build:all": "pnpm run build:esm",
74
75
  "copy-dts": "echo 'not implemented yet'",
75
76
  "clean": "shx rm -rf dist",
76
- "clean:deep": "yarn run clean && shx rm -rf node_modules",
77
+ "clean:deep": "pnpm run clean && shx rm -rf node_modules",
77
78
  "api-check": "api-extractor --debug run ",
78
79
  "cm": "npx git-cz",
79
80
  "clean:dist": "shx rm -rf dist",
@@ -98,7 +99,7 @@
98
99
  "webpack:dynamic-import:watch": "webpack --progress --watch --config .webpack/webpack-dynamic-import",
99
100
  "webpack:dynamic-import:debug": "webpack --progress --watch --config .webpack/webpack-dynamic-import-debug",
100
101
  "webpack:watch": "webpack --progress --watch --config .webpack",
101
- "prepublishOnly": "yarn run build:loader"
102
+ "prepublishOnly": "pnpm run build:loader"
102
103
  },
103
104
  "dependencies": {
104
105
  "@cornerstonejs/codec-charls": "1.2.3",
@@ -111,8 +112,13 @@
111
112
  "pako": "2.1.0",
112
113
  "uuid": "9.0.1"
113
114
  },
115
+ "devDependencies": {
116
+ "@cornerstonejs/core": "5.0.0",
117
+ "@cornerstonejs/metadata": "5.0.0"
118
+ },
114
119
  "peerDependencies": {
115
- "@cornerstonejs/core": "5.0.0-beta.1",
120
+ "@cornerstonejs/core": "5.0.0",
121
+ "@cornerstonejs/metadata": "5.0.0",
116
122
  "dicom-parser": "1.8.21"
117
123
  },
118
124
  "config": {
@@ -120,5 +126,5 @@
120
126
  "path": "./node_modules/cz-conventional-changelog"
121
127
  }
122
128
  },
123
- "gitHead": "269b3089ffd45d016454fde74374389d03feebac"
129
+ "gitHead": "d6f3fba43abcbaaf7468ff534588f6a88720b875"
124
130
  }