Skip to content

Commit 95972e2

Browse files
authored
feat(adapters): overlapping segs with labelmap images (#1815)
1 parent 6fcdd39 commit 95972e2

File tree

20 files changed

+1026
-223
lines changed

20 files changed

+1026
-223
lines changed

packages/adapters/examples/segmentationStack/demo.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
/* eslint-disable */
22
const dicomMap = new Map();
33

4+
dicomMap.set(
5+
"1.3.6.1.4.1.14519.5.2.1.3671.4754.298665348758363466150039312520",
6+
{
7+
fetchDicom: {
8+
StudyInstanceUID:
9+
"1.3.6.1.4.1.14519.5.2.1.3671.4754.298665348758363466150039312520",
10+
SeriesInstanceUID:
11+
"1.3.6.1.4.1.14519.5.2.1.3671.4754.235188122843915982710753948536",
12+
wadoRsRoot: "https://d14fa38qiwhyfd.cloudfront.net/dicomweb"
13+
},
14+
fetchSegmentation: {
15+
StudyInstanceUID:
16+
"1.3.6.1.4.1.14519.5.2.1.3671.4754.298665348758363466150039312520",
17+
SeriesInstanceUID:
18+
"1.2.276.0.7230010.3.1.3.1426846371.15380.1513205183.303",
19+
SOPInstanceUID:
20+
"1.2.276.0.7230010.3.1.4.1426846371.15380.1513205183.304",
21+
wadoRsRoot: "https://d14fa38qiwhyfd.cloudfront.net/dicomweb"
22+
}
23+
}
24+
);
25+
426
dicomMap.set(
527
"1.3.6.1.4.1.14519.5.2.1.256467663913010332776401703474716742458",
628
{
@@ -22,6 +44,7 @@ dicomMap.set(
2244
}
2345
}
2446
);
47+
2548
dicomMap.set("1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046", {
2649
fetchDicom: {
2750
StudyInstanceUID:

packages/adapters/examples/segmentationStack/index.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,18 @@ console.warn(
2323
"Click on index.ts to open source code for this example --------->"
2424
);
2525

26-
const { Enums: csEnums, RenderingEngine, utilities: csUtilities } = cornerstone;
2726
const { segmentation: csToolsSegmentation } = cornerstoneTools;
2827
import {
2928
readDicom,
30-
loadDicom,
3129
readSegmentation,
3230
loadSegmentation,
3331
exportSegmentation,
3432
restart,
35-
getSegmentationIds,
3633
handleFileSelect,
3734
handleDragOver,
38-
createSegmentation
35+
createEmptySegmentation
3936
} from "../segmentationVolume/utils";
4037

41-
const referenceImageIds: string[] = [];
42-
const segImageIds: string[] = [];
4338
// ======== Set up page ======== //
4439

4540
setTitleAndDescription(
@@ -194,7 +189,7 @@ addButtonToToolbar({
194189
id: "CREATE_SEGMENTATION",
195190
title: "Create Empty SEG",
196191
onClick: async () => {
197-
await createSegmentation(state);
192+
await createEmptySegmentation(state);
198193
createSegmentationRepresentation();
199194
},
200195
container: group2

packages/adapters/examples/segmentationVolume/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
handleFileSelect,
3434
handleDragOver,
3535
restart,
36-
createSegmentation
36+
createEmptySegmentation
3737
} from "../segmentationVolume/utils";
3838
import addDropDownToToolbar from "../../../../utils/demo/helpers/addDropdownToToolbar";
3939

@@ -227,7 +227,7 @@ addButtonToToolbar({
227227
onClick: async () => {
228228
const segmentationId = cornerstone.utilities.uuidv4();
229229
state.segmentationId = segmentationId;
230-
await createSegmentation(state);
230+
await createEmptySegmentation(state);
231231
createSegmentationRepresentation();
232232
updateSegmentationDropdown();
233233
},

packages/adapters/examples/segmentationVolume/utils.ts

+22-20
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function readDicom(files: FileList, state) {
1818
}
1919
}
2020

21-
export async function createSegmentation(state) {
21+
export async function createEmptySegmentation(state) {
2222
const { referenceImageIds, segmentationId } = state;
2323

2424
const derivedSegmentationImages =
@@ -44,6 +44,25 @@ export async function createSegmentation(state) {
4444
]);
4545
}
4646

47+
export async function createSegmentation({ state, labelMapImages }) {
48+
const { segmentationId } = state;
49+
50+
const imageIds = labelMapImages?.flat().map(image => image.imageId);
51+
52+
csToolsSegmentation.addSegmentations([
53+
{
54+
segmentationId,
55+
representation: {
56+
type: cornerstoneTools.Enums.SegmentationRepresentations
57+
.Labelmap,
58+
data: {
59+
imageIds
60+
}
61+
}
62+
}
63+
]);
64+
}
65+
4766
export async function readSegmentation(file: File, state) {
4867
const imageId = wadouri.fileManager.add(file);
4968
const image = await imageLoader.loadAndCacheImage(imageId);
@@ -67,7 +86,7 @@ export async function readSegmentation(file: File, state) {
6786
export async function loadSegmentation(arrayBuffer: ArrayBuffer, state) {
6887
const { referenceImageIds, skipOverlapping, segmentationId } = state;
6988

70-
const generateToolState =
89+
const { labelMapImages } =
7190
await Cornerstone3D.Segmentation.createFromDICOMSegBuffer(
7291
referenceImageIds,
7392
arrayBuffer,
@@ -77,24 +96,7 @@ export async function loadSegmentation(arrayBuffer: ArrayBuffer, state) {
7796
}
7897
);
7998

80-
await createSegmentation(state);
81-
82-
const segmentation =
83-
csToolsSegmentation.state.getSegmentation(segmentationId);
84-
85-
const { imageIds } = segmentation.representationData.Labelmap;
86-
const derivedSegmentationImages = imageIds.map(imageId =>
87-
cache.getImage(imageId)
88-
);
89-
90-
const labelmapImagesNonOverlapping = generateToolState.labelMapImages[0];
91-
92-
for (let i = 0; i < derivedSegmentationImages.length; i++) {
93-
const voxelManager = derivedSegmentationImages[i].voxelManager;
94-
const scalarData = voxelManager.getScalarData();
95-
scalarData.set(labelmapImagesNonOverlapping[i].getPixelData());
96-
voxelManager.setScalarData(scalarData);
97-
}
99+
await createSegmentation({ state, labelMapImages });
98100
}
99101

100102
export async function exportSegmentation(state) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
const checkHasOverlapping = ({ largerArray, currentTestedArray, newArray }) =>
2+
largerArray.some((_, currentImageIndex) => {
3+
const originalImagePixelData = currentTestedArray[currentImageIndex];
4+
5+
const newImagePixelData = newArray[currentImageIndex];
6+
7+
if (!originalImagePixelData || !newImagePixelData) {
8+
return false;
9+
}
10+
11+
return originalImagePixelData.some(
12+
(originalPixel, currentPixelIndex) => {
13+
const newPixel = newImagePixelData[currentPixelIndex];
14+
return originalPixel && newPixel;
15+
}
16+
);
17+
});
18+
19+
export const compactMergeSegmentDataWithoutInformationLoss = ({
20+
arrayOfSegmentData,
21+
newSegmentData
22+
}) => {
23+
if (arrayOfSegmentData.length === 0) {
24+
arrayOfSegmentData.push(newSegmentData);
25+
return;
26+
}
27+
28+
for (
29+
let currentTestedIndex = 0;
30+
currentTestedIndex < arrayOfSegmentData.length;
31+
currentTestedIndex++
32+
) {
33+
const currentTestedArray = arrayOfSegmentData[currentTestedIndex];
34+
35+
const originalArrayIsLarger =
36+
currentTestedArray.length > newSegmentData.length;
37+
const largerArray = originalArrayIsLarger
38+
? currentTestedArray
39+
: newSegmentData;
40+
41+
const hasOverlapping = checkHasOverlapping({
42+
currentTestedArray,
43+
largerArray,
44+
newArray: newSegmentData
45+
});
46+
47+
if (hasOverlapping) {
48+
continue;
49+
}
50+
51+
largerArray.forEach((_, currentImageIndex) => {
52+
const originalImagePixelData =
53+
currentTestedArray[currentImageIndex];
54+
const newImagePixelData = newSegmentData[currentImageIndex];
55+
56+
if (
57+
(!originalImagePixelData && !newImagePixelData) ||
58+
!newImagePixelData
59+
) {
60+
return;
61+
}
62+
63+
if (!originalImagePixelData) {
64+
currentTestedArray[currentImageIndex] = newImagePixelData;
65+
return;
66+
}
67+
68+
const mergedPixelData = originalImagePixelData.map(
69+
(originalPixel, currentPixelIndex) => {
70+
const newPixel = newImagePixelData[currentPixelIndex];
71+
return originalPixel || newPixel;
72+
}
73+
);
74+
75+
currentTestedArray[currentImageIndex] = mergedPixelData;
76+
});
77+
return;
78+
}
79+
80+
arrayOfSegmentData.push(newSegmentData);
81+
};

packages/adapters/src/adapters/Cornerstone3D/Segmentation/generateToolState.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,13 @@ function generateToolState(
5353
function createFromDICOMSegBuffer(
5454
referencedImageIds,
5555
arrayBuffer,
56-
{ metadataProvider, skipOverlapping = false, tolerance = 1e-3 }
56+
{ metadataProvider, tolerance = 1e-3 }
5757
) {
5858
return createLabelmapsFromBufferInternal(
5959
referencedImageIds,
6060
arrayBuffer,
6161
metadataProvider,
6262
{
63-
skipOverlapping,
6463
tolerance
6564
}
6665
);

0 commit comments

Comments
 (0)