Skip to content

Commit

Permalink
Merge pull request #2 from dannyrb/vtkjsViewSegmentations-james
Browse files Browse the repository at this point in the history
Vtkjs view segmentations james
  • Loading branch information
JamesAPetts authored Dec 1, 2019
2 parents f115bfe + 08e0181 commit 8b573c4
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 98 deletions.
8 changes: 5 additions & 3 deletions extensions/dicom-seg/src/ExampleSidePanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@

.segment-color {
border-radius: 100%;
width: 12px;
height: 12px;
width: 24px;
height: 24px;
margin-right: 8px;
margin-top: 3px;
text-align: center;
line-height: 25px;
}

.segment-label {
flex-grow: 1;
line-height: 25px;
}
224 changes: 129 additions & 95 deletions extensions/dicom-seg/src/ExampleSidePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { utils } from '@ohif/core';
import PropTypes from 'prop-types';
import { Icon } from '@ohif/ui';
//
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import classnames from 'classnames';
import moment from 'moment';
import { Range } from '@ohif/ui';
import './ExampleSidePanel.css';

const { studyMetadataManager } = utils;
Expand All @@ -15,71 +15,70 @@ function ExampleSidePanel(props) {
const {
studies,
viewports, // viewportSpecificData
activeIndex, // activeViewportIndex
} = props;

// TODO: Needs to be activeViewport
const viewport = viewports[0];
const segModule = cornerstoneTools.getModule('segmentation');

// No viewports, nothing to render
// if (!viewport) {
// return null;
// }

const viewport = viewports[activeIndex];
const {
studyInstanceUid,
seriesInstanceUid,
displaySetInstanceUid,
} = viewport;
const [state, setState] = useState({
activeLabelmapIndex: undefined,
firstImageId: undefined,
});
// This technically defaults to 10 if undefined (bug?)
const [brushRadius, setBrushRadius] = useState(
segModule.getters.radius || 10
);
const [brushColor, setBrushColor] = useState('rgba(221, 85, 85, 1)');

// Find our activeLabelmapIndex and activeLabelmap
// TODO: Another useEffect that captures cornerstone events where these are modified
useEffect(() => {
const segmentationModule = cornerstoneTools.getModule('segmentation');
const studyMetadata = studyMetadataManager.get(studyInstanceUid);
const displaySet = studyMetadata.findDisplaySet(
displaySet => displaySet.displaySetInstanceUid === displaySetInstanceUid
);
const firstImageId = displaySet.images[0].getImageId();

if (segmentationModule.state.series[firstImageId]) {
const activeBrushStackState =
segmentationModule.state.series[firstImageId];
// meta
const studyMetadata = studyMetadataManager.get(studyInstanceUid);
const displaySet = studyMetadata.findDisplaySet(
displaySet => displaySet.displaySetInstanceUid === displaySetInstanceUid
);
const firstImageId = displaySet.images[0].getImageId();

setState({
activeLabelmapIndex: activeBrushStackState.activeLabelmapIndex,
firstImageId: firstImageId,
});
//activeLabelmap3D = activeBrushStackState.labelmaps3D[activeLabelmapIndex];
}
}, [displaySetInstanceUid, studyInstanceUid]);
// CORNERSTONE TOOLS
const [brushStackState, setBrushStackState] = useState(
segModule.state.series[firstImageId]
);
const [updateCount, setUpdateCount] = useState(0);

useEffect(() => {
const labelmapModifiedHandler = function(evt) {
console.log(evt);
console.warn('labelmap modified', evt);
const segmentationModule = cornerstoneTools.getModule('segmentation');
setBrushStackState(segmentationModule.state.series[firstImageId]);
setUpdateCount(updateCount + 1);
};
cornerstone.events.addEventListener(
'cornersontetoolslabelmapmodified',
labelmapModifiedHandler
);

return () => {
cornerstone.events.removeEventListener(
// These are specific to each element;
// Need to iterate cornerstone-tools tracked enabled elements?
// Then only care about the one tied to active viewport?
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
enabledElement.addEventListener(
'cornersontetoolslabelmapmodified',
labelmapModifiedHandler
)
);

return () => {
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
enabledElement.removeEventListener(
'cornersontetoolslabelmapmodified',
labelmapModifiedHandler
)
);
};
});

if (!brushStackState) {
return null;
}

const labelmap3D =
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];

