@cornerstonejs/nifti-volume-loader 2.0.0-beta.8 → 2.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.
- package/dist/esm/constants/index.js +0 -1
- package/dist/esm/constants/niftiLoaderScheme.js +0 -1
- package/dist/esm/cornerstoneNiftiImageLoader.d.ts +2 -7
- package/dist/esm/cornerstoneNiftiImageLoader.js +123 -7
- package/dist/esm/createNiftiImageIdsAndCacheMetadata.d.ts +17 -0
- package/dist/esm/createNiftiImageIdsAndCacheMetadata.js +250 -0
- package/dist/esm/enums/Events.js +0 -1
- package/dist/esm/enums/index.js +0 -1
- package/dist/esm/helpers/affineUtilities.js +0 -1
- package/dist/esm/helpers/convert.d.ts +1 -1
- package/dist/esm/helpers/convert.js +1 -5
- package/dist/esm/helpers/dataTypeCodeHelper.d.ts +1 -0
- package/dist/esm/helpers/dataTypeCodeHelper.js +22 -0
- package/dist/esm/helpers/index.d.ts +1 -2
- package/dist/esm/helpers/index.js +1 -3
- package/dist/esm/helpers/makeVolumeMetadata.d.ts +2 -2
- package/dist/esm/helpers/makeVolumeMetadata.js +4 -14
- package/dist/esm/helpers/modalityScaleNifti.d.ts +1 -1
- package/dist/esm/helpers/modalityScaleNifti.js +6 -19
- package/dist/esm/helpers/niftiConstants.js +0 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +3 -4
- package/dist/umd/index.js +1 -2
- package/package.json +41 -17
- package/dist/esm/NiftiImageVolume.d.ts +0 -21
- package/dist/esm/NiftiImageVolume.js +0 -26
- package/dist/esm/NiftiImageVolume.js.map +0 -1
- package/dist/esm/constants/index.js.map +0 -1
- package/dist/esm/constants/niftiLoaderScheme.js.map +0 -1
- package/dist/esm/cornerstoneNiftiImageLoader.js.map +0 -1
- package/dist/esm/enums/Events.js.map +0 -1
- package/dist/esm/enums/index.js.map +0 -1
- package/dist/esm/helpers/affineUtilities.js.map +0 -1
- package/dist/esm/helpers/convert.js.map +0 -1
- package/dist/esm/helpers/fetchAndAllocateNiftiVolume.d.ts +0 -3
- package/dist/esm/helpers/fetchAndAllocateNiftiVolume.js +0 -95
- package/dist/esm/helpers/fetchAndAllocateNiftiVolume.js.map +0 -1
- package/dist/esm/helpers/index.js.map +0 -1
- package/dist/esm/helpers/makeVolumeMetadata.js.map +0 -1
- package/dist/esm/helpers/modalityScaleNifti.js.map +0 -1
- package/dist/esm/helpers/niftiConstants.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/umd/index.js.LICENSE.txt +0 -3
- package/dist/umd/index.js.map +0 -1
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
promise: Promise<NiftiImageVolume>;
|
|
4
|
-
cancel: () => void;
|
|
5
|
-
}
|
|
6
|
-
export default function cornerstoneNiftiImageVolumeLoader(volumeId: string): IVolumeLoader;
|
|
7
|
-
export {};
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
export default function cornerstoneNiftiImageLoader(imageId: string): Types.IImageLoadObject;
|
|
@@ -1,10 +1,126 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Enums, eventTarget, metaData, triggerEvent, utilities, } from '@cornerstonejs/core';
|
|
2
|
+
import * as NiftiReader from 'nifti-reader-js';
|
|
3
|
+
import { Events } from './enums';
|
|
4
|
+
import { modalityScaleNifti } from './helpers';
|
|
5
|
+
const fetchStarted = new Map();
|
|
6
|
+
let niftiScalarData = null;
|
|
7
|
+
function fetchArrayBuffer({ url, signal, onload, }) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const xhr = new XMLHttpRequest();
|
|
10
|
+
xhr.open('GET', url, true);
|
|
11
|
+
xhr.responseType = 'arraybuffer';
|
|
12
|
+
const onLoadHandler = function (e) {
|
|
13
|
+
if (onload && typeof onload === 'function') {
|
|
14
|
+
onload();
|
|
15
|
+
}
|
|
16
|
+
if (signal) {
|
|
17
|
+
signal.removeEventListener('abort', onAbortHandler);
|
|
18
|
+
}
|
|
19
|
+
resolve(xhr.response);
|
|
20
|
+
};
|
|
21
|
+
const onAbortHandler = () => {
|
|
22
|
+
xhr.abort();
|
|
23
|
+
xhr.removeEventListener('load', onLoadHandler);
|
|
24
|
+
reject(new Error('Request aborted'));
|
|
25
|
+
};
|
|
26
|
+
xhr.addEventListener('load', onLoadHandler);
|
|
27
|
+
const onProgress = (loaded, total) => {
|
|
28
|
+
const data = { url, loaded, total };
|
|
29
|
+
triggerEvent(eventTarget, Events.NIFTI_VOLUME_PROGRESS, { data });
|
|
30
|
+
};
|
|
31
|
+
xhr.onprogress = function (e) {
|
|
32
|
+
onProgress(e.loaded, e.total);
|
|
33
|
+
};
|
|
34
|
+
if (signal && signal.aborted) {
|
|
35
|
+
xhr.abort();
|
|
36
|
+
reject(new Error('Request aborted'));
|
|
37
|
+
}
|
|
38
|
+
else if (signal) {
|
|
39
|
+
signal.addEventListener('abort', onAbortHandler);
|
|
40
|
+
}
|
|
41
|
+
xhr.send();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export default function cornerstoneNiftiImageLoader(imageId) {
|
|
45
|
+
const [url, frame] = imageId.substring(6).split('?frame=');
|
|
46
|
+
const sliceIndex = parseInt(frame, 10);
|
|
47
|
+
const imagePixelModule = metaData.get(Enums.MetadataModules.IMAGE_PIXEL, imageId);
|
|
48
|
+
const imagePlaneModule = metaData.get(Enums.MetadataModules.IMAGE_PLANE, imageId);
|
|
49
|
+
const promise = new Promise((resolve, reject) => {
|
|
50
|
+
if (!fetchStarted.get(url)) {
|
|
51
|
+
fetchStarted.set(url, true);
|
|
52
|
+
fetchAndProcessNiftiData(imageId, url, sliceIndex, imagePixelModule, imagePlaneModule)
|
|
53
|
+
.then(resolve)
|
|
54
|
+
.catch(reject);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
waitForNiftiData(imageId, sliceIndex, imagePixelModule, imagePlaneModule)
|
|
58
|
+
.then(resolve)
|
|
59
|
+
.catch(reject);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
promise: promise,
|
|
64
|
+
cancelFn: undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function fetchAndProcessNiftiData(imageId, url, sliceIndex, imagePixelModule, imagePlaneModule) {
|
|
68
|
+
let niftiBuffer = await fetchArrayBuffer({ url });
|
|
69
|
+
let niftiHeader = null;
|
|
70
|
+
let niftiImage = null;
|
|
71
|
+
if (NiftiReader.isCompressed(niftiBuffer)) {
|
|
72
|
+
niftiBuffer = NiftiReader.decompress(niftiBuffer);
|
|
73
|
+
}
|
|
74
|
+
if (NiftiReader.isNIFTI(niftiBuffer)) {
|
|
75
|
+
niftiHeader = NiftiReader.readHeader(niftiBuffer);
|
|
76
|
+
niftiImage = NiftiReader.readImage(niftiHeader, niftiBuffer);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const errorMessage = 'The provided buffer is not a valid NIFTI file.';
|
|
80
|
+
console.warn(errorMessage);
|
|
81
|
+
throw new Error(errorMessage);
|
|
82
|
+
}
|
|
83
|
+
const { scalarData } = modalityScaleNifti(niftiHeader, niftiImage);
|
|
84
|
+
niftiScalarData = scalarData;
|
|
85
|
+
return createImage(imageId, sliceIndex, imagePixelModule, imagePlaneModule);
|
|
86
|
+
}
|
|
87
|
+
function waitForNiftiData(imageId, sliceIndex, imagePixelModule, imagePlaneModule) {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const intervalId = setInterval(() => {
|
|
90
|
+
if (niftiScalarData) {
|
|
91
|
+
clearInterval(intervalId);
|
|
92
|
+
resolve(createImage(imageId, sliceIndex, imagePixelModule, imagePlaneModule));
|
|
93
|
+
}
|
|
94
|
+
}, 10);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function createImage(imageId, sliceIndex, imagePixelModule, imagePlaneModule) {
|
|
98
|
+
const { rows, columns } = imagePlaneModule;
|
|
99
|
+
const numVoxels = rows * columns;
|
|
100
|
+
const sliceOffset = numVoxels * sliceIndex;
|
|
101
|
+
const pixelData = new niftiScalarData.constructor(numVoxels);
|
|
102
|
+
pixelData.set(niftiScalarData.subarray(sliceOffset, sliceOffset + numVoxels));
|
|
103
|
+
const voxelManager = utilities.VoxelManager.createImageVoxelManager({
|
|
104
|
+
width: columns,
|
|
105
|
+
height: rows,
|
|
106
|
+
numberOfComponents: 1,
|
|
107
|
+
scalarData: pixelData,
|
|
108
|
+
});
|
|
4
109
|
return {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
110
|
+
imageId,
|
|
111
|
+
dataType: niftiScalarData.constructor
|
|
112
|
+
.name,
|
|
113
|
+
columnPixelSpacing: imagePlaneModule.columnPixelSpacing,
|
|
114
|
+
columns: imagePlaneModule.columns,
|
|
115
|
+
height: imagePlaneModule.rows,
|
|
116
|
+
invert: imagePixelModule.photometricInterpretation === 'MONOCHROME1',
|
|
117
|
+
rowPixelSpacing: imagePlaneModule.rowPixelSpacing,
|
|
118
|
+
rows: imagePlaneModule.rows,
|
|
119
|
+
sizeInBytes: rows * columns * niftiScalarData.BYTES_PER_ELEMENT,
|
|
120
|
+
width: imagePlaneModule.columns,
|
|
121
|
+
getPixelData: () => voxelManager.getScalarData(),
|
|
122
|
+
getCanvas: undefined,
|
|
123
|
+
numberOfComponents: undefined,
|
|
124
|
+
voxelManager,
|
|
8
125
|
};
|
|
9
126
|
}
|
|
10
|
-
//# sourceMappingURL=cornerstoneNiftiImageLoader.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const urlsMap: Map<any, any>;
|
|
2
|
+
export declare function fetchArrayBuffer({ url, onProgress, controller, onLoad, onHeader, loadFullVolume, }: {
|
|
3
|
+
url: any;
|
|
4
|
+
onProgress: any;
|
|
5
|
+
controller: any;
|
|
6
|
+
onLoad: any;
|
|
7
|
+
onHeader: any;
|
|
8
|
+
loadFullVolume?: boolean;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
data: Uint8Array;
|
|
11
|
+
headerInfo: any;
|
|
12
|
+
sliceInfo: any;
|
|
13
|
+
}>;
|
|
14
|
+
declare function createNiftiImageIdsAndCacheMetadata({ url }: {
|
|
15
|
+
url: any;
|
|
16
|
+
}): Promise<any[]>;
|
|
17
|
+
export { createNiftiImageIdsAndCacheMetadata };
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import * as NiftiReader from 'nifti-reader-js';
|
|
2
|
+
import { eventTarget, triggerEvent, utilities } from '@cornerstonejs/core';
|
|
3
|
+
import { rasToLps } from './helpers/convert';
|
|
4
|
+
import Events from './enums/Events';
|
|
5
|
+
import { NIFTI_LOADER_SCHEME } from './constants';
|
|
6
|
+
import makeVolumeMetadata from './helpers/makeVolumeMetadata';
|
|
7
|
+
import { getArrayConstructor } from './helpers/dataTypeCodeHelper';
|
|
8
|
+
export const urlsMap = new Map();
|
|
9
|
+
const NIFTI1_HEADER_SIZE = 348;
|
|
10
|
+
const NIFTI2_HEADER_SIZE = 540;
|
|
11
|
+
const HEADER_CHECK_SIZE = Math.max(NIFTI1_HEADER_SIZE, NIFTI2_HEADER_SIZE);
|
|
12
|
+
export async function fetchArrayBuffer({ url, onProgress, controller, onLoad, onHeader, loadFullVolume = false, }) {
|
|
13
|
+
const isCompressed = url.endsWith('.gz');
|
|
14
|
+
let receivedData = new Uint8Array(0);
|
|
15
|
+
let niftiHeader = null;
|
|
16
|
+
const sliceInfo = null;
|
|
17
|
+
let contentLength;
|
|
18
|
+
const receivedLength = 0;
|
|
19
|
+
const signal = controller.signal;
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(url, { signal });
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
24
|
+
}
|
|
25
|
+
contentLength = response.headers.get('Content-Length');
|
|
26
|
+
const reader = response.body.getReader();
|
|
27
|
+
const decompressionStream = isCompressed
|
|
28
|
+
? new DecompressionStream('gzip')
|
|
29
|
+
: null;
|
|
30
|
+
const decompressionWriter = decompressionStream
|
|
31
|
+
? decompressionStream.writable.getWriter()
|
|
32
|
+
: null;
|
|
33
|
+
readStream(reader, decompressionWriter, isCompressed, receivedLength, processChunk, controller).catch(console.error);
|
|
34
|
+
if (isCompressed) {
|
|
35
|
+
const decompressedStream = decompressionStream.readable.getReader();
|
|
36
|
+
while (true) {
|
|
37
|
+
const { done, value } = await decompressedStream.read();
|
|
38
|
+
if (done) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
processChunk(value);
|
|
42
|
+
if (niftiHeader && !loadFullVolume) {
|
|
43
|
+
controller.abort();
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (onLoad && typeof onLoad === 'function') {
|
|
49
|
+
onLoad();
|
|
50
|
+
}
|
|
51
|
+
return { data: receivedData, headerInfo: niftiHeader, sliceInfo };
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.name === 'AbortError') {
|
|
55
|
+
console.log('Fetch aborted');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.error('Fetch error:', error);
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
function processChunk(chunk) {
|
|
63
|
+
appendData(chunk);
|
|
64
|
+
if (onProgress && typeof onProgress === 'function') {
|
|
65
|
+
onProgress(receivedLength, contentLength);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function appendData(data) {
|
|
69
|
+
const newData = new Uint8Array(receivedData.length + data.length);
|
|
70
|
+
newData.set(receivedData);
|
|
71
|
+
newData.set(data, receivedData.length);
|
|
72
|
+
receivedData = newData;
|
|
73
|
+
if (!loadFullVolume &&
|
|
74
|
+
!niftiHeader &&
|
|
75
|
+
receivedData.length >= HEADER_CHECK_SIZE) {
|
|
76
|
+
niftiHeader = handleNiftiHeader(receivedData);
|
|
77
|
+
if (niftiHeader && niftiHeader.isValid) {
|
|
78
|
+
controller.abort();
|
|
79
|
+
}
|
|
80
|
+
onHeader?.(niftiHeader);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function readStream(reader, decompressionWriter, isCompressed, receivedLength, processChunk, controller) {
|
|
85
|
+
while (true) {
|
|
86
|
+
const { done, value } = await reader.read();
|
|
87
|
+
if (done) {
|
|
88
|
+
if (isCompressed) {
|
|
89
|
+
decompressionWriter.close();
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
receivedLength += value.length;
|
|
94
|
+
if (isCompressed) {
|
|
95
|
+
await decompressionWriter.write(value);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
processChunk(value);
|
|
99
|
+
}
|
|
100
|
+
if (controller.signal.aborted) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function handleNiftiHeader(data) {
|
|
106
|
+
if (data.length < HEADER_CHECK_SIZE) {
|
|
107
|
+
return { isValid: false, message: 'Not enough data to check header' };
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const headerBuffer = data.slice(0, HEADER_CHECK_SIZE).buffer;
|
|
111
|
+
const header = NiftiReader.readHeader(headerBuffer);
|
|
112
|
+
const version = header.sizeof_hdr === NIFTI2_HEADER_SIZE ? 2 : 1;
|
|
113
|
+
const { orientation, origin, spacing } = rasToLps(header);
|
|
114
|
+
const { dimensions, direction } = makeVolumeMetadata(header, orientation, 1);
|
|
115
|
+
const arrayConstructor = getArrayConstructor(header.datatypeCode);
|
|
116
|
+
return {
|
|
117
|
+
dimensions,
|
|
118
|
+
direction,
|
|
119
|
+
isValid: true,
|
|
120
|
+
message: `Valid Nifti-${version} header detected`,
|
|
121
|
+
origin,
|
|
122
|
+
version,
|
|
123
|
+
orientation,
|
|
124
|
+
spacing,
|
|
125
|
+
header,
|
|
126
|
+
arrayConstructor,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Error reading Nifti header:', error);
|
|
131
|
+
return { isValid: false, message: 'Error reading Nifti header' };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function fetchAndAllocateNiftiVolume(volumeId) {
|
|
135
|
+
const niftiURL = volumeId.substring(NIFTI_LOADER_SCHEME.length + 1);
|
|
136
|
+
const onProgress = (loaded, total) => {
|
|
137
|
+
const data = { volumeId, loaded, total };
|
|
138
|
+
triggerEvent(eventTarget, Events.NIFTI_VOLUME_PROGRESS, { data });
|
|
139
|
+
};
|
|
140
|
+
const onLoad = () => {
|
|
141
|
+
const data = { volumeId };
|
|
142
|
+
triggerEvent(eventTarget, Events.NIFTI_VOLUME_LOADED, { data });
|
|
143
|
+
};
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
urlsMap.set(niftiURL, { controller, loading: true });
|
|
146
|
+
const niftiHeader = (await new Promise((resolve) => {
|
|
147
|
+
fetchArrayBuffer({
|
|
148
|
+
url: niftiURL,
|
|
149
|
+
onProgress,
|
|
150
|
+
controller,
|
|
151
|
+
onLoad,
|
|
152
|
+
onHeader: resolve,
|
|
153
|
+
});
|
|
154
|
+
}));
|
|
155
|
+
const { dimensions, direction, isValid, message, origin, version, header, spacing, arrayConstructor, } = niftiHeader;
|
|
156
|
+
const numImages = dimensions[2];
|
|
157
|
+
if (!isValid) {
|
|
158
|
+
console.error(message);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const imageIds = [];
|
|
162
|
+
for (let i = 0; i < numImages; i++) {
|
|
163
|
+
const imageId = `nifti:${niftiURL}?frame=${i}`;
|
|
164
|
+
const imageIdIndex = i;
|
|
165
|
+
imageIds.push(imageId);
|
|
166
|
+
const imageOrientationPatient = [
|
|
167
|
+
direction[0],
|
|
168
|
+
direction[1],
|
|
169
|
+
direction[2],
|
|
170
|
+
direction[3],
|
|
171
|
+
direction[4],
|
|
172
|
+
direction[5],
|
|
173
|
+
];
|
|
174
|
+
const precision = 6;
|
|
175
|
+
const imagePositionPatient = [
|
|
176
|
+
parseFloat((origin[0] + imageIdIndex * direction[6] * spacing[0]).toFixed(precision)),
|
|
177
|
+
parseFloat((origin[1] + imageIdIndex * direction[7] * spacing[1]).toFixed(precision)),
|
|
178
|
+
parseFloat((origin[2] + imageIdIndex * direction[8] * spacing[2]).toFixed(precision)),
|
|
179
|
+
];
|
|
180
|
+
const imagePlaneMetadata = {
|
|
181
|
+
frameOfReferenceUID: '1.2.840.10008.1.4',
|
|
182
|
+
rows: dimensions[1],
|
|
183
|
+
columns: dimensions[0],
|
|
184
|
+
imageOrientationPatient,
|
|
185
|
+
rowCosines: direction.slice(0, 3),
|
|
186
|
+
columnCosines: direction.slice(3, 6),
|
|
187
|
+
imagePositionPatient,
|
|
188
|
+
sliceThickness: spacing[2],
|
|
189
|
+
sliceLocation: origin[2] + i * spacing[2],
|
|
190
|
+
pixelSpacing: [spacing[0], spacing[1]],
|
|
191
|
+
rowPixelSpacing: spacing[1],
|
|
192
|
+
columnPixelSpacing: spacing[0],
|
|
193
|
+
};
|
|
194
|
+
const imagePixelMetadata = {
|
|
195
|
+
samplesPerPixel: 1,
|
|
196
|
+
photometricInterpretation: 'MONOCHROME2',
|
|
197
|
+
rows: dimensions[1],
|
|
198
|
+
columns: dimensions[0],
|
|
199
|
+
bitsAllocated: arrayConstructor.BYTES_PER_ELEMENT * 8,
|
|
200
|
+
bitsStored: arrayConstructor.BYTES_PER_ELEMENT * 8,
|
|
201
|
+
highBit: arrayConstructor.BYTES_PER_ELEMENT * 8 - 1,
|
|
202
|
+
pixelRepresentation: 1,
|
|
203
|
+
planarConfiguration: 0,
|
|
204
|
+
pixelAspectRatio: '1\\1',
|
|
205
|
+
redPaletteColorLookupTableDescriptor: [],
|
|
206
|
+
greenPaletteColorLookupTableDescriptor: [],
|
|
207
|
+
bluePaletteColorLookupTableDescriptor: [],
|
|
208
|
+
redPaletteColorLookupTableData: [],
|
|
209
|
+
greenPaletteColorLookupTableData: [],
|
|
210
|
+
bluePaletteColorLookupTableData: [],
|
|
211
|
+
smallestPixelValue: undefined,
|
|
212
|
+
largestPixelValue: undefined,
|
|
213
|
+
};
|
|
214
|
+
const generalSeriesMetadata = {
|
|
215
|
+
seriesDate: new Date(),
|
|
216
|
+
seriesTime: new Date(),
|
|
217
|
+
};
|
|
218
|
+
utilities.genericMetadataProvider.add(imageId, {
|
|
219
|
+
type: 'imagePixelModule',
|
|
220
|
+
metadata: imagePixelMetadata,
|
|
221
|
+
});
|
|
222
|
+
utilities.genericMetadataProvider.add(imageId, {
|
|
223
|
+
type: 'imagePlaneModule',
|
|
224
|
+
metadata: imagePlaneMetadata,
|
|
225
|
+
});
|
|
226
|
+
utilities.genericMetadataProvider.add(imageId, {
|
|
227
|
+
type: 'generalSeriesModule',
|
|
228
|
+
metadata: generalSeriesMetadata,
|
|
229
|
+
});
|
|
230
|
+
utilities.genericMetadataProvider.add(imageId, {
|
|
231
|
+
type: 'niftiVersion',
|
|
232
|
+
metadata: {
|
|
233
|
+
version,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
utilities.genericMetadataProvider.addRaw(imageId, {
|
|
237
|
+
type: 'niftiHeader',
|
|
238
|
+
metadata: {
|
|
239
|
+
header,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
urlsMap.delete(niftiURL);
|
|
244
|
+
return imageIds;
|
|
245
|
+
}
|
|
246
|
+
async function createNiftiImageIdsAndCacheMetadata({ url }) {
|
|
247
|
+
const imageIds = await fetchAndAllocateNiftiVolume(url);
|
|
248
|
+
return imageIds;
|
|
249
|
+
}
|
|
250
|
+
export { createNiftiImageIdsAndCacheMetadata };
|
package/dist/esm/enums/Events.js
CHANGED
package/dist/esm/enums/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { getShouldUseSharedArrayBuffer } from '@cornerstonejs/core';
|
|
2
1
|
import { parseAffineMatrix } from './affineUtilities';
|
|
3
2
|
const invertDataPerFrame = (dimensions, imageDataArray) => {
|
|
4
3
|
let TypedArrayConstructor;
|
|
5
4
|
let bytesPerVoxel;
|
|
6
5
|
if (imageDataArray instanceof Uint8Array ||
|
|
7
|
-
imageDataArray instanceof ArrayBuffer
|
|
8
|
-
(getShouldUseSharedArrayBuffer() &&
|
|
9
|
-
imageDataArray instanceof SharedArrayBuffer)) {
|
|
6
|
+
imageDataArray instanceof ArrayBuffer) {
|
|
10
7
|
TypedArrayConstructor = Uint8Array;
|
|
11
8
|
bytesPerVoxel = 1;
|
|
12
9
|
}
|
|
@@ -82,4 +79,3 @@ function lpsToRas(header) {
|
|
|
82
79
|
};
|
|
83
80
|
}
|
|
84
81
|
export { lpsToRas, rasToLps, invertDataPerFrame };
|
|
85
|
-
//# sourceMappingURL=convert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getArrayConstructor(datatypeCode: number): unknown;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as NIFTICONSTANTS from './niftiConstants';
|
|
2
|
+
export function getArrayConstructor(datatypeCode) {
|
|
3
|
+
switch (datatypeCode) {
|
|
4
|
+
case NIFTICONSTANTS.NIFTI_TYPE_UINT8:
|
|
5
|
+
return Uint8Array;
|
|
6
|
+
case NIFTICONSTANTS.NIFTI_TYPE_INT16:
|
|
7
|
+
return Int16Array;
|
|
8
|
+
case NIFTICONSTANTS.NIFTI_TYPE_INT32:
|
|
9
|
+
return Int32Array;
|
|
10
|
+
case NIFTICONSTANTS.NIFTI_TYPE_FLOAT32: {
|
|
11
|
+
return Float32Array;
|
|
12
|
+
}
|
|
13
|
+
case NIFTICONSTANTS.NIFTI_TYPE_INT8:
|
|
14
|
+
return Int8Array;
|
|
15
|
+
case NIFTICONSTANTS.NIFTI_TYPE_UINT16:
|
|
16
|
+
return Uint16Array;
|
|
17
|
+
case NIFTICONSTANTS.NIFTI_TYPE_UINT32:
|
|
18
|
+
return Uint32Array;
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`NIFTI datatypeCode ${datatypeCode} is not yet supported`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import makeVolumeMetadata from './makeVolumeMetadata';
|
|
2
2
|
import modalityScaleNifti from './modalityScaleNifti';
|
|
3
|
-
|
|
4
|
-
export { modalityScaleNifti, makeVolumeMetadata, fetchAndAllocateNiftiVolume };
|
|
3
|
+
export { modalityScaleNifti, makeVolumeMetadata };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import makeVolumeMetadata from './makeVolumeMetadata';
|
|
2
2
|
import modalityScaleNifti from './modalityScaleNifti';
|
|
3
|
-
|
|
4
|
-
export { modalityScaleNifti, makeVolumeMetadata, fetchAndAllocateNiftiVolume };
|
|
5
|
-
//# sourceMappingURL=index.js.map
|
|
3
|
+
export { modalityScaleNifti, makeVolumeMetadata };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Types } from '@cornerstonejs/core';
|
|
2
|
-
export default function makeVolumeMetadata(niftiHeader: any, orientation: any,
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
export default function makeVolumeMetadata(niftiHeader: any, orientation: any, pixelRepresentation: any): {
|
|
3
3
|
volumeMetadata: Types.Metadata;
|
|
4
4
|
dimensions: Types.Point3;
|
|
5
5
|
direction: Types.Mat3;
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { utilities } from '@cornerstonejs/core';
|
|
2
2
|
import { vec3 } from 'gl-matrix';
|
|
3
3
|
const { windowLevel } = utilities;
|
|
4
|
-
export default function makeVolumeMetadata(niftiHeader, orientation,
|
|
4
|
+
export default function makeVolumeMetadata(niftiHeader, orientation, pixelRepresentation) {
|
|
5
5
|
const { numBitsPerVoxel, littleEndian, pixDims, dims } = niftiHeader;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const min = Infinity;
|
|
7
|
+
const max = -Infinity;
|
|
8
8
|
const frameLength = dims[1] * dims[2];
|
|
9
9
|
const middleFrameIndex = Math.floor(dims[3] / 2);
|
|
10
10
|
const offset = frameLength * middleFrameIndex;
|
|
11
|
-
|
|
12
|
-
const voxelValue = scalarData[voxelIndex];
|
|
13
|
-
if (voxelValue > max) {
|
|
14
|
-
max = voxelValue;
|
|
15
|
-
}
|
|
16
|
-
if (voxelValue < min) {
|
|
17
|
-
min = voxelValue;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const { windowWidth, windowCenter } = windowLevel.toWindowLevel(min, max);
|
|
11
|
+
const { windowWidth, windowCenter } = { windowWidth: 400, windowCenter: 40 };
|
|
21
12
|
const rowCosines = vec3.create();
|
|
22
13
|
const columnCosines = vec3.create();
|
|
23
14
|
const scanAxisNormal = vec3.create();
|
|
@@ -62,4 +53,3 @@ export default function makeVolumeMetadata(niftiHeader, orientation, scalarData,
|
|
|
62
53
|
]),
|
|
63
54
|
};
|
|
64
55
|
}
|
|
65
|
-
//# sourceMappingURL=makeVolumeMetadata.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { cache, Enums
|
|
1
|
+
import { cache, Enums } from '@cornerstonejs/core';
|
|
2
2
|
import * as NIFTICONSTANTS from './niftiConstants';
|
|
3
|
-
const { createFloat32SharedArray, createInt16SharedArray, createUint8SharedArray, createUint16SharedArray, } = utilities;
|
|
4
3
|
export default function modalityScaleNifti(niftiHeader, niftiImageBuffer) {
|
|
5
4
|
const { datatypeCode, scl_slope, scl_inter } = niftiHeader;
|
|
6
5
|
let slope = scl_slope;
|
|
@@ -101,43 +100,32 @@ function checkCacheAvailable(bitsAllocated, length) {
|
|
|
101
100
|
function allocateScalarData(types, niiBuffer) {
|
|
102
101
|
let bitsAllocated;
|
|
103
102
|
let scalarData;
|
|
104
|
-
const useSharedArrayBuffer = getShouldUseSharedArrayBuffer();
|
|
105
103
|
const nVox = niiBuffer.length;
|
|
106
104
|
switch (types) {
|
|
107
105
|
case 'Float32Array':
|
|
108
106
|
bitsAllocated = 32;
|
|
109
107
|
checkCacheAvailable(bitsAllocated, nVox);
|
|
110
|
-
scalarData =
|
|
111
|
-
? createFloat32SharedArray(nVox)
|
|
112
|
-
: new Float32Array(nVox);
|
|
108
|
+
scalarData = new Float32Array(nVox);
|
|
113
109
|
break;
|
|
114
110
|
case 'Int16Array':
|
|
115
111
|
bitsAllocated = 16;
|
|
116
112
|
checkCacheAvailable(bitsAllocated, nVox);
|
|
117
|
-
scalarData =
|
|
118
|
-
? createInt16SharedArray(nVox)
|
|
119
|
-
: new Int16Array(nVox);
|
|
113
|
+
scalarData = new Int16Array(nVox);
|
|
120
114
|
break;
|
|
121
115
|
case 'Int8Array':
|
|
122
116
|
bitsAllocated = 8;
|
|
123
117
|
checkCacheAvailable(bitsAllocated, nVox);
|
|
124
|
-
scalarData =
|
|
125
|
-
? createInt16SharedArray(nVox)
|
|
126
|
-
: new Int16Array(nVox);
|
|
118
|
+
scalarData = new Int16Array(nVox);
|
|
127
119
|
break;
|
|
128
120
|
case 'Uint16Array':
|
|
129
121
|
bitsAllocated = 16;
|
|
130
122
|
checkCacheAvailable(bitsAllocated, nVox);
|
|
131
|
-
scalarData =
|
|
132
|
-
? createUint16SharedArray(nVox)
|
|
133
|
-
: new Uint16Array(nVox);
|
|
123
|
+
scalarData = new Uint16Array(nVox);
|
|
134
124
|
break;
|
|
135
125
|
case 'Uint8Array':
|
|
136
126
|
bitsAllocated = 8;
|
|
137
127
|
checkCacheAvailable(bitsAllocated, nVox);
|
|
138
|
-
scalarData =
|
|
139
|
-
? createUint8SharedArray(nVox)
|
|
140
|
-
: new Uint8Array(nVox);
|
|
128
|
+
scalarData = new Uint8Array(nVox);
|
|
141
129
|
break;
|
|
142
130
|
default:
|
|
143
131
|
throw new Error(`TypedArray ${types} is not yet supported`);
|
|
@@ -145,4 +133,3 @@ function allocateScalarData(types, niiBuffer) {
|
|
|
145
133
|
scalarData.set(niiBuffer);
|
|
146
134
|
return scalarData;
|
|
147
135
|
}
|
|
148
|
-
//# sourceMappingURL=modalityScaleNifti.js.map
|
|
@@ -41,4 +41,3 @@ const NIFTI1_HEADER_ENDOFFILE_STRING = '0000';
|
|
|
41
41
|
const NIFTI1_ALIGNED_ANAT = 2;
|
|
42
42
|
const NIFTI1_XYZT_UNITS_UNKNOWN = 0;
|
|
43
43
|
export { NIFTI_TYPE_UINT8, NIFTI_TYPE_INT16, NIFTI_TYPE_INT32, NIFTI_TYPE_FLOAT32, NIFTI_TYPE_COMPLEX64, NIFTI_TYPE_FLOAT64, NIFTI_TYPE_RGB24, NIFTI_TYPE_INT8, NIFTI_TYPE_UINT16, NIFTI_TYPE_UINT32, NIFTI_TYPE_INT64, NIFTI_TYPE_UINT64, NIFTI_TYPE_FLOAT128, NIFTI_TYPE_COMPLEX128, NIFTI_TYPE_COMPLEX256, NIFTI_TYPE_RGBA32, NIFTI2_HEADER_TOTAL_LENGTH_BYTES, NIFTI2_HEADER_TOTAL_LENGTH_BITS, NIFTI2_HEADER_MAGIC_LENGTH_BYTES, NIFTI2_HEADER_DESCRIPTION_LENGTH_BYTES, NIFTI2_HEADER_AUX_FILE_LENGTH_BYTES, NIFTI2_HEADER_INTENT_NAME_LENGTH_BYTES, NIFTI2_HEADER_DIM_INFO_LENGTH_BYTES, NIFTI2_HEADER_ENDOFFILE_LENGTH_BYTES, NIFTI2_VOX_OFFSET_BYTES, NIFTI2_HEADER_MAGIC_STRING, NIFTI2_HEADER_ENDOFFILE_STRING, NIFTI2_ALIGNED_ANAT, NIFTI2_XYZT_UNITS_UNKNOWN, NIFTI1_HEADER_TOTAL_LENGTH_BYTES, NIFTI1_HEADER_TOTAL_LENGTH_BITS, NIFTI1_HEADER_MAGIC_LENGTH_BYTES, NIFTI1_HEADER_DESCRIPTION_LENGTH_BYTES, NIFTI1_HEADER_AUX_FILE_LENGTH_BYTES, NIFTI1_HEADER_INTENT_NAME_LENGTH_BYTES, NIFTI1_HEADER_DIM_INFO_LENGTH_BYTES, NIFTI1_HEADER_ENDOFFILE_LENGTH_BYTES, NIFTI1_VOX_OFFSET_BYTES, NIFTI1_HEADER_MAGIC_STRING, NIFTI1_HEADER_ENDOFFILE_STRING, NIFTI1_ALIGNED_ANAT, NIFTI1_XYZT_UNITS_UNKNOWN, };
|
|
44
|
-
//# sourceMappingURL=niftiConstants.js.map
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import NiftiImageVolume from './NiftiImageVolume';
|
|
1
|
+
import cornerstoneNiftiImageLoader from './cornerstoneNiftiImageLoader';
|
|
3
2
|
import * as helpers from './helpers';
|
|
4
3
|
import * as Enums from './enums';
|
|
5
|
-
|
|
4
|
+
import { createNiftiImageIdsAndCacheMetadata } from './createNiftiImageIdsAndCacheMetadata';
|
|
5
|
+
export { cornerstoneNiftiImageLoader, helpers, Enums, createNiftiImageIdsAndCacheMetadata, };
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import NiftiImageVolume from './NiftiImageVolume';
|
|
1
|
+
import cornerstoneNiftiImageLoader from './cornerstoneNiftiImageLoader';
|
|
3
2
|
import * as helpers from './helpers';
|
|
4
3
|
import * as Enums from './enums';
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { createNiftiImageIdsAndCacheMetadata } from './createNiftiImageIdsAndCacheMetadata';
|
|
5
|
+
export { cornerstoneNiftiImageLoader, helpers, Enums, createNiftiImageIdsAndCacheMetadata, };
|