feat: Added support for TID 1410 relationship types (#2942)

* Added support for TID 1410 relationship types

* Added POLYLINE rendering from ladeirarodolfo to PR

* Rolled Dockerfile back to master to make merge easier

* Minor cleanup
This commit is contained in:
Sean Doyle 2022-10-10 23:09:38 -04:00 committed by GitHub
parent 7f770e4fb6
commit af09578544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 70 deletions

View File

@ -16,6 +16,7 @@ const sopClassUids = [
'1.2.840.10008.5.1.4.1.1.88.11', //BASIC_TEXT_SR:
'1.2.840.10008.5.1.4.1.1.88.22', //ENHANCED_SR:
'1.2.840.10008.5.1.4.1.1.88.33', //COMPREHENSIVE_SR:
'1.2.840.10008.5.1.4.1.1.88.34', //COMPREHENSIVE_3D_SR:
];
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
@ -44,6 +45,7 @@ const CodingSchemeDesignators = {
const RELATIONSHIP_TYPE = {
INFERRED_FROM: 'INFERRED FROM',
CONTAINS: 'CONTAINS',
};
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
@ -376,7 +378,6 @@ function _processMeasurement(mergedContentSequence) {
function _processTID1410Measurement(mergedContentSequence) {
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
// TODO -> Look at RelationshipType => Contains means
const graphicItem = mergedContentSequence.find(
group => group.ValueType === 'SCOORD'
@ -536,9 +537,14 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
function _getCoordsFromSCOORDOrSCOORD3D(item) {
const { ValueType, RelationshipType, GraphicType, GraphicData } = item;
if (RelationshipType !== RELATIONSHIP_TYPE.INFERRED_FROM) {
if (
!(
RelationshipType == RELATIONSHIP_TYPE.INFERRED_FROM ||
RelationshipType == RELATIONSHIP_TYPE.CONTAINS
)
) {
console.warn(
`Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM."`
`Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM" or "CONTAINS"`
);
return;
@ -597,12 +603,18 @@ function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
_getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
const { ReferencedSOPSequence } = item;
const {
ReferencedSOPClassUID,
ReferencedSOPInstanceUID,
} = ReferencedSOPSequence;
referencedImages.push({ ReferencedSOPClassUID, ReferencedSOPInstanceUID });
if (item.hasOwnProperty('ReferencedSOPClassUID')) {
const {
ReferencedSOPClassUID,
ReferencedSOPInstanceUID,
} = ReferencedSOPSequence;
referencedImages.push({
ReferencedSOPClassUID,
ReferencedSOPInstanceUID,
});
}
});
return referencedImages;

View File

@ -99,7 +99,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
const annotation = filteredAnnotations[i];
const annotationUID = annotation.annotationUID;
const { renderableData } = annotation.data.cachedStats;
const { label, cachedStats } = annotation.data;
const { cachedStats } = annotation.data;
const { referencedImageId } = annotation.metadata;
styleSpecifier.annotationUID = annotationUID;
@ -121,22 +121,30 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
const renderableDataForGraphicType = renderableData[GraphicType];
let renderMethod;
let renderTextBox;
let canvasCoordinatesAdapter;
switch (GraphicType) {
case SCOORD_TYPES.POINT:
renderMethod = this.renderPoint;
renderTextBox = this.renderTextBox;
break;
case SCOORD_TYPES.MULTIPOINT:
renderMethod = this.renderMultipoint;
renderTextBox = this.renderTextBox;
break;
case SCOORD_TYPES.POLYLINE:
renderMethod = this.renderPolyLine;
break;
case SCOORD_TYPES.CIRCLE:
renderMethod = this.renderEllipse;
renderTextBox = this.renderTextBox;
break;
case SCOORD_TYPES.ELLIPSE:
renderMethod = this.renderEllipse;
renderTextBox = this.renderTextBox;
canvasCoordinatesAdapter =
utilities.math.ellipse.getCanvasEllipseCorners;
break;
default:
throw new Error(`Unsupported GraphicType: ${GraphicType}`);
@ -151,60 +159,18 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
options
);
if (!canvasCoordinates) {
return;
}
const textLines = this._getTextBoxLinesFromLabels(label);
let canvasCornersToUseForTextBox = canvasCoordinates;
if (GraphicType === SCOORD_TYPES.ELLIPSE) {
canvasCornersToUseForTextBox = utilities.math.ellipse.getCanvasEllipseCorners(
canvasCoordinates
if (typeof renderTextBox === 'function') {
renderTextBox.call(
this,
svgDrawingHelper,
viewport,
canvasCoordinates,
canvasCoordinatesAdapter,
annotation,
styleSpecifier,
options
);
}
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(
canvasCornersToUseForTextBox
);
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(
canvasTextBoxCoords
);
const textBoxPosition = viewport.worldToCanvas(
annotation.data.handles.textBox.worldPosition
);
const textBoxUID = '1';
const textBoxOptions = this.getLinkedTextBoxStyle(
styleSpecifier,
annotation
);
const boundingBox = drawing.drawLinkedTextBox(
svgDrawingHelper,
annotationUID,
textBoxUID,
textLines,
textBoxPosition,
canvasCoordinates,
{},
{
...textBoxOptions,
color,
}
);
const { x: left, y: top, width, height } = boundingBox;
annotation.data.handles.textBox.worldBoundingBox = {
topLeft: viewport.canvasToWorld([left, top]),
topRight: viewport.canvasToWorld([left + width, top]),
bottomLeft: viewport.canvasToWorld([left, top + height]),
bottomRight: viewport.canvasToWorld([left + width, top + height]),
};
});
}
};
@ -217,28 +183,32 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
referencedImageId,
options
) {
// Todo: this needs to use the drawPolyLine from cs3D since it is implemented
// now, before it was implemented with a loop over drawLine which is hacky
const drawingOptions = {
color: options.color,
width: options.lineWidth,
};
let canvasCoordinates;
renderableData.map((data, index) => {
canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
const lineUID = `${index}`;
if (canvasCoordinates.length === 2) {
const lineUID = `${index}`;
drawing.drawLine(
svgDrawingHelper,
annotationUID,
lineUID,
canvasCoordinates[0],
canvasCoordinates[1],
{
color: options.color,
width: options.lineWidth,
}
drawingOptions
);
} else {
throw new Error('Drawing polyline for SR not yet implemented');
drawing.drawPolyline(
svgDrawingHelper,
annotationUID,
lineUID,
canvasCoordinates,
drawingOptions
);
}
});
@ -367,6 +337,71 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
return canvasCoordinates;
}
renderTextBox(
svgDrawingHelper,
viewport,
canvasCoordinates,
canvasCoordinatesAdapter,
annotation,
styleSpecifier,
options = {}
) {
if (!canvasCoordinates || !annotation) {
return;
}
const { annotationUID, data = {} } = annotation;
const { label } = data;
const { color } = options;
let adaptedCanvasCoordinates = canvasCoordinates;
// adapt coordinates if there is an adapter
if (typeof canvasCoordinatesAdapter === 'function') {
adaptedCanvasCoordinates = canvasCoordinatesAdapter(canvasCoordinates);
}
const textLines = this._getTextBoxLinesFromLabels(label);
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(
adaptedCanvasCoordinates
);
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(
canvasTextBoxCoords
);
const textBoxPosition = viewport.worldToCanvas(
annotation.data.handles.textBox.worldPosition
);
const textBoxUID = '1';
const textBoxOptions = this.getLinkedTextBoxStyle(
styleSpecifier,
annotation
);
const boundingBox = drawing.drawLinkedTextBox(
svgDrawingHelper,
annotationUID,
textBoxUID,
textLines,
textBoxPosition,
canvasCoordinates,
{},
{
...textBoxOptions,
color,
}
);
const { x: left, y: top, width, height } = boundingBox;
annotation.data.handles.textBox.worldBoundingBox = {
topLeft: viewport.canvasToWorld([left, top]),
topRight: viewport.canvasToWorld([left + width, top]),
bottomLeft: viewport.canvasToWorld([left, top + height]),
bottomRight: viewport.canvasToWorld([left + width, top + height]),
};
}
}
const SHORT_HAND_MAP = {