// Get list of SEG labelmaps specific to active viewport (reference series)
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
studyInstanceUid,
Expand All @@ -95,12 +94,12 @@ function ExampleSidePanel(props) {

const labelmapList = referencedSegDisplaysets.map(ds => {
const { labelmapIndex, seriesDate, seriesTime } = ds;
const { activeLabelmapIndex } = state;

// Map to display representation
const dateStr = `${seriesDate}:${seriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const isActiveLabelmap = labelmapIndex === activeLabelmapIndex;
const isActiveLabelmap =
labelmapIndex === brushStackState.activeLabelmapIndex;
const displayDate = date.format('ddd, MMM Do YYYY');
const displayTime = date.format('h:mm:ss a');
const displayDescription = ds.seriesDescription;
Expand All @@ -118,17 +117,12 @@ function ExampleSidePanel(props) {
viewport,
studies,
ds,
state.firstImageId,
state.activeLabelmapIndex
firstImageId,
brushStackState.activeLabelmapIndex
);

if (typeof activatedLabelmapIndex == 'number') {
setState(
Object.assign({}, state, {
activeLabelmapIndex: activatedLabelmapIndex,
})
);
}
// TODO: Notify of change?
setUpdateCount(updateCount + 1);
}}
>
<Icon
Expand Down Expand Up @@ -165,66 +159,93 @@ function ExampleSidePanel(props) {

const segmentList = [];

if (state.activeLabelmapIndex !== undefined) {
const brushStackState = segModule.state.series[state.firstImageId];
const labelmap3D = brushStackState.labelmaps3D[state.activeLabelmapIndex];

if (labelmap3D) {
const colorLutTable =
segModule.state.colorLutTables[labelmap3D.colorLUTIndex];
const segmentsMeta = labelmap3D.metadata.data;
for (
let segmentIndex = 0;
segmentIndex < segmentsMeta.length;
segmentIndex++
) {
const segment = segmentsMeta[segmentIndex];

if (!segment) {
continue;
if (labelmap3D) {
// 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 = labelmap3D.labelmaps2D
.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 colorLutTable =
segModule.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;

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 color = colorLutTable[segmentIndex];

segmentList.push(
<li key={segment.SegmentNumber} className="segment-list-item">
<div
className="segment-color"
style={{ backgroundColor: `rgba(${color.join(',')})` }}
></div>
<div className="segment-label">{segment.SegmentLabel}</div>
</li>
);
}

segmentList.push(
<li key={segmentNumber} className="segment-list-item">
<div
className="segment-color"
style={{ backgroundColor: `rgba(${color.join(',')})` }}
>
{segmentNumber}
</div>
<div className="segment-label">{segmentLabel}</div>
</li>
);
}

// 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
}

function updateBrushSize(evt) {
setBrushRadius(evt.target.value);
segModule.setters.radius(evt.target.value);
const updatedRadius = Number(evt.target.value);

if (updatedRadius !== brushRadius) {
setBrushRadius(updatedRadius);
segModule.setters.radius(updatedRadius);
}
}

function incrementSegment(shouldIncrement = true) {
const brushStackState = segModule.state.series[state.firstImageId];
const labelmap3D = brushStackState.labelmaps3D[state.activeLabelmapIndex];

if (shouldIncrement) {
labelmap3D.activeSegmentIndex++;
} else {
labelmap3D.activeSegmentIndex--;
if (labelmap3D.activeSegmentIndex > 1) {
labelmap3D.activeSegmentIndex--;
}
}

const color = getActiveSegmentColor();
setBrushColor(color);
}

function getActiveSegmentColor() {
const brushStackState = segModule.state.series[state.firstImageId];

if (!brushStackState) {
return 'rgba(255, 255, 255, 1)';
}

const labelmap3D = brushStackState.labelmaps3D[state.activeLabelmapIndex];
const colorLutTable =
segModule.state.colorLutTables[labelmap3D.colorLUTIndex];
const color = colorLutTable[labelmap3D.activeSegmentIndex];
Expand All @@ -251,8 +272,12 @@ function ExampleSidePanel(props) {
height: '32px',
marginTop: '8px',
marginRight: '8px',
textAlign: 'center',
lineHeight: '32px',
}}
></div>
>
{labelmap3D.activeSegmentIndex}
</div>
<div
style={{
display: 'flex',
Expand Down Expand Up @@ -282,14 +307,23 @@ function ExampleSidePanel(props) {
</div>

<div>
<label>Brush Radius</label>
<input
type="range"
min="1"
max="50"
<label
htmlFor="brush-radius"
style={{
display: 'block',
marginBottom: '8px',
}}
>
Brush Radius
</label>
<Range
value={brushRadius}
min={1}
max={50}
step={1}
onChange={updateBrushSize}
></input>
id="brush-radius"
/>
</div>
</form>

Expand Down
24 changes: 24 additions & 0 deletions platform/viewer/src/components/SidePanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
flex: 1;
height: 100%;
transition: var(--sidepanel-transition);
overflow-y: auto;
}

.from-left {
Expand Down Expand Up @@ -39,6 +40,29 @@
width: 100%;
}

.db-button {
color: #333;
background-color: #fff;
padding: 4px 12px;
line-height: 1.4;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
user-select: none;
border: 1px solid #ccc;
border-radius: 4px;
transition: all 0.15s ease;
}

.db-button:hover,
.db-button:active,
.db-button.active {
background-color: var(--active-color);
border-color: var(--ui-border-color-active);
color: #fff;
}

@media only screen and (max-width: 768px) {
/* Account for "Investigational Use" banner height */
.FlexboxLayout {
Expand Down

0 comments on commit 8b573c4

Please sign in to comment.