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

Sr update aim #197

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/adapters/Cornerstone/ArrowAnnotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ class ArrowAnnotate {
static getTID300RepresentationArguments(tool) {
const points = [tool.handles.start];

let { finding, findingSites } = tool;
let { finding, findingSites, trackingIdentifier } = tool;

const TID300RepresentationArguments = {
points,
trackingIdentifierTextValue: `cornerstoneTools@^4.0.0:ArrowAnnotate`,
trackingIdentifierTextValue:
trackingIdentifier || `cornerstoneTools@^4.0.0:ArrowAnnotate`,
findingSites: findingSites || []
};

Expand Down
35 changes: 30 additions & 5 deletions src/adapters/Cornerstone/Bidirectional.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const LONG_AXIS = "Long Axis";
const SHORT_AXIS = "Short Axis";
const FINDING = "121071";
const FINDING_SITE = "G-C0E3";
const COMMENT = "121106";
const TRACKING_IDENTIFIER = "112039";
const TRACKING_UNIQUE_IDENTIFIER = "112040";

class Bidirectional {
constructor() {}
Expand All @@ -24,6 +27,21 @@ class Bidirectional {
group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE
);

const comment = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === COMMENT
);

const trackingIdentifier = toArray(ContentSequence).find(
group =>
group.ConceptNameCodeSequence.CodeValue === TRACKING_IDENTIFIER
);

const trackingUniqueIdentifier = toArray(ContentSequence).find(
group =>
group.ConceptNameCodeSequence.CodeValue ===
TRACKING_UNIQUE_IDENTIFIER
);

const longAxisNUMGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeMeaning === LONG_AXIS
);
Expand Down Expand Up @@ -129,15 +147,17 @@ class Bidirectional {
isCreating: false,
longestDiameter,
shortestDiameter,
toolType: "Bidirectional",
toolName: "Bidirectional",
visible: true,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
})
}),
comment: comment ? comment.TextValue : undefined,
trackingIdentifier: trackingIdentifier.TextValue,
trackingUniqueIdentifier: trackingUniqueIdentifier.UID
};

return state;
Expand All @@ -154,11 +174,14 @@ class Bidirectional {
shortestDiameter,
longestDiameter,
finding,
findingSites
findingSites,
comment,
trackingIdentifier,
trackingUniqueIdentifier
} = tool;

const trackingIdentifierTextValue =
"cornerstoneTools@^4.0.0:Bidirectional";
trackingIdentifier || "cornerstoneTools@^4.0.0:Bidirectional";

