Skip to content

Commit

Permalink
fix: large image rendering, missing metadata for StackViewport, high …
Browse files Browse the repository at this point in the history
…DPI devices (#127)

* first render of imageMapper to display StackViewport

* fix: missing metadata for stackviewport rendering

* fix: transformations for the stackViewport with image mapper

* fix: windowLevel should use image min max as multiplier

* fix: Take into account devicePixelRatio during rendering. Minor fixes for using ImageMapper instead of VolumeMapper

* add comment to vtkOffscreenMultiRenderWindow

* fix Crosshairs which were relying on wrong width/height values

* fix build type issues

* update API markdown

Co-authored-by: Erik <erik.sweed@gmail.com>
  • Loading branch information
sedghi and swederik committed Jun 17, 2022
1 parent 9e627b9 commit d4bf1c8
Show file tree
Hide file tree
Showing 41 changed files with 471 additions and 193 deletions.
26 changes: 20 additions & 6 deletions common/reviews/api/core.api.md
Expand Up @@ -9,6 +9,7 @@ import { vec3 } from 'gl-matrix';
import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import type { vtkCamera } from '@kitware/vtk.js/Rendering/Core/Camera';
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';
import type { VtkObject } from '@kitware/vtk.js/interfaces';
import vtkPlane from '@kitware/vtk.js/Common/DataModel/Plane';
import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
Expand All @@ -19,7 +20,7 @@ type Actor = vtkActor;
// @public (undocumented)
type ActorEntry = {
uid: string;
actor: Actor | VolumeActor;
actor: Actor | VolumeActor | ImageActor;
slabThickness?: number;
};

Expand All @@ -44,13 +45,13 @@ export function addVolumesToViewports(renderingEngine: IRenderingEngine, volumeI
// @public (undocumented)
enum BlendModes {
// (undocumented)
AVERAGE_INTENSITY_BLEND,
AVERAGE_INTENSITY_BLEND = 3,
// (undocumented)
COMPOSITE,
COMPOSITE = 0,
// (undocumented)
MAXIMUM_INTENSITY_BLEND,
MAXIMUM_INTENSITY_BLEND = 1,
// (undocumented)
MINIMUM_INTENSITY_BLEND
MINIMUM_INTENSITY_BLEND = 2
}

// @public (undocumented)
Expand Down Expand Up @@ -344,6 +345,7 @@ type CPUIImageData = {
};
scalarData: number[];
scaling: Scaling;
hasPixelSpacing?: boolean;
};

// @public (undocumented)
Expand Down Expand Up @@ -771,6 +773,8 @@ interface IImageData {
// (undocumented)
direction: Float32Array;
// (undocumented)
hasPixelSpacing?: boolean;
// (undocumented)
imageData: vtkImageData;
// (undocumented)
metadata: {
Expand Down Expand Up @@ -805,6 +809,8 @@ interface IImageVolume {
// (undocumented)
direction: Float32Array;
// (undocumented)
hasPixelSpacing: boolean;
// (undocumented)
imageData?: vtkImageData;
// (undocumented)
imageIds?: Array<string>;
Expand Down Expand Up @@ -973,6 +979,8 @@ export class ImageVolume implements IImageVolume {
// (undocumented)
direction: Float32Array;
// (undocumented)
hasPixelSpacing: boolean;
// (undocumented)
imageData?: any;
// (undocumented)
imageIds?: Array<string>;
Expand Down Expand Up @@ -1091,6 +1099,9 @@ export function isCornerstoneInitialized(): boolean;
// @public (undocumented)
function isEqual(v1: number[] | Float32Array, v2: number[] | Float32Array, tolerance?: number): boolean;

// @public (undocumented)
function isImageActor(actor: vtkActor | vtkVolume | vtkImageSlice): boolean;

// @public (undocumented)
function isOpposite(v1: Point3, v2: Point3, tolerance?: number): boolean;

Expand Down Expand Up @@ -1863,7 +1874,8 @@ declare namespace utilities {
getSliceRange,
snapFocalPointToSlice,
getImageSliceDataForVolumeViewport,
getScalingParameters
getScalingParameters,
isImageActor
}
}
export { utilities }
Expand Down Expand Up @@ -1927,6 +1939,8 @@ export class Viewport implements IViewport {
// (undocumented)
protected getVtkActiveCamera(): vtkCamera | vtkSlabCamera;
// (undocumented)
protected hasPixelSpacing: boolean;
// (undocumented)
readonly id: string;
// (undocumented)
_isInBounds(point: Point3, bounds: number[]): boolean;
Expand Down
6 changes: 5 additions & 1 deletion common/reviews/api/streaming-image-volume-loader.api.md
Expand Up @@ -7,6 +7,7 @@
import type { mat4 } from 'gl-matrix';
import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';
import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';

// @public (undocumented)
Expand All @@ -15,7 +16,7 @@ type Actor = vtkActor;
// @public
type ActorEntry = {
uid: string;
actor: Actor | VolumeActor;
actor: Actor | VolumeActor | ImageActor;
slabThickness?: number;
};

Expand Down Expand Up @@ -310,6 +311,7 @@ type CPUIImageData = {
metadata: { Modality: string };
scalarData: number[];
scaling: Scaling;
hasPixelSpacing?: boolean;
};

// @public (undocumented)
Expand Down Expand Up @@ -587,6 +589,7 @@ interface IImage {
interface IImageData {
dimensions: Point3;
direction: Float32Array;
hasPixelSpacing?: boolean;
imageData: vtkImageData;
metadata: { Modality: string };
origin: Point3;
Expand All @@ -610,6 +613,7 @@ interface IImageVolume {
) => IImageLoadObject;
dimensions: Point3;
direction: Float32Array;
hasPixelSpacing: boolean;
imageData?: vtkImageData;
imageIds?: Array<string>;
isPrescaled: boolean;
Expand Down
18 changes: 13 additions & 5 deletions common/reviews/api/tools.api.md
Expand Up @@ -8,6 +8,7 @@ import type { mat4 } from 'gl-matrix';
import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import type { vtkColorTransferFunction } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';
import type { vtkPiecewiseFunction } from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';

Expand All @@ -24,7 +25,7 @@ type Actor = vtkActor;
// @public
type ActorEntry = {
uid: string;
actor: Actor | VolumeActor;
actor: Actor | VolumeActor | ImageActor;
slabThickness?: number;
};

Expand Down Expand Up @@ -475,6 +476,7 @@ interface BidirectionalAnnotation extends Annotation {
[targetId: string]: {
length: number;
width: number;
unit: string;
};
};
};
Expand Down Expand Up @@ -958,6 +960,7 @@ type CPUIImageData = {
metadata: { Modality: string };
scalarData: number[];
scaling: Scaling;
hasPixelSpacing?: boolean;
};

// @public (undocumented)
Expand Down Expand Up @@ -1901,6 +1904,7 @@ interface IImage {
interface IImageData {
dimensions: Point3;
direction: Float32Array;
hasPixelSpacing?: boolean;
imageData: vtkImageData;
metadata: { Modality: string };
origin: Point3;
Expand All @@ -1924,6 +1928,7 @@ interface IImageVolume {
) => IImageLoadObject;
dimensions: Point3;
direction: Float32Array;
hasPixelSpacing: boolean;
imageData?: vtkImageData;
imageIds?: Array<string>;
isPrescaled: boolean;
Expand Down Expand Up @@ -2505,6 +2510,7 @@ interface LengthAnnotation extends Annotation {
cachedStats: {
[targetId: string]: {
length: number;
unit: string;
};
};
};
Expand Down Expand Up @@ -4341,13 +4347,15 @@ export class WindowLevelTool extends BaseTool {
// (undocumented)
_dragCallback(evt: any): void;
// (undocumented)
_getImageDynamicRange: (volumeId: string) => number;
_getImageDynamicRangeFromMiddleSlice: (scalarData: any, dimensions: any) => number;
// (undocumented)
_getMultiplyerFromDynamicRange(volumeId: any): number;
_getImageDynamicRangeFromViewport(viewport: any): number;
// (undocumented)
getNewRange({ deltaPointsCanvas, useDynamicRange, volumeId, lower, upper }: {
_getMultiplierFromDynamicRange(viewport: any, volumeId: any): number;
// (undocumented)
getNewRange({ viewport, deltaPointsCanvas, volumeId, lower, upper }: {
viewport: any;
deltaPointsCanvas: any;
useDynamicRange: any;
volumeId: any;
lower: any;
upper: any;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Expand Up @@ -25,15 +25,15 @@
"example": "node ../../utils/ExampleRunner/example-runner-cli.js"
},
"peerDependencies": {
"@kitware/vtk.js": "24.0.0",
"@kitware/vtk.js": "24.18.7",
"gl-matrix": "^3.4.3"
},
"dependencies": {
"detect-gpu": "^4.0.7",
"lodash.clonedeep": "4.5.0"
},
"devDependencies": {
"@kitware/vtk.js": "24.0.0",
"@kitware/vtk.js": "24.18.7",
"detect-gpu": "^4.0.7",
"gl-matrix": "^3.4.3",
"resemblejs": "^4.1.0"
Expand Down
41 changes: 26 additions & 15 deletions packages/core/src/RenderingEngine/RenderingEngine.ts
Expand Up @@ -605,6 +605,12 @@ class RenderingEngine implements IRenderingEngine {
const canvas = getOrCreateCanvas(viewportInputEntry.element);
canvasesDrivenByVtkJs.push(canvas);

const devicePixelRatio = window.devicePixelRatio || 1;

const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * devicePixelRatio;
canvas.height = rect.height * devicePixelRatio;

// 2.c Calculating the new size for offScreen Canvas
const { offScreenCanvasWidth, offScreenCanvasHeight } =
this._resizeOffScreenCanvas(canvasesDrivenByVtkJs);
Expand Down Expand Up @@ -767,6 +773,9 @@ class RenderingEngine implements IRenderingEngine {
const { clientWidth, clientHeight } = canvas;

// Set the canvas to be same resolution as the client.
// Note: This ignores devicePixelRatio for now. We may want to change it in the
// future but it has no benefit for the Cornerstone CPU rendering pathway at the
// moment anyway.
if (canvas.width !== clientWidth || canvas.height !== clientHeight) {
canvas.width = clientWidth;
canvas.height = clientHeight;
Expand Down Expand Up @@ -832,11 +841,19 @@ class RenderingEngine implements IRenderingEngine {
// Deal with vtkjs driven viewports
if (viewportInputEntries.length) {
// 1. Getting all the canvases from viewports calculation of the new offScreen size

const vtkDrivenCanvases = viewportInputEntries.map((vp) =>
getOrCreateCanvas(vp.element)
);

// Ensure the canvas size includes any scaling due to device pixel ratio
vtkDrivenCanvases.forEach((canvas) => {
const devicePixelRatio = window.devicePixelRatio || 1;

const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * devicePixelRatio;
canvas.height = rect.height * devicePixelRatio;
});

// 2. Set canvas size based on height and sum of widths
const { offScreenCanvasWidth, offScreenCanvasHeight } =
this._resizeOffScreenCanvas(vtkDrivenCanvases);
Expand Down Expand Up @@ -866,7 +883,7 @@ class RenderingEngine implements IRenderingEngine {

// Incrementing the xOffset which provides the horizontal location of each
// viewport on the offScreen canvas
xOffset += canvas.clientWidth;
xOffset += canvas.width;
}
}
}
Expand All @@ -884,14 +901,14 @@ class RenderingEngine implements IRenderingEngine {
// 1. Calculated the height of the offScreen canvas to be the maximum height
// between canvases
const offScreenCanvasHeight = Math.max(
...canvasesDrivenByVtkJs.map((canvas) => canvas.clientHeight)
...canvasesDrivenByVtkJs.map((canvas) => canvas.height)
);

// 2. Calculating the width of the offScreen canvas to be the sum of all
let offScreenCanvasWidth = 0;

canvasesDrivenByVtkJs.forEach((canvas) => {
offScreenCanvasWidth += canvas.clientWidth;
offScreenCanvasWidth += canvas.width;
});

offScreenCanvasContainer.width = offScreenCanvasWidth;
Expand Down Expand Up @@ -938,7 +955,7 @@ class RenderingEngine implements IRenderingEngine {
_xOffset
);

_xOffset += viewport.element.clientWidth;
_xOffset += viewport.canvas.width;

viewport.sx = sx;
viewport.sy = sy;
Expand Down Expand Up @@ -974,25 +991,19 @@ class RenderingEngine implements IRenderingEngine {
_xOffset: number
): ViewportDisplayCoords {
const { canvas } = viewport;
const { clientWidth, clientHeight } = canvas;

// Set the canvas to be same resolution as the client.
if (canvas.width !== clientWidth || canvas.height !== clientHeight) {
canvas.width = clientWidth;
canvas.height = clientHeight;
}
const { width, height } = canvas;

// Update the canvas drawImage offsets.
const sx = _xOffset;
const sy = 0;
const sWidth = clientWidth;
const sHeight = clientHeight;
const sWidth = width;
const sHeight = height;

const sxStartDisplayCoords = sx / offScreenCanvasWidth;

// Need to offset y if it not max height
const syStartDisplayCoords =
sy + (offScreenCanvasHeight - clientHeight) / offScreenCanvasHeight;
sy + (offScreenCanvasHeight - height) / offScreenCanvasHeight;

const sWidthDisplayCoords = sWidth / offScreenCanvasWidth;
const sHeightDisplayCoords = sHeight / offScreenCanvasHeight;
Expand Down

0 comments on commit d4bf1c8

Please sign in to comment.