feat(VolumeViewport3D): add volumeViewport3Dd sample hp (#3241)
* feat(VolumeViewport3D) - importing and using Cornerstone 3D's VolumeViewport3D viewport - added mprAnd3DVolumeViewport hanging protocol - allowed for W/L preset to be applied to a viewport * Updated cornerstone core dependency * Removed pan and zoom from the volume3d tool group so that tackballrotate is never dropped for that viewport. Removed overlays from 3D volume viewport. Throw an error and display a message whenever an unapplicable tool is selected. * The default/initial orientation for a 3D volume viewport can now be set. Using the 'interleaveTopToBottom' image load strategy for the 'mprAnd3DVolumeViewport' hanging protocol. * Do not set orientation for stack viewport.
This commit is contained in:
parent
5ad5bd232d
commit
550918dd31
@ -46,7 +46,7 @@
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
"@cornerstonejs/adapters": "^0.4.1",
|
||||
"@cornerstonejs/core": "^0.33.2",
|
||||
"@cornerstonejs/core": "^0.36.0",
|
||||
"@cornerstonejs/tools": "^0.50.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^0.4.1",
|
||||
"@cornerstonejs/core": "^0.33.2",
|
||||
"@cornerstonejs/core": "^0.36.0",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^0.14.1",
|
||||
"@cornerstonejs/tools": "^0.50.2",
|
||||
"@kitware/vtk.js": "26.5.6",
|
||||
|
||||
@ -181,6 +181,17 @@ const commandsModule = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toolGroup.getToolInstance(toolName)) {
|
||||
uiNotificationService.show({
|
||||
title: `${toolName} tool`,
|
||||
message: `The ${toolName} tool is not available in this viewport.`,
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
|
||||
throw new Error(`ToolGroup ${toolGroup.id} does not have this tool.`);
|
||||
}
|
||||
|
||||
const activeToolName = toolGroup.getActivePrimaryMouseButtonTool();
|
||||
|
||||
if (activeToolName) {
|
||||
|
||||
@ -160,12 +160,147 @@ const mpr: Types.HangingProtocol.Protocol = {
|
||||
],
|
||||
};
|
||||
|
||||
const mprAnd3DVolumeViewport = {
|
||||
id: 'mprAnd3DVolumeViewport',
|
||||
locked: true,
|
||||
hasUpdatedPriorsInformation: false,
|
||||
name: 'mpr',
|
||||
createdDate: '2023-03-15T10:29:44.894Z',
|
||||
modifiedDate: '2023-03-15T10:29:44.894Z',
|
||||
availableTo: {},
|
||||
editableBy: {},
|
||||
protocolMatchingRules: [],
|
||||
imageLoadStrategy: 'interleaveTopToBottom',
|
||||
displaySetSelectors: {
|
||||
mprDisplaySet: {
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
weight: 1,
|
||||
attribute: 'isReconstructable',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
stages: [
|
||||
{
|
||||
id: 'mpr3Stage',
|
||||
name: 'mpr',
|
||||
viewportStructure: {
|
||||
layoutType: 'grid',
|
||||
properties: {
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'axial',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'volume3d',
|
||||
viewportType: 'volume3d',
|
||||
orientation: 'coronal',
|
||||
customViewportProps: {
|
||||
hideOverlays: true,
|
||||
},
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
options: {
|
||||
presetName: 'CT-Bone',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'coronal',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'sagittal',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function getHangingProtocolModule() {
|
||||
return [
|
||||
{
|
||||
id: 'mpr',
|
||||
protocol: mpr,
|
||||
},
|
||||
{
|
||||
id: mprAnd3DVolumeViewport.id,
|
||||
protocol: mprAnd3DVolumeViewport,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
addTool,
|
||||
annotation,
|
||||
ReferenceLinesTool,
|
||||
TrackballRotateTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
@ -51,6 +52,7 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(SegmentationDisplayTool);
|
||||
addTool(ReferenceLinesTool);
|
||||
addTool(CalibrationLineTool);
|
||||
addTool(TrackballRotateTool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const annotationStyle = {
|
||||
@ -90,6 +92,7 @@ const toolNames = {
|
||||
SegmentationDisplay: SegmentationDisplayTool.toolName,
|
||||
ReferenceLines: ReferenceLinesTool.toolName,
|
||||
CalibrationLine: CalibrationLineTool.toolName,
|
||||
TrackballRotateTool: TrackballRotateTool.toolName,
|
||||
};
|
||||
|
||||
export { toolNames };
|
||||
|
||||
@ -62,12 +62,20 @@ class CornerstoneCacheService {
|
||||
viewportData = await this._getStackViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
initialImageIndex
|
||||
initialImageIndex,
|
||||
cs3DViewportType
|
||||
);
|
||||
}
|
||||
|
||||
if (cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC) {
|
||||
viewportData = await this._getVolumeViewportData(dataSource, displaySets);
|
||||
if (
|
||||
cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC ||
|
||||
cs3DViewportType === Enums.ViewportType.VOLUME_3D
|
||||
) {
|
||||
viewportData = await this._getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
cs3DViewportType
|
||||
);
|
||||
}
|
||||
|
||||
viewportData.viewportType = cs3DViewportType;
|
||||
@ -100,7 +108,8 @@ class CornerstoneCacheService {
|
||||
|
||||
const newViewportData = await this._getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets
|
||||
displaySets,
|
||||
viewportData.viewportType
|
||||
);
|
||||
|
||||
return newViewportData;
|
||||
@ -109,7 +118,8 @@ class CornerstoneCacheService {
|
||||
private _getStackViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
initialImageIndex
|
||||
initialImageIndex,
|
||||
viewportType: Enums.ViewportType
|
||||
): StackViewportData {
|
||||
// For Stack Viewport we don't have fusion currently
|
||||
const displaySet = displaySets[0];
|
||||
@ -126,7 +136,7 @@ class CornerstoneCacheService {
|
||||
const { displaySetInstanceUID, StudyInstanceUID } = displaySet;
|
||||
|
||||
const StackViewportData: StackViewportData = {
|
||||
viewportType: Enums.ViewportType.STACK,
|
||||
viewportType,
|
||||
data: {
|
||||
StudyInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
@ -143,7 +153,8 @@ class CornerstoneCacheService {
|
||||
|
||||
private async _getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets
|
||||
displaySets,
|
||||
viewportType: Enums.ViewportType
|
||||
): Promise<VolumeViewportData> {
|
||||
// Todo: Check the cache for multiple scenarios to see if we need to
|
||||
// decache the volume data from other viewports or not
|
||||
@ -207,7 +218,7 @@ class CornerstoneCacheService {
|
||||
}
|
||||
|
||||
return {
|
||||
viewportType: Enums.ViewportType.ORTHOGRAPHIC,
|
||||
viewportType,
|
||||
data: volumeData,
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,7 +6,10 @@ import {
|
||||
getRenderingEngine,
|
||||
utilities as csUtils,
|
||||
VolumeViewport,
|
||||
VolumeViewport3D,
|
||||
cache,
|
||||
utilities,
|
||||
CONSTANTS,
|
||||
} from '@cornerstonejs/core';
|
||||
|
||||
import { utilities as csToolsUtils } from '@cornerstonejs/tools';
|
||||
@ -682,7 +685,12 @@ class CornerstoneViewportService extends PubSubService
|
||||
}
|
||||
|
||||
_getVOICallbacks(volumeId, displaySetOptions) {
|
||||
const { voi, voiInverted: inverted, colormap } = displaySetOptions;
|
||||
const {
|
||||
voi,
|
||||
voiInverted: inverted,
|
||||
colormap,
|
||||
presetName,
|
||||
} = displaySetOptions;
|
||||
|
||||
const voiCallbackArray = [];
|
||||
|
||||
@ -707,6 +715,16 @@ class CornerstoneViewportService extends PubSubService
|
||||
);
|
||||
}
|
||||
|
||||
if (presetName) {
|
||||
voiCallbackArray.push(volumeActor => {
|
||||
utilities.applyPreset(
|
||||
volumeActor,
|
||||
CONSTANTS.VIEWPORT_PRESETS.find(preset => {
|
||||
return preset.name === presetName;
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return voiCallbackArray;
|
||||
}
|
||||
|
||||
@ -723,7 +741,10 @@ class CornerstoneViewportService extends PubSubService
|
||||
viewportInfo,
|
||||
presentations
|
||||
);
|
||||
} else if (viewport instanceof VolumeViewport) {
|
||||
} else if (
|
||||
viewport instanceof VolumeViewport ||
|
||||
viewport instanceof VolumeViewport3D
|
||||
) {
|
||||
this._setVolumeViewport(
|
||||
viewport,
|
||||
viewportData as VolumeViewportData,
|
||||
|
||||
@ -63,6 +63,7 @@ export type PublicDisplaySetOptions = {
|
||||
blendMode?: string;
|
||||
slabThickness?: number;
|
||||
colormap?: string;
|
||||
presetName?: string;
|
||||
};
|
||||
|
||||
export type DisplaySetOptions = {
|
||||
@ -72,6 +73,7 @@ export type DisplaySetOptions = {
|
||||
blendMode?: Enums.BlendModes;
|
||||
slabThickness?: number;
|
||||
colormap?: string;
|
||||
presetName?: string;
|
||||
};
|
||||
|
||||
type VOI = {
|
||||
@ -84,7 +86,6 @@ export type DisplaySet = {
|
||||
};
|
||||
|
||||
const STACK = 'stack';
|
||||
const VOLUME = 'volume';
|
||||
const DEFAULT_TOOLGROUP_ID = 'default';
|
||||
|
||||
class ViewportInfo {
|
||||
@ -198,10 +199,8 @@ class ViewportInfo {
|
||||
}
|
||||
|
||||
// map SAGITTAL, AXIAL, CORONAL orientation to be used by cornerstone
|
||||
if (viewportOptionsEntry.viewportType?.toLowerCase() === VOLUME) {
|
||||
if (viewportOptionsEntry.viewportType?.toLowerCase() !== STACK) {
|
||||
orientation = getCornerstoneOrientation(viewportOptionsEntry.orientation);
|
||||
} else {
|
||||
orientation = Enums.OrientationAxis.AXIAL;
|
||||
}
|
||||
|
||||
this.setViewportOptions({
|
||||
@ -282,6 +281,7 @@ class ViewportInfo {
|
||||
colormap: option.colormap,
|
||||
slabThickness: option.slabThickness,
|
||||
blendMode,
|
||||
presetName: option.presetName,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import { Enums } from '@cornerstonejs/core';
|
||||
const STACK = 'stack';
|
||||
const VOLUME = 'volume';
|
||||
const ORTHOGRAPHIC = 'orthographic';
|
||||
const VOLUME_3D = 'volume3d';
|
||||
|
||||
export default function getCornerstoneViewportType(
|
||||
viewportType: string
|
||||
@ -16,6 +17,10 @@ export default function getCornerstoneViewportType(
|
||||
return Enums.ViewportType.ORTHOGRAPHIC;
|
||||
}
|
||||
|
||||
if (lowerViewportType === VOLUME_3D) {
|
||||
return Enums.ViewportType.VOLUME_3D;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Invalid viewport type: ${viewportType}. Valid types are: stack, volume`
|
||||
);
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"@cornerstonejs/core": "^0.33.2",
|
||||
"@cornerstonejs/core": "^0.36.0",
|
||||
"@cornerstonejs/tools": "^0.50.2",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||
"dcmjs": "^0.29.4",
|
||||
|
||||
@ -215,6 +215,24 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
|
||||
toolGroupService.createToolGroupAndAddTools('mpr', tools, toolsConfig);
|
||||
}
|
||||
function initVolume3DToolGroup(extensionManager, toolGroupService) {
|
||||
const utilityModule = extensionManager.getModuleEntry(
|
||||
'@ohif/extension-cornerstone.utilityModule.tools'
|
||||
);
|
||||
|
||||
const { toolNames, Enums } = utilityModule.exports;
|
||||
|
||||
const tools = {
|
||||
active: [
|
||||
{
|
||||
toolName: toolNames.TrackballRotateTool,
|
||||
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
toolGroupService.createToolGroupAndAddTools('volume3d', tools);
|
||||
}
|
||||
|
||||
function initToolGroups(extensionManager, toolGroupService, commandsManager) {
|
||||
initDefaultToolGroup(
|
||||
@ -225,6 +243,7 @@ function initToolGroups(extensionManager, toolGroupService, commandsManager) {
|
||||
);
|
||||
initSRToolGroup(extensionManager, toolGroupService, commandsManager);
|
||||
initMPRToolGroup(extensionManager, toolGroupService, commandsManager);
|
||||
initVolume3DToolGroup(extensionManager, toolGroupService);
|
||||
}
|
||||
|
||||
export default initToolGroups;
|
||||
|
||||
@ -1459,6 +1459,14 @@
|
||||
detect-gpu "^4.0.45"
|
||||
lodash.clonedeep "4.5.0"
|
||||
|
||||
"@cornerstonejs/core@^0.36.0":
|
||||
version "0.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-0.36.0.tgz#b8e798e04cfceb6106ce4700b2aef14532d814f0"
|
||||
integrity sha512-vU1wYhezq4x99MT3nuuNS2YVTUn1UkMJ5B9PPSyayQeWxuC0pV0KWBllUAjJA3VR3YDmDUKAfLh9K10u6javYg==
|
||||
dependencies:
|
||||
detect-gpu "^4.0.45"
|
||||
lodash.clonedeep "4.5.0"
|
||||
|
||||
"@cornerstonejs/streaming-image-volume-loader@^0.14.1":
|
||||
version "0.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.14.1.tgz#9830e1cbb65aa0e050336b7f5cb9c81d23e09fc5"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user