feat: Add DICOM SEG support and MPR, referenceLines and StackSync and more (#3015)

* feat: add initial sop class handler for SEG

* feat: move segmentation service

* update segmentation service methods

* fix: viewport data structure

* feat: Add initial render for the DICOM SEG for each displaySet

* fix: various wrong architectural dependencies between services

* feat: initial separate SEG display in each viewport

* feat: refactore viewport action bar

* initial work for SEG hydration

* fix: various bugs regarding drag and dropping different viewports

* fix: rendering issues for multiple seg displaysets

* fix: bugs for thumbnail and hydration

* feat: fix the initial segment color

* update after rebase

* feat: initial design for the segmentation group table

* feat: initial new design for the segmentation panel

* feat: segmentation panel

* feat: make segmentation appear on all related viewports

* fix: segmentation load bug based on functional groups

* initial work for segmentation crosshairs

* fix: various stylings and functionality

* fix: various stylings for the seg panel

* fix: overflow styles

* feat: add more ui components

* feat: added segmentation config

* feat: add jump to segment

* feat: add jump to segment for DICOM SEG viewport

* fix: bugs after rebase

* feat: jump in segmentation viewport

* fix: mpr support for seg

* feat: add more icons

* feat: new icons

* feat: add new side panel

* feat: add segmentation config

* feat: add loading indicator to ohif

* feat: make hanging protocols follow matching rules for viewports

* feat: enhance drag and drop to be hanging protocol aware

* fix: mpr restore previous layout

* fix: crosshairs toggle

* fix: add auth headers to the dicom loader via dicomwebclient

* fix: bug for crosshairs toggle in mpr

* fix: seg viewport reusing old toolGroup

* feat: add loading animation with lottie

* fix: various bugs for Segmentation hydration in mpr

* feat: change outline alpha to outline opacity

* feat: loading indicator for seg viewport

* fix: various segmentation group styles

* fix: local mode for seg

* feat: add animation for the highlight

* fix: loading indicatro to show segment indices

* feat: enhance modality drop down ui

* fix: panels

* fix: download form

* fix: layout shift in loading indicator

* fix: loading indicator to have correct values

* fix: update software number

* fix: image jump between MPR and default

* fix: segmentation cleanup and cine service cleanup

* fix: segmentation toolgroup clena up

* fix: highlight interval should not trigger again

* fix: issue with multiframe sorting

* rename: change onSeriesChange to onArrowsClick

* fix: buttons for tmtv and layout shift

* fix: various bugs wrt crosshairs

* fix: crosshairs re init on reset camera

* fix: middle slice calculation different from cs middle reset camera

* fix: reset camera should reset viewport camera

* fix: loading segments in MPR mode

* fix: tmtv hp back to before

* fix: layout shift

* feat: orientation markers for volume viewport

* fix: capture for volume viewports

* fix: memory leak for back to worklist

* fix: loading bar bg color

* fix: side panel for only one panel

* fix: react select style in production

* fix: various styling for segmentation groups

* fix: various ui styles

* feat: add new segmentation config styles

* fix: hover state for segmentation item

* fix: side panel layout shift

* temp add panels

* try to fix scrollbar for thimbnail

* fix: scroll not appearing for side panels

* feat: changed styles for scrollbar

* feat: add cpu fallback warning

* fix: webworker destroy

* fix: select styles

* fix: orientation marker color and position

* fix: capture screenshot for volume viewports

* fix: hide segment visibility on mpr

* fix: reloading an already loaded seg displayset

* feat: add is equal

* fix: mpr jump to measurements

* apply review comments

* fix: optimization for the segmentation load

* fix: remove unnecessary context menu and hp service reset

* fix: segmentation service wrt brush settings

* feat: initial work for the stack synchronization

* fix: add validateDisplaySetSelectorsForNewDisplaySets to make sure drag and drop can follow requirements

* fix: various react proptypes

* fix: thumbnails for the tmtv and change default props to default params

* feat: make showing loading indicator configurable and add docs

* bump versions and add more docs

* fix demo

* update cornerstone versions

* apply review comments

* feat: add reference lines

* fix build

* fix unit tests

* add reference lines icon

* fix: e2e tests

* fix static wado config

* docs: add segmentation service docs

* fix: docker build

* fix: keep camera and bump versions

* Try re-enabling minification to fix deploy previews

Co-authored-by: Erik Ziegler <erik.sweed@gmail.com>
This commit is contained in:
AlirezaandErik Ziegler authored and GitHub committed 2022-11-16 14:19:52 -05:00
1 parent 08c58b0634
commit 83ea228d86
254 files changed
+12856 -3141

No files matched your search

+24 -3
View File
@@ -10,6 +10,7 @@ const NON_IMAGE_MODALITIES = ['SM', 'ECG', 'SR', 'SEG'];
const ohif = {
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
thumbnailList: '@ohif/extension-default.panelModule.seriesList',
};
const tracked = {
@@ -37,12 +38,20 @@ const dicompdf = {
viewport: '@ohif/extension-dicom-pdf.viewportModule.dicom-pdf',
};
const dicomSeg = {
sopClassHandler:
'@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg',
panel: '@ohif/extension-cornerstone-dicom-seg.panelModule.panelSegmentation',
};
const extensionDependencies = {
// Can derive the versions at least process.env.from npm_package_version
'@ohif/extension-default': '^3.0.0',
'@ohif/extension-cornerstone': '^3.0.0',
'@ohif/extension-measurement-tracking': '^3.0.0',
'@ohif/extension-cornerstone-dicom-sr': '^3.0.0',
'@ohif/extension-cornerstone-dicom-seg': '^3.0.0',
'@ohif/extension-dicom-pdf': '^3.0.1',
'@ohif/extension-dicom-video': '^3.0.1',
};
@@ -102,7 +111,8 @@ function modeFactory() {
'Pan',
'Capture',
'Layout',
// 'MPR',
'MPR',
'Crosshairs',
'MoreTools',
]);
},
@@ -112,12 +122,18 @@ function modeFactory() {
SyncGroupService,
MeasurementService,
ToolBarService,
SegmentationService,
CornerstoneViewportService,
HangingProtocolService,
} = servicesManager.services;
ToolBarService.reset();
MeasurementService.clearMeasurements();
ToolGroupService.destroy();
SyncGroupService.destroy();
SegmentationService.destroy();
CornerstoneViewportService.destroy();
HangingProtocolService.reset();
},
validationTags: {
study: [],
@@ -143,8 +159,8 @@ function modeFactory() {
id: ohif.layout,
props: {
leftPanels: [tracked.thumbnailList],
// TODO: Should be optional, or required to pass empty array for slots?
rightPanels: [tracked.measurements],
rightPanels: [dicomSeg.panel, tracked.measurements],
// rightPanelDefaultClosed: true, // optional prop to start with collapse panels
viewports: [
{
namespace: tracked.viewport,
@@ -162,6 +178,10 @@ function modeFactory() {
namespace: dicompdf.viewport,
displaySetsToDisplay: [dicompdf.sopClassHandler],
},
{
namespace: dicomSeg.viewport,
displaySetsToDisplay: [dicomSeg.sopClassHandler],
},
],
},
};
@@ -177,6 +197,7 @@ function modeFactory() {
// come first to remove video transfer syntax before ohif uses images
sopClassHandlers: [
dicomvideo.sopClassHandler,
dicomSeg.sopClassHandler,
ohif.sopClassHandler,
dicompdf.sopClassHandler,
dicomsr.sopClassHandler,
+70 -6
View File
@@ -36,9 +36,11 @@ function initDefaultToolGroup(
{ toolName: toolNames.StackScroll },
{ toolName: toolNames.Angle },
{ toolName: toolNames.Magnify },
{ toolName: toolNames.SegmentationDisplay },
],
// enabled
// disabled
disabled: [{ toolName: toolNames.ReferenceLines }],
};
const toolsConfig = {
@@ -139,6 +141,73 @@ function initSRToolGroup(extensionManager, ToolGroupService, commandsManager) {
ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, toolsConfig);
}
function initMPRToolGroup(extensionManager, ToolGroupService, commandsManager) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.tools'
);
const { toolNames, Enums } = utilityModule.exports;
const tools = {
active: [
{
toolName: toolNames.WindowLevel,
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
},
{
toolName: toolNames.Pan,
bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],
},
{
toolName: toolNames.Zoom,
bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],
},
{ toolName: toolNames.StackScrollMouseWheel, bindings: [] },
],
passive: [
{ toolName: toolNames.Length },
{ toolName: toolNames.ArrowAnnotate },
{ toolName: toolNames.Bidirectional },
{ toolName: toolNames.DragProbe },
{ toolName: toolNames.EllipticalROI },
{ toolName: toolNames.RectangleROI },
{ toolName: toolNames.StackScroll },
{ toolName: toolNames.Angle },
{ toolName: toolNames.SegmentationDisplay },
],
disabled: [{ toolName: toolNames.Crosshairs }],
// enabled
// disabled
};
const toolsConfig = {
[toolNames.Crosshairs]: {
viewportIndicators: false,
autoPan: {
enabled: false,
panSize: 10,
},
},
[toolNames.ArrowAnnotate]: {
getTextCallback: (callback, eventDetails) =>
commandsManager.runCommand('arrowTextCallback', {
callback,
eventDetails,
}),
changeTextCallback: (data, eventDetails, callback) =>
commandsManager.runCommand('arrowTextCallback', {
callback,
data,
eventDetails,
}),
},
};
ToolGroupService.createToolGroupAndAddTools('mpr', tools, toolsConfig);
}
function initToolGroups(extensionManager, ToolGroupService, commandsManager) {
initDefaultToolGroup(
extensionManager,
@@ -147,12 +216,7 @@ function initToolGroups(extensionManager, ToolGroupService, commandsManager) {
'default'
);
initSRToolGroup(extensionManager, ToolGroupService, commandsManager);
initDefaultToolGroup(
extensionManager,
ToolGroupService,
commandsManager,
'mpr'
);
initMPRToolGroup(extensionManager, ToolGroupService, commandsManager);
}
export default initToolGroups;
+66 -17
View File
@@ -26,6 +26,18 @@ function _createButton(type, id, icon, label, commands, tooltip) {
};
}
function _createCommands(commandName, toolName, toolGroupIds) {
return toolGroupIds.map(toolGroupId => ({
/* It's a command that is being run when the button is clicked. */
commandName,
commandOptions: {
toolName,
toolGroupId,
},
context: 'CORNERSTONE',
}));
}
const _createActionButton = _createButton.bind(null, 'action');
const _createToggleButton = _createButton.bind(null, 'toggle');
const _createToolButton = _createButton.bind(null, 'tool');
@@ -290,23 +302,41 @@ const toolbarButtons = [
columns: 3,
},
},
// Todo: MPR not ready yet for SEG support, not activating it now
// {
// id: 'MPR',
// type: 'ohif.action',
// props: {
// icon: 'old-play',
// label: 'MPR',
// type: 'action',
// commands: [
// {
// commandName: 'setHangingProtocol',
// commandOptions: { protocolId: 'mpr' },
// context: 'CORNERSTONE',
// },
// ],
// },
// },
{
id: 'MPR',
type: 'ohif.action',
props: {
type: 'toggle',
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleMPR',
commandOptions: {},
context: 'CORNERSTONE',
},
],
},
},
{
id: 'Crosshairs',
type: 'ohif.radioGroup',
props: {
type: 'tool',
icon: 'tool-crosshair',
label: 'Crosshairs',
commands: [
{
commandName: 'setToolActive',
commandOptions: {
toolGroupId: 'mpr',
toolName: 'Crosshairs',
},
context: 'CORNERSTONE',
},
],
},
},
// More...
{
id: 'MoreTools',
@@ -373,6 +403,25 @@ const toolbarButtons = [
],
'Flip Horizontal'
),
_createToggleButton('StackImageSync', 'link', 'Stack Image Sync', [
{
commandName: 'toggleStackImageSync',
commandOptions: {},
context: 'CORNERSTONE',
},
]),
_createToggleButton(
'ReferenceLines',
'tool-referenceLines', // change this with the new icon
'Reference Lines',
[
{
commandName: 'toggleReferenceLines',
commandOptions: {},
context: 'CORNERSTONE',
},
]
),
_createToolButton(
'StackScroll',
'tool-stack-scroll',
+8 -2
View File
@@ -140,6 +140,9 @@ function modeFactory({ modeConfiguration }) {
SyncGroupService,
MeasurementService,
ToolBarService,
SegmentationService,
CornerstoneViewportService,
HangingProtocolService,
} = servicesManager.services;
unsubscriptions.forEach(unsubscribe => unsubscribe());
@@ -147,6 +150,9 @@ function modeFactory({ modeConfiguration }) {
MeasurementService.clearMeasurements();
ToolGroupService.destroy();
SyncGroupService.destroy();
SegmentationService.destroy();
CornerstoneViewportService.destroy();
HangingProtocolService.reset();
},
validationTags: {
study: [],
@@ -173,8 +179,8 @@ function modeFactory({ modeConfiguration }) {
return {
id: ohif.layout,
props: {
leftPanels: [],
rightPanels: [tmtv.petSUV, tmtv.ROIThresholdPanel],
// leftPanels: [ohif.thumbnailList],
rightPanels: [tmtv.ROIThresholdPanel, tmtv.petSUV],
viewports: [
{
namespace: cs3d.viewport,
+1
View File
@@ -28,6 +28,7 @@ function initToolGroups(toolNames, Enums, ToolGroupService, commandsManager) {
{ toolName: toolNames.ArrowAnnotate },
{ toolName: toolNames.Bidirectional },
{ toolName: toolNames.DragProbe },
{ toolName: toolNames.Probe },
{ toolName: toolNames.EllipticalROI },
{ toolName: toolNames.RectangleROI },
{ toolName: toolNames.StackScroll },
+4 -3
View File
@@ -228,11 +228,11 @@ const toolbarButtons = [
id: 'Crosshairs',
type: 'ohif.radioGroup',
props: {
type: 'toggle',
type: 'tool',
icon: 'tool-crosshair',
label: 'Crosshairs',
commands: [
..._createCommands('toggleCrosshairs', 'Crosshairs', [
..._createCommands('setToolActive', 'Crosshairs', [
toolGroupIds.CT,
toolGroupIds.PT,
toolGroupIds.Fusion,
@@ -272,7 +272,8 @@ const toolbarButtons = [
commandName: 'displayNotification',
commandOptions: {
title: 'RectangleROI Threshold Tip',
text: 'RectangleROI Threshold tool should be used on PT Axial Viewport',
text:
'RectangleROI Threshold tool should be used on PT Axial Viewport',
type: 'info',
},
},