@cornerstonejs/core 1.29.0 → 1.30.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/StackViewport.d.ts +1 -0
- package/dist/cjs/RenderingEngine/StackViewport.js +3 -0
- package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/VideoViewport.d.ts +42 -3
- package/dist/cjs/RenderingEngine/VideoViewport.js +192 -13
- package/dist/cjs/RenderingEngine/VideoViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/Viewport.d.ts +1 -0
- package/dist/cjs/RenderingEngine/Viewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/VolumeViewport.d.ts +1 -0
- package/dist/cjs/RenderingEngine/VolumeViewport.js +5 -0
- package/dist/cjs/RenderingEngine/VolumeViewport.js.map +1 -1
- package/dist/cjs/enums/MetadataModules.d.ts +11 -0
- package/dist/cjs/enums/MetadataModules.js +15 -0
- package/dist/cjs/enums/MetadataModules.js.map +1 -0
- package/dist/cjs/enums/index.d.ts +2 -1
- package/dist/cjs/enums/index.js +3 -1
- package/dist/cjs/enums/index.js.map +1 -1
- package/dist/cjs/types/IVideoViewport.d.ts +1 -0
- package/dist/cjs/types/IViewport.d.ts +1 -0
- package/dist/cjs/types/VideoViewportProperties.d.ts +1 -1
- package/dist/cjs/utilities/index.d.ts +2 -1
- package/dist/cjs/utilities/index.js +3 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/isVideoTransferSyntax.d.ts +2 -0
- package/dist/cjs/utilities/isVideoTransferSyntax.js +30 -0
- package/dist/cjs/utilities/isVideoTransferSyntax.js.map +1 -0
- package/dist/esm/RenderingEngine/StackViewport.js +3 -0
- package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/VideoViewport.js +170 -14
- package/dist/esm/RenderingEngine/VideoViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/Viewport.js.map +1 -1
- package/dist/esm/RenderingEngine/VolumeViewport.js +5 -0
- package/dist/esm/RenderingEngine/VolumeViewport.js.map +1 -1
- package/dist/esm/enums/MetadataModules.js +13 -0
- package/dist/esm/enums/MetadataModules.js.map +1 -0
- package/dist/esm/enums/index.js +2 -1
- package/dist/esm/enums/index.js.map +1 -1
- package/dist/esm/utilities/index.js +2 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/isVideoTransferSyntax.js +26 -0
- package/dist/esm/utilities/isVideoTransferSyntax.js.map +1 -0
- package/dist/types/RenderingEngine/StackViewport.d.ts +1 -0
- package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/VideoViewport.d.ts +42 -3
- package/dist/types/RenderingEngine/VideoViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/Viewport.d.ts +1 -0
- package/dist/types/RenderingEngine/Viewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/VolumeViewport.d.ts +1 -0
- package/dist/types/RenderingEngine/VolumeViewport.d.ts.map +1 -1
- package/dist/types/enums/MetadataModules.d.ts +12 -0
- package/dist/types/enums/MetadataModules.d.ts.map +1 -0
- package/dist/types/enums/index.d.ts +2 -1
- package/dist/types/enums/index.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/IViewport.d.ts +1 -0
- package/dist/types/types/IViewport.d.ts.map +1 -1
- package/dist/types/types/VideoViewportProperties.d.ts +1 -1
- package/dist/types/types/VideoViewportProperties.d.ts.map +1 -1
- package/dist/types/utilities/index.d.ts +2 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/isVideoTransferSyntax.d.ts +3 -0
- package/dist/types/utilities/isVideoTransferSyntax.d.ts.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
- package/src/RenderingEngine/StackViewport.ts +5 -0
- package/src/RenderingEngine/VideoViewport.ts +255 -16
- package/src/RenderingEngine/Viewport.ts +1 -0
- package/src/RenderingEngine/VolumeViewport.ts +7 -0
- package/src/enums/MetadataModules.ts +20 -0
- package/src/enums/index.ts +2 -0
- package/src/types/IVideoViewport.ts +7 -0
- package/src/types/IViewport.ts +2 -0
- package/src/types/VideoViewportProperties.ts +1 -3
- package/src/utilities/index.ts +2 -0
- package/src/utilities/isVideoTransferSyntax.ts +26 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"type": "individual",
|
|
47
47
|
"url": "https://ohif.org/donate"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "9098e604b0099ed3e779d85faa6a1d0e2a4cc4c7"
|
|
50
50
|
}
|
|
@@ -554,6 +554,11 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader {
|
|
|
554
554
|
return actor;
|
|
555
555
|
};
|
|
556
556
|
|
|
557
|
+
/** Gets the number of slices */
|
|
558
|
+
public getNumberOfSlices = (): number => {
|
|
559
|
+
return this.imageIds.length;
|
|
560
|
+
};
|
|
561
|
+
|
|
557
562
|
/**
|
|
558
563
|
* Retrieves the metadata from the metadata provider, and optionally adds the
|
|
559
564
|
* scaling to the viewport if modality is PET and scaling metadata is provided.
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { vec3 } from 'gl-matrix';
|
|
2
|
+
import {
|
|
3
|
+
Events as EVENTS,
|
|
4
|
+
VideoViewport as VideoViewportEnum,
|
|
5
|
+
MetadataModules,
|
|
6
|
+
} from '../enums';
|
|
2
7
|
import {
|
|
3
8
|
IVideoViewport,
|
|
4
9
|
VideoViewportProperties,
|
|
@@ -7,7 +12,9 @@ import {
|
|
|
7
12
|
ICamera,
|
|
8
13
|
InternalVideoCamera,
|
|
9
14
|
VideoViewportInput,
|
|
15
|
+
VOIRange,
|
|
10
16
|
} from '../types';
|
|
17
|
+
import * as metaData from '../metaData';
|
|
11
18
|
import { Transform } from './helpers/cpuFallback/rendering/transform';
|
|
12
19
|
import { triggerEvent } from '../utilities';
|
|
13
20
|
import Viewport from './Viewport';
|
|
@@ -18,7 +25,9 @@ import { getOrCreateCanvas } from './helpers';
|
|
|
18
25
|
* looking into an internal scene, and an associated target output `canvas`.
|
|
19
26
|
*/
|
|
20
27
|
class VideoViewport extends Viewport implements IVideoViewport {
|
|
28
|
+
public modality;
|
|
21
29
|
// Viewport Data
|
|
30
|
+
protected imageId: string;
|
|
22
31
|
readonly uid;
|
|
23
32
|
readonly renderingEngineId: string;
|
|
24
33
|
readonly canvasContext: CanvasRenderingContext2D;
|
|
@@ -30,12 +39,43 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
30
39
|
private mute = true;
|
|
31
40
|
private isPlaying = false;
|
|
32
41
|
private scrollSpeed = 1;
|
|
33
|
-
private
|
|
42
|
+
private playbackRate = 1;
|
|
43
|
+
|
|
44
|
+
protected metadata;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The fps, frames per second is used to calculate time/frame mapping values.
|
|
48
|
+
* It is provided by the CINE Module in the metadata, defaulting to 30 if not
|
|
49
|
+
* provided.
|
|
50
|
+
*/
|
|
51
|
+
private fps = 30;
|
|
52
|
+
|
|
34
53
|
private videoCamera: InternalVideoCamera = {
|
|
35
54
|
panWorld: [0, 0],
|
|
36
55
|
parallelScale: 1,
|
|
37
56
|
};
|
|
38
57
|
|
|
58
|
+
/**
|
|
59
|
+
* feFilter is an inline string value for the CSS filter on the video
|
|
60
|
+
* CSS filters can reference SVG filters, so for the typical use case here
|
|
61
|
+
* the CSS filter is actually an link link to a SVG filter.
|
|
62
|
+
*/
|
|
63
|
+
private feFilter: string;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An average white point value, used to color balance the image so that
|
|
67
|
+
* the given white is mapped to [255,255,255] via multiplication per channel.
|
|
68
|
+
*/
|
|
69
|
+
private averageWhite: [number, number, number];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The VOI Range is used to apply contrast/brightness adjustments to the image.
|
|
73
|
+
*/
|
|
74
|
+
private voiRange: VOIRange = {
|
|
75
|
+
lower: 0,
|
|
76
|
+
upper: 255,
|
|
77
|
+
};
|
|
78
|
+
|
|
39
79
|
constructor(props: VideoViewportInput) {
|
|
40
80
|
super({
|
|
41
81
|
...props,
|
|
@@ -82,6 +122,86 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
82
122
|
this.videoElement.remove();
|
|
83
123
|
}
|
|
84
124
|
|
|
125
|
+
private _getImageDataMetadata() {
|
|
126
|
+
const imagePlaneModule = metaData.get(
|
|
127
|
+
MetadataModules.IMAGE_PLANE,
|
|
128
|
+
this.imageId
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
let rowCosines = <Point3>imagePlaneModule.rowCosines;
|
|
132
|
+
let columnCosines = <Point3>imagePlaneModule.columnCosines;
|
|
133
|
+
|
|
134
|
+
// if null or undefined
|
|
135
|
+
if (rowCosines == null || columnCosines == null) {
|
|
136
|
+
rowCosines = <Point3>[1, 0, 0];
|
|
137
|
+
columnCosines = <Point3>[0, 1, 0];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const rowCosineVec = vec3.fromValues(
|
|
141
|
+
rowCosines[0],
|
|
142
|
+
rowCosines[1],
|
|
143
|
+
rowCosines[2]
|
|
144
|
+
);
|
|
145
|
+
const colCosineVec = vec3.fromValues(
|
|
146
|
+
columnCosines[0],
|
|
147
|
+
columnCosines[1],
|
|
148
|
+
columnCosines[2]
|
|
149
|
+
);
|
|
150
|
+
const scanAxisNormal = vec3.create();
|
|
151
|
+
vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
|
|
152
|
+
|
|
153
|
+
let origin = imagePlaneModule.imagePositionPatient;
|
|
154
|
+
// if null or undefined
|
|
155
|
+
if (origin == null) {
|
|
156
|
+
origin = [0, 0, 0];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const xSpacing = imagePlaneModule.columnPixelSpacing;
|
|
160
|
+
const ySpacing = imagePlaneModule.rowPixelSpacing;
|
|
161
|
+
const xVoxels = imagePlaneModule.columns;
|
|
162
|
+
const yVoxels = imagePlaneModule.rows;
|
|
163
|
+
|
|
164
|
+
const zSpacing = 1;
|
|
165
|
+
const zVoxels = 1;
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
bitsAllocated: 8,
|
|
169
|
+
numComps: 3,
|
|
170
|
+
origin,
|
|
171
|
+
direction: [...rowCosineVec, ...colCosineVec, ...scanAxisNormal],
|
|
172
|
+
dimensions: [xVoxels, yVoxels, zVoxels],
|
|
173
|
+
spacing: [xSpacing, ySpacing, zSpacing],
|
|
174
|
+
numVoxels: xVoxels * yVoxels * zVoxels,
|
|
175
|
+
imagePlaneModule,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Sets the video image id to show and hte frame number.
|
|
181
|
+
* Requirements are to have the imageUrlModule in the metadata
|
|
182
|
+
* with the rendered endpoint being the raw video in video/mp4 format.
|
|
183
|
+
*/
|
|
184
|
+
public setVideo(
|
|
185
|
+
imageIds: string | string[],
|
|
186
|
+
frameNumber?: number
|
|
187
|
+
): Promise<unknown> {
|
|
188
|
+
this.imageId = Array.isArray(imageIds) ? imageIds[0] : imageIds;
|
|
189
|
+
const { imageId } = this;
|
|
190
|
+
const { rendered } = metaData.get(MetadataModules.IMAGE_URL, imageId);
|
|
191
|
+
const generalSeries = metaData.get(MetadataModules.GENERAL_SERIES, imageId);
|
|
192
|
+
this.modality = generalSeries?.Modality;
|
|
193
|
+
this.metadata = this._getImageDataMetadata();
|
|
194
|
+
|
|
195
|
+
return this.setVideoURL(rendered).then(() => {
|
|
196
|
+
const { cineRate = 30 } = metaData.get(MetadataModules.CINE, imageId);
|
|
197
|
+
this.fps = cineRate;
|
|
198
|
+
if (frameNumber !== undefined) {
|
|
199
|
+
this.pause();
|
|
200
|
+
this.setFrame(frameNumber);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
85
205
|
public async setVideoURL(videoURL: string) {
|
|
86
206
|
return new Promise((resolve) => {
|
|
87
207
|
this.videoElement.src = videoURL;
|
|
@@ -126,10 +246,8 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
126
246
|
}
|
|
127
247
|
|
|
128
248
|
public async pause() {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.isPlaying = false;
|
|
132
|
-
}
|
|
249
|
+
await this.videoElement.pause();
|
|
250
|
+
this.isPlaying = false;
|
|
133
251
|
}
|
|
134
252
|
|
|
135
253
|
public async scroll(delta = 1) {
|
|
@@ -162,8 +280,6 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
162
280
|
if (videoElement.paused) {
|
|
163
281
|
// Need to wait for seek update
|
|
164
282
|
const seekEventListener = (evt) => {
|
|
165
|
-
console.log('seeked');
|
|
166
|
-
|
|
167
283
|
renderFrame();
|
|
168
284
|
|
|
169
285
|
videoElement.removeEventListener('seeked', seekEventListener);
|
|
@@ -214,21 +330,30 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
214
330
|
this.setTime((frame - 1) / this.fps);
|
|
215
331
|
}
|
|
216
332
|
|
|
217
|
-
public setProperties(
|
|
218
|
-
if (
|
|
219
|
-
this.videoElement.loop =
|
|
333
|
+
public setProperties(props: VideoViewportProperties) {
|
|
334
|
+
if (props.loop !== undefined) {
|
|
335
|
+
this.videoElement.loop = props.loop;
|
|
220
336
|
}
|
|
221
337
|
|
|
222
|
-
if (
|
|
223
|
-
this.videoElement.muted =
|
|
338
|
+
if (props.muted !== undefined) {
|
|
339
|
+
this.videoElement.muted = props.muted;
|
|
224
340
|
}
|
|
225
341
|
|
|
226
|
-
if (
|
|
227
|
-
this.setPlaybackRate(
|
|
342
|
+
if (props.playbackRate !== undefined) {
|
|
343
|
+
this.setPlaybackRate(props.playbackRate);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (props.scrollSpeed !== undefined) {
|
|
347
|
+
this.setScrollSpeed(props.scrollSpeed);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (props.voiRange) {
|
|
351
|
+
this.setVOI(props.voiRange);
|
|
228
352
|
}
|
|
229
353
|
}
|
|
230
354
|
|
|
231
355
|
public setPlaybackRate(rate = 1) {
|
|
356
|
+
this.playbackRate = rate;
|
|
232
357
|
// Minimum playback speed in chrome is 0.0625 compared to normal
|
|
233
358
|
if (rate < 0.0625) {
|
|
234
359
|
this.pause();
|
|
@@ -255,6 +380,9 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
255
380
|
return {
|
|
256
381
|
loop: this.videoElement.loop,
|
|
257
382
|
muted: this.videoElement.muted,
|
|
383
|
+
playbackRate: this.playbackRate,
|
|
384
|
+
scrollSpeed: this.scrollSpeed,
|
|
385
|
+
voiRange: { ...this.voiRange },
|
|
258
386
|
};
|
|
259
387
|
};
|
|
260
388
|
|
|
@@ -265,8 +393,101 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
265
393
|
});
|
|
266
394
|
}
|
|
267
395
|
|
|
396
|
+
protected getScalarData() {
|
|
397
|
+
const canvas = document.createElement('canvas');
|
|
398
|
+
canvas.width = this.videoWidth;
|
|
399
|
+
canvas.height = this.videoHeight;
|
|
400
|
+
const context = canvas.getContext('2d');
|
|
401
|
+
context.drawImage(this.videoElement, 0, 0);
|
|
402
|
+
const canvasData = context.getImageData(
|
|
403
|
+
0,
|
|
404
|
+
0,
|
|
405
|
+
this.videoWidth,
|
|
406
|
+
this.videoHeight
|
|
407
|
+
);
|
|
408
|
+
const { data: scalarData } = canvasData;
|
|
409
|
+
(scalarData as any).getRange = () => [0, 255];
|
|
410
|
+
return scalarData;
|
|
411
|
+
}
|
|
412
|
+
|
|
268
413
|
public getImageData() {
|
|
269
|
-
|
|
414
|
+
const { metadata } = this;
|
|
415
|
+
|
|
416
|
+
const spacing = metadata.spacing;
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
dimensions: metadata.dimensions,
|
|
420
|
+
spacing,
|
|
421
|
+
origin: metadata.origin,
|
|
422
|
+
direction: metadata.direction,
|
|
423
|
+
metadata: { Modality: this.modality },
|
|
424
|
+
imageData: {
|
|
425
|
+
getDirection: () => metadata.direction,
|
|
426
|
+
getDimensions: () => metadata.dimensions,
|
|
427
|
+
getRange: () => [0, 255],
|
|
428
|
+
getScalarData: () => this.getScalarData(),
|
|
429
|
+
getSpacing: () => metadata.spacing,
|
|
430
|
+
worldToIndex: (point: Point3) => {
|
|
431
|
+
const canvasPoint = this.worldToCanvas(point);
|
|
432
|
+
const pixelCoord = this.canvasToIndex(canvasPoint);
|
|
433
|
+
return [pixelCoord[0], pixelCoord[1], 0];
|
|
434
|
+
},
|
|
435
|
+
indexToWorld: (point: Point3) => {
|
|
436
|
+
const canvasPoint = this.indexToCanvas([point[0], point[1]]);
|
|
437
|
+
return this.canvasToWorld(canvasPoint);
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
hasPixelSpacing: this.hasPixelSpacing,
|
|
441
|
+
calibration: this.calibration,
|
|
442
|
+
preScale: {
|
|
443
|
+
scaled: false,
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
public setVOI(voiRange: VOIRange): void {
|
|
449
|
+
this.voiRange = voiRange;
|
|
450
|
+
this.setColorTransform();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
public setWindowLevel(windowWidth = 256, windowCenter = 128) {
|
|
454
|
+
const lower = windowCenter - windowWidth / 2;
|
|
455
|
+
const upper = windowCenter + windowWidth / 2 - 1;
|
|
456
|
+
this.setVOI({ lower, upper });
|
|
457
|
+
this.setColorTransform();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
public setAverageWhite(averageWhite: [number, number, number]) {
|
|
461
|
+
this.averageWhite = averageWhite;
|
|
462
|
+
this.setColorTransform();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
protected setColorTransform() {
|
|
466
|
+
if (!this.voiRange && !this.averageWhite) {
|
|
467
|
+
this.feFilter = null;
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const white = this.averageWhite || [255, 255, 255];
|
|
471
|
+
const maxWhite = Math.max(...white);
|
|
472
|
+
const scaleWhite = white.map((c) => maxWhite / c);
|
|
473
|
+
// From the DICOM standard: ((x - (c - 0.5)) / (w-1) + 0.5) * (ymax- ymin) + ymin
|
|
474
|
+
// which is x/(w-1) - (c - 0.5) / (w-1) + 0.5 for this case
|
|
475
|
+
const { lower = 0, upper = 255 } = this.voiRange || {};
|
|
476
|
+
const wlScale = (upper - lower + 1) / 255;
|
|
477
|
+
const wlDelta = lower / 255;
|
|
478
|
+
this.feFilter = `url('data:image/svg+xml,\
|
|
479
|
+
<svg xmlns="http://www.w3.org/2000/svg">\
|
|
480
|
+
<filter id="colour" color-interpolation-filters="linearRGB">\
|
|
481
|
+
<feColorMatrix type="matrix" \
|
|
482
|
+
values="\
|
|
483
|
+
${scaleWhite[0] * wlScale} 0 0 0 ${wlDelta} \
|
|
484
|
+
0 ${scaleWhite[1] * wlScale} 0 0 ${wlDelta} \
|
|
485
|
+
0 0 ${scaleWhite[2] * wlScale} 0 ${wlDelta} \
|
|
486
|
+
0 0 0 1 0" />\
|
|
487
|
+
</filter>\
|
|
488
|
+
</svg>#colour')`;
|
|
489
|
+
|
|
490
|
+
this.canvas.style.filter = this.feFilter;
|
|
270
491
|
}
|
|
271
492
|
|
|
272
493
|
public setCamera(camera: ICamera): void {
|
|
@@ -339,6 +560,10 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
339
560
|
return true;
|
|
340
561
|
};
|
|
341
562
|
|
|
563
|
+
public getNumberOfSlices = (): number => {
|
|
564
|
+
return (this.videoElement.duration * this.fps) / this.scrollSpeed;
|
|
565
|
+
};
|
|
566
|
+
|
|
342
567
|
public getFrameOfReferenceUID = (): string => {
|
|
343
568
|
// The video itself is the frame of reference.
|
|
344
569
|
return this.videoElement.src;
|
|
@@ -411,6 +636,20 @@ class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
411
636
|
return canvasPos;
|
|
412
637
|
};
|
|
413
638
|
|
|
639
|
+
protected canvasToIndex = (canvasPos: Point2): Point2 => {
|
|
640
|
+
const [x, y] = canvasPos;
|
|
641
|
+
const ratio = this.videoWidth / this.canvas.width;
|
|
642
|
+
const pan = this.getPan();
|
|
643
|
+
return [(x + pan[0]) * ratio, (y + pan[1]) * ratio];
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
protected indexToCanvas = (indexPos: Point2): Point2 => {
|
|
647
|
+
const [x, y] = indexPos;
|
|
648
|
+
const ratio = this.canvas.width / this.videoWidth;
|
|
649
|
+
const pan = this.getPan();
|
|
650
|
+
return [x * ratio - pan[0], y * ratio - pan[1]];
|
|
651
|
+
};
|
|
652
|
+
|
|
414
653
|
private refreshRenderValues() {
|
|
415
654
|
// this means that each unit (pixel) in the world (video) would be
|
|
416
655
|
// represented by n pixels in the canvas.
|
|
@@ -25,6 +25,7 @@ import BaseVolumeViewport from './BaseVolumeViewport';
|
|
|
25
25
|
import setDefaultVolumeVOI from './helpers/setDefaultVolumeVOI';
|
|
26
26
|
import { setTransferFunctionNodes } from '../utilities/transferFunctionUtils';
|
|
27
27
|
import { ImageActor } from '../types/IActor';
|
|
28
|
+
import getImageSliceDataForVolumeViewport from '../utilities/getImageSliceDataForVolumeViewport';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* An object representing a VolumeViewport. VolumeViewports are used to render
|
|
@@ -82,6 +83,12 @@ class VolumeViewport extends BaseVolumeViewport {
|
|
|
82
83
|
return super.setVolumes(volumeInputArray, immediate, suppressEvents);
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/** Gets the number of slices the volume is broken up into in the camera direction */
|
|
87
|
+
public getNumberOfSlices = (): number => {
|
|
88
|
+
const { numberOfSlices } = getImageSliceDataForVolumeViewport(this);
|
|
89
|
+
return numberOfSlices;
|
|
90
|
+
};
|
|
91
|
+
|
|
85
92
|
/**
|
|
86
93
|
* Creates and adds volume actors for all volumes defined in the `volumeInputArray`.
|
|
87
94
|
* For each entry, if a `callback` is supplied, it will be called with the new volume actor as input.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contains the names for the metadata modules.
|
|
3
|
+
* Recommendation is to add all module names here rather than having them
|
|
4
|
+
* just use string names.
|
|
5
|
+
* The naming convention is that the enum has the modules in it, so the
|
|
6
|
+
* enum key does not repeat the Modules, but the enum value does (to agree
|
|
7
|
+
* with existing naming conventions)
|
|
8
|
+
*/
|
|
9
|
+
enum MetadataModules {
|
|
10
|
+
CINE = 'cineModule',
|
|
11
|
+
IMAGE_URL = 'imageUrlModule',
|
|
12
|
+
GENERAL_SERIES = 'generalSeriesModule',
|
|
13
|
+
PATIENT_STUDY = 'patientStudyModule',
|
|
14
|
+
NM_MULTIFRAME_GEOMETRY = 'nmMultiframeGeometryModule',
|
|
15
|
+
IMAGE_PLANE = 'imagePlaneModule',
|
|
16
|
+
IMAGE_PIXEL = 'imagePixelModule',
|
|
17
|
+
MULTIFRAME = 'multiframeModule',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default MetadataModules;
|
package/src/enums/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import CalibrationTypes from './CalibrationTypes';
|
|
|
13
13
|
import ViewportStatus from './ViewportStatus';
|
|
14
14
|
import ImageQualityStatus from './ImageQualityStatus';
|
|
15
15
|
import * as VideoViewport from './VideoViewport';
|
|
16
|
+
import MetadataModules from './MetadataModules';
|
|
16
17
|
|
|
17
18
|
export {
|
|
18
19
|
Events,
|
|
@@ -29,5 +30,6 @@ export {
|
|
|
29
30
|
DynamicOperatorType,
|
|
30
31
|
ViewportStatus,
|
|
31
32
|
VideoViewport,
|
|
33
|
+
MetadataModules,
|
|
32
34
|
ImageQualityStatus,
|
|
33
35
|
};
|
|
@@ -19,15 +19,22 @@ export default interface IVideoViewport extends IViewport {
|
|
|
19
19
|
*/
|
|
20
20
|
getProperties: () => VideoViewportProperties;
|
|
21
21
|
|
|
22
|
+
setVideo: (
|
|
23
|
+
imageIds: string | string[],
|
|
24
|
+
imageIdIndex?: number
|
|
25
|
+
) => Promise<unknown>;
|
|
26
|
+
|
|
22
27
|
setVideoURL: (url: string) => void;
|
|
23
28
|
|
|
24
29
|
play: () => void;
|
|
25
30
|
|
|
26
31
|
pause: () => void;
|
|
32
|
+
|
|
27
33
|
/**
|
|
28
34
|
* Reset the viewport properties to the default values
|
|
29
35
|
*/
|
|
30
36
|
resetProperties(): void;
|
|
37
|
+
|
|
31
38
|
/**
|
|
32
39
|
* Centers Pan and resets the zoom for stack viewport.
|
|
33
40
|
*/
|
package/src/types/IViewport.ts
CHANGED
|
@@ -104,6 +104,8 @@ interface IViewport {
|
|
|
104
104
|
setPan(pan: Point2, storeAsInitialCamera?: boolean);
|
|
105
105
|
/** sets the camera */
|
|
106
106
|
setCamera(cameraInterface: ICamera, storeAsInitialCamera?: boolean): void;
|
|
107
|
+
/** Gets the number of slices in the current camera orientation */
|
|
108
|
+
getNumberOfSlices(): number;
|
|
107
109
|
/** whether the viewport has custom rendering */
|
|
108
110
|
customRenderViewportToCanvas: () => unknown;
|
|
109
111
|
_getCorners(bounds: Array<number>): Array<number>[];
|
|
@@ -9,9 +9,7 @@ type VideoViewportProperties = ViewportProperties & {
|
|
|
9
9
|
muted?: boolean;
|
|
10
10
|
pan?: Point2;
|
|
11
11
|
playbackRate?: number;
|
|
12
|
-
|
|
13
|
-
// but this isn't necessarily necessary.
|
|
14
|
-
parallelScale?: number;
|
|
12
|
+
scrollSpeed?: number;
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
export default VideoViewportProperties;
|
package/src/utilities/index.ts
CHANGED
|
@@ -52,6 +52,7 @@ import getImageLegacy from './getImageLegacy';
|
|
|
52
52
|
import ProgressiveIterator from './ProgressiveIterator';
|
|
53
53
|
import decimate from './decimate';
|
|
54
54
|
import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
|
|
55
|
+
import isVideoTransferSyntax from './isVideoTransferSyntax';
|
|
55
56
|
|
|
56
57
|
// name spaces
|
|
57
58
|
import * as planar from './planar';
|
|
@@ -119,4 +120,5 @@ export {
|
|
|
119
120
|
decimate,
|
|
120
121
|
imageRetrieveMetadataProvider,
|
|
121
122
|
transferFunctionUtils,
|
|
123
|
+
isVideoTransferSyntax,
|
|
122
124
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const videoUIDs = new Set<string>([
|
|
2
|
+
'1.2.840.10008.1.2.4.100',
|
|
3
|
+
'1.2.840.10008.1.2.4.100.1',
|
|
4
|
+
'1.2.840.10008.1.2.4.101',
|
|
5
|
+
'1.2.840.10008.1.2.4.101.1',
|
|
6
|
+
'1.2.840.10008.1.2.4.102',
|
|
7
|
+
'1.2.840.10008.1.2.4.102.1',
|
|
8
|
+
'1.2.840.10008.1.2.4.103',
|
|
9
|
+
'1.2.840.10008.1.2.4.103.1',
|
|
10
|
+
'1.2.840.10008.1.2.4.104',
|
|
11
|
+
'1.2.840.10008.1.2.4.104.1',
|
|
12
|
+
'1.2.840.10008.1.2.4.105',
|
|
13
|
+
'1.2.840.10008.1.2.4.105.1',
|
|
14
|
+
'1.2.840.10008.1.2.4.106',
|
|
15
|
+
'1.2.840.10008.1.2.4.106.1',
|
|
16
|
+
'1.2.840.10008.1.2.4.107',
|
|
17
|
+
'1.2.840.10008.1.2.4.108',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export default function isVideoTransferSyntax(uidOrUids: string | string[]) {
|
|
21
|
+
if (!uidOrUids) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const uids = Array.isArray(uidOrUids) ? uidOrUids : [uidOrUids];
|
|
25
|
+
return uids.find((uid) => videoUIDs.has(uid));
|
|
26
|
+
}
|