Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OHIF 3.9 Recovery 3 #4697

Merged
merged 10 commits into from
Jan 21, 2025
2 changes: 1 addition & 1 deletion addOns/externals/devDependencies/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"prettier-plugin-tailwindcss": "^0.5.4",
"react-refresh": "^0.14.2",
"semver": "^7.5.1",
"serve": "^14.2.0",
"serve": "^14.2.4",
"shader-loader": "^1.3.1",
"shx": "^0.3.3",
"source-map-loader": "^4.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ function OHIFCornerstonePMAPViewport(props: withAppTypes) {
viewportType: 'volume',
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,
presentationIds: viewportOptions.presentationIds,
}}
displaySetOptions={[{}, pmapDisplaySetOptions]}
></Component>
Expand Down
10 changes: 9 additions & 1 deletion extensions/cornerstone-dicom-rt/src/loadRTStruct.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
SeriesInstanceUID: instance.SeriesInstanceUID,
ROIContours: [],
visible: true,
ReferencedSOPInstanceUIDsSet: new Set(),
};

for (let i = 0; i < ROIContourSequence.length; i++) {
Expand All @@ -142,7 +143,8 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,

const contourPoints = [];
for (let c = 0; c < ContourSequenceArray.length; c++) {
const { ContourData, NumberOfContourPoints, ContourGeometricType } = ContourSequenceArray[c];
const { ContourData, NumberOfContourPoints, ContourGeometricType, ContourImageSequence } =
ContourSequenceArray[c];

let isSupported = false;

Expand Down Expand Up @@ -172,6 +174,12 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
type: ContourGeometricType,
isSupported,
});

if (ContourImageSequence?.ReferencedSOPInstanceUID) {
structureSet.ReferencedSOPInstanceUIDsSet.add(
ContourImageSequence?.ReferencedSOPInstanceUID
);
}
}