return {
longAxis: {
Expand All @@ -172,8 +195,10 @@ class Bidirectional {
longAxisLength: longestDiameter,
shortAxisLength: shortestDiameter,
trackingIdentifierTextValue,
trackingUniqueIdentifier,
finding: finding,
findingSites: findingSites || []
findingSites: findingSites || [],
comment: comment
};
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/adapters/Cornerstone/EllipticalRoi.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,13 @@ class EllipticalRoi {
}

static getTID300RepresentationArguments(tool) {
const { cachedStats, handles, finding, findingSites } = tool;
const {
cachedStats,
handles,
finding,
findingSites,
trackingIdentifier
} = tool;
const { start, end } = handles;
const { area } = cachedStats;

Expand Down Expand Up @@ -145,7 +151,7 @@ class EllipticalRoi {
}

const trackingIdentifierTextValue =
"cornerstoneTools@^4.0.0:EllipticalRoi";
trackingIdentifier || "cornerstoneTools@^4.0.0:EllipticalRoi";

return {
area,
Expand Down
166 changes: 148 additions & 18 deletions src/adapters/Cornerstone/Freehand.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,158 @@
import MeasurementReport from "./MeasurementReport";
import TID300Polyline from "../../utilities/TID300/Polyline";
import CORNERSTONE_4_TAG from "./cornerstone4Tag";
import { toArray } from "../helpers.js";

const FREEHAND = "Freehand";
const FINDING = "121071";
const FINDING_SITE = "G-C0E3";
const COMMENT = "121106";
const TRACKING_IDENTIFIER = "112039";
const TRACKING_UNIQUE_IDENTIFIER = "112040";

const statNameMap = {
Minimum: "min",
Maximum: "max",
Mean: "mean",
"Standard Deviation": "stdDev"
};

class Freehand {
constructor() {}

static measurementContentToLengthState(groupItemContent) {
const content = groupItemContent.ContentSequence;
const { ReferencedSOPSequence } = content.ContentSequence;
const {
ReferencedSOPInstanceUID,
ReferencedFrameNumber
} = ReferencedSOPSequence;
const state = {
sopInstanceUid: ReferencedSOPInstanceUID,
frameIndex: ReferencedFrameNumber || 0,
toolType: Freehand.toolType
static parseNumComponent(num) {
const SCOORDGroup = toArray(num.ContentSequence).find(
group => group.ValueType === "SCOORD"
);
const { ReferencedSOPSequence } = SCOORDGroup.ContentSequence;
return {
type: num.ConceptNameCodeSequence.CodeMeaning,
value: num.MeasuredValueSequence.NumericValue,
points: Freehand.extractPoints(SCOORDGroup.GraphicData),
ReferencedSOPSequence: ReferencedSOPSequence
};
}

static parseNumGroup(numGroup) {
const stats = {};
const scoords = [];
const refs = [];

numGroup.forEach(num => {
const {
type,
value,
points,
ReferencedSOPSequence
} = Freehand.parseNumComponent(num);
scoords.push(points);
refs.push(ReferencedSOPSequence);
stats[statNameMap[type]] = value;
});
return { scoords, refs, stats };
}

static extractPoints(points) {
const allPoints = [];

for (let i = 0; i < points.length; i += 2) {
// TODO z
allPoints.push({ x: points[i], y: points[i + 1] });
}

return allPoints;
}

// TODO: To be implemented!
// Needs to add points, lengths
static measurementContentToLengthState(MeasurementGroup) {
const { ContentSequence } = MeasurementGroup;

const findingGroup = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === FINDING
);

const findingSiteGroups = toArray(ContentSequence).filter(
group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE
);

const comment = toArray(ContentSequence).find(
group => group.ConceptNameCodeSequence.CodeValue === COMMENT
);

const trackingIdentifier = toArray(ContentSequence).find(
group =>
group.ConceptNameCodeSequence.CodeValue === TRACKING_IDENTIFIER
);

const trackingUniqueIdentifier = toArray(ContentSequence).find(
group =>
group.ConceptNameCodeSequence.CodeValue ===
TRACKING_UNIQUE_IDENTIFIER
);
const NUMGroup = toArray(ContentSequence).filter(
group => group.ValueType === "NUM"
);
const { scoords, refs, stats } = Freehand.parseNumGroup(NUMGroup);
// TODO get/handle distinct shapes. using the first shape for now
const state = {
sopInstanceUid: refs[0].ReferencedSOPInstanceUID,
frameIndex: refs[0].ReferencedFrameNumber || 0,
toolType: Freehand.toolType,
handles: {
points: scoords[0],
textBox: {},
invalidHandlePlacement: false
},
active: false,
visible: true,
toolName: "Freehand",
invalidated: false,
finding: findingGroup
? findingGroup.ConceptCodeSequence
: undefined,
findingSites: findingSiteGroups.map(fsg => {
return { ...fsg.ConceptCodeSequence };
}),
comment: comment ? comment.TextValue : undefined,
trackingIdentifier: trackingIdentifier.TextValue,
trackingUniqueIdentifier: trackingUniqueIdentifier.UID,
meanStdDev: stats // TODO check if SUV/Pet
};

return state;
}

// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
static getMeasurementData(measurementContent) {
return measurementContent.map(Freehand.measurementContentToLengthState);
return Freehand.measurementContentToLengthState(measurementContent);
}

static getTID300RepresentationArguments(/*tool*/) {
// TO BE IMPLEMENTED
static getTID300RepresentationArguments(tool) {
const {
handles,
finding,
findingSites,
trackingIdentifier,
perimeter,
area,
meanStdDev,
meanStdDevSUV,
pixelUnit
} = tool;
const points = handles.points;
// console.error('tool', handles);
const trackingIdentifierTextValue =
trackingIdentifier || "cornerstoneTools@^4.0.0:Freehand";

return {
/*points, lengths*/
points,
perimeter,
area,
meanStdDev,
meanStdDevSUV,
pixelUnit,
trackingIdentifierTextValue,
finding,
findingSites: findingSites || []
};
}
}
Expand All @@ -41,7 +161,17 @@ Freehand.toolType = "Freehand";
Freehand.utilityToolType = "Freehand";
Freehand.TID300Representation = TID300Polyline;
Freehand.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
return false; // TODO
if (!TrackingIdentifier.includes(":")) {
return false;
}

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

if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
return false;
}

return toolType === FREEHAND;
};

MeasurementReport.registerTool(Freehand);
Expand Down
5 changes: 3 additions & 2 deletions src/adapters/Cornerstone/Length.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,13 @@ class Length {
}

static getTID300RepresentationArguments(tool) {
const { handles, finding, findingSites } = tool;
const { handles, finding, findingSites, trackingIdentifier } = tool;
const point1 = handles.start;
const point2 = handles.end;
const distance = tool.length;

const trackingIdentifierTextValue = "cornerstoneTools@^4.0.0:Length";
const trackingIdentifierTextValue =
trackingIdentifier || "cornerstoneTools@^4.0.0:Length";

return {
point1,
Expand Down
21 changes: 18 additions & 3 deletions src/adapters/Cornerstone/MeasurementReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,18 @@ export default class MeasurementReport {
"generalSeriesModule",
firstImageId
);
const patientModule = metadataProvider.get(
"patientModule",
firstImageId
);
//const sopCommonModule = metadataProvider.get('sopCommonModule', firstImageId);
const { studyInstanceUID, seriesInstanceUID } = generalSeriesModule;
const {
patientID,
patientName,
patientBirthDate,
patientSex
} = patientModule;

// Loop through each image in the toolData
Object.keys(toolState).forEach(imageId => {
Expand Down Expand Up @@ -125,9 +135,13 @@ export default class MeasurementReport {

const derivationSourceDataset = {
StudyInstanceUID: studyInstanceUID,
SeriesInstanceUID: seriesInstanceUID
SeriesInstanceUID: seriesInstanceUID,
//SOPInstanceUID: sopInstanceUID, // TODO: Necessary?
//SOPClassUID: sopClassUID,
PatientID: patientID,
PatientName: patientName,
PatientBirthDate: patientBirthDate,
PatientSex: patientSex
};

const _meta = {
Expand Down Expand Up @@ -158,9 +172,10 @@ export default class MeasurementReport {
derivationSourceDataset._meta = _meta;
derivationSourceDataset._vrMap = _vrMap;

const report = new StructuredReport([derivationSourceDataset]);
const report = new StructuredReport([derivationSourceDataset], options);
const contentItem = MeasurementReport.contentItem(
derivationSourceDataset
derivationSourceDataset,
options
);

// Merge the derived dataset with the content from the Measurement Report
Expand Down
Loading