Skip to content

Commit

Permalink
Feat/cs3d planar freehand sr (#268)
Browse files Browse the repository at this point in the history
* freehand and probe tool and reformat

* Generalise the CS3D dcmjs controller to be able to save and encode annotations onto multiple referenced series from one SR.

* Remove debugger

* Add erronously added data

* Make changes based on Alireza's feedback
  • Loading branch information
JamesAPetts committed May 19, 2022
1 parent cef28a9 commit 7da0083
Show file tree
Hide file tree
Showing 10 changed files with 573 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export default class MeasurementReport {
derivationSourceDataset._vrMap = _vrMap;

const report = new StructuredReport([derivationSourceDataset]);

const contentItem = MeasurementReport.contentItem(
derivationSourceDataset
);
Expand Down
50 changes: 30 additions & 20 deletions packages/adapters/src/adapters/Cornerstone3D/ArrowAnnotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,54 @@ import CORNERSTONE_3D_TAG from "./cornerstone3DTag";
import CodingScheme from "./CodingScheme";

const ARROW_ANNOTATE = "ArrowAnnotate";
const trackingIdentifierTextValue = "Cornerstone3DTools@^0.1.0:ArrowAnnotate";
const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${ARROW_ANNOTATE}`;

const { codeValues, CodingSchemeDesignator } = CodingScheme;

class ArrowAnnotate {
constructor() {}

static getMeasurementData(MeasurementGroup, imageId, imageToWorldCoords) {
static getMeasurementData(
MeasurementGroup,
sopInstanceUIDToImageIdMap,
imageToWorldCoords,
metadata
) {
const {
defaultState,
SCOORDGroup,
findingGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);
SCOORDGroup
} = MeasurementReport.getSetupMeasurementData(
MeasurementGroup,
sopInstanceUIDToImageIdMap,
metadata,
ArrowAnnotate.toolType
);

const text = findingGroup.ConceptCodeSequence.CodeMeaning;
const referencedImageId =
defaultState.annotation.metadata.referencedImageId;

const text = defaultState.metadata.label;

const { GraphicData } = SCOORDGroup;

const worldCoords = [];
for (let i = 0; i < GraphicData.length; i += 2) {
const point = imageToWorldCoords(imageId, [
const point = imageToWorldCoords(referencedImageId, [
GraphicData[i],
GraphicData[i + 1]
]);
worldCoords.push(point);
}

const state = {
...defaultState,
toolType: ArrowAnnotate.toolType,
data: {
text,
handles: {
points: [worldCoords[0], worldCoords[1]],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
const state = defaultState;

state.annotation.data = {
text,
handles: {
points: [worldCoords[0], worldCoords[1]],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
}
};
Expand Down Expand Up @@ -99,9 +109,9 @@ ArrowAnnotate.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
return false;
}

const [cornerstone4Tag, toolType] = TrackingIdentifier.split(":");
const [cornerstone3DTag, toolType] = TrackingIdentifier.split(":");

if (cornerstone4Tag !== CORNERSTONE_3D_TAG) {
if (cornerstone3DTag !== CORNERSTONE_3D_TAG) {
return false;
}

Expand Down
79 changes: 36 additions & 43 deletions packages/adapters/src/adapters/Cornerstone3D/Bidirectional.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ const LONG_AXIS = "Long Axis";
const SHORT_AXIS = "Short Axis";
const FINDING = "121071";
const FINDING_SITE = "G-C0E3";
const trackingIdentifierTextValue = "Cornerstone3DTools@^0.1.0:Bidirectional";
const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${BIDIRECTIONAL}`;

class Bidirectional {
constructor() {}

static getMeasurementData(MeasurementGroup, imageId, imageToWorldCoords) {
static getMeasurementData(
MeasurementGroup,
sopInstanceUIDToImageIdMap,
imageToWorldCoords,
metadata
) {
const { defaultState } = MeasurementReport.getSetupMeasurementData(
MeasurementGroup
);
const { ContentSequence } = MeasurementGroup;

const findingGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === FINDING
MeasurementGroup,
sopInstanceUIDToImageIdMap,
metadata,
Bidirectional.toolType
);

const findingSiteGroups = toArray(ContentSequence).filter(
group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE
);
const referencedImageId =
defaultState.annotation.metadata.referencedImageId;
const { ContentSequence } = MeasurementGroup;

const longAxisNUMGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeMeaning === LONG_AXIS
Expand All @@ -49,43 +52,33 @@ class Bidirectional {
[longAxisSCOORDGroup, shortAxisSCOORDGroup].forEach(group => {
const { GraphicData } = group;
for (let i = 0; i < GraphicData.length; i += 2) {
const point = imageToWorldCoords(imageId, [
const point = imageToWorldCoords(referencedImageId, [
GraphicData[i],
GraphicData[i + 1]
]);
worldCoords.push(point);
}
});

const state = {
...defaultState,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
}),
toolType: Bidirectional.toolType,
data: {
handles: {
points: [
worldCoords[0],
worldCoords[1],
worldCoords[2],
worldCoords[3]
],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
},
cachedStats: {
[`imageId:${imageId}`]: {
length:
longAxisNUMGroup.MeasuredValueSequence.NumericValue,
width:
shortAxisNUMGroup.MeasuredValueSequence.NumericValue
}
const state = defaultState;

state.annotation.data = {
handles: {
points: [
worldCoords[0],
worldCoords[1],
worldCoords[2],
worldCoords[3]
],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
},
cachedStats: {
[`imageId:${referencedImageId}`]: {
length: longAxisNUMGroup.MeasuredValueSequence.NumericValue,
width: shortAxisNUMGroup.MeasuredValueSequence.NumericValue
}
}
};
Expand Down Expand Up @@ -189,9 +182,9 @@ Bidirectional.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
return false;
}

const [cornerstone4Tag, toolType] = TrackingIdentifier.split(":");
const [cornerstone3DTag, toolType] = TrackingIdentifier.split(":");

if (cornerstone4Tag !== CORNERSTONE_3D_TAG) {
if (cornerstone3DTag !== CORNERSTONE_3D_TAG) {
return false;
}

Expand Down
54 changes: 32 additions & 22 deletions packages/adapters/src/adapters/Cornerstone3D/EllipticalROI.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,30 @@ const FINDING = "121071";
const FINDING_SITE = "G-C0E3";
const EPSILON = 1e-4;

const trackingIdentifierTextValue = "Cornerstone3DTools@^0.1.0:EllipticalROI";
const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${ELLIPTICALROI}`;

class EllipticalROI {
constructor() {}

static getMeasurementData(
MeasurementGroup,
imageId,
sopInstanceUIDToImageIdMap,
imageToWorldCoords,
metadata
) {
const {
defaultState,
NUMGroup,
SCOORDGroup
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup);
} = MeasurementReport.getSetupMeasurementData(
MeasurementGroup,
sopInstanceUIDToImageIdMap,
metadata,
EllipticalROI.toolType
);

const referencedImageId =
defaultState.annotation.metadata.referencedImageId;

const { GraphicData } = SCOORDGroup;

Expand All @@ -33,7 +41,7 @@ class EllipticalROI {
// in the image plane and then choose the correct points to use for the ellipse.
const pointsWorld = [];
for (let i = 0; i < GraphicData.length; i += 2) {
const worldPos = imageToWorldCoords(imageId, [
const worldPos = imageToWorldCoords(referencedImageId, [
GraphicData[i],
GraphicData[i + 1]
]);
Expand All @@ -56,7 +64,10 @@ class EllipticalROI {
vec3.sub(minorAxisVec, minorAxisEnd, minorAxisStart);
vec3.normalize(minorAxisVec, minorAxisVec);

const imagePlaneModule = metadata.get("imagePlaneModule", imageId);
const imagePlaneModule = metadata.get(
"imagePlaneModule",
referencedImageId
);

if (!imagePlaneModule) {
throw new Error("imageId does not have imagePlaneModule metadata");
Expand Down Expand Up @@ -99,24 +110,23 @@ class EllipticalROI {
console.warn("OBLIQUE ELLIPSE NOT YET SUPPORTED");
}

const state = {
...defaultState,
toolType: EllipticalROI.toolType,
data: {
handles: {
points: [...ellipsePoints],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
},
cachedStats: {
[`imageId:${imageId}`]: {
area: NUMGroup.MeasuredValueSequence.NumericValue
}
const state = defaultState;

state.annotation.data = {
handles: {
points: [...ellipsePoints],
activeHandleIndex: 0,
textBox: {
hasMoved: false
}
},
cachedStats: {
[`imageId:${referencedImageId}`]: {
area: NUMGroup.MeasuredValueSequence.NumericValue
}
}
};

return state;
}

Expand Down Expand Up @@ -180,9 +190,9 @@ EllipticalROI.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
return false;
}

const [cornerstone4Tag, toolType] = TrackingIdentifier.split(":");
const [cornerstone3DTag, toolType] = TrackingIdentifier.split(":");

if (cornerstone4Tag !== CORNERSTONE_3D_TAG) {
if (cornerstone3DTag !== CORNERSTONE_3D_TAG) {
return false;
}

Expand Down
Loading

0 comments on commit 7da0083

Please sign in to comment.