From 165c52e1898c593fcc36f10c2ca2fdb7bcb496cd Mon Sep 17 00:00:00 2001 From: Abhijith SB Date: Tue, 28 Oct 2025 15:03:07 +0530 Subject: [PATCH 01/14] saving additional metrics to SR --- src/utilities/TID300/Circle.js | 99 +++++++++++++++-- src/utilities/TID300/Ellipse.js | 87 ++++++++++++--- src/utilities/TID300/Polyline.js | 105 +++++++++++++------ src/utilities/TID300/buildContentSequence.js | 30 ++++++ 4 files changed, 262 insertions(+), 59 deletions(-) create mode 100644 src/utilities/TID300/buildContentSequence.js diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index e35c2250..83fbcb38 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Circle extends TID300Measurement { contentItem() { @@ -11,6 +12,12 @@ export default class Circle extends TID300Measurement { area, areaUnit = "mm2", unit = "mm", + max, + min, + mean, + stdDev, + radiusUnit, + modalityUnit, ReferencedFrameOfReferenceUID } = this.props; @@ -23,7 +30,13 @@ export default class Circle extends TID300Measurement { use3DSpatialCoordinates }); - // TODO: Add Mean and STDev value of (modality?) pixels + const graphicContentSequence = buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); return this.getMeasurement([ { @@ -38,14 +51,39 @@ export default class Circle extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "131190003", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Radius" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: unit2CodingValue(radiusUnit), + NumericValue: perimeter + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "G-A166", + CodingSchemeDesignator: "SRT", + CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), + NumericValue: area + }, ContentSequence: { RelationshipType: "INFERRED FROM", ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", GraphicType: "CIRCLE", GraphicData, - ReferencedFrameOfReferenceUID: use3DSpatialCoordinates - ? ReferencedFrameOfReferenceUID - : undefined, ContentSequence: use3DSpatialCoordinates ? undefined : { @@ -56,17 +94,30 @@ export default class Circle extends TID300Measurement { } }, { - // TODO: This feels weird to repeat the GraphicData RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-A166", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary + CodeValue: "56851009", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Maximum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), - NumericValue: area + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: max + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "255605001", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Minimum" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: min }, ContentSequence: { RelationshipType: "INFERRED FROM", @@ -81,6 +132,34 @@ export default class Circle extends TID300Measurement { ReferencedSOPSequence } } + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "373098007", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Mean" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: mean + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "386136009", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Standard Deviation" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: stdDev + }, + ContentSequence: graphicContentSequence } ]); } diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 0fd4696f..9caa175f 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Ellipse extends TID300Measurement { contentItem() { @@ -9,6 +10,11 @@ export default class Ellipse extends TID300Measurement { ReferencedSOPSequence, area, areaUnit, + max, + min, + mean, + stdDev, + modalityUnit, ReferencedFrameOfReferenceUID } = this.props; @@ -17,6 +23,14 @@ export default class Ellipse extends TID300Measurement { use3DSpatialCoordinates }); + const graphicContentSequence = buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -30,22 +44,63 @@ export default class Ellipse extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "ELLIPSE", - GraphicData, - ReferencedFrameOfReferenceUID: use3DSpatialCoordinates - ? ReferencedFrameOfReferenceUID - : undefined, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "56851009", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Maximum" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: max + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "255605001", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Minimum" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: min + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "373098007", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Mean" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: mean + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "386136009", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Standard Deviation" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: stdDev + }, + ContentSequence: graphicContentSequence } ]); } diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index 285fe2b0..46509d8c 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement"; import unit2CodingValue from "./unit2CodingValue"; +import buildContentSequence from "./buildContentSequence.js"; export default class Polyline extends TID300Measurement { contentItem() { @@ -11,6 +12,11 @@ export default class Polyline extends TID300Measurement { use3DSpatialCoordinates = false, perimeter, unit = "mm", + modalityUnit, + min, + max, + mean, + stdDev, ReferencedFrameOfReferenceUID } = this.props; @@ -19,7 +25,14 @@ export default class Polyline extends TID300Measurement { use3DSpatialCoordinates }); - // TODO: Add Mean and STDev value of (modality?) pixels + const graphicContentSequence = buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -33,22 +46,7 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - 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 }, { // TODO: This feels weird to repeat the GraphicData @@ -63,22 +61,63 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - 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 + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "56851009", + CodingSchemeDesignator: "SRT", + CodeMeaning: "Maximum" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: max + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "255605001", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Minimum" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: min + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "373098007", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Mean" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: mean + }, + ContentSequence: graphicContentSequence + }, + { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: "386136009", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Standard Deviation" + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: modalityUnit, + NumericValue: stdDev + }, + ContentSequence: graphicContentSequence } ]); } diff --git a/src/utilities/TID300/buildContentSequence.js b/src/utilities/TID300/buildContentSequence.js new file mode 100644 index 00000000..e156b6f8 --- /dev/null +++ b/src/utilities/TID300/buildContentSequence.js @@ -0,0 +1,30 @@ +/** + * Builds a DICOM SR ContentSequence block for geometric measurements + * that share the same structure across tools (Circle, Ellipse, Polyline, etc.) + */ +export default function buildContentSequence({ + graphicType, + graphicData, + use3DSpatialCoordinates = false, + referencedSOPSequence, + referencedFrameOfReferenceUID +}) { + const content = { + RelationshipType: "INFERRED FROM", + ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", + GraphicType: graphicType, + GraphicData: graphicData + }; + + if (use3DSpatialCoordinates) { + content.ReferencedFrameOfReferenceUID = referencedFrameOfReferenceUID; + } else { + content.ContentSequence = { + RelationshipType: "SELECTED FROM", + ValueType: "IMAGE", + ReferencedSOPSequence: referencedSOPSequence + }; + } + + return content; +} From cc8b84b36739bc595a791b8a8fc04da746f7b63c Mon Sep 17 00:00:00 2001 From: Abhijith SB Date: Tue, 28 Oct 2025 17:44:59 +0530 Subject: [PATCH 02/14] added buildContentSequence support for all tools --- src/utilities/TID300/Bidirectional.js | 51 ++++++++++----------------- src/utilities/TID300/Calibration.js | 26 ++++++-------- src/utilities/TID300/CobbAngle.js | 26 ++++++-------- src/utilities/TID300/Point.js | 26 ++++++-------- src/utilities/TID300/Polygon.js | 43 ++++++---------------- 5 files changed, 60 insertions(+), 112 deletions(-) diff --git a/src/utilities/TID300/Bidirectional.js b/src/utilities/TID300/Bidirectional.js index 76d23dd2..f09beea8 100644 --- a/src/utilities/TID300/Bidirectional.js +++ b/src/utilities/TID300/Bidirectional.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Bidirectional extends TID300Measurement { contentItem() { @@ -23,6 +24,22 @@ export default class Bidirectional extends TID300Measurement { use3DSpatialCoordinates }); + const longAxisContentSequence = buildContentSequence({ + graphicType: "POLYLINE", + graphicData: longAxisGraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + + const shortAxisContentSequence = buildContentSequence({ + graphicType: "POLYLINE", + graphicData: shortAxisGraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -36,22 +53,7 @@ export default class Bidirectional extends TID300Measurement { 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", @@ -65,22 +67,7 @@ export default class Bidirectional extends TID300Measurement { 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 } ]); } diff --git a/src/utilities/TID300/Calibration.js b/src/utilities/TID300/Calibration.js index db695065..4729660c 100644 --- a/src/utilities/TID300/Calibration.js +++ b/src/utilities/TID300/Calibration.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Calibration extends TID300Measurement { contentItem() { @@ -18,6 +19,14 @@ export default class Calibration extends TID300Measurement { use3DSpatialCoordinates }); + const graphicContentSequence = buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -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 } ]); } diff --git a/src/utilities/TID300/CobbAngle.js b/src/utilities/TID300/CobbAngle.js index afefd7ce..bcc0074b 100644 --- a/src/utilities/TID300/CobbAngle.js +++ b/src/utilities/TID300/CobbAngle.js @@ -1,4 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class CobbAngle extends TID300Measurement { contentItem() { @@ -18,6 +19,14 @@ export default class CobbAngle extends TID300Measurement { use3DSpatialCoordinates }); + const graphicContentSequence = buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -36,22 +45,7 @@ export default class CobbAngle extends TID300Measurement { }, NumericValue: rAngle }, - 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 } ]); } diff --git a/src/utilities/TID300/Point.js b/src/utilities/TID300/Point.js index cf022904..7735ee62 100644 --- a/src/utilities/TID300/Point.js +++ b/src/utilities/TID300/Point.js @@ -1,4 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Point extends TID300Measurement { contentItem() { @@ -15,6 +16,14 @@ export default class Point extends TID300Measurement { use3DSpatialCoordinates }); + const graphicContentSequence = buildContentSequence({ + graphicType: "POINT", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -25,22 +34,7 @@ export default class Point extends TID300Measurement { CodeMeaning: "Center" }, //MeasuredValueSequence: , - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "POINT", - GraphicData, - ReferencedFrameOfReferenceUID: use3DSpatialCoordinates - ? ReferencedFrameOfReferenceUID - : undefined, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } + ContentSequence: graphicContentSequence } ]); } diff --git a/src/utilities/TID300/Polygon.js b/src/utilities/TID300/Polygon.js index 74da2a6c..fefc1b83 100644 --- a/src/utilities/TID300/Polygon.js +++ b/src/utilities/TID300/Polygon.js @@ -1,5 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; +import buildContentSequence from "./buildContentSequence.js"; export default class Polygon extends TID300Measurement { contentItem() { @@ -19,6 +20,14 @@ export default class Polygon extends TID300Measurement { use3DSpatialCoordinates }); + const graphicContentSequence = buildContentSequence({ + graphicType: "POLYGON", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }); + return this.getMeasurement([ { RelationshipType: "CONTAINS", @@ -32,22 +41,7 @@ export default class Polygon extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "POLYGON", - GraphicData, - ReferencedFrameOfReferenceUID: use3DSpatialCoordinates - ? ReferencedFrameOfReferenceUID - : undefined, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } + ContentSequence: graphicContentSequence }, { RelationshipType: "CONTAINS", @@ -61,22 +55,7 @@ export default class Polygon extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "POLYGON", - GraphicData, - ReferencedFrameOfReferenceUID: use3DSpatialCoordinates - ? ReferencedFrameOfReferenceUID - : undefined, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } + ContentSequence: graphicContentSequence } ]); } From 745816a09a3ae3097a248e6ce20e78dafa9f3418 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Wed, 19 Nov 2025 19:37:52 +0530 Subject: [PATCH 03/14] fixed errors on generated dicom files getting verified on dciodvfy --- src/utilities/TID300/Circle.js | 165 ++++++++++++++++++------------- src/utilities/TID300/Ellipse.js | 96 ++++++++++++------ src/utilities/TID300/Polyline.js | 101 +++++++++++++------ 3 files changed, 237 insertions(+), 125 deletions(-) diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index 83fbcb38..7391fd3a 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -18,7 +18,8 @@ export default class Circle extends TID300Measurement { stdDev, radiusUnit, modalityUnit, - ReferencedFrameOfReferenceUID + ReferencedFrameOfReferenceUID, + radius } = this.props; // Combine all lengths to save the perimeter @@ -30,15 +31,7 @@ export default class Circle extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); - - return this.getMeasurement([ + const measurements = [ { RelationshipType: "CONTAINS", ValueType: "NUM", @@ -51,49 +44,61 @@ export default class Circle extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: graphicContentSequence + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) }, { + // TODO: This feels weird to repeat the GraphicData RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "131190003", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Radius" + CodeValue: "G-A166", + CodingSchemeDesignator: "SRT", + CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(radiusUnit), - NumericValue: perimeter + MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), + NumericValue: area }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + } + ]; + if (radius) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-A166", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary + CodeValue: "131190003", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Radius" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), - NumericValue: area + MeasurementUnitsCodeSequence: unit2CodingValue(radiusUnit), + NumericValue: radius }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "CIRCLE", - GraphicData, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } - }, - { + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (max) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -102,12 +107,22 @@ export default class Circle extends TID300Measurement { CodeMeaning: "Maximum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (min) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -116,24 +131,22 @@ export default class Circle extends TID300Measurement { CodeMeaning: "Minimum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: "CIRCLE", - GraphicData, - ContentSequence: use3DSpatialCoordinates - ? undefined - : { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence - } - } - }, - { + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (mean) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -142,12 +155,22 @@ export default class Circle extends TID300Measurement { CodeMeaning: "Mean" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (stdDev) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -156,11 +179,19 @@ export default class Circle extends TID300Measurement { CodeMeaning: "Standard Deviation" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: graphicContentSequence - } - ]); + ContentSequence: buildContentSequence({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + return this.getMeasurement(measurements); } } diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 9caa175f..1641ee26 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -23,15 +23,7 @@ export default class Ellipse extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); - - return this.getMeasurement([ + const measurements = [ { RelationshipType: "CONTAINS", ValueType: "NUM", @@ -44,9 +36,18 @@ export default class Ellipse extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + } + ]; + + if (max) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -55,12 +56,22 @@ export default class Ellipse extends TID300Measurement { CodeMeaning: "Maximum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (min) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -69,12 +80,22 @@ export default class Ellipse extends TID300Measurement { CodeMeaning: "Minimum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (mean) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -83,12 +104,22 @@ export default class Ellipse extends TID300Measurement { CodeMeaning: "Mean" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + if (stdDev) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -97,11 +128,20 @@ export default class Ellipse extends TID300Measurement { CodeMeaning: "Standard Deviation" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: graphicContentSequence - } - ]); + ContentSequence: buildContentSequence({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + + return this.getMeasurement(measurements); } } diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index 46509d8c..f4788260 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -24,16 +24,7 @@ export default class Polyline extends TID300Measurement { points, use3DSpatialCoordinates }); - - const graphicContentSequence = buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); - - return this.getMeasurement([ + const measurements = [ { RelationshipType: "CONTAINS", ValueType: "NUM", @@ -46,7 +37,13 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: graphicContentSequence + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) }, { // TODO: This feels weird to repeat the GraphicData @@ -61,9 +58,18 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + } + ]; + + if (max) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -72,12 +78,21 @@ export default class Polyline extends TID300Measurement { CodeMeaning: "Maximum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + if (min) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -86,12 +101,21 @@ export default class Polyline extends TID300Measurement { CodeMeaning: "Minimum" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + if (mean) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -100,12 +124,21 @@ export default class Polyline extends TID300Measurement { CodeMeaning: "Mean" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: graphicContentSequence - }, - { + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + if (stdDev) { + measurements.push({ RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { @@ -114,11 +147,19 @@ export default class Polyline extends TID300Measurement { CodeMeaning: "Standard Deviation" }, MeasuredValueSequence: { - MeasurementUnitsCodeSequence: modalityUnit, + MeasurementUnitsCodeSequence: + unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: graphicContentSequence - } - ]); + ContentSequence: buildContentSequence({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }) + }); + } + return this.getMeasurement(measurements); } } From db08d7db5ea2716bff2dd8d37ee0f136fb30508d Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Thu, 20 Nov 2025 15:01:29 +0530 Subject: [PATCH 04/14] Added additional units support --- src/utilities/TID300/unit2CodingValue.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/utilities/TID300/unit2CodingValue.js b/src/utilities/TID300/unit2CodingValue.js index 54de41ce..a0a371d9 100644 --- a/src/utilities/TID300/unit2CodingValue.js +++ b/src/utilities/TID300/unit2CodingValue.js @@ -30,7 +30,14 @@ const measurementMap = { "mm\xB2": MM2_UNIT, "px\xB2": NO2_UNIT }; - +const generateUnitMap = unit => { + return { + CodeValue: unit, + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeMeaning: unit + }; +}; /** Converts the given unit into the * specified coding values. * Has .measurementMap on the function specifying global units for measurements. @@ -41,8 +48,7 @@ const unit2CodingValue = units => { const baseUnit = space === -1 ? units : units.substring(0, space); const codingUnit = measurementMap[units] || measurementMap[baseUnit]; if (!codingUnit) { - log.error("Unspecified units", units); - return MM_UNIT; + return generateUnitMap(units); } return codingUnit; }; From f1c200801ef9d4593e03f58f3cdc2be5e0883c7e Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Wed, 26 Nov 2025 20:03:58 +0530 Subject: [PATCH 05/14] Used ReferencedContentItemIdentifier --- src/utilities/TID300/Circle.js | 66 ++++++++++++-------------------- src/utilities/TID300/Ellipse.js | 44 ++++++++------------- src/utilities/TID300/Polyline.js | 55 ++++++++++---------------- 3 files changed, 60 insertions(+), 105 deletions(-) diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index 7391fd3a..2e053d74 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -65,13 +65,10 @@ export default class Circle extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } } ]; if (radius) { @@ -87,13 +84,10 @@ export default class Circle extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(radiusUnit), NumericValue: radius }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -111,13 +105,10 @@ export default class Circle extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -135,13 +126,10 @@ export default class Circle extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -159,13 +147,10 @@ export default class Circle extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -183,13 +168,10 @@ export default class Circle extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: buildContentSequence({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } return this.getMeasurement(measurements); diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 1641ee26..be81b8cc 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -60,13 +60,10 @@ export default class Ellipse extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: buildContentSequence({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -84,13 +81,10 @@ export default class Ellipse extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: buildContentSequence({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -108,13 +102,10 @@ export default class Ellipse extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: buildContentSequence({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } @@ -132,13 +123,10 @@ export default class Ellipse extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: buildContentSequence({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index f4788260..06f68fec 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -58,13 +58,10 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } } ]; @@ -82,13 +79,10 @@ export default class Polyline extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: max }, - ContentSequence: buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } if (min) { @@ -105,13 +99,10 @@ export default class Polyline extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: min }, - ContentSequence: buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } if (mean) { @@ -128,13 +119,10 @@ export default class Polyline extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: mean }, - ContentSequence: buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } if (stdDev) { @@ -151,13 +139,10 @@ export default class Polyline extends TID300Measurement { unit2CodingValue(modalityUnit), NumericValue: stdDev }, - ContentSequence: buildContentSequence({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: 1 + } }); } return this.getMeasurement(measurements); From 4e0f2e2c012952f48062f5649359b0550fbe0558 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Fri, 28 Nov 2025 20:41:05 +0530 Subject: [PATCH 06/14] corrected ReferencedContentItemIdentifier --- src/utilities/TID300/Circle.js | 15 ++++++++------- src/utilities/TID300/Ellipse.js | 11 ++++++----- src/utilities/TID300/Polyline.js | 13 +++++++------ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index 2e053d74..b2e8c54c 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -19,7 +19,8 @@ export default class Circle extends TID300Measurement { radiusUnit, modalityUnit, ReferencedFrameOfReferenceUID, - radius + radius, + annotationIndex } = this.props; // Combine all lengths to save the perimeter @@ -67,7 +68,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } } ]; @@ -86,7 +87,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -107,7 +108,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -128,7 +129,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -149,7 +150,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -170,7 +171,7 @@ export default class Circle extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index be81b8cc..3b739fd4 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -15,7 +15,8 @@ export default class Ellipse extends TID300Measurement { mean, stdDev, modalityUnit, - ReferencedFrameOfReferenceUID + ReferencedFrameOfReferenceUID, + annotationIndex } = this.props; const GraphicData = this.flattenPoints({ @@ -62,7 +63,7 @@ export default class Ellipse extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -83,7 +84,7 @@ export default class Ellipse extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -104,7 +105,7 @@ export default class Ellipse extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -125,7 +126,7 @@ export default class Ellipse extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index 06f68fec..e693f850 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -17,7 +17,8 @@ export default class Polyline extends TID300Measurement { max, mean, stdDev, - ReferencedFrameOfReferenceUID + ReferencedFrameOfReferenceUID, + annotationIndex } = this.props; const GraphicData = this.flattenPoints({ @@ -60,7 +61,7 @@ export default class Polyline extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } } ]; @@ -81,7 +82,7 @@ export default class Polyline extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -101,7 +102,7 @@ export default class Polyline extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -121,7 +122,7 @@ export default class Polyline extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } @@ -141,7 +142,7 @@ export default class Polyline extends TID300Measurement { }, ContentSequence: { RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: 1 + ReferencedContentItemIdentifier: [1, 1, annotationIndex] } }); } From ad336b43191f7676cd587eb3ab064e4b3f733629 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Tue, 2 Dec 2025 09:49:31 +0000 Subject: [PATCH 07/14] Expand UCUM units, Used Tid320ContentItem class, Used builder pattern --- src/utilities/MeasurementBuilder.js | 98 ++++++++++ src/utilities/TID300/Bidirectional.js | 10 +- src/utilities/TID300/Calibration.js | 6 +- src/utilities/TID300/Circle.js | 167 +++++----------- src/utilities/TID300/CobbAngle.js | 6 +- src/utilities/TID300/Ellipse.js | 126 ++++-------- src/utilities/TID300/Point.js | 6 +- src/utilities/TID300/Polygon.js | 6 +- src/utilities/TID300/Polyline.js | 139 ++++--------- src/utilities/TID300/Tid320ContentItem.js | 41 ++++ src/utilities/TID300/buildContentSequence.js | 30 --- src/utilities/TID300/unit2CodingValue.js | 193 ++++++++++++++++--- 12 files changed, 444 insertions(+), 384 deletions(-) create mode 100644 src/utilities/MeasurementBuilder.js create mode 100644 src/utilities/TID300/Tid320ContentItem.js delete mode 100644 src/utilities/TID300/buildContentSequence.js diff --git a/src/utilities/MeasurementBuilder.js b/src/utilities/MeasurementBuilder.js new file mode 100644 index 00000000..6178f042 --- /dev/null +++ b/src/utilities/MeasurementBuilder.js @@ -0,0 +1,98 @@ +import unit2CodingValue from "./TID300/unit2CodingValue"; + +class MeasurementBuilder { + static createNumericMeasurement( + codeValue, + codingScheme, + codeMeaning, + value, + unit, + annotationIndex + ) { + return { + RelationshipType: "CONTAINS", + ValueType: "NUM", + ConceptNameCodeSequence: { + CodeValue: codeValue, + CodingSchemeDesignator: codingScheme, + CodeMeaning: codeMeaning + }, + MeasuredValueSequence: { + MeasurementUnitsCodeSequence: unit2CodingValue(unit), + NumericValue: value + }, + ContentSequence: { + RelationshipType: "INFERRED FROM", + ReferencedContentItemIdentifier: [1, 1, annotationIndex] + } + }; + } + + static createAreaMeasurement(area, areaUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "G-A166", + "SRT", + "Area", + area, + areaUnit, + annotationIndex + ); + } + + static createRadiusMeasurement(radius, radiusUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "131190003", + "SCT", + "Radius", + radius, + radiusUnit, + annotationIndex + ); + } + + static createMaxMeasurement(max, modalityUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "56851009", + "SCT", + "Maximum", + max, + modalityUnit, + annotationIndex + ); + } + + static createMinMeasurement(min, modalityUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "255605001", + "SCT", + "Minimum", + min, + modalityUnit, + annotationIndex + ); + } + + static createMeanMeasurement(mean, modalityUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "373098007", + "SCT", + "Mean", + mean, + modalityUnit, + annotationIndex + ); + } + + static createStdDevMeasurement(stdDev, modalityUnit, annotationIndex) { + return MeasurementBuilder.createNumericMeasurement( + "386136009", + "SCT", + "Standard Deviation", + stdDev, + modalityUnit, + annotationIndex + ); + } +} + +export default MeasurementBuilder; diff --git a/src/utilities/TID300/Bidirectional.js b/src/utilities/TID300/Bidirectional.js index f09beea8..9ea6eb90 100644 --- a/src/utilities/TID300/Bidirectional.js +++ b/src/utilities/TID300/Bidirectional.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; export default class Bidirectional extends TID300Measurement { contentItem() { @@ -24,21 +24,21 @@ export default class Bidirectional extends TID300Measurement { use3DSpatialCoordinates }); - const longAxisContentSequence = buildContentSequence({ + const longAxisContentSequence = new Tid320ContentItem({ graphicType: "POLYLINE", graphicData: longAxisGraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); - const shortAxisContentSequence = buildContentSequence({ + const shortAxisContentSequence = new Tid320ContentItem({ graphicType: "POLYLINE", graphicData: shortAxisGraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); return this.getMeasurement([ { diff --git a/src/utilities/TID300/Calibration.js b/src/utilities/TID300/Calibration.js index 4729660c..6f443515 100644 --- a/src/utilities/TID300/Calibration.js +++ b/src/utilities/TID300/Calibration.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; export default class Calibration extends TID300Measurement { contentItem() { @@ -19,13 +19,13 @@ export default class Calibration extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ + const graphicContentSequence = new Tid320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); return this.getMeasurement([ { diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index b2e8c54c..a9958883 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -1,6 +1,7 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; +import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Circle extends TID300Measurement { contentItem() { @@ -32,6 +33,39 @@ export default class Circle extends TID300Measurement { use3DSpatialCoordinates }); + const measurementConfigs = [ + { + value: area, + unit: areaUnit, + builder: MeasurementBuilder.createAreaMeasurement + }, + { + value: radius, + unit: radiusUnit, + builder: MeasurementBuilder.createRadiusMeasurement + }, + { + value: max, + unit: modalityUnit, + builder: MeasurementBuilder.createMaxMeasurement + }, + { + value: min, + unit: modalityUnit, + builder: MeasurementBuilder.createMinMeasurement + }, + { + value: mean, + unit: modalityUnit, + builder: MeasurementBuilder.createMeanMeasurement + }, + { + value: stdDev, + unit: modalityUnit, + builder: MeasurementBuilder.createStdDevMeasurement + } + ]; + const measurements = [ { RelationshipType: "CONTAINS", @@ -39,142 +73,27 @@ export default class Circle extends TID300Measurement { ConceptNameCodeSequence: { CodeValue: "G-A197", CodingSchemeDesignator: "SRT", - CodeMeaning: "Perimeter" // TODO: Look this up from a Code Meaning dictionary + CodeMeaning: "Perimeter" }, MeasuredValueSequence: { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: buildContentSequence({ + ContentSequence: new Tid320ContentItem({ graphicType: "CIRCLE", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + }).contentItem() }, - { - // TODO: This feels weird to repeat the GraphicData - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "G-A166", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), - NumericValue: area - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - } + ...measurementConfigs + .filter(config => config.value !== undefined) + .map(config => + config.builder(config.value, config.unit, annotationIndex) + ) ]; - if (radius) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "131190003", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Radius" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(radiusUnit), - NumericValue: radius - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (max) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "56851009", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Maximum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: max - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (min) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "255605001", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Minimum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: min - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (mean) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "373098007", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Mean" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: mean - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - if (stdDev) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "386136009", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Standard Deviation" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: stdDev - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } return this.getMeasurement(measurements); } } diff --git a/src/utilities/TID300/CobbAngle.js b/src/utilities/TID300/CobbAngle.js index bcc0074b..a2129f8a 100644 --- a/src/utilities/TID300/CobbAngle.js +++ b/src/utilities/TID300/CobbAngle.js @@ -1,5 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; export default class CobbAngle extends TID300Measurement { contentItem() { @@ -19,13 +19,13 @@ export default class CobbAngle extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ + const graphicContentSequence = new Tid320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); return this.getMeasurement([ { diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 3b739fd4..2437e6e3 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -1,6 +1,7 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; +import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Ellipse extends TID300Measurement { contentItem() { @@ -24,6 +25,34 @@ export default class Ellipse extends TID300Measurement { use3DSpatialCoordinates }); + const measurementConfigs = [ + { + value: area, + unit: areaUnit, + builder: MeasurementBuilder.createAreaMeasurement + }, + { + value: max, + unit: modalityUnit, + builder: MeasurementBuilder.createMaxMeasurement + }, + { + value: min, + unit: modalityUnit, + builder: MeasurementBuilder.createMinMeasurement + }, + { + value: mean, + unit: modalityUnit, + builder: MeasurementBuilder.createMeanMeasurement + }, + { + value: stdDev, + unit: modalityUnit, + builder: MeasurementBuilder.createStdDevMeasurement + } + ]; + const measurements = [ { RelationshipType: "CONTAINS", @@ -37,100 +66,21 @@ export default class Ellipse extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: buildContentSequence({ + ContentSequence: new Tid320ContentItem({ graphicType: "ELLIPSE", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) - } + }).contentItem() + }, + ...measurementConfigs + .filter(config => config.value !== undefined) + .map(config => + config.builder(config.value, config.unit, annotationIndex) + ) ]; - if (max) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "56851009", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Maximum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: max - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (min) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "255605001", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Minimum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: min - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (mean) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "373098007", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Mean" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: mean - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - - if (stdDev) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "386136009", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Standard Deviation" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: stdDev - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - return this.getMeasurement(measurements); } } diff --git a/src/utilities/TID300/Point.js b/src/utilities/TID300/Point.js index 7735ee62..9d2d9410 100644 --- a/src/utilities/TID300/Point.js +++ b/src/utilities/TID300/Point.js @@ -1,5 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; export default class Point extends TID300Measurement { contentItem() { @@ -16,13 +16,13 @@ export default class Point extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ + const graphicContentSequence = new Tid320ContentItem({ graphicType: "POINT", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); return this.getMeasurement([ { diff --git a/src/utilities/TID300/Polygon.js b/src/utilities/TID300/Polygon.js index fefc1b83..11e669db 100644 --- a/src/utilities/TID300/Polygon.js +++ b/src/utilities/TID300/Polygon.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; export default class Polygon extends TID300Measurement { contentItem() { @@ -20,13 +20,13 @@ export default class Polygon extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = buildContentSequence({ + const graphicContentSequence = new Tid320ContentItem({ graphicType: "POLYGON", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }); + }).contentItem(); return this.getMeasurement([ { diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index e693f850..4fdb255b 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -1,6 +1,7 @@ import TID300Measurement from "./TID300Measurement"; import unit2CodingValue from "./unit2CodingValue"; -import buildContentSequence from "./buildContentSequence.js"; +import Tid320ContentItem from "./Tid320ContentItem.js"; +import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Polyline extends TID300Measurement { contentItem() { @@ -25,6 +26,35 @@ export default class Polyline extends TID300Measurement { points, use3DSpatialCoordinates }); + + const measurementConfigs = [ + { + value: area, + unit: areaUnit, + builder: MeasurementBuilder.createAreaMeasurement + }, + { + value: max, + unit: modalityUnit, + builder: MeasurementBuilder.createMaxMeasurement + }, + { + value: min, + unit: modalityUnit, + builder: MeasurementBuilder.createMinMeasurement + }, + { + value: mean, + unit: modalityUnit, + builder: MeasurementBuilder.createMeanMeasurement + }, + { + value: stdDev, + unit: modalityUnit, + builder: MeasurementBuilder.createStdDevMeasurement + } + ]; + const measurements = [ { RelationshipType: "CONTAINS", @@ -38,114 +68,21 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: buildContentSequence({ + ContentSequence: new Tid320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, referencedSOPSequence: ReferencedSOPSequence, referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }) + }).contentItem() }, - { - // TODO: This feels weird to repeat the GraphicData - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "G-A166", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Area" // TODO: Look this up from a Code Meaning dictionary - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), - NumericValue: area - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - } + ...measurementConfigs + .filter(config => config.value !== undefined) + .map(config => + config.builder(config.value, config.unit, annotationIndex) + ) ]; - if (max) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "56851009", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Maximum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: max - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - if (min) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "255605001", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Minimum" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: min - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - if (mean) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "373098007", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Mean" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: mean - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } - if (stdDev) { - measurements.push({ - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "386136009", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Standard Deviation" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: - unit2CodingValue(modalityUnit), - NumericValue: stdDev - }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } - }); - } return this.getMeasurement(measurements); } } diff --git a/src/utilities/TID300/Tid320ContentItem.js b/src/utilities/TID300/Tid320ContentItem.js new file mode 100644 index 00000000..b78d5d92 --- /dev/null +++ b/src/utilities/TID300/Tid320ContentItem.js @@ -0,0 +1,41 @@ +/** + * Builds a DICOM SR ContentSequence block for geometric measurements + * that share the same structure across tools (Circle, Ellipse, Polyline, etc.) + */ +export default class Tid320ContentItem { + constructor({ + graphicType, + graphicData, + use3DSpatialCoordinates = false, + referencedSOPSequence, + referencedFrameOfReferenceUID + }) { + this.graphicType = graphicType; + this.graphicData = graphicData; + this.use3DSpatialCoordinates = use3DSpatialCoordinates; + this.referencedSOPSequence = referencedSOPSequence; + this.referencedFrameOfReferenceUID = referencedFrameOfReferenceUID; + } + + contentItem() { + const content = { + RelationshipType: "INFERRED FROM", + ValueType: this.use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", + GraphicType: this.graphicType, + GraphicData: this.graphicData + }; + + if (this.use3DSpatialCoordinates) { + content.ReferencedFrameOfReferenceUID = + this.referencedFrameOfReferenceUID; + } else { + content.ContentSequence = { + RelationshipType: "SELECTED FROM", + ValueType: "IMAGE", + ReferencedSOPSequence: this.referencedSOPSequence + }; + } + + return content; + } +} diff --git a/src/utilities/TID300/buildContentSequence.js b/src/utilities/TID300/buildContentSequence.js deleted file mode 100644 index e156b6f8..00000000 --- a/src/utilities/TID300/buildContentSequence.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Builds a DICOM SR ContentSequence block for geometric measurements - * that share the same structure across tools (Circle, Ellipse, Polyline, etc.) - */ -export default function buildContentSequence({ - graphicType, - graphicData, - use3DSpatialCoordinates = false, - referencedSOPSequence, - referencedFrameOfReferenceUID -}) { - const content = { - RelationshipType: "INFERRED FROM", - ValueType: use3DSpatialCoordinates ? "SCOORD3D" : "SCOORD", - GraphicType: graphicType, - GraphicData: graphicData - }; - - if (use3DSpatialCoordinates) { - content.ReferencedFrameOfReferenceUID = referencedFrameOfReferenceUID; - } else { - content.ContentSequence = { - RelationshipType: "SELECTED FROM", - ValueType: "IMAGE", - ReferencedSOPSequence: referencedSOPSequence - }; - } - - return content; -} diff --git a/src/utilities/TID300/unit2CodingValue.js b/src/utilities/TID300/unit2CodingValue.js index a0a371d9..92d3c24a 100644 --- a/src/utilities/TID300/unit2CodingValue.js +++ b/src/utilities/TID300/unit2CodingValue.js @@ -1,18 +1,174 @@ import log from "../../log.js"; -const MM_UNIT = { - CodeValue: "mm", - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", - CodeMeaning: "millimeter" -}; +const knownUnits = [ + // Standard UCUM units. + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "mm", + CodeMeaning: "millimeter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "mm2", + CodeMeaning: "SquareMilliMeter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "mm\xB2", + CodeMeaning: "SquareMilliMeter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "1", + CodeMeaning: "px" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "1", + CodeMeaning: "px\xB2" + }, + // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_83.html + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "[hnsf'U]", + CodeMeaning: "Hounsfield unit" + }, + // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_84.html + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "{counts}", + CodeMeaning: "Counts" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "{counts}/s", + CodeMeaning: "Counts per second" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "{propcounts}", + CodeMeaning: "Proportional to counts" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "{propcounts}/s", + CodeMeaning: "Proportional to counts per second" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "cm2", + CodeMeaning: "Centimeter**2" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "cm2/ml", + CodeMeaning: "Centimeter**2/milliliter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "%", + CodeMeaning: "Percent" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "Bq/ml", + CodeMeaning: "Becquerels/milliliter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "mg/min/ml", + CodeMeaning: "Milligrams/minute/milliliter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "umol/min/ml", + CodeMeaning: "Micromole/minute/milliliter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "ml/min/g", + CodeMeaning: "Milliliter/minute/gram" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "ml/g", + CodeMeaning: "Milliliter/gram" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "/cm", + CodeMeaning: "/Centimeter" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "umol/ml", + CodeMeaning: "Micromole/milliliter" + }, + // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_85.html + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "g/ml{SUVbw}", + CodeMeaning: "Standardized Uptake Value body weight" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "g/ml{SUVlbm}", + CodeMeaning: "Standardized Uptake Value lean body mass (James)" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "g/ml{SUVlbm(James128)}", + CodeMeaning: + "Standardized Uptake Value lean body mass (James 128 multiplier)" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "g/ml{SUVlbm(Janma)}", + CodeMeaning: "Standardized Uptake Value lean body mass (Janma)" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "cm2/ml{SUVbsa}", + CodeMeaning: "Standardized Uptake Value body surface area" + }, + { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "g/ml{SUVibw}", + CodeMeaning: "Standardized Uptake Value ideal body weight" + } +]; -const MM2_UNIT = { - CodeValue: "mm2", - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", - CodeMeaning: "SquareMilliMeter" -}; +// Create measurementMap from knownUnits for efficient lookup +const measurementMap = {}; +knownUnits.forEach(unit => { + measurementMap[unit.CodeValue] = unit; +}); const NO_UNIT = { CodeValue: "1", @@ -20,21 +176,10 @@ const NO_UNIT = { CodingSchemeVersion: "1.4", CodeMeaning: "px" }; - -const NO2_UNIT = NO_UNIT; - -const measurementMap = { - px: NO_UNIT, - mm: MM_UNIT, - mm2: MM2_UNIT, - "mm\xB2": MM2_UNIT, - "px\xB2": NO2_UNIT -}; const generateUnitMap = unit => { return { CodeValue: unit, - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", + CodingSchemeDesignator: "99dcmjsUnit", CodeMeaning: unit }; }; From 712662a32d4e9632a3f828980e027f1754e5783f Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Tue, 2 Dec 2025 10:51:15 +0000 Subject: [PATCH 08/14] corrected px code unit --- src/utilities/TID300/unit2CodingValue.js | 28 ++++++++++-------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/utilities/TID300/unit2CodingValue.js b/src/utilities/TID300/unit2CodingValue.js index 92d3c24a..785b0367 100644 --- a/src/utilities/TID300/unit2CodingValue.js +++ b/src/utilities/TID300/unit2CodingValue.js @@ -20,18 +20,6 @@ const knownUnits = [ CodeValue: "mm\xB2", CodeMeaning: "SquareMilliMeter" }, - { - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", - CodeValue: "1", - CodeMeaning: "px" - }, - { - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", - CodeValue: "1", - CodeMeaning: "px\xB2" - }, // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_83.html { CodingSchemeDesignator: "UCUM", @@ -164,18 +152,23 @@ const knownUnits = [ } ]; -// Create measurementMap from knownUnits for efficient lookup -const measurementMap = {}; +// Create unitCodeMap from knownUnits for efficient lookup +const unitCodeMap = {}; knownUnits.forEach(unit => { - measurementMap[unit.CodeValue] = unit; + unitCodeMap[unit.CodeValue] = unit; }); +const noUnitCodeValues = ["px", "px\xB2"]; const NO_UNIT = { CodeValue: "1", CodingSchemeDesignator: "UCUM", CodingSchemeVersion: "1.4", CodeMeaning: "px" }; +noUnitCodeValues.forEach(codeValue => { + unitCodeMap[codeValue] = NO_UNIT; +}); + const generateUnitMap = unit => { return { CodeValue: unit, @@ -191,13 +184,14 @@ const unit2CodingValue = units => { if (!units) return NO_UNIT; const space = units.indexOf(" "); const baseUnit = space === -1 ? units : units.substring(0, space); - const codingUnit = measurementMap[units] || measurementMap[baseUnit]; + const codingUnit = unitCodeMap[units] || unitCodeMap[baseUnit]; if (!codingUnit) { + log.error("Unspecified units", units); return generateUnitMap(units); } return codingUnit; }; -unit2CodingValue.measurementMap = measurementMap; +unit2CodingValue.measurementMap = unitCodeMap; export default unit2CodingValue; From be7a5f84862cf6da823d7f868aa08ec708d44f23 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Wed, 3 Dec 2025 05:05:30 +0000 Subject: [PATCH 09/14] corrected ellipse area unit --- src/utilities/TID300/unit2CodingValue.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/utilities/TID300/unit2CodingValue.js b/src/utilities/TID300/unit2CodingValue.js index 785b0367..c539e966 100644 --- a/src/utilities/TID300/unit2CodingValue.js +++ b/src/utilities/TID300/unit2CodingValue.js @@ -14,12 +14,6 @@ const knownUnits = [ CodeValue: "mm2", CodeMeaning: "SquareMilliMeter" }, - { - CodingSchemeDesignator: "UCUM", - CodingSchemeVersion: "1.4", - CodeValue: "mm\xB2", - CodeMeaning: "SquareMilliMeter" - }, // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_83.html { CodingSchemeDesignator: "UCUM", @@ -169,6 +163,13 @@ noUnitCodeValues.forEach(codeValue => { unitCodeMap[codeValue] = NO_UNIT; }); +unitCodeMap["mm\xB2"] = { + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeValue: "mm2", + CodeMeaning: "SquareMilliMeter" +}; + const generateUnitMap = unit => { return { CodeValue: unit, From b5245cb50e614fe4aab8ecd7076ef4d7e53e0cc1 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Wed, 3 Dec 2025 05:17:38 +0000 Subject: [PATCH 10/14] Resolved test failed issue for Ellipse Unit --- src/utilities/TID300/Ellipse.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 2437e6e3..eb79b5a5 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -26,11 +26,6 @@ export default class Ellipse extends TID300Measurement { }); const measurementConfigs = [ - { - value: area, - unit: areaUnit, - builder: MeasurementBuilder.createAreaMeasurement - }, { value: max, unit: modalityUnit, From 0595cd680a84d804ea836284740f7bdeb0fa9aab Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Thu, 4 Dec 2025 06:50:51 +0000 Subject: [PATCH 11/14] Corrected renamed as per review comment --- src/utilities/MeasurementBuilder.js | 2 +- src/utilities/TID300/Bidirectional.js | 6 +++--- src/utilities/TID300/Calibration.js | 4 ++-- src/utilities/TID300/Circle.js | 4 ++-- src/utilities/TID300/CobbAngle.js | 4 ++-- src/utilities/TID300/Ellipse.js | 4 ++-- src/utilities/TID300/Point.js | 4 ++-- src/utilities/TID300/Polygon.js | 4 ++-- src/utilities/TID300/Polyline.js | 4 ++-- .../TID300/{Tid320ContentItem.js => TID320ContentItem.js} | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) rename src/utilities/TID300/{Tid320ContentItem.js => TID320ContentItem.js} (96%) diff --git a/src/utilities/MeasurementBuilder.js b/src/utilities/MeasurementBuilder.js index 6178f042..9b0a3afc 100644 --- a/src/utilities/MeasurementBuilder.js +++ b/src/utilities/MeasurementBuilder.js @@ -7,7 +7,7 @@ class MeasurementBuilder { codeMeaning, value, unit, - annotationIndex + annotationIndex // Used to provide correct ReferencedContentItemIdentifier to the SCOORD. ) { return { RelationshipType: "CONTAINS", diff --git a/src/utilities/TID300/Bidirectional.js b/src/utilities/TID300/Bidirectional.js index 9ea6eb90..f2e0dc69 100644 --- a/src/utilities/TID300/Bidirectional.js +++ b/src/utilities/TID300/Bidirectional.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; export default class Bidirectional extends TID300Measurement { contentItem() { @@ -24,7 +24,7 @@ export default class Bidirectional extends TID300Measurement { use3DSpatialCoordinates }); - const longAxisContentSequence = new Tid320ContentItem({ + const longAxisContentSequence = new TID320ContentItem({ graphicType: "POLYLINE", graphicData: longAxisGraphicData, use3DSpatialCoordinates, @@ -32,7 +32,7 @@ export default class Bidirectional extends TID300Measurement { referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID }).contentItem(); - const shortAxisContentSequence = new Tid320ContentItem({ + const shortAxisContentSequence = new TID320ContentItem({ graphicType: "POLYLINE", graphicData: shortAxisGraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Calibration.js b/src/utilities/TID300/Calibration.js index 6f443515..8c6627a1 100644 --- a/src/utilities/TID300/Calibration.js +++ b/src/utilities/TID300/Calibration.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; export default class Calibration extends TID300Measurement { contentItem() { @@ -19,7 +19,7 @@ export default class Calibration extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = new Tid320ContentItem({ + const graphicContentSequence = new TID320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index a9958883..748b50e4 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Circle extends TID300Measurement { @@ -79,7 +79,7 @@ export default class Circle extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: new Tid320ContentItem({ + ContentSequence: new TID320ContentItem({ graphicType: "CIRCLE", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/CobbAngle.js b/src/utilities/TID300/CobbAngle.js index a2129f8a..a074988d 100644 --- a/src/utilities/TID300/CobbAngle.js +++ b/src/utilities/TID300/CobbAngle.js @@ -1,5 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; export default class CobbAngle extends TID300Measurement { contentItem() { @@ -19,7 +19,7 @@ export default class CobbAngle extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = new Tid320ContentItem({ + const graphicContentSequence = new TID320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index eb79b5a5..e893a4e2 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Ellipse extends TID300Measurement { @@ -61,7 +61,7 @@ export default class Ellipse extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), NumericValue: area }, - ContentSequence: new Tid320ContentItem({ + ContentSequence: new TID320ContentItem({ graphicType: "ELLIPSE", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Point.js b/src/utilities/TID300/Point.js index 9d2d9410..01777584 100644 --- a/src/utilities/TID300/Point.js +++ b/src/utilities/TID300/Point.js @@ -1,5 +1,5 @@ import TID300Measurement from "./TID300Measurement.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; export default class Point extends TID300Measurement { contentItem() { @@ -16,7 +16,7 @@ export default class Point extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = new Tid320ContentItem({ + const graphicContentSequence = new TID320ContentItem({ graphicType: "POINT", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Polygon.js b/src/utilities/TID300/Polygon.js index 11e669db..b26cf4ae 100644 --- a/src/utilities/TID300/Polygon.js +++ b/src/utilities/TID300/Polygon.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement.js"; import unit2CodingValue from "./unit2CodingValue.js"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; export default class Polygon extends TID300Measurement { contentItem() { @@ -20,7 +20,7 @@ export default class Polygon extends TID300Measurement { use3DSpatialCoordinates }); - const graphicContentSequence = new Tid320ContentItem({ + const graphicContentSequence = new TID320ContentItem({ graphicType: "POLYGON", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index 4fdb255b..b0aa55ed 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -1,6 +1,6 @@ import TID300Measurement from "./TID300Measurement"; import unit2CodingValue from "./unit2CodingValue"; -import Tid320ContentItem from "./Tid320ContentItem.js"; +import TID320ContentItem from "./TID320ContentItem.js"; import MeasurementBuilder from "../MeasurementBuilder.js"; export default class Polyline extends TID300Measurement { @@ -68,7 +68,7 @@ export default class Polyline extends TID300Measurement { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: perimeter }, - ContentSequence: new Tid320ContentItem({ + ContentSequence: new TID320ContentItem({ graphicType: "POLYLINE", graphicData: GraphicData, use3DSpatialCoordinates, diff --git a/src/utilities/TID300/Tid320ContentItem.js b/src/utilities/TID300/TID320ContentItem.js similarity index 96% rename from src/utilities/TID300/Tid320ContentItem.js rename to src/utilities/TID300/TID320ContentItem.js index b78d5d92..81b2c7d1 100644 --- a/src/utilities/TID300/Tid320ContentItem.js +++ b/src/utilities/TID300/TID320ContentItem.js @@ -2,7 +2,7 @@ * Builds a DICOM SR ContentSequence block for geometric measurements * that share the same structure across tools (Circle, Ellipse, Polyline, etc.) */ -export default class Tid320ContentItem { +export default class TID320ContentItem { constructor({ graphicType, graphicData, From 096f2a34c7a5dc927129c5dbb3dcead98970acbd Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Thu, 4 Dec 2025 08:23:12 +0000 Subject: [PATCH 12/14] Doscstring updated --- src/utilities/MeasurementBuilder.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/utilities/MeasurementBuilder.js b/src/utilities/MeasurementBuilder.js index 9b0a3afc..1610a1f4 100644 --- a/src/utilities/MeasurementBuilder.js +++ b/src/utilities/MeasurementBuilder.js @@ -1,13 +1,40 @@ 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 // Used to provide correct ReferencedContentItemIdentifier to the SCOORD. + annotationIndex ) { return { RelationshipType: "CONTAINS", From 6ab3c627e640970518fa45c23fa032767f87deca Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Thu, 4 Dec 2025 13:23:01 +0000 Subject: [PATCH 13/14] Updated SRT to SCT --- src/utilities/MeasurementBuilder.js | 4 ++-- src/utilities/TID300/Bidirectional.js | 8 ++++---- src/utilities/TID300/Circle.js | 2 +- src/utilities/TID300/Ellipse.js | 6 +++--- src/utilities/TID300/Length.js | 4 ++-- src/utilities/TID300/unit2CodingValue.js | 19 +++++++++---------- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/utilities/MeasurementBuilder.js b/src/utilities/MeasurementBuilder.js index 1610a1f4..d8e82563 100644 --- a/src/utilities/MeasurementBuilder.js +++ b/src/utilities/MeasurementBuilder.js @@ -57,8 +57,8 @@ class MeasurementBuilder { static createAreaMeasurement(area, areaUnit, annotationIndex) { return MeasurementBuilder.createNumericMeasurement( - "G-A166", - "SRT", + "42798000", + "SCT", "Area", area, areaUnit, diff --git a/src/utilities/TID300/Bidirectional.js b/src/utilities/TID300/Bidirectional.js index f2e0dc69..b74959cb 100644 --- a/src/utilities/TID300/Bidirectional.js +++ b/src/utilities/TID300/Bidirectional.js @@ -45,8 +45,8 @@ export default class Bidirectional extends TID300Measurement { RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-A185", - CodingSchemeDesignator: "SRT", + CodeValue: "103339001", + CodingSchemeDesignator: "SCT", CodeMeaning: "Long Axis" }, MeasuredValueSequence: { @@ -59,8 +59,8 @@ export default class Bidirectional extends TID300Measurement { RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-A186", - CodingSchemeDesignator: "SRT", + CodeValue: "103340004", + CodingSchemeDesignator: "SCT", CodeMeaning: "Short Axis" }, MeasuredValueSequence: { diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index 748b50e4..91521155 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -71,7 +71,7 @@ export default class Circle extends TID300Measurement { RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-A197", + CodeValue: "131191004", CodingSchemeDesignator: "SRT", CodeMeaning: "Perimeter" }, diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index e893a4e2..0d0e05ea 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -53,9 +53,9 @@ export default class Ellipse extends TID300Measurement { RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-D7FE", - CodingSchemeDesignator: "SRT", - CodeMeaning: "AREA" + CodeValue: "42798000", + CodingSchemeDesignator: "SCT", + CodeMeaning: "Area" }, MeasuredValueSequence: { MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), diff --git a/src/utilities/TID300/Length.js b/src/utilities/TID300/Length.js index ddc1dcac..27ce6696 100644 --- a/src/utilities/TID300/Length.js +++ b/src/utilities/TID300/Length.js @@ -23,8 +23,8 @@ export default class Length extends TID300Measurement { RelationshipType: "CONTAINS", ValueType: "NUM", ConceptNameCodeSequence: { - CodeValue: "G-D7FE", - CodingSchemeDesignator: "SRT", + CodeValue: "410668003", + CodingSchemeDesignator: "SCT", CodeMeaning: "Length" }, MeasuredValueSequence: { diff --git a/src/utilities/TID300/unit2CodingValue.js b/src/utilities/TID300/unit2CodingValue.js index c539e966..baf0ae04 100644 --- a/src/utilities/TID300/unit2CodingValue.js +++ b/src/utilities/TID300/unit2CodingValue.js @@ -6,13 +6,13 @@ const knownUnits = [ CodingSchemeDesignator: "UCUM", CodingSchemeVersion: "1.4", CodeValue: "mm", - CodeMeaning: "millimeter" + CodeMeaning: "mm" }, { CodingSchemeDesignator: "UCUM", CodingSchemeVersion: "1.4", CodeValue: "mm2", - CodeMeaning: "SquareMilliMeter" + CodeMeaning: "mm2" }, // Units defined in https://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_83.html { @@ -167,15 +167,14 @@ unitCodeMap["mm\xB2"] = { CodingSchemeDesignator: "UCUM", CodingSchemeVersion: "1.4", CodeValue: "mm2", - CodeMeaning: "SquareMilliMeter" + CodeMeaning: "mm2" }; -const generateUnitMap = unit => { - return { - CodeValue: unit, - CodingSchemeDesignator: "99dcmjsUnit", - CodeMeaning: unit - }; +const MM_UNIT = { + CodeValue: "mm", + CodingSchemeDesignator: "UCUM", + CodingSchemeVersion: "1.4", + CodeMeaning: "millimeter" }; /** Converts the given unit into the * specified coding values. @@ -188,7 +187,7 @@ const unit2CodingValue = units => { const codingUnit = unitCodeMap[units] || unitCodeMap[baseUnit]; if (!codingUnit) { log.error("Unspecified units", units); - return generateUnitMap(units); + return MM_UNIT; } return codingUnit; }; From c2cc162eee19735d0b3aac2e535ce48beaecb855 Mon Sep 17 00:00:00 2001 From: nithin-trenser Date: Wed, 10 Dec 2025 09:36:00 +0000 Subject: [PATCH 14/14] Fixed dsrdump errors for polyline --- src/utilities/MeasurementBuilder.js | 90 +++++++++++++++++++++++------ src/utilities/TID300/Circle.js | 40 ++++++------- src/utilities/TID300/Ellipse.js | 40 ++++++------- src/utilities/TID300/Polyline.js | 39 ++++++------- 4 files changed, 126 insertions(+), 83 deletions(-) diff --git a/src/utilities/MeasurementBuilder.js b/src/utilities/MeasurementBuilder.js index d8e82563..cbadebe8 100644 --- a/src/utilities/MeasurementBuilder.js +++ b/src/utilities/MeasurementBuilder.js @@ -34,7 +34,8 @@ class MeasurementBuilder { codeMeaning, value, unit, - annotationIndex + annotationIndex, + { scoordContentItem = null } = {} ) { return { RelationshipType: "CONTAINS", @@ -48,76 +49,131 @@ class MeasurementBuilder { MeasurementUnitsCodeSequence: unit2CodingValue(unit), NumericValue: value }, - ContentSequence: { - RelationshipType: "INFERRED FROM", - ReferencedContentItemIdentifier: [1, 1, annotationIndex] - } + ContentSequence: scoordContentItem + ? scoordContentItem // FIRST item -> SCOORD + : { + RelationshipType: "INFERRED FROM", // Remaining items -> reference + ReferencedContentItemIdentifier: [1, 1, annotationIndex] + } }; } - static createAreaMeasurement(area, areaUnit, annotationIndex) { + static createAreaMeasurement( + area, + areaUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "42798000", "SCT", "Area", area, areaUnit, - annotationIndex + annotationIndex, + { scoordContentItem } ); } - static createRadiusMeasurement(radius, radiusUnit, annotationIndex) { + static createRadiusMeasurement( + radius, + radiusUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "131190003", "SCT", "Radius", radius, radiusUnit, - annotationIndex + annotationIndex, + { scoordContentItem } ); } - static createMaxMeasurement(max, modalityUnit, annotationIndex) { + static createMaxMeasurement( + max, + modalityUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "56851009", "SCT", "Maximum", max, modalityUnit, - annotationIndex + annotationIndex, + { scoordContentItem } ); } - static createMinMeasurement(min, modalityUnit, annotationIndex) { + static createMinMeasurement( + min, + modalityUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "255605001", "SCT", "Minimum", min, modalityUnit, - annotationIndex + annotationIndex, + { scoordContentItem } ); } - static createMeanMeasurement(mean, modalityUnit, annotationIndex) { + static createMeanMeasurement( + mean, + modalityUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "373098007", "SCT", "Mean", mean, modalityUnit, - annotationIndex + annotationIndex, + { scoordContentItem } ); } - static createStdDevMeasurement(stdDev, modalityUnit, annotationIndex) { + static createStdDevMeasurement( + stdDev, + modalityUnit, + annotationIndex, + { scoordContentItem } + ) { return MeasurementBuilder.createNumericMeasurement( "386136009", "SCT", "Standard Deviation", stdDev, modalityUnit, - annotationIndex + annotationIndex, + { scoordContentItem } + ); + } + + static createPerimeterMeasurement( + perimeter, + unit, + annotationIndex, + { scoordContentItem } + ) { + return MeasurementBuilder.createNumericMeasurement( + "131191004", + "SCT", + "Perimeter", + perimeter, + unit, + annotationIndex, + { scoordContentItem } ); } } diff --git a/src/utilities/TID300/Circle.js b/src/utilities/TID300/Circle.js index 91521155..13fe8edf 100644 --- a/src/utilities/TID300/Circle.js +++ b/src/utilities/TID300/Circle.js @@ -34,6 +34,11 @@ export default class Circle extends TID300Measurement { }); const measurementConfigs = [ + { + value: perimeter, + unit: unit, + builder: MeasurementBuilder.createPerimeterMeasurement + }, { value: area, unit: areaUnit, @@ -66,31 +71,22 @@ export default class Circle extends TID300Measurement { } ]; + const scoordContentItem = new TID320ContentItem({ + graphicType: "CIRCLE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }).contentItem(); + const measurements = [ - { - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "131191004", - CodingSchemeDesignator: "SRT", - CodeMeaning: "Perimeter" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(unit), - NumericValue: perimeter - }, - ContentSequence: new TID320ContentItem({ - graphicType: "CIRCLE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }).contentItem() - }, ...measurementConfigs .filter(config => config.value !== undefined) - .map(config => - config.builder(config.value, config.unit, annotationIndex) + .map((config, index) => + config.builder(config.value, config.unit, annotationIndex, { + scoordContentItem: + index === 0 ? scoordContentItem : null + }) ) ]; diff --git a/src/utilities/TID300/Ellipse.js b/src/utilities/TID300/Ellipse.js index 0d0e05ea..a6ac2455 100644 --- a/src/utilities/TID300/Ellipse.js +++ b/src/utilities/TID300/Ellipse.js @@ -26,6 +26,11 @@ export default class Ellipse extends TID300Measurement { }); const measurementConfigs = [ + { + value: area, + unit: areaUnit, + builder: MeasurementBuilder.createAreaMeasurement + }, { value: max, unit: modalityUnit, @@ -48,31 +53,22 @@ export default class Ellipse extends TID300Measurement { } ]; + const scoordContentItem = new TID320ContentItem({ + graphicType: "ELLIPSE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }).contentItem(); + const measurements = [ - { - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "42798000", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Area" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(areaUnit), - NumericValue: area - }, - ContentSequence: new TID320ContentItem({ - graphicType: "ELLIPSE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }).contentItem() - }, ...measurementConfigs .filter(config => config.value !== undefined) - .map(config => - config.builder(config.value, config.unit, annotationIndex) + .map((config, index) => + config.builder(config.value, config.unit, annotationIndex, { + scoordContentItem: + index === 0 ? scoordContentItem : null + }) ) ]; diff --git a/src/utilities/TID300/Polyline.js b/src/utilities/TID300/Polyline.js index b0aa55ed..254c3c89 100644 --- a/src/utilities/TID300/Polyline.js +++ b/src/utilities/TID300/Polyline.js @@ -28,6 +28,11 @@ export default class Polyline extends TID300Measurement { }); const measurementConfigs = [ + { + value: perimeter, + unit: unit, + builder: MeasurementBuilder.createPerimeterMeasurement + }, { value: area, unit: areaUnit, @@ -54,32 +59,22 @@ export default class Polyline extends TID300Measurement { builder: MeasurementBuilder.createStdDevMeasurement } ]; + const scoordContentItem = new TID320ContentItem({ + graphicType: "POLYLINE", + graphicData: GraphicData, + use3DSpatialCoordinates, + referencedSOPSequence: ReferencedSOPSequence, + referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID + }).contentItem(); const measurements = [ - { - RelationshipType: "CONTAINS", - ValueType: "NUM", - ConceptNameCodeSequence: { - CodeValue: "131191004", - CodingSchemeDesignator: "SCT", - CodeMeaning: "Perimeter" - }, - MeasuredValueSequence: { - MeasurementUnitsCodeSequence: unit2CodingValue(unit), - NumericValue: perimeter - }, - ContentSequence: new TID320ContentItem({ - graphicType: "POLYLINE", - graphicData: GraphicData, - use3DSpatialCoordinates, - referencedSOPSequence: ReferencedSOPSequence, - referencedFrameOfReferenceUID: ReferencedFrameOfReferenceUID - }).contentItem() - }, ...measurementConfigs .filter(config => config.value !== undefined) - .map(config => - config.builder(config.value, config.unit, annotationIndex) + .map((config, index) => + config.builder(config.value, config.unit, annotationIndex, { + scoordContentItem: + index === 0 ? scoordContentItem : null + }) ) ];