feat: improve segment label (#5217)
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
@ -1,7 +1,14 @@
|
||||
function createSEGToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
|
||||
function createSEGToolGroupAndAddTools({
|
||||
commandsManager,
|
||||
toolGroupService,
|
||||
customizationService,
|
||||
toolGroupId,
|
||||
}) {
|
||||
const tools = customizationService.getCustomization('cornerstone.overlayViewportTools');
|
||||
|
||||
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
|
||||
const updatedTools = commandsManager.run('initializeSegmentLabelTool', { tools });
|
||||
|
||||
return toolGroupService.createToolGroupAndAddTools(toolGroupId, updatedTools);
|
||||
}
|
||||
|
||||
export default createSEGToolGroupAndAddTools;
|
||||
|
||||
@ -214,7 +214,12 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
||||
|
||||
// This creates a custom tool group which has the lifetime of this view
|
||||
// only, and does NOT interfere with currently displayed segmentations.
|
||||
toolGroup = createSEGToolGroupAndAddTools(toolGroupService, customizationService, toolGroupId);
|
||||
toolGroup = createSEGToolGroupAndAddTools({
|
||||
commandsManager,
|
||||
toolGroupService,
|
||||
customizationService,
|
||||
toolGroupId,
|
||||
});
|
||||
|
||||
return () => {
|
||||
// remove the segmentation representations if seg displayset changed
|
||||
|
||||
@ -102,6 +102,7 @@ let segmentAIEnabled = false;
|
||||
function commandsModule({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
}: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule {
|
||||
const {
|
||||
viewportGridService,
|
||||
@ -1743,17 +1744,62 @@ function commandsModule({
|
||||
}
|
||||
});
|
||||
},
|
||||
toggleSegmentLabel: ({ toggle }) => {
|
||||
toggleSegmentLabel: () => {
|
||||
const toolName = cornerstoneTools.SegmentLabelTool.toolName;
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
|
||||
const isOn = toolGroupIds.some(toolGroupId => {
|
||||
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
|
||||
const mode = toolGroup.getToolInstance(toolName)?.mode;
|
||||
return mode === 'Active';
|
||||
});
|
||||
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
|
||||
if (toggle) {
|
||||
toolGroup.setToolActive(cornerstoneTools.SegmentLabelTool.toolName);
|
||||
if (isOn) {
|
||||
toolGroup.setToolDisabled(toolName);
|
||||
} else {
|
||||
toolGroup.setToolDisabled(cornerstoneTools.SegmentLabelTool.toolName);
|
||||
toolGroup.setToolActive(toolName);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Used to sync the apps initial state with the config file settings.
|
||||
*
|
||||
* Will mutate the tools object of the given tool group and add the segmentLabelTool to the proper place.
|
||||
*
|
||||
* Use it before initializing the toolGroup with the tools.
|
||||
*/
|
||||
initializeSegmentLabelTool: ({ tools }) => {
|
||||
const appConfig = extensionManager.appConfig;
|
||||
const segmentLabelConfig = appConfig.segmentation?.segmentLabel;
|
||||
|
||||
if (segmentLabelConfig?.enabledByDefault) {
|
||||
const activeTools = tools?.active ?? [];
|
||||
activeTools.push({
|
||||
toolName: toolNames.SegmentLabel,
|
||||
configuration: {
|
||||
hoverTimeout: segmentLabelConfig?.hoverTimeout ?? 1,
|
||||
color: segmentLabelConfig?.labelColor,
|
||||
background: segmentLabelConfig?.background,
|
||||
},
|
||||
});
|
||||
|
||||
tools.active = activeTools;
|
||||
return tools;
|
||||
}
|
||||
|
||||
const disabledTools = tools?.disabled ?? [];
|
||||
disabledTools.push({
|
||||
toolName: toolNames.SegmentLabel,
|
||||
configuration: {
|
||||
hoverTimeout: segmentLabelConfig?.hoverTimeout ?? 1,
|
||||
color: segmentLabelConfig?.labelColor,
|
||||
},
|
||||
});
|
||||
tools.disabled = disabledTools;
|
||||
return tools;
|
||||
},
|
||||
toggleUseCenterSegmentIndex: ({ toggle }) => {
|
||||
let labelmapTools = getLabelmapTools({ toolGroupService });
|
||||
labelmapTools = labelmapTools.filter(tool => !tool.toolName.includes('Eraser'));
|
||||
@ -2369,6 +2415,7 @@ function commandsModule({
|
||||
startRecordingForAnnotationGroup: actions.startRecordingForAnnotationGroup,
|
||||
endRecordingForAnnotationGroup: actions.endRecordingForAnnotationGroup,
|
||||
toggleSegmentLabel: actions.toggleSegmentLabel,
|
||||
initializeSegmentLabelTool: actions.initializeSegmentLabelTool,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -38,7 +38,6 @@ export default function getSegmentationPanelCustomization({ commandsManager, ser
|
||||
const [previewEdits, setPreviewEdits] = useState(false);
|
||||
const [toggleSegmentEnabled, setToggleSegmentEnabled] = useState(false);
|
||||
const [useCenterAsSegmentIndex, setUseCenterAsSegmentIndex] = useState(false);
|
||||
const [shouldShowLabelOnHover, setShouldShowLabelOnHover] = useState(false);
|
||||
const handlePreviewEditsChange = checked => {
|
||||
setPreviewEdits(checked);
|
||||
commandsManager.run('toggleSegmentPreviewEdit', { toggle: checked });
|
||||
@ -54,11 +53,6 @@ export default function getSegmentationPanelCustomization({ commandsManager, ser
|
||||
commandsManager.run('toggleUseCenterSegmentIndex', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleToggleShowLabelOnHover = checked => {
|
||||
setShouldShowLabelOnHover(checked);
|
||||
commandsManager.run('toggleSegmentLabel', { toggle: checked });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex flex-col gap-4 border-b border-b-[2px] border-black px-2 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
@ -84,14 +78,6 @@ export default function getSegmentationPanelCustomization({ commandsManager, ser
|
||||
/>
|
||||
<span className="text-base text-white">Hover on segment border to activate</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={shouldShowLabelOnHover}
|
||||
onCheckedChange={handleToggleShowLabelOnHover}
|
||||
/>
|
||||
<span className="text-base text-white">Show segment label on mouse hover</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@ -12,13 +12,17 @@ import miscCustomization from './customizations/miscCustomization';
|
||||
import captureViewportModalCustomization from './customizations/captureViewportModalCustomization';
|
||||
import viewportDownloadWarningCustomization from './customizations/viewportDownloadWarningCustomization';
|
||||
|
||||
function getCustomizationModule({ commandsManager, servicesManager }) {
|
||||
function getCustomizationModule({ commandsManager, servicesManager, extensionManager }) {
|
||||
return [
|
||||
{
|
||||
name: 'default',
|
||||
value: {
|
||||
...viewportOverlayCustomization,
|
||||
...getSegmentationPanelCustomization({ commandsManager, servicesManager }),
|
||||
...getSegmentationPanelCustomization({
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}),
|
||||
...layoutSelectorCustomization,
|
||||
...viewportToolsCustomization,
|
||||
...viewportClickCommandsCustomization,
|
||||
|
||||
@ -161,6 +161,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
'AdvancedMagnify',
|
||||
'UltrasoundDirectionalTool',
|
||||
'WindowLevelRegion',
|
||||
'SegmentLabelTool',
|
||||
]);
|
||||
|
||||
customizationService.setCustomizations({
|
||||
|
||||
@ -95,16 +95,12 @@ function initDefaultToolGroup(extensionManager, toolGroupService, commandsManage
|
||||
{
|
||||
toolName: toolNames.AdvancedMagnify,
|
||||
},
|
||||
{
|
||||
toolName: toolNames.SegmentLabel,
|
||||
configuration: {
|
||||
hoverTimeout: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
toolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
|
||||
const updatedTools = commandsManager.run('initializeSegmentLabelTool', { tools });
|
||||
|
||||
toolGroupService.createToolGroupAndAddTools(toolGroupId, updatedTools);
|
||||
}
|
||||
|
||||
function initSRToolGroup(extensionManager, toolGroupService) {
|
||||
|
||||
@ -654,6 +654,30 @@ const toolbarButtons: Button[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
// Section containers for the nested toolbox
|
||||
{
|
||||
id: 'SegmentationUtilities',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentLabelTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'tool-segment-label',
|
||||
label: 'Segment Label Display',
|
||||
tooltip: 'Click to show or hide segment labels when hovering with your mouse.',
|
||||
commands: { commandName: 'toggleSegmentLabel' },
|
||||
evaluate: [
|
||||
'evaluate.cornerstoneTool.toggle',
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentation',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// {
|
||||
// id: 'Undo',
|
||||
// uiType: 'ohif.toolButton',
|
||||
|
||||
@ -122,6 +122,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
'LabelmapSlicePropagation',
|
||||
'InterpolateLabelmap',
|
||||
'SegmentBidirectional',
|
||||
'SegmentLabelTool',
|
||||
]);
|
||||
toolbarService.updateSection('SegmentationTools', [
|
||||
'BrushTools',
|
||||
|
||||
@ -10,9 +10,10 @@ const colorsByOrientation = {
|
||||
coronal: 'rgb(0, 200, 0)',
|
||||
};
|
||||
|
||||
function createTools(utilityModule) {
|
||||
function createTools({ utilityModule, commandsManager }) {
|
||||
const { toolNames, Enums } = utilityModule.exports;
|
||||
return {
|
||||
|
||||
const tools = {
|
||||
active: [
|
||||
{ toolName: toolNames.WindowLevel, bindings: [{ mouseButton: Enums.MouseBindings.Primary }] },
|
||||
{ toolName: toolNames.Pan, bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }] },
|
||||
@ -114,24 +115,19 @@ function createTools(utilityModule) {
|
||||
|
||||
{ toolName: toolNames.UltrasoundDirectional },
|
||||
],
|
||||
disabled: [
|
||||
{ toolName: toolNames.ReferenceLines },
|
||||
{ toolName: toolNames.AdvancedMagnify },
|
||||
{
|
||||
toolName: toolNames.SegmentLabel,
|
||||
configuration: {
|
||||
hoverTimeout: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
disabled: [{ toolName: toolNames.ReferenceLines }, { toolName: toolNames.AdvancedMagnify }],
|
||||
};
|
||||
|
||||
const updatedTools = commandsManager.run('initializeSegmentLabelTool', { tools });
|
||||
|
||||
return updatedTools;
|
||||
}
|
||||
|
||||
function initDefaultToolGroup(extensionManager, toolGroupService, commandsManager, toolGroupId) {
|
||||
const utilityModule = extensionManager.getModuleEntry(
|
||||
'@ohif/extension-cornerstone.utilityModule.tools'
|
||||
);
|
||||
const tools = createTools(utilityModule);
|
||||
const tools = createTools({ commandsManager, utilityModule });
|
||||
toolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
|
||||
}
|
||||
|
||||
@ -141,7 +137,7 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
|
||||
);
|
||||
const servicesManager = extensionManager._servicesManager;
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const tools = createTools(utilityModule);
|
||||
const tools = createTools({ commandsManager, utilityModule });
|
||||
tools.disabled.push(
|
||||
{
|
||||
toolName: utilityModule.exports.toolNames.Crosshairs,
|
||||
|
||||
@ -739,6 +739,22 @@ const toolbarButtons: Button[] = [
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentLabelTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'tool-segment-label',
|
||||
label: 'Segment Label Display',
|
||||
tooltip: 'Click to show or hide segment labels when hovering with your mouse.',
|
||||
commands: { commandName: 'toggleSegmentLabel' },
|
||||
evaluate: [
|
||||
'evaluate.cornerstoneTool.toggle',
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentation',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default toolbarButtons;
|
||||
|
||||
@ -275,6 +275,14 @@ window.config = {
|
||||
// Could use services manager here to bring up a dialog/modal if needed.
|
||||
console.warn('test, navigate to https://ohif.org/');
|
||||
},
|
||||
// segmentation: {
|
||||
// segmentLabel: {
|
||||
// enabledByDefault: true,
|
||||
// labelColor: [255, 255, 0, 1], // must be an array
|
||||
// hoverTimeout: 1,
|
||||
// background: 'rgba(100, 100, 100, 0.5)', // can be any valid css color
|
||||
// },
|
||||
// },
|
||||
// whiteLabeling: {
|
||||
// createLogoComponentFn: function (React) {
|
||||
// return React.createElement(
|
||||
|
||||
@ -145,6 +145,7 @@ import {
|
||||
ToolContract,
|
||||
ToolExpand,
|
||||
ToolClickSegment,
|
||||
ToolSegmentLabel,
|
||||
} from './Sources/Tools';
|
||||
import ActionNewDialog from './Sources/ActionNewDialog';
|
||||
import NotificationInfo from './Sources/NotificationInfo';
|
||||
@ -730,6 +731,7 @@ export const Icons = {
|
||||
'tool-ultrasound-bidirectional': (props: IconProps) => ToolUltrasoundBidirectional(props),
|
||||
'tool-window-level': (props: IconProps) => ToolWindowLevel(props),
|
||||
'tool-window-region': (props: IconProps) => ToolWindowRegion(props),
|
||||
'tool-segment-label': (props: IconProps) => ToolSegmentLabel(props),
|
||||
'icon-tool-window-region': (props: IconProps) => ToolWindowRegion(props),
|
||||
'icon-tool-ultrasound-bidirectional': (props: IconProps) => ToolUltrasoundBidirectional(props),
|
||||
'icon-tool-cobb-angle': (props: IconProps) => ToolCobbAngle(props),
|
||||
|
||||
@ -2983,6 +2983,32 @@ export const ToolUltrasoundBidirectional = (props: IconProps) => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ToolSegmentLabel = (props: IconProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M2.6636 14.3578C2.58841 14.4105 2.53094 14.4847 2.49878 14.5707C2.46661 14.6567 2.46127 14.7504 2.48345 14.8395C2.50564 14.9286 2.5543 15.0088 2.62302 15.0697C2.69175 15.1305 2.77731 15.1691 2.86842 15.1804L7.42867 16.5722L8.82125 21.1316C8.83209 21.2229 8.87047 21.3088 8.93131 21.3779C8.99214 21.4469 9.07254 21.4958 9.16181 21.518C9.25109 21.5402 9.34502 21.5348 9.43111 21.5023C9.5172 21.4699 9.59139 21.412 9.6438 21.3364L20.4127 10.5683C20.7607 10.1801 20.9661 9.68484 20.9951 9.16423L21 3.82256C21 3.6044 20.9133 3.39518 20.7591 3.24092C20.6048 3.08666 20.3956 3 20.1774 3H14.8382C14.3176 3.02823 13.8223 3.2334 13.4341 3.58155L2.6636 14.3578Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16.8882 8.75787C15.9796 8.75787 15.2431 8.02133 15.2431 7.11276C15.2431 6.20419 15.9796 5.46765 16.8882 5.46765C17.7968 5.46765 18.5333 6.20419 18.5333 7.11276C18.5333 8.02133 17.7968 8.75787 16.8882 8.75787Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ToolWindowLevel = (props: IconProps) => (
|
||||
<svg
|
||||
width="28px"
|
||||
|
||||
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 207 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 140 KiB |