Skip to content

Commit

Permalink
fix(seg): compatibility issue for SEG sourcing MG images (#351)
Browse files Browse the repository at this point in the history
* added unit test for SEG sourcing MG images

* fix incompatibility issue for SEG sourcing MG images
  • Loading branch information
kimtaejin922 authored Jan 24, 2024
1 parent 4252063 commit e84a36c
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 64 deletions.
164 changes: 101 additions & 63 deletions src/adapters/Cornerstone/Segmentation_4X.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,23 +295,32 @@ function generateToolState(

const SeriesInstanceUID = generalSeriesModule.seriesInstanceUID;

if (!imagePlaneModule) {
console.warn("Insufficient metadata, imagePlaneModule missing.");
}
let ImageOrientationPatient;
let validOrientations;

const ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines)
? [...imagePlaneModule.rowCosines, ...imagePlaneModule.columnCosines]
: [
imagePlaneModule.rowCosines.x,
imagePlaneModule.rowCosines.y,
imagePlaneModule.rowCosines.z,
imagePlaneModule.columnCosines.x,
imagePlaneModule.columnCosines.y,
imagePlaneModule.columnCosines.z
];
const hasCoordinateSystem = "FrameOfReferenceUID" in multiframe;
if (hasCoordinateSystem) {
if (!imagePlaneModule) {
console.warn("Insufficient metadata, imagePlaneModule missing.");
}

// Get IOP from ref series, compute supported orientations:
const validOrientations = getValidOrientations(ImageOrientationPatient);
ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines)
? [
...imagePlaneModule.rowCosines,
...imagePlaneModule.columnCosines
]
: [
imagePlaneModule.rowCosines.x,
imagePlaneModule.rowCosines.y,
imagePlaneModule.rowCosines.z,
imagePlaneModule.columnCosines.x,
imagePlaneModule.columnCosines.y,
imagePlaneModule.columnCosines.z
];

// Get IOP from ref series, compute supported orientations:
validOrientations = getValidOrientations(ImageOrientationPatient);
}

const sliceLength = multiframe.Columns * multiframe.Rows;
const segMetadata = getSegmentMetadata(multiframe, SeriesInstanceUID);
Expand Down Expand Up @@ -344,12 +353,17 @@ function generateToolState(
}
}

const orientation = checkOrientation(
multiframe,
validOrientations,
[imagePlaneModule.rows, imagePlaneModule.columns, imageIds.length],
tolerance
);
let orientation;
if (hasCoordinateSystem) {
orientation = checkOrientation(
multiframe,
validOrientations,
[imagePlaneModule.rows, imagePlaneModule.columns, imageIds.length],
tolerance
);
} else {
orientation = "Planar";
}

