feat: overlapping segments (#4849)

This commit is contained in:
Pedro Köhler authored and GitHub committed 2025-03-18 17:07:13 -04:00
1 parent 5a6c2aeed9
commit 55d6393a3f
13 files changed
+232 -248

No files matched your search

+6 -6
View File
@@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^3.7.0",
"@cornerstonejs/dicom-image-loader": "^3.7.1",
"@ohif/core": "3.10.0-beta.128",
"@ohif/ui": "3.10.0-beta.128",
"dcmjs": "*",
@@ -51,11 +51,11 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.7.0",
"@cornerstonejs/core": "^3.7.0",
"@cornerstonejs/labelmap-interpolation": "^3.7.0",
"@cornerstonejs/polymorphic-segmentation": "^3.7.0",
"@cornerstonejs/tools": "^3.7.0",
"@cornerstonejs/adapters": "^3.7.1",
"@cornerstonejs/core": "^3.7.1",
"@cornerstonejs/labelmap-interpolation": "^3.7.1",
"@cornerstonejs/polymorphic-segmentation": "^3.7.1",
"@cornerstonejs/tools": "^3.7.1",
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
"@kitware/vtk.js": "32.12.0",
"html2canvas": "^1.4.1",
@@ -391,13 +391,13 @@ class SegmentationService extends PubSubService {
): Promise<string> {
const { type } = options;
let { segmentationId } = options;
const { labelmapBufferArray } = segDisplaySet;
const { labelMapImages } = segDisplaySet;
if (type !== LABELMAP) {
throw new Error('Only labelmap type is supported for SEG display sets right now');
}
if (!labelmapBufferArray) {
if (!labelMapImages || !labelMapImages.length) {
throw new Error('SEG reading failed');
}
@@ -414,16 +414,32 @@ class SegmentationService extends PubSubService {
}
const imageIds = images.map(image => image.imageId);
const derivedImages = labelMapImages?.flat();
const derivedImageIds = derivedImages.map(image => image.imageId);
const derivedSegmentationImages = await imageLoader.createAndCacheDerivedLabelmapImages(
imageIds as string[]
);
segDisplaySet.images = derivedSegmentationImages.map(image => ({
segDisplaySet.images = derivedImages.map(image => ({
...image,
...metaData.get('instance', image.referencedImageId),
}));
// 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 < derivedImages.length; i++) {
const voxelManager = derivedImages[i].voxelManager as csTypes.IVoxelManager<number>;
const scalarData = voxelManager.getScalarData();
voxelManager.setScalarData(scalarData);
// Check if this slice has any non-zero voxels and we haven't found one yet
if (!firstSegmentedSliceImageId && scalarData.some(value => value !== 0)) {
firstSegmentedSliceImageId = derivedImages[i].referencedImageId;
}
}
// assign the first non zero voxel image id to the segDisplaySet
segDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId;
const segmentsInfo = segDisplaySet.segMetadata.data;
const segments: { [segmentIndex: string]: cstTypes.Segment } = {};
@@ -481,30 +497,6 @@ class SegmentationService extends PubSubService {
addColorLUT(colorLUT, colorLUTIndex);
this._segmentationIdToColorLUTIndexMap.set(segmentationId, colorLUTIndex);
// now we need to chop the volume array into chunks and set the scalar data for each derived segmentation image
const volumeScalarData = new Uint8Array(labelmapBufferArray[0]);
// 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();
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,
@@ -515,7 +507,7 @@ class SegmentationService extends PubSubService {
representation: {
type: LABELMAP,
data: {
imageIds: derivedSegmentationImages.map(image => image.imageId),
imageIds: derivedImageIds,
referencedVolumeId: this._getVolumeIdForDisplaySet(referencedDisplaySet),
referencedImageIds: imageIds as string[],
},
@@ -1163,8 +1155,8 @@ class SegmentationService extends PubSubService {
highlightFunctionType = 'ease-in-out' // todo: make animation functions configurable from outside
): void {
const center = this._getSegmentCenter(segmentationId, segmentIndex);
if (!center) {
console.warn('No center found for segmentation', segmentationId, segmentIndex);
return;
}