Skip to content
Open
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
181 changes: 181 additions & 0 deletions src/utilities/MeasurementBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import unit2CodingValue from "./TID300/unit2CodingValue";

/**
* Utility class for constructing DICOM SR Numeric (NUM) measurement items
* associated with a spatial coordinate (SCOORD) annotation.
*
* Each measurement produced by this builder includes:
* - A NUM content item describing the measurement value
* - A MeasuredValueSequence with its unit encoded in UCUM or another scheme
* - A ContentSequence containing an INFERRED FROM reference linking the
* measurement back to the SCOORD item using ReferencedContentItemIdentifier.
*
* This ensures that all derived measurements correctly reference the
* annotation from which they were computed.
*/
class MeasurementBuilder {
/**
* Creates a NUM (Numeric Measurement) content item for a DICOM SR.
*
* @param {string} codeValue - Code value representing the type of measurement.
* @param {string} codingScheme - Coding scheme designator (e.g., "SCT", "DCM").
* @param {string} codeMeaning - Human-readable meaning of the measurement code.
* @param {number|string} value - The numeric measurement value.
* @param {Object} unit - Unit definition object (UCUM or other coding scheme).
* @param {number} annotationIndex - Index used to populate ReferencedContentItemIdentifier,
* ensuring the NUM item correctly references its SCOORD.
*
* @returns {Object} DICOM SR content item representing a numeric measurement.
*/

static createNumericMeasurement(
codeValue,
codingScheme,
codeMeaning,
value,
unit,
annotationIndex,
{ scoordContentItem = null } = {}
) {
return {
RelationshipType: "CONTAINS",
ValueType: "NUM",
ConceptNameCodeSequence: {
CodeValue: codeValue,
CodingSchemeDesignator: codingScheme,
CodeMeaning: codeMeaning
},
MeasuredValueSequence: {
MeasurementUnitsCodeSequence: unit2CodingValue(unit),
NumericValue: value
},
ContentSequence: scoordContentItem
? scoordContentItem // FIRST item -> SCOORD
: {
RelationshipType: "INFERRED FROM", // Remaining items -> reference
ReferencedContentItemIdentifier: [1, 1, annotationIndex]
}
};
}

static createAreaMeasurement(
area,
areaUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"42798000",
"SCT",
"Area",
area,
areaUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createRadiusMeasurement(
radius,
radiusUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"131190003",
"SCT",
"Radius",
radius,
radiusUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createMaxMeasurement(
max,
modalityUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"56851009",
"SCT",
"Maximum",
max,
modalityUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createMinMeasurement(
min,
modalityUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"255605001",
"SCT",
"Minimum",
min,
modalityUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createMeanMeasurement(
mean,
modalityUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"373098007",
"SCT",
"Mean",
mean,
modalityUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createStdDevMeasurement(
stdDev,
modalityUnit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"386136009",
"SCT",
"Standard Deviation",
stdDev,
modalityUnit,
annotationIndex,
{ scoordContentItem }
);
}

static createPerimeterMeasurement(
perimeter,
unit,
annotationIndex,
{ scoordContentItem }
) {
return MeasurementBuilder.createNumericMeasurement(
"131191004",
"SCT",
"Perimeter",
perimeter,
unit,
annotationIndex,
{ scoordContentItem }
);
}
}

export default MeasurementBuilder;
59 changes: 23 additions & 36 deletions src/utilities/TID300/Bidirectional.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TID300Measurement from "./TID300Measurement.js";
import unit2CodingValue from "./unit2CodingValue.js";
import TID320ContentItem from "./TID320ContentItem.js";

export default class Bidirectional extends TID300Measurement {
contentItem() {
Expand All @@ -23,64 +24,50 @@ export default class Bidirectional extends TID300Measurement {
use3DSpatialCoordinates
});

const longAxisContentSequence = new TID320ContentItem({
graphicType: "POLYLINE",
graphicData: longAxisGraphicData,
use3DSpatialCoordinates,
referencedSOPSequence: ReferencedSOPSequence,
referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID
}).contentItem();

const shortAxisContentSequence = new TID320ContentItem({
graphicType: "POLYLINE",
graphicData: shortAxisGraphicData,
use3DSpatialCoordinates,
referencedSOPSequence: ReferencedSOPSequence,
referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID
}).contentItem();

return this.getMeasurement([
{
RelationshipType: "CONTAINS",
ValueType: "NUM",
ConceptNameCodeSequence: {
CodeValue: "G-A185",
CodingSchemeDesignator: "SRT",
CodeValue: "103339001",
CodingSchemeDesignator: "SCT",
CodeMeaning: "Long Axis"
},
MeasuredValueSequence: {
MeasurementUnitsCodeSequence: unit2CodingValue(unit),
NumericValue: longAxisLength
},
ContentSequence: {
RelationshipType: "INFERRED FROM",
ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD",
GraphicType: "POLYLINE",
GraphicData: longAxisGraphicData,
ReferencedFrameOfReferenceUID: use3DSpatialCoordinates
? ReferencedFrameOfReferenceUID
: undefined,
ContentSequence: use3DSpatialCoordinates
? undefined
: {
RelationshipType: "SELECTED FROM",
ValueType: "IMAGE",
ReferencedSOPSequence
}
}
ContentSequence: longAxisContentSequence
},
{
RelationshipType: "CONTAINS",
ValueType: "NUM",
ConceptNameCodeSequence: {
CodeValue: "G-A186",
CodingSchemeDesignator: "SRT",
CodeValue: "103340004",
CodingSchemeDesignator: "SCT",
CodeMeaning: "Short Axis"
},
MeasuredValueSequence: {
MeasurementUnitsCodeSequence: unit2CodingValue(unit),
NumericValue: shortAxisLength
},
ContentSequence: {
RelationshipType: "INFERRED FROM",
ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD",
GraphicType: "POLYLINE",
GraphicData: shortAxisGraphicData,
ReferencedFrameOfReferenceUID: use3DSpatialCoordinates
? ReferencedFrameOfReferenceUID
: undefined,
ContentSequence: use3DSpatialCoordinates
? undefined
: {
RelationshipType: "SELECTED FROM",
ValueType: "IMAGE",
ReferencedSOPSequence
}
}
ContentSequence: shortAxisContentSequence
}
]);
}
Expand Down
26 changes: 10 additions & 16 deletions src/utilities/TID300/Calibration.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TID300Measurement from "./TID300Measurement.js";
import unit2CodingValue from "./unit2CodingValue.js";
import TID320ContentItem from "./TID320ContentItem.js";

export default class Calibration extends TID300Measurement {
contentItem() {
Expand All @@ -18,6 +19,14 @@ export default class Calibration extends TID300Measurement {
use3DSpatialCoordinates
});

const graphicContentSequence = new TID320ContentItem({
graphicType: "POLYLINE",
graphicData: GraphicData,
use3DSpatialCoordinates,
referencedSOPSequence: ReferencedSOPSequence,
referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID
}).contentItem();

return this.getMeasurement([
{
RelationshipType: "CONTAINS",
Expand All @@ -31,22 +40,7 @@ export default class Calibration extends TID300Measurement {
MeasurementUnitsCodeSequence: unit2CodingValue(unit),
NumericValue: distance
},
ContentSequence: {
RelationshipType: "INFERRED FROM",
ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD",
GraphicType: "POLYLINE",
GraphicData,
ReferencedFrameOfReferenceUID: use3DSpatialCoordinates
? ReferencedFrameOfReferenceUID
: undefined,
ContentSequence: use3DSpatialCoordinates
? undefined
: {
RelationshipType: "SELECTED FROM",
ValueType: "IMAGE",
ReferencedSOPSequence
}
}
ContentSequence: graphicContentSequence
}
]);
}
Expand Down
Loading