let overlapping = false;
if (!skipOverlapping) {
Expand Down Expand Up @@ -636,9 +650,7 @@ function findReferenceSourceImageId(
}

let frameSourceImageSequence = undefined;
if (SourceImageSequence && SourceImageSequence.length !== 0) {
frameSourceImageSequence = SourceImageSequence[frameSegment];
} else if (PerFrameFunctionalGroup.DerivationImageSequence) {
if (PerFrameFunctionalGroup.DerivationImageSequence) {
let DerivationImageSequence =
PerFrameFunctionalGroup.DerivationImageSequence;
if (Array.isArray(DerivationImageSequence)) {
Expand All @@ -660,6 +672,8 @@ function findReferenceSourceImageId(
}
}
}
} else if (SourceImageSequence && SourceImageSequence.length !== 0) {
frameSourceImageSequence = SourceImageSequence[frameSegment];
}

if (frameSourceImageSequence) {
Expand Down Expand Up @@ -781,11 +795,6 @@ function checkSEGsOverlapping(
const PerFrameFunctionalGroups =
PerFrameFunctionalGroupsSequence[frameSegment];

const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

const pixelDataI2D = ndarray(
new Uint8Array(
pixelData.buffer,
Expand All @@ -795,12 +804,24 @@ function checkSEGsOverlapping(
[Rows, Columns]
);

const alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
let alignedPixelDataI;

const hasCoordinateSystem = "FrameOfReferenceUID" in multiframe;
if (hasCoordinateSystem) {
const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
} else {
alignedPixelDataI = pixelDataI2D;
}

if (!alignedPixelDataI) {
console.warn(
Expand Down Expand Up @@ -894,22 +915,29 @@ function insertOverlappingPixelDataPlanar(
continue;
}

const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

const pixelDataI2D = ndarray(
new Uint8Array(pixelData.buffer, i * sliceLength, sliceLength),
[Rows, Columns]
);

const alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
let alignedPixelDataI;

const hasCoordinateSystem = "FrameOfReferenceUID" in multiframe;
if (hasCoordinateSystem) {
const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
} else {
alignedPixelDataI = pixelDataI2D;
}

if (!alignedPixelDataI) {
throw new Error(
Expand All @@ -936,12 +964,12 @@ function insertOverlappingPixelDataPlanar(
}

const sourceImageMetadata = metadataProvider.get(
"instance",
"imagePixelModule",
imageId
);
if (
Rows !== sourceImageMetadata.Rows ||
Columns !== sourceImageMetadata.Columns
Rows !== sourceImageMetadata.rows ||
Columns !== sourceImageMetadata.columns
) {
throw new Error(
"Individual SEG frames have different geometry dimensions (Rows and Columns) " +
Expand Down Expand Up @@ -1060,22 +1088,29 @@ function insertPixelDataPlanar(
) {
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];

const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

const pixelDataI2D = ndarray(
new Uint8Array(pixelData.buffer, i * sliceLength, sliceLength),
[Rows, Columns]
);

const alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
let alignedPixelDataI;

const hasCoordinateSystem = "FrameOfReferenceUID" in multiframe;
if (hasCoordinateSystem) {
const ImageOrientationPatientI =
sharedImageOrientationPatient ||
PerFrameFunctionalGroups.PlaneOrientationSequence
.ImageOrientationPatient;

alignedPixelDataI = alignPixelDataWithSourceData(
pixelDataI2D,
ImageOrientationPatientI,
validOrientations,
tolerance
);
} else {
alignedPixelDataI = pixelDataI2D;
}

if (!alignedPixelDataI) {
throw new Error(
Expand Down Expand Up @@ -1106,10 +1141,13 @@ function insertPixelDataPlanar(
continue;
}

const sourceImageMetadata = metadataProvider.get("instance", imageId);
const sourceImageMetadata = metadataProvider.get(
"imagePixelModule",
imageId
);
if (
Rows !== sourceImageMetadata.Rows ||
Columns !== sourceImageMetadata.Columns
Rows !== sourceImageMetadata.rows ||
Columns !== sourceImageMetadata.columns
) {
throw new Error(
"Individual SEG frames have different geometry dimensions (Rows and Columns) " +
Expand Down
46 changes: 45 additions & 1 deletion test/adapters.test.js
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
it("No tests yet", () => {});
import "regenerator-runtime/runtime.js";
import fs from "fs";
import Segmentation_4X from "../src/adapters/Cornerstone/Segmentation_4X"
import { getTestDataset } from "./testUtils.js";

const mockMetadataProvider = {
get: (type, imageId) => {
// Unlike CT, is missing coordinate system such as frameOfReferenceUID or rowCosines attributes
if (imageId === "mg://1") {
return {
seriesInstanceUID: "1.2.840.113681.167838594.1562401072.4432.2070.71100000",
rows: 3328,
columns: 2560,
pixelSpacing: [0.065238, 0.065238],
rowPixelSpacing: 0.065238,
columnPixelSpacing: 0.065238,
columnCosines: null,
frameOfReferenceUID: undefined,
imageOrientationPatient: undefined,
imagePositionPatient: undefined,
rowCosines: null,
sliceLocation: undefined,
sliceThickness: undefined
};
}
}
}

it("Can generate tool state (4X) with SEG sourcing MG images without throwing any errors", async () => {
const url =
"https://github.com/dcmjs-org/data/releases/download/mg-seg/seg-test-SEG.dcm";
const dcmPath = await getTestDataset(url, "seg-test-SEG.dcm")
const arrayBuffer = fs.readFileSync(dcmPath).buffer

expect(
() => {
Segmentation_4X.generateToolState(
["mg://1"],
arrayBuffer,
mockMetadataProvider
)
},
).not.toThrowError();

});

0 comments on commit e84a36c

Please sign in to comment.