Merge branch 'master' of github.com:OHIF/Viewers into IDC-2223
This commit is contained in:
commit
cffb7d6f2f
@ -39,6 +39,8 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icons .angle-double-down, .icons .angle-double-up {
|
.icons .angle-double-down,
|
||||||
|
.icons .angle-double-up {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
|
margin-right: -5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,16 +9,21 @@
|
|||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header {
|
.dcmrt-panel .dcmrt-panel-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dcmrt-panel .dcmrt-panel-header h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header .cog-icon {
|
.dcmrt-panel .dcmrt-panel-header .cog-icon {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
color: var(--default-color);
|
color: var(--default-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 20px;
|
min-height: 23px;
|
||||||
|
max-height: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
|
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const RTPanel = ({
|
|||||||
activeContexts = [],
|
activeContexts = [],
|
||||||
contexts = {},
|
contexts = {},
|
||||||
activeViewport,
|
activeViewport,
|
||||||
getActiveViewport
|
getActiveViewport,
|
||||||
}) => {
|
}) => {
|
||||||
const isVTK = () => activeContexts.includes(contexts.VTK);
|
const isVTK = () => activeContexts.includes(contexts.VTK);
|
||||||
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
||||||
@ -55,7 +55,7 @@ const RTPanel = ({
|
|||||||
referencedDisplaysets: [],
|
referencedDisplaysets: [],
|
||||||
sets: [],
|
sets: [],
|
||||||
selectedSet: null,
|
selectedSet: null,
|
||||||
isLocked: false
|
isLocked: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const [state, setState] = useState(DEFAULT_STATE);
|
const [state, setState] = useState(DEFAULT_STATE);
|
||||||
@ -84,12 +84,17 @@ const RTPanel = ({
|
|||||||
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
document.removeEventListener(
|
||||||
|
'extensiondicomrtrtloaded',
|
||||||
|
updateStructureSets
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
activeViewport.StudyInstanceUID
|
||||||
|
);
|
||||||
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||||
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
||||||
Modality: 'RTSTRUCT',
|
Modality: 'RTSTRUCT',
|
||||||
@ -235,7 +240,7 @@ const RTPanel = ({
|
|||||||
return (
|
return (
|
||||||
<div className="dcmrt-panel">
|
<div className="dcmrt-panel">
|
||||||
<div className="dcmrt-panel-header">
|
<div className="dcmrt-panel-header">
|
||||||
<h3>RT Structure Sets</h3>
|
{' '}
|
||||||
{!state.isLocked && (
|
{!state.isLocked && (
|
||||||
<Icon
|
<Icon
|
||||||
className="cog-icon"
|
className="cog-icon"
|
||||||
@ -245,71 +250,75 @@ const RTPanel = ({
|
|||||||
onClick={() => setShowSettings(true)}
|
onClick={() => setShowSettings(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<h3>RT Structure Sets</h3>
|
||||||
</div>
|
</div>
|
||||||
{!state.isLocked && !state.referencedDisplaysets.length && <LoadingIndicator expand height="70px" width="70px" />}
|
{!state.isLocked && !state.referencedDisplaysets.length && (
|
||||||
{state.sets && state.referencedDisplaysets.map(displaySet => {
|
<LoadingIndicator expand height="70px" width="70px" />
|
||||||
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
|
)}
|
||||||
|
{state.sets &&
|
||||||
|
state.referencedDisplaysets.map(displaySet => {
|
||||||
|
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
|
||||||
|
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
activeViewport.SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadedSet = sets.find(
|
const loadedSet = sets.find(
|
||||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!loadedSet) return null;
|
if (!loadedSet) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection
|
<PanelSection
|
||||||
key={SeriesInstanceUID}
|
key={SeriesInstanceUID}
|
||||||
title={metadata.StructureSetLabel}
|
title={metadata.StructureSetLabel}
|
||||||
loading={!isLoaded || !loadedSet}
|
loading={!isLoaded || !loadedSet}
|
||||||
visible={isLoaded && loadedSet && loadedSet.visible}
|
visible={isLoaded && loadedSet && loadedSet.visible}
|
||||||
hideVisibleButton={!isLoaded}
|
hideVisibleButton={!isLoaded}
|
||||||
expanded={
|
expanded={
|
||||||
isLoaded &&
|
isLoaded &&
|
||||||
loadedSet.SeriesInstanceUID ===
|
loadedSet.SeriesInstanceUID ===
|
||||||
state.selectedSet.SeriesInstanceUID
|
state.selectedSet.SeriesInstanceUID
|
||||||
}
|
|
||||||
onVisibilityChange={newVisibility => {
|
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
|
||||||
|
|
||||||
if (newVisibility) {
|
|
||||||
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
|
|
||||||
} else {
|
|
||||||
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
|
|
||||||
}
|
}
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
onVisibilityChange={newVisibility => {
|
||||||
activeViewport.SeriesInstanceUID
|
|
||||||
);
|
|
||||||
setState(state => ({ ...state, sets }));
|
|
||||||
refreshViewport();
|
|
||||||
}}
|
|
||||||
onExpandChange={async () => {
|
|
||||||
if (!isLoaded) {
|
|
||||||
await displaySet.load(activeViewport, studies);
|
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
|
|
||||||
|
if (newVisibility) {
|
||||||
|
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
|
||||||
|
} else {
|
||||||
|
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
|
||||||
|
}
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
activeViewport.SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
const selectedSet = sets.find(
|
setState(state => ({ ...state, sets }));
|
||||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
refreshViewport();
|
||||||
);
|
}}
|
||||||
setState(state => ({ ...state, selectedSet, sets }));
|
onExpandChange={async () => {
|
||||||
}
|
if (!isLoaded) {
|
||||||
}}
|
await displaySet.load(activeViewport, studies);
|
||||||
>
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
<ScrollableArea>
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
<TableList headless>
|
activeViewport.SeriesInstanceUID
|
||||||
{isLoaded &&
|
);
|
||||||
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
|
const selectedSet = sets.find(
|
||||||
</TableList>
|
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||||
</ScrollableArea>
|
);
|
||||||
</PanelSection>
|
setState(state => ({ ...state, selectedSet, sets }));
|
||||||
);
|
}
|
||||||
})}
|
}}
|
||||||
|
>
|
||||||
|
<ScrollableArea>
|
||||||
|
<TableList headless>
|
||||||
|
{isLoaded &&
|
||||||
|
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
|
||||||
|
</TableList>
|
||||||
|
</ScrollableArea>
|
||||||
|
</PanelSection>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const SegmentItem = ({
|
|||||||
onClick,
|
onClick,
|
||||||
itemClass,
|
itemClass,
|
||||||
color,
|
color,
|
||||||
visible = true,
|
visible,
|
||||||
onVisibilityChange,
|
onVisibilityChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [isVisible, setIsVisible] = useState(visible);
|
const [isVisible, setIsVisible] = useState(visible);
|
||||||
@ -33,6 +33,15 @@ const SegmentItem = ({
|
|||||||
setIsVisible(visible);
|
setIsVisible(visible);
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
|
|
||||||
|
const onClickHandler = () => onClick(index);
|
||||||
|
|
||||||
|
const onVisibilityChangeHandler = event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const newVisibility = !isVisible;
|
||||||
|
setIsVisible(newVisibility);
|
||||||
|
onVisibilityChange(newVisibility, index);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dcmseg-segment-item">
|
<div className="dcmseg-segment-item">
|
||||||
<TableListItem
|
<TableListItem
|
||||||
@ -42,7 +51,7 @@ const SegmentItem = ({
|
|||||||
itemClass={itemClass}
|
itemClass={itemClass}
|
||||||
itemMeta={<ColoredCircle color={color} />}
|
itemMeta={<ColoredCircle color={color} />}
|
||||||
itemMetaClass="segment-color-section"
|
itemMetaClass="segment-color-section"
|
||||||
onItemClick={onClick}
|
onItemClick={onClickHandler}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="segment-label" style={{ marginBottom: 4 }}>
|
<div className="segment-label" style={{ marginBottom: 4 }}>
|
||||||
@ -63,12 +72,7 @@ const SegmentItem = ({
|
|||||||
name={isVisible ? 'eye' : 'eye-closed'}
|
name={isVisible ? 'eye' : 'eye-closed'}
|
||||||
width="20px"
|
width="20px"
|
||||||
height="20px"
|
height="20px"
|
||||||
onClick={event => {
|
onClick={onVisibilityChangeHandler}
|
||||||
event.stopPropagation();
|
|
||||||
const newVisibility = !isVisible;
|
|
||||||
setIsVisible(newVisibility);
|
|
||||||
onVisibilityChange(newVisibility);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{false && <div className="segment-info">{'...'}</div>}
|
{false && <div className="segment-info">{'...'}</div>}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel.disabled {
|
.dcmseg-segmentation-panel.disabled {
|
||||||
@ -14,19 +15,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel h3 {
|
.dcmseg-segmentation-panel h3 {
|
||||||
padding-left: 20px;
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .segmentations {
|
.dcmseg-segmentation-panel .segmentations {
|
||||||
padding: 0 20px 20px 20px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .cog-icon {
|
.dcmseg-segmentation-panel .cog-icon {
|
||||||
margin: 20px 20px 0 0;
|
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
color: var(--default-color);
|
color: var(--default-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
min-height: 23px;
|
||||||
|
max-height: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .cog-icon:hover {
|
.dcmseg-segmentation-panel .cog-icon:hover {
|
||||||
@ -48,11 +49,11 @@
|
|||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .selector-form>div {
|
.dcmseg-segmentation-panel .selector-form > div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .selector-form>div:first-of-type {
|
.dcmseg-segmentation-panel .selector-form > div:first-of-type {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -64,3 +65,61 @@
|
|||||||
flex: unset;
|
flex: unset;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SegmentsSection {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--ui-gray-dark);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .count {
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--active-color);
|
||||||
|
margin-left: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: var(--ui-gray-darker);
|
||||||
|
text-align: center;
|
||||||
|
min-width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon,
|
||||||
|
.SegmentsSection .header .angle-double-down,
|
||||||
|
.SegmentsSection .header .angle-double-up {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon:hover,
|
||||||
|
.SegmentsSection .header .angle-double-down:hover,
|
||||||
|
.SegmentsSection .header .angle-double-up:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon.expanded {
|
||||||
|
color: var(--default-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .icons .angle-double-down,
|
||||||
|
.SegmentsSection .header .icons .angle-double-up {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { utils, log } from '@ohif/core';
|
import { utils, log } from '@ohif/core';
|
||||||
import { ScrollableArea, TableList, Icon } from '@ohif/ui';
|
import { ScrollableArea, TableList, Icon } from '@ohif/ui';
|
||||||
import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool';
|
import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool';
|
||||||
@ -67,29 +68,104 @@ const SegmentationPanel = ({
|
|||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
brushRadius: DEFAULT_BRUSH_RADIUS,
|
brushRadius: DEFAULT_BRUSH_RADIUS,
|
||||||
brushColor: 'rgba(221, 85, 85, 1)',
|
brushColor: 'rgba(221, 85, 85, 1)',
|
||||||
selectedSegment: null,
|
selectedSegment: 0,
|
||||||
selectedSegmentation: null,
|
selectedSegmentation: 0,
|
||||||
showSegmentationSettings: false,
|
showSettings: false,
|
||||||
brushStackState: null,
|
labelMapList: [],
|
||||||
labelmapList: [],
|
|
||||||
segmentList: [],
|
segmentList: [],
|
||||||
cachedSegmentsProperties: [],
|
segmentsHidden: [],
|
||||||
|
segmentNumbers: [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getActiveViewport = () => viewports[activeIndex];
|
||||||
|
|
||||||
|
const getFirstImageId = () => {
|
||||||
|
const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport();
|
||||||
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
return studyMetadata.getFirstImageId(displaySetInstanceUID);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getActiveLabelMaps3D = () => {
|
||||||
|
const { labelmaps3D, activeLabelmapIndex } = getBrushStackState();
|
||||||
|
return labelmaps3D[activeLabelmapIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getActiveLabelMapIndex = () => {
|
||||||
|
const { activeLabelmapIndex } = getBrushStackState();
|
||||||
|
return activeLabelmapIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getActiveSegmentIndex = () => {
|
||||||
|
const { activeSegmentIndex } = getActiveLabelMaps3D();
|
||||||
|
return activeSegmentIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getActiveLabelMaps2D = () => {
|
||||||
|
const { labelmaps2D } = getActiveLabelMaps3D();
|
||||||
|
return labelmaps2D;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentDisplaySet = () => {
|
||||||
|
const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport();
|
||||||
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
const allDisplaySets = studyMetadata.getDisplaySets();
|
||||||
|
return allDisplaySets.find(
|
||||||
|
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActiveSegment = segmentIndex => {
|
||||||
|
const activeSegmentIndex = getActiveSegmentIndex();
|
||||||
|
const activeViewport = getActiveViewport();
|
||||||
|
|
||||||
|
if (segmentIndex === activeSegmentIndex) {
|
||||||
|
log.info(`${activeSegmentIndex} is already the active segment`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelmap3D = getActiveLabelMaps3D();
|
||||||
|
labelmap3D.activeSegmentIndex = segmentIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the correct label map if clicked segment
|
||||||
|
* does not belong to the active labelmap
|
||||||
|
*/
|
||||||
|
const { StudyInstanceUID } = activeViewport;
|
||||||
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
const allDisplaySets = studyMetadata.getDisplaySets();
|
||||||
|
let selectedSegmentation;
|
||||||
|
let newLabelmapIndex = getActiveLabelMapIndex();
|
||||||
|
allDisplaySets.forEach(displaySet => {
|
||||||
|
if (displaySet.labelmapSegments) {
|
||||||
|
Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => {
|
||||||
|
if (
|
||||||
|
displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex)
|
||||||
|
) {
|
||||||
|
newLabelmapIndex = labelmapIndex;
|
||||||
|
selectedSegmentation =
|
||||||
|
displaySet.hasOverlapping === true
|
||||||
|
? displaySet.originLabelMapIndex
|
||||||
|
: labelmapIndex;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const brushStackState = getBrushStackState();
|
||||||
|
brushStackState.activeLabelmapIndex = newLabelmapIndex;
|
||||||
|
setState(state => ({ ...state, selectedSegmentation }));
|
||||||
|
|
||||||
|
refreshViewports();
|
||||||
|
|
||||||
|
return segmentIndex;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const labelmapModifiedHandler = event => {
|
const labelmapModifiedHandler = event => {
|
||||||
log.warn('Segmentation Panel: labelmap modified', event);
|
log.warn('Segmentation Panel: labelmap modified', event);
|
||||||
const module = cornerstoneTools.getModule('segmentation');
|
refreshSegmentations();
|
||||||
const activeViewport = viewports[activeIndex];
|
|
||||||
const studyMetadata = studyMetadataManager.get(
|
|
||||||
activeViewport.StudyInstanceUID
|
|
||||||
);
|
|
||||||
const firstImageId = studyMetadata.getFirstImageId(
|
|
||||||
activeViewport.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
updateState('brushStackState', module.state.series[firstImageId]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -140,50 +216,38 @@ const SegmentationPanel = ({
|
|||||||
};
|
};
|
||||||
}, [activeIndex, viewports]);
|
}, [activeIndex, viewports]);
|
||||||
|
|
||||||
const refreshSegmentations = useCallback(() => {
|
const refreshSegmentations = () => {
|
||||||
const module = cornerstoneTools.getModule('segmentation');
|
const activeViewport = getActiveViewport();
|
||||||
const activeViewport = viewports[activeIndex];
|
|
||||||
|
|
||||||
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
||||||
if (!isDisabled) {
|
if (!isDisabled) {
|
||||||
const studyMetadata = studyMetadataManager.get(
|
const brushStackState = getBrushStackState();
|
||||||
activeViewport.StudyInstanceUID
|
|
||||||
);
|
|
||||||
const firstImageId = studyMetadata.getFirstImageId(
|
|
||||||
activeViewport.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
const brushStackState = module.state.series[firstImageId];
|
|
||||||
if (brushStackState) {
|
if (brushStackState) {
|
||||||
const labelmap3D =
|
const labelMapList = getLabelMapList();
|
||||||
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
|
const {
|
||||||
const labelmapList = getLabelmapList(
|
items: segmentList,
|
||||||
brushStackState,
|
numbers: segmentNumbers,
|
||||||
firstImageId,
|
segmentsHidden,
|
||||||
activeViewport,
|
} = getSegmentList();
|
||||||
);
|
|
||||||
const segmentList = getSegmentList(
|
|
||||||
labelmap3D,
|
|
||||||
firstImageId,
|
|
||||||
brushStackState,
|
|
||||||
);
|
|
||||||
setState(state => ({
|
setState(state => ({
|
||||||
...state,
|
...state,
|
||||||
brushStackState,
|
segmentsHidden,
|
||||||
selectedSegmentation: brushStackState.activeLabelmapIndex,
|
segmentNumbers,
|
||||||
labelmapList,
|
labelMapList,
|
||||||
segmentList,
|
segmentList,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
setState(state => ({
|
setState(state => ({
|
||||||
...state,
|
...state,
|
||||||
labelmapList: [],
|
segmentsHidden: [],
|
||||||
|
segmentNumbers: [],
|
||||||
|
labelMapList: [],
|
||||||
segmentList: [],
|
segmentList: [],
|
||||||
isDisabled,
|
isDisabled,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [viewports, activeIndex, state.isLoading]);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshSegmentations();
|
refreshSegmentations();
|
||||||
@ -200,309 +264,285 @@ const SegmentationPanel = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setState(state => ({
|
setState(state => ({
|
||||||
...state,
|
...state,
|
||||||
showSegmentationSettings: state.showSegmentationSettings && !isOpen,
|
showSettings: state.showSettings && !isOpen,
|
||||||
}));
|
}));
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
const getLabelmapList = useCallback(
|
const getLabelMapList = () => {
|
||||||
(brushStackState, firstImageId, activeViewport) => {
|
const activeViewport = getActiveViewport();
|
||||||
/* Get list of SEG labelmaps specific to active viewport (reference series) */
|
|
||||||
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
|
|
||||||
activeViewport.StudyInstanceUID,
|
|
||||||
activeViewport.SeriesInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
return referencedSegDisplaysets.map((displaySet, index) => {
|
/* Get list of SEG labelmaps specific to active viewport (reference series) */
|
||||||
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet;
|
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
|
||||||
|
activeViewport.StudyInstanceUID,
|
||||||
|
activeViewport.SeriesInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
/* Map to display representation */
|
return referencedSegDisplaysets.map((displaySet, index) => {
|
||||||
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
const {
|
||||||
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
labelmapIndex,
|
||||||
const isActiveLabelmap =
|
originLabelMapIndex,
|
||||||
labelmapIndex === brushStackState.activeLabelmapIndex;
|
hasOverlapping,
|
||||||
const displayDate = date.format('ddd, MMM Do YYYY');
|
SeriesDate,
|
||||||
const displayTime = date.format('h:mm:ss a');
|
SeriesTime,
|
||||||
const displayDescription = displaySet.SeriesDescription;
|
} = displaySet;
|
||||||
|
|
||||||
return {
|
/* Map to display representation */
|
||||||
value: labelmapIndex,
|
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
||||||
title: displayDescription,
|
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
||||||
description: displayDate,
|
const displayDate = date.format('ddd, MMM Do YYYY');
|
||||||
onClick: async () => {
|
const displayDescription = displaySet.SeriesDescription;
|
||||||
const activatedLabelmapIndex = await setActiveLabelmap(
|
|
||||||
activeViewport,
|
|
||||||
studies,
|
|
||||||
displaySet,
|
|
||||||
() => onSelectedSegmentationChange(),
|
|
||||||
onDisplaySetLoadFailure
|
|
||||||
);
|
|
||||||
updateState('selectedSegmentation', activatedLabelmapIndex);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[studies]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getSegmentList = useCallback(
|
return {
|
||||||
(labelmap3D, firstImageId, brushStackState) => {
|
value: hasOverlapping === true ? originLabelMapIndex : labelmapIndex,
|
||||||
/*
|
title: displayDescription,
|
||||||
* Newly created segments have no `meta`
|
description: displayDate,
|
||||||
* So we instead build a list of all segment indexes in use
|
onClick: async () => {
|
||||||
* Then find any associated metadata
|
const activatedLabelmapIndex = await setActiveLabelmap(
|
||||||
*/
|
activeViewport,
|
||||||
const uniqueSegmentIndexes = labelmap3D.labelmaps2D
|
studies,
|
||||||
.reduce((acc, labelmap2D) => {
|
displaySet,
|
||||||
if (labelmap2D) {
|
onSelectedSegmentationChange,
|
||||||
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
|
onDisplaySetLoadFailure
|
||||||
|
|
||||||
for (let i = 0; i < segmentIndexes.length; i++) {
|
|
||||||
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
|
|
||||||
acc.push(segmentIndexes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [])
|
|
||||||
.sort((a, b) => a - b);
|
|
||||||
|
|
||||||
const module = cornerstoneTools.getModule('segmentation');
|
|
||||||
const colorLutTable =
|
|
||||||
module.state.colorLutTables[labelmap3D.colorLUTIndex];
|
|
||||||
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
|
|
||||||
|
|
||||||
const segmentList = [];
|
|
||||||
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
|
|
||||||
const segmentIndex = uniqueSegmentIndexes[i];
|
|
||||||
|
|
||||||
const color = colorLutTable[segmentIndex];
|
|
||||||
let segmentLabel = '(unlabeled)';
|
|
||||||
let segmentNumber = segmentIndex;
|
|
||||||
|
|
||||||
/* Meta */
|
|
||||||
if (hasLabelmapMeta) {
|
|
||||||
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
|
|
||||||
|
|
||||||
if (segmentMeta) {
|
|
||||||
segmentNumber = segmentMeta.SegmentNumber;
|
|
||||||
segmentLabel = segmentMeta.SegmentLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sameSegment = state.selectedSegment === segmentNumber;
|
|
||||||
const setCurrentSelectedSegment = () => {
|
|
||||||
const activeViewport = viewports[activeIndex];
|
|
||||||
|
|
||||||
_setActiveSegment(
|
|
||||||
firstImageId,
|
|
||||||
segmentNumber,
|
|
||||||
labelmap3D.activeSegmentIndex,
|
|
||||||
activeViewport
|
|
||||||
);
|
);
|
||||||
updateState('selectedSegment', sameSegment ? null : segmentNumber);
|
setState(state => ({
|
||||||
|
...state,
|
||||||
const validIndexList = [];
|
selectedSegmentation: activatedLabelmapIndex,
|
||||||
labelmap3D.labelmaps2D.forEach((labelMap2D, index) => {
|
}));
|
||||||
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
|
},
|
||||||
validIndexList.push(index);
|
};
|
||||||
}
|
});
|
||||||
});
|
|
||||||
const avg = array => array.reduce((a, b) => a + b) / array.length;
|
|
||||||
const average = avg(validIndexList);
|
|
||||||
const closest = validIndexList.reduce((prev, curr) => {
|
|
||||||
return Math.abs(curr - average) < Math.abs(prev - average)
|
|
||||||
? curr
|
|
||||||
: prev;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isCornerstone()) {
|
|
||||||
const enabledElements = cornerstone.getEnabledElements();
|
|
||||||
const element = enabledElements[activeIndex].element;
|
|
||||||
const toolState = cornerstoneTools.getToolState(element, 'stack');
|
|
||||||
|
|
||||||
if (!toolState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageIds = toolState.data[0].imageIds;
|
|
||||||
const imageId = imageIds[closest];
|
|
||||||
const frameIndex = imageIds.indexOf(imageId);
|
|
||||||
|
|
||||||
const SOPInstanceUID = cornerstone.metaData.get(
|
|
||||||
'SOPInstanceUID',
|
|
||||||
imageId
|
|
||||||
);
|
|
||||||
const StudyInstanceUID = cornerstone.metaData.get(
|
|
||||||
'StudyInstanceUID',
|
|
||||||
imageId
|
|
||||||
);
|
|
||||||
|
|
||||||
DICOMSegTempCrosshairsTool.addCrosshair(
|
|
||||||
element,
|
|
||||||
imageId,
|
|
||||||
segmentNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
onSegmentItemClick({
|
|
||||||
StudyInstanceUID,
|
|
||||||
SOPInstanceUID,
|
|
||||||
frameIndex,
|
|
||||||
activeViewportIndex: activeIndex,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVTK()) {
|
|
||||||
const activeViewport = viewports[activeIndex];
|
|
||||||
const studyMetadata = studyMetadataManager.get(
|
|
||||||
activeViewport.StudyInstanceUID
|
|
||||||
);
|
|
||||||
const allDisplaySets = studyMetadata.getDisplaySets();
|
|
||||||
const currentDisplaySet = allDisplaySets.find(
|
|
||||||
displaySet =>
|
|
||||||
displaySet.displaySetInstanceUID ===
|
|
||||||
activeViewport.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
const frame = labelmap3D.labelmaps2D[closest];
|
|
||||||
|
|
||||||
onSegmentItemClick({
|
|
||||||
studies,
|
|
||||||
StudyInstanceUID: currentDisplaySet.StudyInstanceUID,
|
|
||||||
displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID,
|
|
||||||
SOPClassUID: viewports[activeIndex].sopClassUIDs[0],
|
|
||||||
SOPInstanceUID: currentDisplaySet.SOPInstanceUID,
|
|
||||||
segmentNumber,
|
|
||||||
frameIndex: closest,
|
|
||||||
frame,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSegmentVisible = () => {
|
|
||||||
return !labelmap3D.segmentsHidden[segmentIndex];
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSegmentVisibility = () => {
|
|
||||||
const segmentsHidden = labelmap3D.segmentsHidden;
|
|
||||||
segmentsHidden[segmentIndex] = !segmentsHidden[segmentIndex];
|
|
||||||
return !segmentsHidden[segmentIndex];
|
|
||||||
};
|
|
||||||
|
|
||||||
const cachedSegmentProperties =
|
|
||||||
state.cachedSegmentsProperties[segmentNumber];
|
|
||||||
let visible = isSegmentVisible();
|
|
||||||
if (
|
|
||||||
cachedSegmentProperties &&
|
|
||||||
cachedSegmentProperties.visible !== visible
|
|
||||||
) {
|
|
||||||
toggleSegmentVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentList.push(
|
|
||||||
<SegmentItem
|
|
||||||
key={segmentNumber}
|
|
||||||
itemClass={`segment-item ${sameSegment && 'selected'}`}
|
|
||||||
onClick={() => setCurrentSelectedSegment()}
|
|
||||||
label={segmentLabel}
|
|
||||||
index={segmentNumber}
|
|
||||||
color={color}
|
|
||||||
visible={visible}
|
|
||||||
onVisibilityChange={newVisibility => {
|
|
||||||
if (isCornerstone()) {
|
|
||||||
const enabledElements = cornerstone.getEnabledElements();
|
|
||||||
const element = enabledElements[activeIndex].element;
|
|
||||||
module.setters.toggleSegmentVisibility(
|
|
||||||
element,
|
|
||||||
segmentNumber,
|
|
||||||
brushStackState.activeLabelmapIndex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVTK()) {
|
|
||||||
onSegmentVisibilityChange(segmentNumber, newVisibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCachedSegmentsProperties(segmentNumber, {
|
|
||||||
visible: newVisibility,
|
|
||||||
});
|
|
||||||
refreshViewports();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return segmentList;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Let's iterate over segmentIndexes ^ above
|
|
||||||
* If meta has a match, use it to show info
|
|
||||||
* If now, add "no-meta" class
|
|
||||||
* Show default name
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
[activeIndex, onSegmentItemClick, state.selectedSegment, state.isLoading]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateCachedSegmentsProperties = (segmentNumber, properties) => {
|
|
||||||
const segmentsProperties = state.cachedSegmentsProperties;
|
|
||||||
const segmentProperties = state.cachedSegmentsProperties[segmentNumber];
|
|
||||||
|
|
||||||
segmentsProperties[segmentNumber] = segmentProperties
|
|
||||||
? { ...segmentProperties, ...properties }
|
|
||||||
: properties;
|
|
||||||
|
|
||||||
updateState('cachedSegmentsProperties', segmentsProperties);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const setCurrentSelectedSegment = segmentNumber => {
|
||||||
updateState('cachedSegmentsProperties', []);
|
setActiveSegment(segmentNumber);
|
||||||
}, [activeContexts]);
|
|
||||||
|
|
||||||
const updateState = (field, value) => {
|
const sameSegment = state.selectedSegment === segmentNumber;
|
||||||
setState(state => ({ ...state, [field]: value }));
|
if (!sameSegment) {
|
||||||
|
setState(state => ({ ...state, selectedSegment: segmentNumber }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const validIndexList = [];
|
||||||
|
getActiveLabelMaps2D().forEach((labelMap2D, index) => {
|
||||||
|
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
|
||||||
|
validIndexList.push(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const avg = array => array.reduce((a, b) => a + b) / array.length;
|
||||||
|
const average = avg(validIndexList);
|
||||||
|
const closest = validIndexList.reduce((prev, curr) => {
|
||||||
|
return Math.abs(curr - average) < Math.abs(prev - average) ? curr : prev;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isCornerstone()) {
|
||||||
|
const element = getEnabledElement();
|
||||||
|
const toolState = cornerstoneTools.getToolState(element, 'stack');
|
||||||
|
|
||||||
|
if (!toolState) return;
|
||||||
|
|
||||||
|
const imageIds = toolState.data[0].imageIds;
|
||||||
|
const imageId = imageIds[closest];
|
||||||
|
const frameIndex = imageIds.indexOf(imageId);
|
||||||
|
|
||||||
|
const SOPInstanceUID = cornerstone.metaData.get(
|
||||||
|
'SOPInstanceUID',
|
||||||
|
imageId
|
||||||
|
);
|
||||||
|
const StudyInstanceUID = cornerstone.metaData.get(
|
||||||
|
'StudyInstanceUID',
|
||||||
|
imageId
|
||||||
|
);
|
||||||
|
|
||||||
|
DICOMSegTempCrosshairsTool.addCrosshair(element, imageId, segmentNumber);
|
||||||
|
|
||||||
|
onSegmentItemClick({
|
||||||
|
StudyInstanceUID,
|
||||||
|
SOPInstanceUID,
|
||||||
|
frameIndex,
|
||||||
|
activeViewportIndex: activeIndex,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVTK()) {
|
||||||
|
const labelMaps3D = getActiveLabelMaps3D();
|
||||||
|
const currentDisplaySet = getCurrentDisplaySet();
|
||||||
|
const frame = labelMaps3D.labelmaps2D[closest];
|
||||||
|
|
||||||
|
onSegmentItemClick({
|
||||||
|
studies,
|
||||||
|
StudyInstanceUID: currentDisplaySet.StudyInstanceUID,
|
||||||
|
displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID,
|
||||||
|
SOPClassUID: getActiveViewport().sopClassUIDs[0],
|
||||||
|
SOPInstanceUID: currentDisplaySet.SOPInstanceUID,
|
||||||
|
segmentNumber,
|
||||||
|
frameIndex: closest,
|
||||||
|
frame,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getColorLUTTable = () => {
|
||||||
|
const { state } = cornerstoneTools.getModule('segmentation');
|
||||||
|
const { colorLUTIndex } = getActiveLabelMaps3D();
|
||||||
|
return state.colorLutTables[colorLUTIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEnabledElement = () => {
|
||||||
|
const enabledElements = cornerstone.getEnabledElements();
|
||||||
|
return enabledElements[activeIndex].element;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSegmentVisibilityChangeHandler = (isVisible, segmentNumber) => {
|
||||||
|
/** Get all labelmaps with this segmentNumber (overlapping segments) */
|
||||||
|
const { labelmaps3D } = getBrushStackState();
|
||||||
|
const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => {
|
||||||
|
return labelmaps2D.some(({ segmentsOnLabelmap }) =>
|
||||||
|
segmentsOnLabelmap.includes(segmentNumber)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let segmentsHidden = [];
|
||||||
|
possibleLabelMaps3D.forEach(labelmap3D => {
|
||||||
|
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
|
||||||
|
|
||||||
|
segmentsHidden = [
|
||||||
|
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
setState(state => ({ ...state, segmentsHidden }));
|
||||||
|
|
||||||
|
refreshSegmentations();
|
||||||
|
refreshViewports();
|
||||||
|
|
||||||
|
if (isVTK()) {
|
||||||
|
onSegmentVisibilityChange(segmentNumber, isVisible);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSegmentList = () => {
|
||||||
|
/*
|
||||||
|
* Newly created segments have no `meta`
|
||||||
|
* So we instead build a list of all segment indexes in use
|
||||||
|
* Then find any associated metadata
|
||||||
|
*/
|
||||||
|
const uniqueSegmentIndexes = getActiveLabelMaps2D()
|
||||||
|
.reduce((acc, labelmap2D) => {
|
||||||
|
if (labelmap2D) {
|
||||||
|
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
|
||||||
|
|
||||||
|
for (let i = 0; i < segmentIndexes.length; i++) {
|
||||||
|
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
|
||||||
|
acc.push(segmentIndexes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
const labelmap3D = getActiveLabelMaps3D();
|
||||||
|
const colorLutTable = getColorLUTTable();
|
||||||
|
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
|
||||||
|
|
||||||
|
const segmentList = [];
|
||||||
|
const segmentNumbers = [];
|
||||||
|
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
|
||||||
|
const segmentIndex = uniqueSegmentIndexes[i];
|
||||||
|
|
||||||
|
const color = colorLutTable[segmentIndex];
|
||||||
|
let segmentLabel = '(unlabeled)';
|
||||||
|
let segmentNumber = segmentIndex;
|
||||||
|
|
||||||
|
/* Meta */
|
||||||
|
if (hasLabelmapMeta) {
|
||||||
|
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
|
||||||
|
if (segmentMeta) {
|
||||||
|
segmentNumber = segmentMeta.SegmentNumber;
|
||||||
|
segmentLabel = segmentMeta.SegmentLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sameSegment = state.selectedSegment === segmentNumber;
|
||||||
|
|
||||||
|
segmentNumbers.push(segmentNumber);
|
||||||
|
segmentList.push(
|
||||||
|
<SegmentItem
|
||||||
|
key={segmentNumber}
|
||||||
|
itemClass={`segment-item ${sameSegment && 'selected'}`}
|
||||||
|
onClick={setCurrentSelectedSegment}
|
||||||
|
label={segmentLabel}
|
||||||
|
index={segmentNumber}
|
||||||
|
color={color}
|
||||||
|
visible={!labelmap3D.segmentsHidden[segmentIndex]}
|
||||||
|
onVisibilityChange={onSegmentVisibilityChangeHandler}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: segmentList,
|
||||||
|
numbers: segmentNumbers,
|
||||||
|
segmentsHidden: labelmap3D.segmentsHidden,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let's iterate over segmentIndexes ^ above
|
||||||
|
* If meta has a match, use it to show info
|
||||||
|
* If now, add "no-meta" class
|
||||||
|
* Show default name
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBrushSize = evt => {
|
const updateBrushSize = evt => {
|
||||||
const updatedRadius = Number(evt.target.value);
|
const updatedRadius = Number(evt.target.value);
|
||||||
|
|
||||||
if (updatedRadius !== brushRadius) {
|
if (updatedRadius !== brushRadius) {
|
||||||
updateState('brushRadius', updatedRadius);
|
setState(state => ({ ...state, brushRadius: updatedRadius }));
|
||||||
const module = cornerstoneTools.getModule('segmentation');
|
const module = cornerstoneTools.getModule('segmentation');
|
||||||
module.setters.radius(updatedRadius);
|
module.setters.radius(updatedRadius);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const decrementSegment = event => {
|
const decrementSegment = event => {
|
||||||
|
const activeSegmentIndex = getActiveSegmentIndex();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (labelmap3D.activeSegmentIndex > 1) {
|
if (activeSegmentIndex > 1) {
|
||||||
labelmap3D.activeSegmentIndex--;
|
activeSegmentIndex--;
|
||||||
}
|
}
|
||||||
updateState('selectedSegment', labelmap3D.activeSegmentIndex);
|
setState(state => ({ ...state, selectedSegment: activeSegmentIndex }));
|
||||||
updateActiveSegmentColor();
|
updateActiveSegmentColor();
|
||||||
};
|
};
|
||||||
|
|
||||||
const incrementSegment = event => {
|
const incrementSegment = event => {
|
||||||
|
const activeSegmentIndex = getActiveSegmentIndex();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
labelmap3D.activeSegmentIndex++;
|
activeSegmentIndex++;
|
||||||
updateState('selectedSegment', labelmap3D.activeSegmentIndex);
|
setState(state => ({ ...state, selectedSegment: activeSegmentIndex }));
|
||||||
updateActiveSegmentColor();
|
updateActiveSegmentColor();
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateActiveSegmentColor = () => {
|
const updateActiveSegmentColor = () => {
|
||||||
const color = getActiveSegmentColor();
|
const color = getActiveSegmentColor();
|
||||||
updateState('brushColor', color);
|
setState(state => ({ ...state, brushColor: color }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBrushStackState = () => {
|
||||||
|
const module = cornerstoneTools.getModule('segmentation');
|
||||||
|
const firstImageId = getFirstImageId();
|
||||||
|
const brushStackState = module.state.series[firstImageId];
|
||||||
|
return brushStackState;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getActiveSegmentColor = () => {
|
const getActiveSegmentColor = () => {
|
||||||
if (!state.brushStackState) {
|
const brushStackState = getBrushStackState();
|
||||||
|
if (!brushStackState) {
|
||||||
return 'rgba(255, 255, 255, 1)';
|
return 'rgba(255, 255, 255, 1)';
|
||||||
}
|
}
|
||||||
|
|
||||||
const module = cornerstoneTools.getModule('segmentation');
|
const colorLutTable = getColorLUTTable();
|
||||||
const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex];
|
|
||||||
const color = colorLutTable[labelmap3D.activeSegmentIndex];
|
const color = colorLutTable[labelmap3D.activeSegmentIndex];
|
||||||
|
|
||||||
return `rgba(${color.join(',')})`;
|
return `rgba(${color.join(',')})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -520,65 +560,103 @@ const SegmentationPanel = ({
|
|||||||
refreshViewports();
|
refreshViewports();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onVisibilityChangeHandler = isVisible => {
|
||||||
|
let segmentsHidden = [];
|
||||||
|
state.segmentNumbers.forEach(segmentNumber => {
|
||||||
|
if (isVTK()) {
|
||||||
|
onSegmentVisibilityChange(segmentNumber, isVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all labelmaps with this segmentNumber (overlapping segments) */
|
||||||
|
const { labelmaps3D } = getBrushStackState();
|
||||||
|
const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => {
|
||||||
|
return labelmaps2D.some(({ segmentsOnLabelmap }) =>
|
||||||
|
segmentsOnLabelmap.includes(segmentNumber)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
possibleLabelMaps3D.forEach(labelmap3D => {
|
||||||
|
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
|
||||||
|
segmentsHidden = [
|
||||||
|
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setState(state => ({ ...state, segmentsHidden }));
|
||||||
|
|
||||||
|
refreshSegmentations();
|
||||||
|
refreshViewports();
|
||||||
|
};
|
||||||
|
|
||||||
const disabledConfigurationFields = [
|
const disabledConfigurationFields = [
|
||||||
'outlineAlpha',
|
'outlineAlpha',
|
||||||
'shouldRenderInactiveLabelmaps',
|
'shouldRenderInactiveLabelmaps',
|
||||||
];
|
];
|
||||||
if (state.showSegmentationSettings) {
|
|
||||||
|
const selectedSegmentationOption = state.labelMapList.find(
|
||||||
|
i => i.value === state.selectedSegmentation
|
||||||
|
);
|
||||||
|
|
||||||
|
if (state.showSettings) {
|
||||||
return (
|
return (
|
||||||
<SegmentationSettings
|
<SegmentationSettings
|
||||||
disabledFields={isVTK() ? disabledConfigurationFields : []}
|
disabledFields={isVTK() ? disabledConfigurationFields : []}
|
||||||
configuration={configuration}
|
configuration={configuration}
|
||||||
onBack={() => updateState('showSegmentationSettings', false)}
|
onBack={() => setState(state => ({ ...state, showSettings: false }))}
|
||||||
onChange={updateConfiguration}
|
onChange={updateConfiguration}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`dcmseg-segmentation-panel ${state.isDisabled &&
|
className={classNames('dcmseg-segmentation-panel', {
|
||||||
'disabled'}`}
|
disabled: state.isDisabled,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Icon
|
|
||||||
className="cog-icon"
|
|
||||||
name="cog"
|
|
||||||
width="25px"
|
|
||||||
height="25px"
|
|
||||||
onClick={() => updateState('showSegmentationSettings', true)}
|
|
||||||
/>
|
|
||||||
{false && (
|
{false && (
|
||||||
<form className="selector-form">
|
<form className="selector-form">
|
||||||
<BrushColorSelector
|
<BrushColorSelector
|
||||||
defaultColor={brushColor}
|
defaultColor={state.brushColor}
|
||||||
index={state.selectedSegment}
|
index={state.selectedSegment}
|
||||||
onNext={incrementSegment}
|
onNext={incrementSegment}
|
||||||
onPrev={decrementSegment}
|
onPrev={decrementSegment}
|
||||||
/>
|
/>
|
||||||
<BrushRadius
|
<BrushRadius
|
||||||
value={brushRadius}
|
value={state.brushRadius}
|
||||||
onChange={updateBrushSize}
|
onChange={updateBrushSize}
|
||||||
min={configuration.minRadius}
|
min={configuration.minRadius}
|
||||||
max={configuration.maxRadius}
|
max={configuration.maxRadius}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
<Icon
|
||||||
|
className="cog-icon"
|
||||||
|
name="cog"
|
||||||
|
width="25px"
|
||||||
|
height="25px"
|
||||||
|
onClick={() => setState(state => ({ ...state, showSettings: true }))}
|
||||||
|
/>
|
||||||
<h3>Segmentations</h3>
|
<h3>Segmentations</h3>
|
||||||
<div className="segmentations">
|
<div className="segmentations">
|
||||||
<SegmentationSelect
|
<SegmentationSelect
|
||||||
value={state.labelmapList.find(
|
value={selectedSegmentationOption}
|
||||||
i => i.value === state.selectedSegmentation
|
|
||||||
)}
|
|
||||||
formatOptionLabel={SegmentationItem}
|
formatOptionLabel={SegmentationItem}
|
||||||
options={state.labelmapList}
|
options={state.labelMapList}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ScrollableArea>
|
<SegmentsSection
|
||||||
<TableList
|
count={state.segmentList.length}
|
||||||
customHeader={<SegmentsHeader count={state.segmentList.length} />}
|
isVisible={
|
||||||
>
|
state.segmentsHidden.filter(isHidden => isHidden === true).length <
|
||||||
{state.segmentList}
|
state.segmentNumbers.length
|
||||||
</TableList>
|
}
|
||||||
</ScrollableArea>
|
onVisibilityChange={onVisibilityChangeHandler}
|
||||||
|
>
|
||||||
|
<ScrollableArea>
|
||||||
|
<TableList headless>{state.segmentList}</TableList>
|
||||||
|
</ScrollableArea>
|
||||||
|
</SegmentsSection>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -613,7 +691,7 @@ SegmentationPanel.propTypes = {
|
|||||||
SegmentationPanel.defaultProps = {};
|
SegmentationPanel.defaultProps = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns SEG Displaysets that reference the target series, sorted by dateTime
|
* Returns SEG DisplaySets that reference the target series, sorted by dateTime
|
||||||
*
|
*
|
||||||
* @param {string} StudyInstanceUID
|
* @param {string} StudyInstanceUID
|
||||||
* @param {string} SeriesInstanceUID
|
* @param {string} SeriesInstanceUID
|
||||||
@ -637,56 +715,48 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => {
|
|||||||
return referencedDisplaysets;
|
return referencedDisplaysets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
const SegmentsSection = ({
|
||||||
*
|
count,
|
||||||
* @param {*} firstImageId
|
children,
|
||||||
* @param {*} activeSegmentIndex
|
isVisible: defaultVisibility,
|
||||||
* @returns
|
onVisibilityChange,
|
||||||
*/
|
}) => {
|
||||||
const _setActiveSegment = (firstImageId, segmentIndex, activeSegmentIndex, activeViewport) => {
|
const [isVisible, setIsVisible] = useState(defaultVisibility);
|
||||||
if (segmentIndex === activeSegmentIndex) {
|
|
||||||
log.info(`${activeSegmentIndex} is already the active segment`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { state } = cornerstoneTools.getModule('segmentation');
|
const onVisibilityChangeHandler = () => {
|
||||||
const brushStackState = state.series[firstImageId];
|
const newVisibility = !isVisible;
|
||||||
|
setIsVisible(newVisibility);
|
||||||
|
onVisibilityChange(newVisibility);
|
||||||
|
};
|
||||||
|
|
||||||
const labelmap3D =
|
useEffect(() => {
|
||||||
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
|
setIsVisible(defaultVisibility);
|
||||||
labelmap3D.activeSegmentIndex = segmentIndex;
|
}, [defaultVisibility]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Activates the correct label map if clicked segment
|
|
||||||
* does not belong to the active labelmap
|
|
||||||
*/
|
|
||||||
const { StudyInstanceUID } = activeViewport;
|
|
||||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
|
||||||
const allDisplaySets = studyMetadata.getDisplaySets();
|
|
||||||
let newLabelmapIndex = brushStackState.activeLabelmapIndex;
|
|
||||||
allDisplaySets.forEach(displaySet => {
|
|
||||||
if (displaySet.labelmapSegments) {
|
|
||||||
Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => {
|
|
||||||
if (displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex)) {
|
|
||||||
newLabelmapIndex = labelmapIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
brushStackState.activeLabelmapIndex = newLabelmapIndex;
|
|
||||||
|
|
||||||
refreshViewports();
|
|
||||||
return segmentIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SegmentsHeader = ({ count }) => {
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<div className="SegmentsSection">
|
||||||
<div className="tableListHeaderTitle">Segments</div>
|
<div className="header">
|
||||||
<div className="numberOfItems">{count}</div>
|
<div>Segments</div>
|
||||||
</React.Fragment>
|
<div className="icons">
|
||||||
|
<Icon
|
||||||
|
className={`eye-icon ${isVisible && 'expanded'}`}
|
||||||
|
name={isVisible ? 'eye' : 'eye-closed'}
|
||||||
|
width="20px"
|
||||||
|
height="20px"
|
||||||
|
onClick={onVisibilityChangeHandler}
|
||||||
|
/>
|
||||||
|
<div className="count">{count}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
SegmentsSection.defaultProps = {
|
||||||
|
onVisibilityChange: noop,
|
||||||
|
};
|
||||||
|
|
||||||
export default SegmentationPanel;
|
export default SegmentationPanel;
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
id: 'OHIFDicomSegSopClassHandler',
|
id: 'OHIFDicomSegSopClassHandler',
|
||||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||||
sopClassUIDs,
|
sopClassUIDs,
|
||||||
getDisplaySetFromSeries: function (
|
getDisplaySetFromSeries: function(
|
||||||
series,
|
series,
|
||||||
study,
|
study,
|
||||||
dicomWebClient,
|
dicomWebClient,
|
||||||
@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
referencedDisplaySetUID: null, // Assigned when loaded.
|
referencedDisplaySetUID: null, // Assigned when loaded.
|
||||||
labelmapIndex: null, // Assigned when loaded.
|
labelmapIndex: null, // Assigned when loaded.
|
||||||
isLoaded: false,
|
isLoaded: false,
|
||||||
|
hasOverlapping: false,
|
||||||
SeriesDate,
|
SeriesDate,
|
||||||
SeriesTime,
|
SeriesTime,
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
@ -62,11 +63,11 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
metadata,
|
metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.getSourceDisplaySet = function (studies) {
|
segDisplaySet.getSourceDisplaySet = function(studies) {
|
||||||
return getSourceDisplaySet(studies, segDisplaySet);
|
return getSourceDisplaySet(studies, segDisplaySet);
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.load = async function (referencedDisplaySet, studies) {
|
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
||||||
segDisplaySet.isLoaded = true;
|
segDisplaySet.isLoaded = true;
|
||||||
const { StudyInstanceUID } = referencedDisplaySet;
|
const { StudyInstanceUID } = referencedDisplaySet;
|
||||||
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
||||||
@ -81,7 +82,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
referencedDisplaySet.SeriesInstanceUID
|
referencedDisplaySet.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let results;
|
let results;
|
||||||
try {
|
try {
|
||||||
results = _parseSeg(segArrayBuffer, imageIds);
|
results = _parseSeg(segArrayBuffer, imageIds);
|
||||||
@ -90,18 +91,47 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
segDisplaySet.loadError = true;
|
segDisplaySet.loadError = true;
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
const { labelmapBufferArray, segMetadata, segmentsOnFrame, segmentsOnFrameArray } = results;
|
const {
|
||||||
|
labelmapBufferArray,
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
segmentsOnFrameArray,
|
||||||
|
} = results;
|
||||||
|
|
||||||
|
let labelmapIndex;
|
||||||
if (labelmapBufferArray.length > 1) {
|
if (labelmapBufferArray.length > 1) {
|
||||||
|
let labelmapIndexes = [];
|
||||||
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
||||||
loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[i], segMetadata, segmentsOnFrame, segmentsOnFrameArray[i]);
|
labelmapIndexes.push(
|
||||||
|
await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[i],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
segmentsOnFrameArray[i]
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Since overlapping segmentations have virtual labelmaps,
|
||||||
|
* originLabelMapIndex is used in the panel to select the correct dropdown value.
|
||||||
|
*/
|
||||||
|
segDisplaySet.hasOverlapping = true;
|
||||||
|
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
|
||||||
|
labelmapIndex = labelmapIndexes[0];
|
||||||
|
console.warn('Overlapping segmentations!');
|
||||||
} else {
|
} else {
|
||||||
loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[0], segMetadata, segmentsOnFrame, []);
|
labelmapIndex = await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[0],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
[]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
segDisplaySet.labelmapIndex = 0;
|
resolve(labelmapIndex);
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,11 @@ export default async function loadSegmentation(
|
|||||||
* Cache each labelmap segments.
|
* Cache each labelmap segments.
|
||||||
* This data is used to determine the active label map when a given segment is activated/clicked.
|
* This data is used to determine the active label map when a given segment is activated/clicked.
|
||||||
*/
|
*/
|
||||||
segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length ? Array.from(new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))) : [];
|
segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length
|
||||||
|
? Array.from(
|
||||||
|
new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))
|
||||||
|
)
|
||||||
|
: [];
|
||||||
segDisplaySet.labelmapIndex = labelmapIndex;
|
segDisplaySet.labelmapIndex = labelmapIndex;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -57,6 +61,8 @@ export default async function loadSegmentation(
|
|||||||
console.log('Segmentation loaded.');
|
console.log('Segmentation loaded.');
|
||||||
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
|
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getNextLabelmapIndex(firstImageId) {
|
function _getNextLabelmapIndex(firstImageId) {
|
||||||
|
|||||||
@ -37,9 +37,14 @@ export default async function setActiveLabelmap(
|
|||||||
? brushStackState.activeLabelmapIndex
|
? brushStackState.activeLabelmapIndex
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (displaySet.labelmapIndex === activeLabelmapIndex) {
|
let labelmapIndex =
|
||||||
|
displaySet.hasOverlapping === true
|
||||||
|
? displaySet.originLabelMapIndex
|
||||||
|
: displaySet.labelmapIndex;
|
||||||
|
|
||||||
|
if (labelmapIndex === activeLabelmapIndex) {
|
||||||
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
|
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
|
||||||
return displaySet.labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!displaySet.isLoaded) {
|
if (!displaySet.isLoaded) {
|
||||||
@ -55,13 +60,18 @@ export default async function setActiveLabelmap(
|
|||||||
await loadPromise;
|
await loadPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labelmapIndex =
|
||||||
|
displaySet.hasOverlapping === true
|
||||||
|
? displaySet.originLabelMapIndex
|
||||||
|
: displaySet.labelmapIndex;
|
||||||
|
|
||||||
// This might have just been created, so need to use the non-cached value.
|
// This might have just been created, so need to use the non-cached value.
|
||||||
state = cornerstoneTools.getModule('segmentation').state;
|
state = cornerstoneTools.getModule('segmentation').state;
|
||||||
brushStackState = state.series[firstImageId];
|
brushStackState = state.series[firstImageId];
|
||||||
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex;
|
brushStackState.activeLabelmapIndex = labelmapIndex;
|
||||||
|
|
||||||
refreshViewports();
|
refreshViewports();
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
return displaySet.labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.1...@ohif/extension-vtk@1.10.2) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.0...@ohif/extension-vtk@1.10.1) (2020-12-10)
|
## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.0...@ohif/extension-vtk@1.10.1) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "1.10.1",
|
"version": "1.10.2",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -53,8 +53,8 @@
|
|||||||
"react-vtkjs-viewport": "^0.14.2"
|
"react-vtkjs-viewport": "^0.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.12.0",
|
"@ohif/core": "^2.12.1",
|
||||||
"@ohif/ui": "^1.8.0",
|
"@ohif/ui": "^1.8.1",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^3.1.0",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.0...@ohif/core@2.12.1) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10)
|
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/core",
|
"name": "@ohif/core",
|
||||||
"version": "2.12.0",
|
"version": "2.12.1",
|
||||||
"description": "Generic business logic for web-based medical imaging applications",
|
"description": "Generic business logic for web-based medical imaging applications",
|
||||||
"author": "OHIF Core Team",
|
"author": "OHIF Core Team",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.8.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.0...@ohif/ui@1.8.1) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.7.1...@ohif/ui@1.8.0) (2020-12-10)
|
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.7.1...@ohif/ui@1.8.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/ui",
|
"name": "@ohif/ui",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"description": "A set of React components for Medical Imaging Viewers",
|
"description": "A set of React components for Medical Imaging Viewers",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [4.8.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.1...@ohif/viewer@4.8.2) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.8.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.0...@ohif/viewer@4.8.1) (2020-12-10)
|
## [4.8.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.0...@ohif/viewer@4.8.1) (2020-12-10)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/viewer
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/viewer",
|
"name": "@ohif/viewer",
|
||||||
"version": "4.8.1",
|
"version": "4.8.2",
|
||||||
"description": "OHIF Viewer",
|
"description": "OHIF Viewer",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"@ohif/core": "^2.12.0",
|
"@ohif/core": "^2.12.1",
|
||||||
"@ohif/extension-cornerstone": "^2.10.0",
|
"@ohif/extension-cornerstone": "^2.10.0",
|
||||||
"@ohif/extension-debugging": "^0.1.6",
|
"@ohif/extension-debugging": "^0.1.6",
|
||||||
"@ohif/extension-dicom-html": "^1.3.0",
|
"@ohif/extension-dicom-html": "^1.3.0",
|
||||||
@ -58,9 +58,9 @@
|
|||||||
"@ohif/extension-dicom-rt": "^0.6.1",
|
"@ohif/extension-dicom-rt": "^0.6.1",
|
||||||
"@ohif/extension-dicom-segmentation": "^0.5.0",
|
"@ohif/extension-dicom-segmentation": "^0.5.0",
|
||||||
"@ohif/extension-lesion-tracker": "^0.2.1",
|
"@ohif/extension-lesion-tracker": "^0.2.1",
|
||||||
"@ohif/extension-vtk": "^1.10.1",
|
"@ohif/extension-vtk": "^1.10.2",
|
||||||
"@ohif/i18n": "^0.53.0",
|
"@ohif/i18n": "^0.53.0",
|
||||||
"@ohif/ui": "^1.8.0",
|
"@ohif/ui": "^1.8.1",
|
||||||
"@tanem/react-nprogress": "^1.1.25",
|
"@tanem/react-nprogress": "^1.1.25",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user