_setROIContourMetadata(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import promptHydrateRT from '../utils/promptHydrateRT';
import _getStatusComponent from './_getStatusComponent';

import createRTToolGroupAndAddTools from '../utils/initRTToolGroup';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';

const RT_TOOLGROUP_BASE_NAME = 'RTToolGroup';

Expand Down Expand Up @@ -43,8 +43,8 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
const [viewportGrid, viewportGridService] = useViewportGrid();

// States
const [isToolGroupCreated, setToolGroupCreated] = useState(false);
const [selectedSegment, setSelectedSegment] = useState(1);
const { setPositionPresentation } = usePositionPresentationStore();

// Hydration means that the RT is opened and segments are loaded into the
// segmentation panel, and RT is also rendered on any viewport that is in the
Expand Down Expand Up @@ -123,6 +123,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
toolGroupId: toolGroupId,
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,
presentationIds: viewportOptions.presentationIds,
}}
onElementEnabled={evt => {
props.onElementEnabled?.(evt);
Expand Down Expand Up @@ -185,6 +186,19 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
setRtIsLoading(false);
}

if (rtDisplaySet?.firstSegmentedSliceImageId && viewportOptions?.presentationIds) {
const { firstSegmentedSliceImageId } = rtDisplaySet;
const { presentationIds } = viewportOptions;

setPositionPresentation(presentationIds.positionPresentationId, {
viewportType: 'stack',
viewReference: {
referencedImageId: firstSegmentedSliceImageId,
},
viewPresentation: {},
});
}

if (evt.overlappingSegments) {
uiNotificationService.show({
title: 'Overlapping Segments',
Expand Down Expand Up @@ -247,8 +261,6 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {

toolGroup = createRTToolGroupAndAddTools(toolGroupService, customizationService, toolGroupId);

setToolGroupCreated(true);

return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentations(viewportId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { LoadingIndicatorTotalPercent, useViewportGrid, ViewportActionArrows } f
import createSEGToolGroupAndAddTools from '../utils/initSEGToolGroup';
import promptHydrateSEG from '../utils/promptHydrateSEG';
import _getStatusComponent from './_getStatusComponent';
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';

const SEG_TOOLGROUP_BASE_NAME = 'SEGToolGroup';
Expand Down Expand Up @@ -41,6 +42,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {

// States
const [selectedSegment, setSelectedSegment] = useState(1);
const { setPositionPresentation } = usePositionPresentationStore();

// Hydration means that the SEG is opened and segments are loaded into the
// segmentation panel, and SEG is also rendered on any viewport that is in the
Expand Down Expand Up @@ -198,6 +200,17 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
setSegIsLoading(false);
}

if (segDisplaySet?.firstSegmentedSliceImageId && viewportOptions?.presentationIds) {
const { firstSegmentedSliceImageId } = segDisplaySet;
const { presentationIds } = viewportOptions;

setPositionPresentation(presentationIds.positionPresentationId, {
viewReference: {
referencedImageId: firstSegmentedSliceImageId,
},
});
}
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,20 @@ import { Icon, Tooltip, useViewportGrid, ViewportActionArrows } from '@ohif/ui';
import hydrateStructuredReport from '../utils/hydrateStructuredReport';
import { useAppConfig } from '@state';
import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet';
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';

const MEASUREMENT_TRACKING_EXTENSION_ID = '@ohif/extension-measurement-tracking';

const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup';

function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
const {
commandsManager,
children,
dataSource,
displaySets,
viewportOptions,
servicesManager,
extensionManager,
} = props;
const { children, dataSource, displaySets, viewportOptions, servicesManager, extensionManager } =
props;

const [appConfig] = useAppConfig();

const { setPositionPresentation } = usePositionPresentationStore();

const {
displaySetService,
cornerstoneViewportService,
Expand Down Expand Up @@ -157,30 +153,13 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
setActiveImageDisplaySetData(referencedDisplaySet);
setReferencedDisplaySetMetadata(referencedDisplaySetMetadata);

if (
referencedDisplaySet.displaySetInstanceUID ===
activeImageDisplaySetData?.displaySetInstanceUID
) {
const { measurements } = srDisplaySet;

// it means that we have a new referenced display set, and the
// imageIdIndex will handle it by updating the viewport, but if they
// are the same we just need to use measurementService to jump to the
// new measurement
const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);

if (!csViewport) {
return;
}

const imageIds = csViewport.getImageIds();

const imageIdIndex = imageIds.indexOf(measurements[newMeasurementSelected].imageId);

if (imageIdIndex !== -1) {
csViewport.setImageIdIndex(imageIdIndex);
}
}
const { presentationIds } = viewportOptions;
const measurement = srDisplaySet.measurements[newMeasurementSelected];
setPositionPresentation(presentationIds.positionPresentationId, {
viewReference: {
referencedImageId: measurement.imageId,
},
});
});
},
[dataSource, srDisplaySet, activeImageDisplaySetData, viewportId]
Expand All @@ -202,10 +181,6 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
return null;
}

const initialImageIndex = activeImageDisplaySetData.images.findIndex(
image => image.imageId === measurement.imageId
);

return (
<Component
{...props}
Expand All @@ -230,7 +205,6 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
props.onElementEnabled?.(evt);
onElementEnabled(evt);
}}
initialImageIndex={initialImageIndex}
isJumpToMeasurementDisabled={true}
></Component>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const contentItemFormatters = {
return `${NumericValue} ${CodeValue}`;
},
PNAME: contentItem => {
const personName = contentItem.PersonName?.[0]?.Alphabetic;
const personName = contentItem.PersonName?.[0];
return personName ? utils.formatPN(personName) : undefined;
},
DATE: contentItem => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ function _getInstanceFromSegmentations(segmentations, { servicesManager }) {
}

function updateSegmentationsChartDisplaySet({ servicesManager }: withAppTypes): void {
debugger;
const { segmentationService } = servicesManager.services;
const segmentations = segmentationService.getSegmentations();
const { seriesMetadata, instance } =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { metaData, Enums, utilities } from '@cornerstonejs/core';
import type { ImageSliceData } from '@cornerstonejs/core/types';
import { ViewportOverlay } from '@ohif/ui';
import type { InstanceMetadata } from '@ohif/core/src/types';
import { formatPN, formatDICOMDate, formatDICOMTime, formatNumberPrecision } from './utils';
import { formatDICOMDate, formatDICOMTime, formatNumberPrecision } from './utils';
import { utils } from '@ohif/core';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';

import './CustomizableViewportOverlay.css';

const EPSILON = 1e-4;
const { formatPN } = utils;

type ViewportData = StackViewportData | VolumeViewportData;

Expand Down
25 changes: 0 additions & 25 deletions extensions/cornerstone/src/Viewport/Overlays/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,6 @@ export function formatDICOMTime(time, strFormat = 'HH:mm:ss') {
return moment(time, 'HH:mm:ss').format(strFormat);
}

/**
* Formats a patient name for display purposes
*
* @param {string} name
* @returns {string} formatted name.
*/
export function formatPN(name) {
if (!name) {
return '';
}
if (typeof name === 'object') {
name = name.Alphabetic;
if (!name) {
return '';
}
}

const cleaned = name
.split('^')
.filter(s => !!s)
.join(', ')
.trim();
return cleaned === ',' || cleaned === '' ? '' : cleaned;
}

/**
* Gets compression type
*
Expand Down
2 changes: 1 addition & 1 deletion extensions/cornerstone/src/hps/frameView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const frameView: Types.HangingProtocol.Protocol = {
},
{
attribute: 'isDisplaySetFromUrl',
weight: 10,
weight: 20,
constraint: {
equals: true,
},
Expand Down
8 changes: 8 additions & 0 deletions extensions/cornerstone/src/panels/PanelSegmentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ export default function PanelSegmentation({
const firstImageId = referencedImageIds[0];

const instance = metaData.get('instance', firstImageId);

if (!instance) {
return {
segmentationId,
isExportable: false,
};
}

const { SOPInstanceUID, SeriesInstanceUID } = instance;

const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,14 +478,24 @@ class SegmentationService extends PubSubService {
// We should parse the segmentation as separate slices to support overlapping segments.
// This parsing should occur in the CornerstoneJS library adapters.
// For now, we use the volume returned from the library and chop it here.
let firstSegmentedSliceImageId = null;
for (let i = 0; i < derivedSegmentationImages.length; i++) {
const voxelManager = derivedSegmentationImages[i]
.voxelManager as csTypes.IVoxelManager<number>;
const scalarData = voxelManager.getScalarData();
scalarData.set(volumeScalarData.slice(i * scalarData.length, (i + 1) * scalarData.length));
const sliceData = volumeScalarData.slice(i * scalarData.length, (i + 1) * scalarData.length);
scalarData.set(sliceData);
voxelManager.setScalarData(scalarData);

// Check if this slice has any non-zero voxels and we haven't found one yet
if (!firstSegmentedSliceImageId && sliceData.some(value => value !== 0)) {
firstSegmentedSliceImageId = derivedSegmentationImages[i].referencedImageId;
}
}

// assign the first non zero voxel image id to the segDisplaySet
segDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId;

this._broadcastEvent(EVENTS.SEGMENTATION_LOADING_COMPLETE, {
segmentationId,
segDisplaySet,
Expand Down Expand Up @@ -542,7 +552,19 @@ class SegmentationService extends PubSubService {
}

const rtDisplaySetUID = rtDisplaySet.displaySetInstanceUID;
const referencedDisplaySet = this.servicesManager.services.displaySetService.getDisplaySetByUID(
rtDisplaySet.referencedDisplaySetInstanceUID
);

const referencedImageIdsWithGeometry = Array.from(structureSet.ReferencedSOPInstanceUIDsSet);

const referencedImageIds = referencedDisplaySet.instances.map(image => image.imageId);
// find the first image id that contains a referenced SOP instance UID
const firstSegmentedSliceImageId = referencedImageIds.find(imageId =>
referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId))
);

rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId;
// Map ROI contours to RT Struct Data
const allRTStructData = mapROIContoursToRTStructData(structureSet, rtDisplaySetUID);

Expand Down
Loading
Loading