From 835f64d47a9994f6a25aaf3941a4974e215e7e7f Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Fri, 24 Apr 2020 07:25:05 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Seg=20jump=20to=20slice?= =?UTF-8?q?=20+=20show/hide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add segment part 2 (jump to frame and visibility toggle) * Cr updates * Filter displaysets with images * feat: 🎸 Seg jump to slice + show/hide Co-authored-by: James Petts --- extensions/cornerstone/package.json | 2 +- extensions/cornerstone/src/commandsModule.js | 2 +- extensions/dicom-html/package.json | 2 +- extensions/dicom-rt/package.json | 2 +- .../StructureSetItem/StructureSetItem.css | 15 +- .../StructureSetItem/StructureSetItem.js | 2 +- extensions/dicom-rt/src/index.js | 11 +- extensions/dicom-segmentation/package.json | 2 +- .../components/SegmentItem/SegmentItem.css | 35 +- .../src/components/SegmentItem/SegmentItem.js | 93 +++-- .../SegmentationPanel/SegmentationPanel.js | 334 +++++++++++------- .../src/components/SegmentationSelect.js | 4 + extensions/dicom-segmentation/src/index.js | 54 ++- .../dicom-segmentation/src/panelModule.js | 39 -- extensions/vtk/package.json | 2 +- platform/core/package.json | 2 +- platform/core/src/classes/MetadataProvider.js | 1 + platform/viewer/package.json | 2 +- yarn.lock | 8 +- 19 files changed, 380 insertions(+), 232 deletions(-) delete mode 100644 extensions/dicom-segmentation/src/panelModule.js diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index a8d426c843f..0c9e60dc0de 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -36,7 +36,7 @@ "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.12.5", "cornerstone-wado-image-loader": "^3.1.0", - "dcmjs": "^0.10.1", + "dcmjs": "^0.12.2", "dicom-parser": "^1.8.3", "hammerjs": "^2.0.8", "prop-types": "^15.6.2", diff --git a/extensions/cornerstone/src/commandsModule.js b/extensions/cornerstone/src/commandsModule.js index c5394362bef..99d93cc3040 100644 --- a/extensions/cornerstone/src/commandsModule.js +++ b/extensions/cornerstone/src/commandsModule.js @@ -267,7 +267,7 @@ const commandsModule = ({ servicesManager }) => { const study = studyMetadataManager.get(StudyInstanceUID); const displaySet = study.findDisplaySet(ds => { - return ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID) + return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID) }); displaySet.SOPInstanceUID = SOPInstanceUID; diff --git a/extensions/dicom-html/package.json b/extensions/dicom-html/package.json index c92de172298..f6477026cfe 100644 --- a/extensions/dicom-html/package.json +++ b/extensions/dicom-html/package.json @@ -29,7 +29,7 @@ }, "peerDependencies": { "@ohif/core": "^0.50.0", - "dcmjs": "^0.10.1", + "dcmjs": "^0.12.2", "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/extensions/dicom-rt/package.json b/extensions/dicom-rt/package.json index 5dd9c5b0f99..4dc687cc44c 100644 --- a/extensions/dicom-rt/package.json +++ b/extensions/dicom-rt/package.json @@ -31,7 +31,7 @@ "@ohif/core": "^0.50.0", "cornerstone-core": "^2.2.8", "cornerstone-tools": "^4.0.9", - "dcmjs": "^0.8.3", + "dcmjs": "^0.12.2", "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.css b/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.css index 36dd7ddfd97..53389b6313a 100644 --- a/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.css +++ b/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.css @@ -61,14 +61,17 @@ } .dcmrt-structure-set-item .item-label { - overflow: hidden; - max-width: calc(100% - 15px); /* calc(100% - 50px); 20px = eye icon */ - text-overflow: ellipsis; - overflow-wrap: normal; - white-space: nowrap; - display: flex; justify-content: space-between; + padding-right: 10px; +} + +.dcmrt-structure-set-item .item-label span { + overflow-wrap: normal; + white-space: nowrap; + overflow: hidden; + max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */ + text-overflow: ellipsis; } .dcmrt-structure-set-item .item-label .eye-icon { diff --git a/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.js b/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.js index 3df23cca704..1c28cb76494 100644 --- a/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.js +++ b/extensions/dicom-rt/src/components/StructureSetItem/StructureSetItem.js @@ -41,7 +41,7 @@ const StructureSetItem = ({ >
- {label} + {label} + ); }; @@ -51,8 +48,10 @@ export default { if (study && study.series) { for (let j = 0; j < study.series.length; j++) { const series = study.series[j]; - console.log(studies); - if (['RTSTRUCT', 'RTPLAN', 'RTDOSE'].includes(series.Modality)) { + if ( + /* Could be expanded to contain RTPLAN and RTDOSE information in the future */ + ['RTSTRUCT'].includes(series.Modality) + ) { return false; } } diff --git a/extensions/dicom-segmentation/package.json b/extensions/dicom-segmentation/package.json index ea01dabe7e2..51260ed44d6 100644 --- a/extensions/dicom-segmentation/package.json +++ b/extensions/dicom-segmentation/package.json @@ -31,7 +31,7 @@ "@ohif/core": "^0.50.0", "cornerstone-core": "^2.2.8", "cornerstone-tools": "4.12.5", - "dcmjs": "^0.6.1", + "dcmjs": "^0.12.2", "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.css b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.css index 738df87cef7..645e48c83fe 100644 --- a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.css +++ b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.css @@ -12,14 +12,6 @@ height: 20px; } -.dcmseg-segment-item .segment-label { - overflow: hidden; - max-width: calc(100% - 50px); - text-overflow: ellipsis; - overflow-wrap: normal; - white-space: nowrap; -} - .dcmseg-segment-item .segment-info { display: inline-block; margin-top: 9px; @@ -62,3 +54,30 @@ .dcmseg-segment-item .segment-actions .btnAction i { margin-right: 4px; } + +.dcmseg-segment-item .segment-label { + display: flex; + justify-content: space-between; + padding-right: 20px; +} + +.dcmseg-segment-item .segment-label span { + overflow-wrap: normal; + white-space: nowrap; + overflow: hidden; + max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */ + text-overflow: ellipsis; +} + +.dcmseg-segment-item .segment-label .eye-icon { + cursor: pointer; + color: var(--active-color); +} + +.dcmseg-segment-item .segment-label .eye-icon:hover { + color: var(--hover-color); +} + +.dcmseg-segment-item .segment-label .eye-icon.--visible { + color: var(--default-color); +} diff --git a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js index 0b61d5fe9cc..e3dd32c041c 100644 --- a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js +++ b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { TableListItem, Icon } from '@ohif/ui'; @@ -17,48 +17,63 @@ ColoredCircle.propTypes = { color: PropTypes.array.isRequired, }; -const SegmentItem = ({ index, label, onClick, itemClass, color }) => ( -
- } - itemMetaClass="segment-color-section" - onItemClick={onClick} - > -
-
- {label} -
- {false &&
{'...'}
} - {false && ( -
- - -
- )} -
-
-
-); +
+ )} +
+ + + ); +}; SegmentItem.propTypes = { index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, diff --git a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js index cc30afce1ce..728b50d4254 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js +++ b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import cornerstoneTools from 'cornerstone-tools'; import cornerstone from 'cornerstone-core'; @@ -28,13 +28,20 @@ const refreshViewport = () => { /** * SegmentationPanel component * - * @param {Object} props - * @param {Array} props.studies - * @param {Array} props.viewports - viewportSpecificData - * @param {number} props.activeIndex - activeViewportIndex + * @param {Array} props.studies - Studies data + * @param {Array} props.viewports - Viewports data (viewportSpecificData) + * @param {number} props.activeIndex - Active viewport index + * @param {boolean} props.isOpen - Boolean that indicates if the panel is expanded + * @param {Function} props.onSegItemClick - Segment click handler * @returns component */ -const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { +const SegmentationPanel = ({ + studies, + viewports, + activeIndex, + isOpen, + onSegItemClick, +}) => { /* * TODO: wrap get/set interactions with the cornerstoneTools * store with context to make these kind of things less blurry. @@ -43,13 +50,14 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { const DEFAULT_BRUSH_RADIUS = configuration.radius || 10; const [state, setState] = useState({ brushRadius: DEFAULT_BRUSH_RADIUS, - brushColor: 'rgba(221, 85, 85, 1)', /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */ + brushColor: + 'rgba(221, 85, 85, 1)' /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */, selectedSegment: null, selectedSegmentation: null, showSegSettings: false, brushStackState: null, labelmapList: [], - segmentList: [] + segmentList: [], }); useEffect(() => { @@ -57,7 +65,12 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { log.warn('Segmentation Panel: labelmap modified', event); const module = cornerstoneTools.getModule('segmentation'); const activeViewport = viewports[activeIndex]; - const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID); + const studyMetadata = studyMetadataManager.get( + activeViewport.StudyInstanceUID + ); + const firstImageId = studyMetadata.getFirstImageId( + activeViewport.displaySetInstanceUID + ); updateState('brushStackState', module.state.series[firstImageId]); }; @@ -86,20 +99,29 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { useEffect(() => { const module = cornerstoneTools.getModule('segmentation'); const activeViewport = viewports[activeIndex]; - const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID); - const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID); + const studyMetadata = studyMetadataManager.get( + activeViewport.StudyInstanceUID + ); + const firstImageId = studyMetadata.getFirstImageId( + activeViewport.displaySetInstanceUID + ); const brushStackState = module.state.series[firstImageId]; if (brushStackState) { - const labelmap3D = brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; - const labelmapList = getLabelmapList(brushStackState, firstImageId, activeViewport); - const segmentList = getSegmentList(labelmap3D, firstImageId); + const labelmap3D = + brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; + const labelmapList = getLabelmapList( + brushStackState, + firstImageId, + activeViewport + ); + const segmentList = getSegmentList(labelmap3D, firstImageId, brushStackState); setState(state => ({ ...state, brushStackState, selectedSegmentation: brushStackState.activeLabelmapIndex, labelmapList, - segmentList + segmentList, })); } else { setState(state => ({ @@ -108,126 +130,201 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { segmentList: [], })); } - }, [studies, viewports, activeIndex]); + }, [studies, viewports, activeIndex, getLabelmapList, getSegmentList]); /* Handle open/closed panel behaviour */ useEffect(() => { - updateState('showSegSettings', state.showSegSettings && !isOpen); + setState(state => ({ + ...state, + showSegSettings: state.showSegSettings && !isOpen, + })); }, [isOpen]); - const getLabelmapList = (brushStackState, firstImageId, activeViewport) => { - /* Get list of SEG labelmaps specific to active viewport (reference series) */ - const referencedSegDisplaysets = _getReferencedSegDisplaysets( - activeViewport.StudyInstanceUID, - activeViewport.SeriesInstanceUID - ); + const getLabelmapList = useCallback( + (brushStackState, firstImageId, activeViewport) => { + /* Get list of SEG labelmaps specific to active viewport (reference series) */ + const referencedSegDisplaysets = _getReferencedSegDisplaysets( + activeViewport.StudyInstanceUID, + activeViewport.SeriesInstanceUID + ); - return referencedSegDisplaysets.map((displaySet, index) => { - const { labelmapIndex, SeriesDate, SeriesTime } = displaySet; - - /* Map to display representation */ - const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; - const date = moment(dateStr, 'YYYYMMDD:HHmmss'); - const isActiveLabelmap = - labelmapIndex === brushStackState.activeLabelmapIndex; - const displayDate = date.format('ddd, MMM Do YYYY'); - const displayTime = date.format('h:mm:ss a'); - const displayDescription = displaySet.SeriesDescription; - - return { - value: labelmapIndex, - title: displayDescription, - description: displayDate, - onClick: async () => { - const activatedLabelmapIndex = await _setActiveLabelmap( - activeViewport, - studies, - displaySet, - firstImageId, - brushStackState.activeLabelmapIndex - ); - updateState('selectedSegmentation', activatedLabelmapIndex); - }, - }; - }); - }; + return referencedSegDisplaysets.map((displaySet, index) => { + const { labelmapIndex, SeriesDate, SeriesTime } = displaySet; + + /* Map to display representation */ + const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; + const date = moment(dateStr, 'YYYYMMDD:HHmmss'); + const isActiveLabelmap = + labelmapIndex === brushStackState.activeLabelmapIndex; + const displayDate = date.format('ddd, MMM Do YYYY'); + const displayTime = date.format('h:mm:ss a'); + const displayDescription = displaySet.SeriesDescription; + + return { + value: labelmapIndex, + title: displayDescription, + description: displayDate, + onClick: async () => { + const activatedLabelmapIndex = await _setActiveLabelmap( + activeViewport, + studies, + displaySet, + firstImageId, + brushStackState.activeLabelmapIndex + ); + updateState('selectedSegmentation', activatedLabelmapIndex); + }, + }; + }); + }, + [studies] + ); - const getSegmentList = (labelmap3D, firstImageId) => { - /* - * 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]); + const getSegmentList = useCallback( + (labelmap3D, firstImageId, brushStackState) => { + /* + * 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); + 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 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 segmentList = []; + for (let i = 0; i < uniqueSegmentIndexes.length; i++) { + const segmentIndex = uniqueSegmentIndexes[i]; - const color = colorLutTable[segmentIndex]; - let segmentLabel = '(unlabeled)'; - let segmentNumber = segmentIndex; + const color = colorLutTable[segmentIndex]; + let segmentLabel = '(unlabeled)'; + let segmentNumber = segmentIndex; - /* Meta */ - if (hasLabelmapMeta) { - const segmentMeta = labelmap3D.metadata.data[segmentIndex]; + /* Meta */ + if (hasLabelmapMeta) { + const segmentMeta = labelmap3D.metadata.data[segmentIndex]; - if (segmentMeta) { - segmentNumber = segmentMeta.SegmentNumber; - segmentLabel = segmentMeta.SegmentLabel; + if (segmentMeta) { + segmentNumber = segmentMeta.SegmentNumber; + segmentLabel = segmentMeta.SegmentLabel; + } + } + + const sameSegment = state.selectedSegment === segmentNumber; + const setCurrentSelectedSegment = () => { + _setActiveSegment( + firstImageId, + segmentNumber, + labelmap3D.activeSegmentIndex + ); + updateState('selectedSegment', sameSegment ? null : segmentNumber); + + const validIndexList = []; + 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; + }); + + 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 + ); + + onSegItemClick({ + StudyInstanceUID, + SOPInstanceUID, + frameIndex, + activeViewportIndex: activeIndex, + }); + }; + + const enabledElements = cornerstone.getEnabledElements(); + const enabledElementViewport = enabledElements[activeIndex]; + + let isVisible = true; + if (enabledElementViewport) { + const element = enabledElementViewport.element; + const module = cornerstoneTools.getModule('segmentation'); + isVisible = module.getters.isSegmentVisible( + element, + segmentNumber, + brushStackState.activeLabelmapIndex + ); } - } - const sameSegment = state.selectedSegment === segmentNumber; - const setCurrentSelectedSegment = () => { - _setActiveSegment( - firstImageId, - segmentNumber, - labelmap3D.activeSegmentIndex + segmentList.push( + { + const element = enabledElements[activeIndex].element; + module.setters.toggleSegmentVisibility( + element, + segmentNumber, + brushStackState.activeLabelmapIndex + ); + refreshViewport(); + }} + /> ); - updateState('selectedSegment', sameSegment ? null : segmentNumber); - }; - - segmentList.push( - - ); - } + } - return segmentList; + 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 - */ - }; + /* + * 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, onSegItemClick, state.selectedSegment] + ); const updateState = (field, value) => { setState(state => ({ ...state, [field]: value })); @@ -270,8 +367,7 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => { } const module = cornerstoneTools.getModule('segmentation'); - const colorLutTable = - module.state.colorLutTables[labelmap3D.colorLUTIndex]; + const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex]; const color = colorLutTable[labelmap3D.activeSegmentIndex]; return `rgba(${color.join(',')})`; @@ -329,7 +425,9 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
i.value === state.selectedSegmentation) || null + state.labelmapList.find( + i => i.value === state.selectedSegmentation + ) || null } formatOptionLabel={SegmentationItem} options={state.labelmapList} diff --git a/extensions/dicom-segmentation/src/components/SegmentationSelect.js b/extensions/dicom-segmentation/src/components/SegmentationSelect.js index 20162b210b6..52b2ca8f043 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationSelect.js +++ b/extensions/dicom-segmentation/src/components/SegmentationSelect.js @@ -17,6 +17,10 @@ const defaultColor = computedstyle.getPropertyValue('--default-color'); const uiGrayDark = computedstyle.getPropertyValue('--ui-gray-dark'); const segmentationSelectStyles = { + singleValue: (base, state) => ({ + ...base, + width: '100%' + }), control: (base, state) => ({ ...base, cursor: 'pointer', diff --git a/extensions/dicom-segmentation/src/index.js b/extensions/dicom-segmentation/src/index.js index d2143f3587b..22a146326f9 100644 --- a/extensions/dicom-segmentation/src/index.js +++ b/extensions/dicom-segmentation/src/index.js @@ -1,7 +1,9 @@ +import React from 'react'; + import init from './init.js'; import toolbarModule from './toolbarModule.js'; -import panelModule from './panelModule.js'; import sopClassHandlerModule from './OHIFDicomSegSopClassHandler.js'; +import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js'; export default { /** @@ -21,8 +23,54 @@ export default { getToolbarModule({ servicesManager }) { return toolbarModule; }, - getPanelModule({ servicesManager }) { - return panelModule; + getPanelModule({ commandsManager }) { + const ExtendedSegmentationPanel = props => { + const segItemClickHandler = segData => { + commandsManager.runCommand('jumpToImage', segData); + }; + + return ( + + ); + }; + + return { + menuOptions: [ + { + icon: 'list', + label: 'Segmentations', + target: 'segmentation-panel', + isDisabled: studies => { + if (!studies) { + return true; + } + + for (let i = 0; i < studies.length; i++) { + const study = studies[i]; + + if (study && study.series) { + for (let j = 0; j < study.series.length; j++) { + const series = study.series[j]; + + if (series.Modality === 'SEG') { + return false; + } + } + } + } + + return true; + }, + }, + ], + components: [ + { + id: 'segmentation-panel', + component: ExtendedSegmentationPanel, + }, + ], + defaultContext: ['VIEWER'], + }; }, getSopClassHandlerModule({ servicesManager }) { return sopClassHandlerModule; diff --git a/extensions/dicom-segmentation/src/panelModule.js b/extensions/dicom-segmentation/src/panelModule.js deleted file mode 100644 index a2f104ac80d..00000000000 --- a/extensions/dicom-segmentation/src/panelModule.js +++ /dev/null @@ -1,39 +0,0 @@ -import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js'; - -export default { - menuOptions: [ - { - icon: 'list', - label: 'Segmentations', - target: 'segmentation-panel', - isDisabled: studies => { - if (!studies) { - return true; - } - - for (let i = 0; i < studies.length; i++) { - const study = studies[i]; - - if (study && study.series) { - for (let j = 0; j < study.series.length; j++) { - const series = study.series[j]; - - if (series.Modality === 'SEG') { - return false; - } - } - } - } - - return true; - }, - }, - ], - components: [ - { - id: 'segmentation-panel', - component: SegmentationPanel, - }, - ], - defaultContext: ['VIEWER'], -}; diff --git a/extensions/vtk/package.json b/extensions/vtk/package.json index b6fca9fe28f..01793027c39 100644 --- a/extensions/vtk/package.json +++ b/extensions/vtk/package.json @@ -35,7 +35,7 @@ "cornerstone-core": "^2.2.8", "cornerstone-tools": "4.12.5", "cornerstone-wado-image-loader": "^3.1.0", - "dcmjs": "^0.10.1", + "dcmjs": "^0.12.2", "dicom-parser": "^1.8.3", "i18next": "^17.0.3", "i18next-browser-languagedetector": "^3.0.1", diff --git a/platform/core/package.json b/platform/core/package.json index 85390fe89da..ff62af67dc1 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -39,7 +39,7 @@ "dependencies": { "@babel/runtime": "^7.5.5", "ajv": "^6.10.0", - "dcmjs": "^0.12.0", + "dcmjs": "^0.12.2", "dicomweb-client": "^0.6.0", "immer": "6.0.2", "isomorphic-base64": "^1.0.2", diff --git a/platform/core/src/classes/MetadataProvider.js b/platform/core/src/classes/MetadataProvider.js index 30eef3fc7bc..ff360a3e3ec 100644 --- a/platform/core/src/classes/MetadataProvider.js +++ b/platform/core/src/classes/MetadataProvider.js @@ -400,6 +400,7 @@ class MetadataProvider { case WADO_IMAGE_LOADER_TAGS.GENERAL_IMAGE_MODULE: metadata = { + sopInstanceUid: instance.SOPInstanceUID, instanceNumber: instance.InstanceNumber, lossyImageCompression: instance.LossyImageCompression, lossyImageCompressionRatio: instance.LossyImageCompressionRatio, diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 8e2c1875564..30e6fc22f16 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -67,7 +67,7 @@ "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.12.5", "cornerstone-wado-image-loader": "^3.1.0", - "dcmjs": "^0.12.0", + "dcmjs": "^0.12.2", "dicom-parser": "^1.8.3", "dicomweb-client": "^0.4.4", "hammerjs": "^2.0.8", diff --git a/yarn.lock b/yarn.lock index 597c60d0117..dd55091327e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6534,10 +6534,10 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dcmjs@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.0.tgz#8b1634f9b66e452075295f5d1f2c5bf3dabdf03e" - integrity sha512-AZAnFMvzAxUv5+KWoZcxnTYflLKk0rNPeeFt2KrVAgaExFBFfDgJQh1lEgqdNlIP+XQDFrEEUmp726SQhgmVCg== +dcmjs@^0.12.2: + version "0.12.2" + resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.2.tgz#1c16e19c27ff43202abc7cd25c5ecd6d8bf7672e" + integrity sha512-B7cEVCfDi3mOLKoBYUMYy+6COjLZhtKaesNpq5XNA4IbEH4EJYVweJfiI9gsR5hypv9LAPg72xG/xrbqjy8TgQ== dependencies: "@babel/polyfill" "^7.8.3" "@babel/runtime" "^7.8.4"