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:
parent
7f770e4fb6
commit
af09578544
@ -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.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.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.33', //COMPREHENSIVE_SR:
|
||||||
|
'1.2.840.10008.5.1.4.1.1.88.34', //COMPREHENSIVE_3D_SR:
|
||||||
];
|
];
|
||||||
|
|
||||||
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
|
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
|
||||||
@ -44,6 +45,7 @@ const CodingSchemeDesignators = {
|
|||||||
|
|
||||||
const RELATIONSHIP_TYPE = {
|
const RELATIONSHIP_TYPE = {
|
||||||
INFERRED_FROM: 'INFERRED FROM',
|
INFERRED_FROM: 'INFERRED FROM',
|
||||||
|
CONTAINS: 'CONTAINS',
|
||||||
};
|
};
|
||||||
|
|
||||||
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
|
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
|
||||||
@ -376,7 +378,6 @@ function _processMeasurement(mergedContentSequence) {
|
|||||||
function _processTID1410Measurement(mergedContentSequence) {
|
function _processTID1410Measurement(mergedContentSequence) {
|
||||||
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
|
// 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
|
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
|
||||||
// TODO -> Look at RelationshipType => Contains means
|
|
||||||
|
|
||||||
const graphicItem = mergedContentSequence.find(
|
const graphicItem = mergedContentSequence.find(
|
||||||
group => group.ValueType === 'SCOORD'
|
group => group.ValueType === 'SCOORD'
|
||||||
@ -536,9 +537,14 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
|
|||||||
function _getCoordsFromSCOORDOrSCOORD3D(item) {
|
function _getCoordsFromSCOORDOrSCOORD3D(item) {
|
||||||
const { ValueType, RelationshipType, GraphicType, GraphicData } = 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(
|
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;
|
return;
|
||||||
@ -597,12 +603,18 @@ function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
|
|||||||
|
|
||||||
_getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
|
_getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
|
||||||
const { ReferencedSOPSequence } = item;
|
const { ReferencedSOPSequence } = item;
|
||||||
|
|
||||||
|
if (item.hasOwnProperty('ReferencedSOPClassUID')) {
|
||||||
const {
|
const {
|
||||||
ReferencedSOPClassUID,
|
ReferencedSOPClassUID,
|
||||||
ReferencedSOPInstanceUID,
|
ReferencedSOPInstanceUID,
|
||||||
} = ReferencedSOPSequence;
|
} = ReferencedSOPSequence;
|
||||||
|
|
||||||
referencedImages.push({ ReferencedSOPClassUID, ReferencedSOPInstanceUID });
|
referencedImages.push({
|
||||||
|
ReferencedSOPClassUID,
|
||||||
|
ReferencedSOPInstanceUID,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return referencedImages;
|
return referencedImages;
|
||||||
|
|||||||
@ -99,7 +99,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
|||||||
const annotation = filteredAnnotations[i];
|
const annotation = filteredAnnotations[i];
|
||||||
const annotationUID = annotation.annotationUID;
|
const annotationUID = annotation.annotationUID;
|
||||||
const { renderableData } = annotation.data.cachedStats;
|
const { renderableData } = annotation.data.cachedStats;
|
||||||
const { label, cachedStats } = annotation.data;
|
const { cachedStats } = annotation.data;
|
||||||
const { referencedImageId } = annotation.metadata;
|
const { referencedImageId } = annotation.metadata;
|
||||||
|
|
||||||
styleSpecifier.annotationUID = annotationUID;
|
styleSpecifier.annotationUID = annotationUID;
|
||||||
@ -121,22 +121,30 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
|||||||
const renderableDataForGraphicType = renderableData[GraphicType];
|
const renderableDataForGraphicType = renderableData[GraphicType];
|
||||||
|
|
||||||
let renderMethod;
|
let renderMethod;
|
||||||
|
let renderTextBox;
|
||||||
|
let canvasCoordinatesAdapter;
|
||||||
|
|
||||||
switch (GraphicType) {
|
switch (GraphicType) {
|
||||||
case SCOORD_TYPES.POINT:
|
case SCOORD_TYPES.POINT:
|
||||||
renderMethod = this.renderPoint;
|
renderMethod = this.renderPoint;
|
||||||
|
renderTextBox = this.renderTextBox;
|
||||||
break;
|
break;
|
||||||
case SCOORD_TYPES.MULTIPOINT:
|
case SCOORD_TYPES.MULTIPOINT:
|
||||||
renderMethod = this.renderMultipoint;
|
renderMethod = this.renderMultipoint;
|
||||||
|
renderTextBox = this.renderTextBox;
|
||||||
break;
|
break;
|
||||||
case SCOORD_TYPES.POLYLINE:
|
case SCOORD_TYPES.POLYLINE:
|
||||||
renderMethod = this.renderPolyLine;
|
renderMethod = this.renderPolyLine;
|
||||||
break;
|
break;
|
||||||
case SCOORD_TYPES.CIRCLE:
|
case SCOORD_TYPES.CIRCLE:
|
||||||
renderMethod = this.renderEllipse;
|
renderMethod = this.renderEllipse;
|
||||||
|
renderTextBox = this.renderTextBox;
|
||||||
break;
|
break;
|
||||||
case SCOORD_TYPES.ELLIPSE:
|
case SCOORD_TYPES.ELLIPSE:
|
||||||
renderMethod = this.renderEllipse;
|
renderMethod = this.renderEllipse;
|
||||||
|
renderTextBox = this.renderTextBox;
|
||||||
|
canvasCoordinatesAdapter =
|
||||||
|
utilities.math.ellipse.getCanvasEllipseCorners;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported GraphicType: ${GraphicType}`);
|
throw new Error(`Unsupported GraphicType: ${GraphicType}`);
|
||||||
@ -151,60 +159,18 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
|||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!canvasCoordinates) {
|
if (typeof renderTextBox === 'function') {
|
||||||
return;
|
renderTextBox.call(
|
||||||
}
|
this,
|
||||||
|
|
||||||
const textLines = this._getTextBoxLinesFromLabels(label);
|
|
||||||
|
|
||||||
let canvasCornersToUseForTextBox = canvasCoordinates;
|
|
||||||
|
|
||||||
if (GraphicType === SCOORD_TYPES.ELLIPSE) {
|
|
||||||
canvasCornersToUseForTextBox = utilities.math.ellipse.getCanvasEllipseCorners(
|
|
||||||
canvasCoordinates
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
svgDrawingHelper,
|
||||||
annotationUID,
|
viewport,
|
||||||
textBoxUID,
|
|
||||||
textLines,
|
|
||||||
textBoxPosition,
|
|
||||||
canvasCoordinates,
|
canvasCoordinates,
|
||||||
{},
|
canvasCoordinatesAdapter,
|
||||||
{
|
annotation,
|
||||||
...textBoxOptions,
|
styleSpecifier,
|
||||||
color,
|
options
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
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,
|
referencedImageId,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
// Todo: this needs to use the drawPolyLine from cs3D since it is implemented
|
const drawingOptions = {
|
||||||
// now, before it was implemented with a loop over drawLine which is hacky
|
color: options.color,
|
||||||
|
width: options.lineWidth,
|
||||||
|
};
|
||||||
let canvasCoordinates;
|
let canvasCoordinates;
|
||||||
renderableData.map((data, index) => {
|
renderableData.map((data, index) => {
|
||||||
canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
|
canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
|
||||||
|
|
||||||
if (canvasCoordinates.length === 2) {
|
|
||||||
const lineUID = `${index}`;
|
const lineUID = `${index}`;
|
||||||
|
if (canvasCoordinates.length === 2) {
|
||||||
drawing.drawLine(
|
drawing.drawLine(
|
||||||
svgDrawingHelper,
|
svgDrawingHelper,
|
||||||
annotationUID,
|
annotationUID,
|
||||||
lineUID,
|
lineUID,
|
||||||
canvasCoordinates[0],
|
canvasCoordinates[0],
|
||||||
canvasCoordinates[1],
|
canvasCoordinates[1],
|
||||||
{
|
drawingOptions
|
||||||
color: options.color,
|
|
||||||
width: options.lineWidth,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} else {
|
} 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;
|
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 = {
|
const SHORT_HAND_MAP = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user