@cornerstonejs/core 1.55.0 → 1.56.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/cjs/RenderingEngine/CanvasActor/CanvasMapper.d.ts +6 -0
- package/dist/cjs/RenderingEngine/CanvasActor/CanvasMapper.js +12 -0
- package/dist/cjs/RenderingEngine/CanvasActor/CanvasMapper.js.map +1 -0
- package/dist/cjs/RenderingEngine/CanvasActor/CanvasProperties.d.ts +15 -0
- package/dist/cjs/RenderingEngine/CanvasActor/CanvasProperties.js +33 -0
- package/dist/cjs/RenderingEngine/CanvasActor/CanvasProperties.js.map +1 -0
- package/dist/cjs/RenderingEngine/CanvasActor/index.d.ts +23 -0
- package/dist/cjs/RenderingEngine/CanvasActor/index.js +163 -0
- package/dist/cjs/RenderingEngine/CanvasActor/index.js.map +1 -0
- package/dist/cjs/RenderingEngine/VideoViewport.d.ts +28 -6
- package/dist/cjs/RenderingEngine/VideoViewport.js +55 -13
- package/dist/cjs/RenderingEngine/VideoViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/Viewport.js +3 -2
- package/dist/cjs/RenderingEngine/Viewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js +2 -3
- package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -1
- package/dist/cjs/enums/{VideoViewport.js → VideoEnums.js} +1 -1
- package/dist/cjs/enums/VideoEnums.js.map +1 -0
- package/dist/cjs/enums/index.d.ts +2 -2
- package/dist/cjs/enums/index.js +3 -3
- package/dist/cjs/enums/index.js.map +1 -1
- package/dist/cjs/loaders/imageLoader.d.ts +9 -7
- package/dist/cjs/loaders/imageLoader.js +9 -7
- package/dist/cjs/loaders/imageLoader.js.map +1 -1
- package/dist/cjs/types/IActor.d.ts +8 -1
- package/dist/cjs/types/IImage.d.ts +3 -0
- package/dist/cjs/types/IImageVolume.d.ts +3 -1
- package/dist/cjs/types/IVideoViewport.d.ts +1 -0
- package/dist/cjs/types/index.d.ts +2 -2
- package/dist/cjs/utilities/RLEVoxelMap.d.ts +26 -0
- package/dist/cjs/utilities/RLEVoxelMap.js +178 -0
- package/dist/cjs/utilities/RLEVoxelMap.js.map +1 -0
- package/dist/cjs/utilities/VoxelManager.d.ts +11 -3
- package/dist/cjs/utilities/VoxelManager.js +76 -1
- package/dist/cjs/utilities/VoxelManager.js.map +1 -1
- package/dist/cjs/utilities/updateVTKImageDataWithCornerstoneImage.js +3 -0
- package/dist/cjs/utilities/updateVTKImageDataWithCornerstoneImage.js.map +1 -1
- package/dist/esm/RenderingEngine/CanvasActor/CanvasMapper.js +9 -0
- package/dist/esm/RenderingEngine/CanvasActor/CanvasMapper.js.map +1 -0
- package/dist/esm/RenderingEngine/CanvasActor/CanvasProperties.js +30 -0
- package/dist/esm/RenderingEngine/CanvasActor/CanvasProperties.js.map +1 -0
- package/dist/esm/RenderingEngine/CanvasActor/index.js +157 -0
- package/dist/esm/RenderingEngine/CanvasActor/index.js.map +1 -0
- package/dist/esm/RenderingEngine/VideoViewport.js +53 -12
- package/dist/esm/RenderingEngine/VideoViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/Viewport.js +2 -2
- package/dist/esm/RenderingEngine/Viewport.js.map +1 -1
- package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js +2 -3
- package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -1
- package/dist/esm/enums/{VideoViewport.js → VideoEnums.js} +1 -1
- package/dist/esm/enums/VideoEnums.js.map +1 -0
- package/dist/esm/enums/index.js +2 -2
- package/dist/esm/enums/index.js.map +1 -1
- package/dist/esm/loaders/imageLoader.js +7 -7
- package/dist/esm/loaders/imageLoader.js.map +1 -1
- package/dist/esm/utilities/RLEVoxelMap.js +175 -0
- package/dist/esm/utilities/RLEVoxelMap.js.map +1 -0
- package/dist/esm/utilities/VoxelManager.js +73 -1
- package/dist/esm/utilities/VoxelManager.js.map +1 -1
- package/dist/esm/utilities/updateVTKImageDataWithCornerstoneImage.js +3 -0
- package/dist/esm/utilities/updateVTKImageDataWithCornerstoneImage.js.map +1 -1
- package/dist/types/RenderingEngine/CanvasActor/CanvasMapper.d.ts +7 -0
- package/dist/types/RenderingEngine/CanvasActor/CanvasMapper.d.ts.map +1 -0
- package/dist/types/RenderingEngine/CanvasActor/CanvasProperties.d.ts +16 -0
- package/dist/types/RenderingEngine/CanvasActor/CanvasProperties.d.ts.map +1 -0
- package/dist/types/RenderingEngine/CanvasActor/index.d.ts +24 -0
- package/dist/types/RenderingEngine/CanvasActor/index.d.ts.map +1 -0
- package/dist/types/RenderingEngine/VideoViewport.d.ts +28 -6
- package/dist/types/RenderingEngine/VideoViewport.d.ts.map +1 -1
- package/dist/types/enums/{VideoViewport.d.ts → VideoEnums.d.ts} +1 -1
- package/dist/types/enums/VideoEnums.d.ts.map +1 -0
- package/dist/types/enums/index.d.ts +2 -2
- package/dist/types/enums/index.d.ts.map +1 -1
- package/dist/types/loaders/imageLoader.d.ts +9 -7
- package/dist/types/loaders/imageLoader.d.ts.map +1 -1
- package/dist/types/types/IActor.d.ts +8 -1
- package/dist/types/types/IActor.d.ts.map +1 -1
- package/dist/types/types/IImage.d.ts +3 -0
- package/dist/types/types/IImage.d.ts.map +1 -1
- package/dist/types/types/IImageVolume.d.ts +3 -1
- package/dist/types/types/IImageVolume.d.ts.map +1 -1
- package/dist/types/types/IVideoViewport.d.ts +1 -0
- package/dist/types/types/IVideoViewport.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +2 -2
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utilities/RLEVoxelMap.d.ts +27 -0
- package/dist/types/utilities/RLEVoxelMap.d.ts.map +1 -0
- package/dist/types/utilities/VoxelManager.d.ts +11 -3
- package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
- package/dist/types/utilities/updateVTKImageDataWithCornerstoneImage.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
- package/src/RenderingEngine/CanvasActor/CanvasMapper.ts +17 -0
- package/src/RenderingEngine/CanvasActor/CanvasProperties.ts +49 -0
- package/src/RenderingEngine/CanvasActor/index.ts +231 -0
- package/src/RenderingEngine/VideoViewport.ts +83 -18
- package/src/RenderingEngine/Viewport.ts +2 -2
- package/src/RenderingEngine/helpers/addImageSlicesToViewports.ts +2 -2
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLTexture.js +1 -1
- package/src/enums/index.ts +2 -2
- package/src/loaders/imageLoader.ts +39 -16
- package/src/types/IActor.ts +13 -1
- package/src/types/IImage.ts +4 -0
- package/src/types/IImageVolume.ts +5 -0
- package/src/types/IVideoViewport.ts +5 -0
- package/src/types/index.ts +8 -1
- package/src/utilities/RLEVoxelMap.ts +331 -0
- package/src/utilities/VoxelManager.ts +175 -5
- package/src/utilities/updateVTKImageDataWithCornerstoneImage.ts +4 -0
- package/dist/cjs/enums/VideoViewport.js.map +0 -1
- package/dist/esm/enums/VideoViewport.js.map +0 -1
- package/dist/types/enums/VideoViewport.d.ts.map +0 -1
- /package/dist/cjs/enums/{VideoViewport.d.ts → VideoEnums.d.ts} +0 -0
- /package/src/enums/{VideoViewport.ts → VideoEnums.ts} +0 -0
package/src/types/IActor.ts
CHANGED
|
@@ -6,6 +6,18 @@ export type Actor = vtkActor;
|
|
|
6
6
|
export type VolumeActor = vtkVolume;
|
|
7
7
|
export type ImageActor = vtkImageSlice;
|
|
8
8
|
|
|
9
|
+
export interface ICanvasActor {
|
|
10
|
+
render(viewport, context): void;
|
|
11
|
+
|
|
12
|
+
getMapper();
|
|
13
|
+
|
|
14
|
+
getProperty();
|
|
15
|
+
|
|
16
|
+
isA(actorType): boolean;
|
|
17
|
+
|
|
18
|
+
getClassName(): string;
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
/**
|
|
10
22
|
* Cornerstone Actor Entry including actor uid, actual Actor, and
|
|
11
23
|
* slabThickness for the actor. ActorEntry is the object that
|
|
@@ -15,7 +27,7 @@ export type ActorEntry = {
|
|
|
15
27
|
/** actor UID */
|
|
16
28
|
uid: string;
|
|
17
29
|
/** actual actor object */
|
|
18
|
-
actor: Actor | VolumeActor | ImageActor;
|
|
30
|
+
actor: Actor | VolumeActor | ImageActor | ICanvasActor;
|
|
19
31
|
/** the id of the reference volume from which this actor is derived or created*/
|
|
20
32
|
referenceId?: string;
|
|
21
33
|
/** slab thickness for the actor */
|
package/src/types/IImage.ts
CHANGED
|
@@ -2,8 +2,10 @@ import type CPUFallbackLUT from './CPUFallbackLUT';
|
|
|
2
2
|
import type CPUFallbackColormap from './CPUFallbackColormap';
|
|
3
3
|
import type CPUFallbackEnabledElement from './CPUFallbackEnabledElement';
|
|
4
4
|
import type { PixelDataTypedArray } from './PixelDataTypedArray';
|
|
5
|
+
import type VoxelManager from '../utilities/VoxelManager';
|
|
5
6
|
import { ImageQualityStatus } from '../enums';
|
|
6
7
|
import IImageCalibration from './IImageCalibration';
|
|
8
|
+
import RGB from './RGB';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Cornerstone Image interface, it is used for both CPU and GPU rendering
|
|
@@ -122,6 +124,8 @@ interface IImage {
|
|
|
122
124
|
calibration?: IImageCalibration;
|
|
123
125
|
imageFrame?: any;
|
|
124
126
|
|
|
127
|
+
voxelManager?: VoxelManager<number> | VoxelManager<RGB>;
|
|
128
|
+
|
|
125
129
|
bufferView?: {
|
|
126
130
|
buffer: ArrayBuffer;
|
|
127
131
|
offset: number;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
2
|
+
import type { VoxelManager } from '../utilities';
|
|
2
3
|
import {
|
|
3
4
|
Metadata,
|
|
4
5
|
PixelDataTypedArray,
|
|
5
6
|
Point3,
|
|
6
7
|
IImageLoadObject,
|
|
7
8
|
Mat3,
|
|
9
|
+
RGB,
|
|
8
10
|
} from '../types';
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -69,6 +71,9 @@ interface IImageVolume {
|
|
|
69
71
|
/** return the volume scalar data */
|
|
70
72
|
getScalarData(): PixelDataTypedArray;
|
|
71
73
|
|
|
74
|
+
/** A voxel manager to manage the scalar data */
|
|
75
|
+
voxelManager?: VoxelManager<number> | VoxelManager<RGB>;
|
|
76
|
+
|
|
72
77
|
convertToImageSlicesAndCache(): string[];
|
|
73
78
|
|
|
74
79
|
/** return the index of a given imageId */
|
|
@@ -65,6 +65,11 @@ export default interface IVideoViewport extends IViewport {
|
|
|
65
65
|
*/
|
|
66
66
|
setFrameNumber(frameNo: number);
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Sets the current video time, in seconds
|
|
70
|
+
*/
|
|
71
|
+
setTime(time: number);
|
|
72
|
+
|
|
68
73
|
/**
|
|
69
74
|
* Sets the range of frames for displaying. This is the range of frames
|
|
70
75
|
* that are shown/looped over when the video is playing.
|
package/src/types/index.ts
CHANGED
|
@@ -18,7 +18,13 @@ import type {
|
|
|
18
18
|
PublicViewportInput,
|
|
19
19
|
TargetSpecifier,
|
|
20
20
|
} from './IViewport';
|
|
21
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
VolumeActor,
|
|
23
|
+
Actor,
|
|
24
|
+
ActorEntry,
|
|
25
|
+
ImageActor,
|
|
26
|
+
ICanvasActor,
|
|
27
|
+
} from './IActor';
|
|
22
28
|
import type {
|
|
23
29
|
IImageLoadObject,
|
|
24
30
|
IVolumeLoadObject,
|
|
@@ -160,6 +166,7 @@ export type {
|
|
|
160
166
|
Actor,
|
|
161
167
|
ActorEntry,
|
|
162
168
|
ImageActor,
|
|
169
|
+
ICanvasActor,
|
|
163
170
|
IImageLoadObject,
|
|
164
171
|
IVolumeLoadObject,
|
|
165
172
|
IVolumeInput,
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { PixelDataTypedArray } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The RLERun specifies a contigous run of values for a row,
|
|
5
|
+
* where all indices (i only) from `[start,end)` have the specified
|
|
6
|
+
* value.
|
|
7
|
+
*/
|
|
8
|
+
export type RLERun<T> = {
|
|
9
|
+
value: T;
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* RLE based implementation of a voxel map.
|
|
16
|
+
* This can be used as single or multi-plane, as the underlying indexes are
|
|
17
|
+
* mapped to rows and hte rows are indexed started at 0 and continuing
|
|
18
|
+
* incrementing for all rows in the multi-plane voxel.
|
|
19
|
+
*/
|
|
20
|
+
export default class RLEVoxelMap<T> {
|
|
21
|
+
/**
|
|
22
|
+
* The rows for the voxel map is a map from the j index location (or for
|
|
23
|
+
* volumes, `j + k*height`) to a list of RLE runs. That is, each entry in
|
|
24
|
+
* the rows specifies the voxel data for a given row in the image.
|
|
25
|
+
* Then, the RLE runs themselves specify the pixel values for given rows as
|
|
26
|
+
* a pair of start/end indices, plus the value to apply.
|
|
27
|
+
*/
|
|
28
|
+
protected rows = new Map<number, RLERun<T>[]>();
|
|
29
|
+
/** The height of the images stored in the voxel map (eg the height of each plane) */
|
|
30
|
+
protected height = 1;
|
|
31
|
+
/** The width of the image planes */
|
|
32
|
+
protected width = 1;
|
|
33
|
+
/**
|
|
34
|
+
* The number of image planes stored (the depth of the indices), with the k
|
|
35
|
+
* index going from 0...depth.
|
|
36
|
+
*/
|
|
37
|
+
protected depth = 1;
|
|
38
|
+
/**
|
|
39
|
+
* A multiplier value to go from j values to overall index values.
|
|
40
|
+
*/
|
|
41
|
+
protected jMultiple = 1;
|
|
42
|
+
/**
|
|
43
|
+
* A multiplier value to go from k values to overall index values.
|
|
44
|
+
*/
|
|
45
|
+
protected kMultiple = 1;
|
|
46
|
+
/** Number of components in the value */
|
|
47
|
+
protected numComps = 1;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The default value returned for get.
|
|
51
|
+
* This allows treting the voxel map more like scalar data, returning the right
|
|
52
|
+
* default value for unset values.
|
|
53
|
+
* Set to 0 by default, but any maps where 0 not in T should update this value.
|
|
54
|
+
*/
|
|
55
|
+
public defaultValue: T = 0 as unknown as T;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The constructor for creating pixel data.
|
|
59
|
+
*/
|
|
60
|
+
public pixelDataConstructor = Uint8Array;
|
|
61
|
+
|
|
62
|
+
constructor(width: number, height: number, depth = 1) {
|
|
63
|
+
this.width = width;
|
|
64
|
+
this.height = height;
|
|
65
|
+
this.depth = depth;
|
|
66
|
+
this.jMultiple = width;
|
|
67
|
+
this.kMultiple = this.jMultiple * height;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Gets the value encoded in the map at the given index, which is
|
|
72
|
+
* an integer `[i,j,k]` voxel index, equal to `index=i+(j+k*height)*width`
|
|
73
|
+
* value (eg a standard ScalarData index for stack/volume single component
|
|
74
|
+
* indices.)
|
|
75
|
+
*
|
|
76
|
+
* Returns defaultValue if the RLE value is not found.
|
|
77
|
+
*/
|
|
78
|
+
public get = (index: number): T => {
|
|
79
|
+
const i = index % this.jMultiple;
|
|
80
|
+
const j = (index - i) / this.jMultiple;
|
|
81
|
+
const rle = this.getRLE(i, j);
|
|
82
|
+
return rle?.value || this.defaultValue;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Gets a list of RLERun values which specify the data on the row j
|
|
87
|
+
* This allows applying or modifying the run directly. See CanvasActor
|
|
88
|
+
* for an example in the RLE rendering.
|
|
89
|
+
*/
|
|
90
|
+
protected getRLE(i: number, j: number, k = 0): RLERun<T> {
|
|
91
|
+
const row = this.rows.get(j + k * this.height);
|
|
92
|
+
if (!row) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const index = this.findIndex(row, i);
|
|
96
|
+
const rle = row[index];
|
|
97
|
+
return i >= rle?.start ? rle : undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Finds the index in the row that i is contained in, OR that i would be
|
|
102
|
+
* before. That is, the rle value for the returned index in that row
|
|
103
|
+
* has `i ε [start,end)` if a direct RLE is found, or `i ε [end_-1,start)` if
|
|
104
|
+
* in the prefix. If no RLE is found with that index, then
|
|
105
|
+
* `i ε [end_final,length)`
|
|
106
|
+
*/
|
|
107
|
+
protected findIndex(row: RLERun<T>[], i: number) {
|
|
108
|
+
for (let index = 0; index < row.length; index++) {
|
|
109
|
+
const { end: iEnd } = row[index];
|
|
110
|
+
if (i < iEnd) {
|
|
111
|
+
return index;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return row.length;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Gets the run for the given j,k indices. This is used to allow fast access
|
|
119
|
+
* to runs for data for things like rendering entire rows of data.
|
|
120
|
+
*/
|
|
121
|
+
public getRun = (j: number, k: number) => {
|
|
122
|
+
const runIndex = j + k * this.height;
|
|
123
|
+
return this.rows.get(runIndex);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Adds to the RLE at the given position. This is unfortunately fairly
|
|
128
|
+
* complex since it is desirable to minimize the number of runs, but to still
|
|
129
|
+
* allow it to be efficient.
|
|
130
|
+
*/
|
|
131
|
+
public set = (index: number, value: T) => {
|
|
132
|
+
if (value === undefined) {
|
|
133
|
+
throw new Error(`Can't set undefined at ${index % this.width}`);
|
|
134
|
+
}
|
|
135
|
+
const i = index % this.width;
|
|
136
|
+
const j = (index - i) / this.width;
|
|
137
|
+
const row = this.rows.get(j);
|
|
138
|
+
if (!row) {
|
|
139
|
+
this.rows.set(j, [{ start: i, end: i + 1, value }]);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const rleIndex = this.findIndex(row, i);
|
|
143
|
+
const rle1 = row[rleIndex];
|
|
144
|
+
const rle0 = row[rleIndex - 1];
|
|
145
|
+
|
|
146
|
+
// Adding to the end of the row
|
|
147
|
+
if (!rle1) {
|
|
148
|
+
// We are at the end, check if the previous rle can be extended
|
|
149
|
+
if (!rle0 || rle0.value !== value || rle0.end !== i) {
|
|
150
|
+
row[rleIndex] = { start: i, end: i + 1, value };
|
|
151
|
+
// validateRow(row, i, rleIndex, value);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Just add it to the previous element.
|
|
155
|
+
rle0.end++;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const { start, end, value: oldValue } = rle1;
|
|
160
|
+
|
|
161
|
+
// Handle the already in place case
|
|
162
|
+
if (value === oldValue && i >= start) {
|
|
163
|
+
// validateRow(row, i, rleIndex, value, start);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const rleInsert = { start: i, end: i + 1, value };
|
|
168
|
+
const isAfter = i > start;
|
|
169
|
+
const insertIndex = isAfter ? rleIndex + 1 : rleIndex;
|
|
170
|
+
const rlePrev = isAfter ? rle1 : rle0;
|
|
171
|
+
let rleNext = isAfter ? row[rleIndex + 1] : rle1;
|
|
172
|
+
|
|
173
|
+
// Can merge with previous value, so no insert
|
|
174
|
+
if (rlePrev?.value === value && rlePrev?.end === i) {
|
|
175
|
+
rlePrev.end++;
|
|
176
|
+
if (rleNext?.value === value && rleNext.start === i + 1) {
|
|
177
|
+
rlePrev.end = rleNext.end;
|
|
178
|
+
row.splice(rleIndex, 1);
|
|
179
|
+
// validateRow(row, i, rleIndex, value);
|
|
180
|
+
} else if (rleNext?.start === i) {
|
|
181
|
+
rleNext.start++;
|
|
182
|
+
if (rleNext.start === rleNext.end) {
|
|
183
|
+
row.splice(rleIndex, 1);
|
|
184
|
+
rleNext = row[rleIndex];
|
|
185
|
+
// Check if we can merge twice
|
|
186
|
+
if (rleNext?.start === i + 1 && rleNext.value === value) {
|
|
187
|
+
rlePrev.end = rleNext.end;
|
|
188
|
+
row.splice(rleIndex, 1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// validateRow(row, i, rleIndex, value);
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Can merge with next, so no insert
|
|
197
|
+
if (rleNext?.value === value && rleNext.start === i + 1) {
|
|
198
|
+
rleNext.start--;
|
|
199
|
+
if (rlePrev?.end > i) {
|
|
200
|
+
rlePrev.end = i;
|
|
201
|
+
if (rlePrev.end === rlePrev.start) {
|
|
202
|
+
row.splice(rleIndex, 1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// validateRow(row, i, rleIndex, value);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Can't merge, need to see if we can replace
|
|
210
|
+
if (rleNext?.start === i && rleNext.end === i + 1) {
|
|
211
|
+
rleNext.value = value;
|
|
212
|
+
const nextnext = row[rleIndex + 1];
|
|
213
|
+
if (nextnext?.start == i + 1 && nextnext.value === value) {
|
|
214
|
+
row.splice(rleIndex + 1, 1);
|
|
215
|
+
rleNext.end = nextnext.end;
|
|
216
|
+
}
|
|
217
|
+
// validateRow(row, i, rleIndex, value);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Need to fix the next start value
|
|
222
|
+
if (i === rleNext?.start) {
|
|
223
|
+
rleNext.start++;
|
|
224
|
+
}
|
|
225
|
+
if (isAfter && end > i + 1) {
|
|
226
|
+
// Insert two items, to split the existing into three
|
|
227
|
+
row.splice(insertIndex, 0, rleInsert, {
|
|
228
|
+
start: i + 1,
|
|
229
|
+
end: rlePrev.end,
|
|
230
|
+
value: rlePrev.value,
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
row.splice(insertIndex, 0, rleInsert);
|
|
234
|
+
}
|
|
235
|
+
if (rlePrev?.end > i) {
|
|
236
|
+
rlePrev.end = i;
|
|
237
|
+
}
|
|
238
|
+
// validateRow(row, i, rleIndex, value, insertIndex);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Clears all entries.
|
|
243
|
+
*/
|
|
244
|
+
public clear() {
|
|
245
|
+
this.rows.clear();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Gets the set of key entries - that is j values. This may include
|
|
250
|
+
* `j>=height`, where `j = key % height`, and `k = Math.floor(j / height)`
|
|
251
|
+
*/
|
|
252
|
+
public keys(): number[] {
|
|
253
|
+
return [...this.rows.keys()];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Gets the pixel data into the provided pixel data array, or creates one
|
|
258
|
+
* according to the assigned type.
|
|
259
|
+
*/
|
|
260
|
+
public getPixelData(
|
|
261
|
+
k = 0,
|
|
262
|
+
pixelData?: PixelDataTypedArray
|
|
263
|
+
): PixelDataTypedArray {
|
|
264
|
+
if (!pixelData) {
|
|
265
|
+
pixelData = new this.pixelDataConstructor(
|
|
266
|
+
this.width * this.height * this.numComps
|
|
267
|
+
);
|
|
268
|
+
} else {
|
|
269
|
+
pixelData.fill(0);
|
|
270
|
+
}
|
|
271
|
+
const { width, height, numComps } = this;
|
|
272
|
+
|
|
273
|
+
for (let j = 0; j < height; j++) {
|
|
274
|
+
const row = this.getRun(j, k);
|
|
275
|
+
if (!row) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (numComps === 1) {
|
|
279
|
+
for (const rle of row) {
|
|
280
|
+
const rowOffset = j * width;
|
|
281
|
+
const { start, end, value } = rle;
|
|
282
|
+
for (let i = start; i < end; i++) {
|
|
283
|
+
pixelData[rowOffset + i] = value as unknown as number;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
for (const rle of row) {
|
|
288
|
+
const rowOffset = j * width * numComps;
|
|
289
|
+
const { start, end, value } = rle;
|
|
290
|
+
for (let i = start; i < end; i += numComps) {
|
|
291
|
+
for (let comp = 0; comp < numComps; comp++) {
|
|
292
|
+
pixelData[rowOffset + i + comp] = value[comp];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return pixelData;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// This is some code to allow debugging RLE maps
|
|
303
|
+
// To be deleted along with references once RLE is better tested.
|
|
304
|
+
// Might move to testing code at that point
|
|
305
|
+
// function validateRow(row, ...inputs) {
|
|
306
|
+
// if (!row) {
|
|
307
|
+
// return;
|
|
308
|
+
// }
|
|
309
|
+
// let lastRle;
|
|
310
|
+
// for (const rle of row) {
|
|
311
|
+
// const { start, end, value } = rle;
|
|
312
|
+
// if (start < 0 || end > 1920 || start >= end) {
|
|
313
|
+
// console.log('Wrong order', ...inputs);
|
|
314
|
+
// debugger;
|
|
315
|
+
// }
|
|
316
|
+
// if (!lastRle) {
|
|
317
|
+
// lastRle = rle;
|
|
318
|
+
// continue;
|
|
319
|
+
// }
|
|
320
|
+
// const { start: lastStart, end: lastEnd, value: lastValue } = lastRle;
|
|
321
|
+
// lastRle = rle;
|
|
322
|
+
// if (start < lastEnd) {
|
|
323
|
+
// console.log('inputs for wrong overlap', ...inputs);
|
|
324
|
+
// debugger;
|
|
325
|
+
// }
|
|
326
|
+
// if (start === lastEnd && value === lastValue) {
|
|
327
|
+
// console.log('inputs for two in a row same', ...inputs);
|
|
328
|
+
// debugger;
|
|
329
|
+
// }
|
|
330
|
+
// }
|
|
331
|
+
// }
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BoundsIJK,
|
|
3
|
+
Point3,
|
|
4
|
+
PixelDataTypedArray,
|
|
5
|
+
IImage,
|
|
6
|
+
RGB,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import RLEVoxelMap from './RLEVoxelMap';
|
|
9
|
+
import isEqual from './isEqual';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Have a default size for cached RLE encoded images. This is hard to guess
|
|
13
|
+
* up front because the RLE is usually used to store new/updated data, but this
|
|
14
|
+
* is a first guess.
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_RLE_SIZE = 5 * 1024;
|
|
2
17
|
|
|
3
18
|
/**
|
|
4
19
|
* This is a simple, standard interface to values associated with a voxel.
|
|
@@ -13,10 +28,11 @@ export default class VoxelManager<T> {
|
|
|
13
28
|
|
|
14
29
|
// Provide direct access to the underlying data, if any
|
|
15
30
|
public scalarData: PixelDataTypedArray;
|
|
16
|
-
public map: Map<number, T>;
|
|
31
|
+
public map: Map<number, T> | RLEVoxelMap<T>;
|
|
17
32
|
public sourceVoxelManager: VoxelManager<T>;
|
|
18
33
|
public isInObject: (pointIPS, pointIJK) => boolean;
|
|
19
34
|
public readonly dimensions: Point3;
|
|
35
|
+
public numComps = 1;
|
|
20
36
|
|
|
21
37
|
points: Set<number>;
|
|
22
38
|
width: number;
|
|
@@ -233,21 +249,89 @@ export default class VoxelManager<T> {
|
|
|
233
249
|
bounds[2][1] = Math.max(point[2], bounds[2][1]);
|
|
234
250
|
}
|
|
235
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Gets the pixel data for the given array.
|
|
254
|
+
*/
|
|
255
|
+
public getPixelData: (
|
|
256
|
+
sliceIndex?: number,
|
|
257
|
+
pixelData?: PixelDataTypedArray
|
|
258
|
+
) => PixelDataTypedArray;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Creates a voxel manager backed by an array of scalar data having the
|
|
262
|
+
* given number of components.
|
|
263
|
+
* Note that the number of components can be larger than three, in case data
|
|
264
|
+
* is stored in additional pixels. However, the return type is still RGB.
|
|
265
|
+
*/
|
|
266
|
+
public static createRGBVolumeVoxelManager(
|
|
267
|
+
dimensions: Point3,
|
|
268
|
+
scalarData,
|
|
269
|
+
numComponents
|
|
270
|
+
): VoxelManager<RGB> {
|
|
271
|
+
const voxels = new VoxelManager<RGB>(
|
|
272
|
+
dimensions,
|
|
273
|
+
(index) => {
|
|
274
|
+
index *= numComponents;
|
|
275
|
+
return [scalarData[index++], scalarData[index++], scalarData[index++]];
|
|
276
|
+
},
|
|
277
|
+
(index, v) => {
|
|
278
|
+
index *= 3;
|
|
279
|
+
const isChanged = !isEqual(scalarData[index], v);
|
|
280
|
+
scalarData[index++] = v[0];
|
|
281
|
+
scalarData[index++] = v[1];
|
|
282
|
+
scalarData[index++] = v[2];
|
|
283
|
+
return isChanged;
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
voxels.numComps = numComponents;
|
|
287
|
+
voxels.scalarData = scalarData;
|
|
288
|
+
return voxels;
|
|
289
|
+
}
|
|
290
|
+
|
|
236
291
|
/**
|
|
237
292
|
* Creates a volume value accessor, based on a volume scalar data instance.
|
|
238
293
|
* This also works for image value accessors for single plane (k=0) accessors.
|
|
239
294
|
*/
|
|
240
295
|
public static createVolumeVoxelManager(
|
|
241
296
|
dimensions: Point3,
|
|
242
|
-
scalarData
|
|
243
|
-
|
|
297
|
+
scalarData,
|
|
298
|
+
numComponents = 0
|
|
299
|
+
): VoxelManager<number> | VoxelManager<RGB> {
|
|
244
300
|
if (dimensions.length !== 3) {
|
|
245
301
|
throw new Error(
|
|
246
302
|
'Dimensions must be provided as [number, number, number] for [width, height, depth]'
|
|
247
303
|
);
|
|
248
304
|
}
|
|
305
|
+
if (!numComponents) {
|
|
306
|
+
numComponents =
|
|
307
|
+
scalarData.length / dimensions[0] / dimensions[1] / dimensions[2];
|
|
308
|
+
// We only support 1,3,4 component data, and sometimes the scalar data
|
|
309
|
+
// doesn't match for some reason, so throw an exception
|
|
310
|
+
if (numComponents > 4 || numComponents < 1 || numComponents === 2) {
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Number of components ${numComponents} must be 1, 3 or 4`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (numComponents > 1) {
|
|
317
|
+
return VoxelManager.createRGBVolumeVoxelManager(
|
|
318
|
+
dimensions,
|
|
319
|
+
scalarData,
|
|
320
|
+
numComponents
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
return VoxelManager.createNumberVolumeVoxelManager(dimensions, scalarData);
|
|
324
|
+
}
|
|
249
325
|
|
|
250
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Creates a volume voxel manager that works on single numeric values stored
|
|
328
|
+
* in an array like structure of numbers.
|
|
329
|
+
*/
|
|
330
|
+
public static createNumberVolumeVoxelManager(
|
|
331
|
+
dimensions: Point3,
|
|
332
|
+
scalarData
|
|
333
|
+
): VoxelManager<number> {
|
|
334
|
+
const voxels = new VoxelManager<number>(
|
|
251
335
|
dimensions,
|
|
252
336
|
(index) => scalarData[index],
|
|
253
337
|
(index, v) => {
|
|
@@ -308,6 +392,92 @@ export default class VoxelManager<T> {
|
|
|
308
392
|
voxelManager.sourceVoxelManager = sourceVoxelManager;
|
|
309
393
|
return voxelManager;
|
|
310
394
|
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Creates a lazy voxel manager that will create an image plane as required
|
|
398
|
+
* for each slice of a volume as it gets changed. This can be used to
|
|
399
|
+
* store image data that gets created as required.
|
|
400
|
+
*/
|
|
401
|
+
public static createLazyVoxelManager<T>(
|
|
402
|
+
dimensions: Point3,
|
|
403
|
+
planeFactory: (width: number, height: number) => T
|
|
404
|
+
): VoxelManager<T> {
|
|
405
|
+
const map = new Map<number, T>();
|
|
406
|
+
const [width, height, depth] = dimensions;
|
|
407
|
+
const planeSize = width * height;
|
|
408
|
+
|
|
409
|
+
const voxelManager = new VoxelManager(
|
|
410
|
+
dimensions,
|
|
411
|
+
(index) => map.get(Math.floor(index / planeSize))?.[index % planeSize],
|
|
412
|
+
(index, v) => {
|
|
413
|
+
const k = Math.floor(index / planeSize);
|
|
414
|
+
let layer = map.get(k);
|
|
415
|
+
if (!layer) {
|
|
416
|
+
layer = planeFactory(width, height);
|
|
417
|
+
map.set(k, layer);
|
|
418
|
+
}
|
|
419
|
+
layer[index % planeSize] = v;
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
voxelManager.map = map;
|
|
423
|
+
return voxelManager;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Creates a RLE based voxel manager. This is effective for storing
|
|
428
|
+
* segmentation maps or already RLE encoded data such as ultrasounds.
|
|
429
|
+
*/
|
|
430
|
+
public static createRLEVoxelManager<T>(dimensions: Point3): VoxelManager<T> {
|
|
431
|
+
const [width, height, depth] = dimensions;
|
|
432
|
+
const map = new RLEVoxelMap<T>(width, height, depth);
|
|
433
|
+
|
|
434
|
+
const voxelManager = new VoxelManager<T>(
|
|
435
|
+
dimensions,
|
|
436
|
+
(index) => map.get(index),
|
|
437
|
+
(index, v) => map.set(index, v)
|
|
438
|
+
);
|
|
439
|
+
voxelManager.map = map;
|
|
440
|
+
voxelManager.getPixelData = map.getPixelData.bind(map);
|
|
441
|
+
return voxelManager;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* This method adds a voxelManager instance to the image object
|
|
446
|
+
* where the object added is of type:
|
|
447
|
+
* 1. RLE map if the scalar data is missing or too small (dummy data)
|
|
448
|
+
* 2. Volume VoxelManager scalar data representations
|
|
449
|
+
*/
|
|
450
|
+
public static addInstanceToImage(image: IImage) {
|
|
451
|
+
const { width, height } = image;
|
|
452
|
+
const scalarData = image.getPixelData();
|
|
453
|
+
// This test works for single images, or single representations of images
|
|
454
|
+
// from a volume representation, for grayscale, indexed and RGB or RGBA images.
|
|
455
|
+
if (scalarData?.length >= width * height) {
|
|
456
|
+
// This case means there is enough scalar data for at least one image,
|
|
457
|
+
// with 1 or more components, and creates a volume voxel manager
|
|
458
|
+
// that can lookup the data
|
|
459
|
+
image.voxelManager = VoxelManager.createVolumeVoxelManager(
|
|
460
|
+
[width, height, 1],
|
|
461
|
+
scalarData
|
|
462
|
+
);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
// This case occurs when the image data is a dummy image data set
|
|
466
|
+
// created just to prevent exceptions in the caching logic. Then, the
|
|
467
|
+
// RLE voxel manager can be created to store the data instead.
|
|
468
|
+
image.voxelManager = VoxelManager.createRLEVoxelManager<number>([
|
|
469
|
+
width,
|
|
470
|
+
height,
|
|
471
|
+
1,
|
|
472
|
+
]);
|
|
473
|
+
// The RLE voxel manager knows how to get scalar data pixel data representations.
|
|
474
|
+
// That allows using the RLE representation as a normal pixel data representation
|
|
475
|
+
// for VIEWING purposes.
|
|
476
|
+
image.getPixelData = image.voxelManager.getPixelData;
|
|
477
|
+
// Assign a different size to the cached data because this is actually
|
|
478
|
+
// storing an RLE representation, which doesn't have an up front size.
|
|
479
|
+
image.sizeInBytes = DEFAULT_RLE_SIZE;
|
|
480
|
+
}
|
|
311
481
|
}
|
|
312
482
|
|
|
313
483
|
export type { VoxelManager };
|
|
@@ -6,6 +6,10 @@ function updateVTKImageDataWithCornerstoneImage(
|
|
|
6
6
|
image: IImage
|
|
7
7
|
) {
|
|
8
8
|
const pixelData = image.getPixelData();
|
|
9
|
+
if (!sourceImageData.getPointData) {
|
|
10
|
+
// This happens for a CanvasActor, that doesn't have the getPointData
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
const scalarData = sourceImageData
|
|
10
14
|
.getPointData()
|
|
11
15
|
.getScalars()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VideoViewport.js","sourceRoot":"","sources":["../../../src/enums/VideoViewport.ts"],"names":[],"mappings":";;;AAGA,IAAK,SAGJ;AAHD,WAAK,SAAS;IACZ,wBAAW,CAAA;IACX,yBAAY,CAAA;AACd,CAAC,EAHI,SAAS,KAAT,SAAS,QAGb;AAEQ,8BAAS"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VideoViewport.js","sourceRoot":"","sources":["../../../src/enums/VideoViewport.ts"],"names":[],"mappings":"AAGA,IAAK,SAGJ;AAHD,WAAK,SAAS;IACZ,wBAAW,CAAA;IACX,yBAAY,CAAA;AACd,CAAC,EAHI,SAAS,KAAT,SAAS,QAGb;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VideoViewport.d.ts","sourceRoot":"","sources":["../../../src/enums/VideoViewport.ts"],"names":[],"mappings":"AAGA,aAAK,SAAS;IACZ,KAAK,MAAM;IACX,MAAM,MAAM;CACb;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
File without changes
|
|
File without changes
|