rtkit 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,157 @@
1
+ module RTKIT
2
+
3
+ # Contains DICOM data and methods related to a RT Beam Limiting Device
4
+ # item, defined in a Beam.
5
+ #
6
+ # === Relations
7
+ #
8
+ # * A Beam has many Collimators.
9
+ #
10
+ class Collimator
11
+
12
+ # The Beam that the CollimatorSetup is defined for.
13
+ attr_reader :beam
14
+ # Collimator boundaries (for multi leaf collimators only: the number of boundaries equals the number of leaves + 1).
15
+ attr_reader :boundaries
16
+ # The number of Leaf/Jaw (Collimator) Pairs.
17
+ attr_reader :num_pairs
18
+ # Collimator type.
19
+ attr_reader :type
20
+
21
+ # Creates a new Collimator instance from the RT Beam Limiting Device item of the RTPlan file.
22
+ # Returns the Collimator instance.
23
+ #
24
+ # === Parameters
25
+ #
26
+ # * <tt>coll_item</tt> -- The patient setup item from the DObject of a RTPlan file.
27
+ # * <tt>beam</tt> -- The Beam instance that this Collimator belongs to.
28
+ #
29
+ def self.create_from_item(coll_item, beam)
30
+ raise ArgumentError, "Invalid argument 'coll_item'. Expected DICOM::Item, got #{coll_item.class}." unless coll_item.is_a?(DICOM::Item)
31
+ raise ArgumentError, "Invalid argument 'beam'. Expected Beam, got #{beam.class}." unless beam.is_a?(Beam)
32
+ options = Hash.new
33
+ # Values from the Beam Limiting Device Type Item:
34
+ type = coll_item.value(COLL_TYPE)
35
+ num_pairs = coll_item.value(NR_COLLIMATORS)
36
+ boundaries = coll_item.value(COLL_BOUNDARIES)
37
+ # Create the Collimator instance:
38
+ c = self.new(type, num_pairs, beam, :boundaries => boundaries)
39
+ return c
40
+ end
41
+
42
+ # Creates a new Collimator instance.
43
+ #
44
+ # === Parameters
45
+ #
46
+ # * <tt>type</tt> -- String. The RT Beam Limiting Device Type.
47
+ # * <tt>num_pairs</tt> -- Integer. The Number of Leaf/Jaw Pairs.
48
+ # * <tt>beam</tt> -- The Beam instance that this Collimator belongs to.
49
+ # * <tt>options</tt> -- A hash of parameters.
50
+ #
51
+ # === Options
52
+ #
53
+ # * <tt>:boundaries</tt> -- Array/String. The Leaf Position Boundaries (300A,00BE). Defaults to nil.
54
+ #
55
+ def initialize(type, num_pairs, beam, options={})
56
+ raise ArgumentError, "Invalid argument 'beam'. Expected Beam, got #{beam.class}." unless beam.is_a?(Beam)
57
+ # Set values:
58
+ self.type = type
59
+ self.num_pairs = num_pairs
60
+ self.boundaries = options[:boundaries]
61
+ # Set references:
62
+ @beam = beam
63
+ # Register ourselves with the Beam:
64
+ @beam.add_collimator(self)
65
+ end
66
+
67
+ # Returns true if the argument is an instance with attributes equal to self.
68
+ #
69
+ def ==(other)
70
+ if other.respond_to?(:to_collimator)
71
+ other.send(:state) == state
72
+ end
73
+ end
74
+
75
+ alias_method :eql?, :==
76
+
77
+ # Generates a Fixnum hash value for this instance.
78
+ #
79
+ def hash
80
+ state.hash
81
+ end
82
+
83
+ # Creates and returns a Beam Limiting Device Sequence Item
84
+ # from the attributes of the Collimator.
85
+ #
86
+ def to_item
87
+ item = DICOM::Item.new
88
+ item.add(DICOM::Element.new(ROI_COLOR, @color))
89
+ s = DICOM::Sequence.new(CONTOUR_SQ)
90
+ item.add(s)
91
+ item.add(DICOM::Element.new(REF_ROI_NUMBER, @number.to_s))
92
+ # Add Contour items to the Contour Sequence (one or several items per Slice):
93
+ @slices.each do |slice|
94
+ slice.contours.each do |contour|
95
+ s.add_item(contour.to_item)
96
+ end
97
+ end
98
+ return item
99
+ end
100
+
101
+ # Sets new Leaf Position Boundaries (an array of positions).
102
+ #
103
+ # === Parameters
104
+ #
105
+ # * <tt>value</tt> -- Array/String. The Leaf Position Boundaries (300A,00BE).
106
+ #
107
+ def boundaries=(value)
108
+ if !value
109
+ @boundaries = nil
110
+ elsif value.is_a?(Array)
111
+ @boundaries = value
112
+ else
113
+ # Split the string & convert to float:
114
+ @boundaries = value.to_s.split("\\").collect {|str| str.to_f}
115
+ end
116
+ end
117
+
118
+ # Sets a new RT Beam Limiting Device Type.
119
+ #
120
+ # === Parameters
121
+ #
122
+ # * <tt>value</tt> -- Integer. The Number of Leaf/Jaw Pairs (300A,00BC).
123
+ #
124
+ def num_pairs=(value)
125
+ raise ArgumentError, "Argument 'value' must be defined (got #{value.class})." unless value
126
+ @num_pairs = value.to_i
127
+ end
128
+
129
+ # Returns self.
130
+ #
131
+ def to_collimator
132
+ self
133
+ end
134
+
135
+ # Sets a new RT Beam Limiting Device Type.
136
+ #
137
+ # === Parameters
138
+ #
139
+ # * <tt>value</tt> -- String. The RT Beam Limiting Device Type (300A,00B8).
140
+ #
141
+ def type=(value)
142
+ raise ArgumentError, "Argument 'value' must be defined (got #{value.class})." unless value
143
+ @type = value.to_s
144
+ end
145
+
146
+
147
+ private
148
+
149
+
150
+ # Returns the attributes of this instance in an array (for comparison purposes).
151
+ #
152
+ def state
153
+ [@boundaries, @num_pairs, @type]
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,143 @@
1
+ module RTKIT
2
+
3
+ # Contains DICOM data and methods related to a RT Beam Limiting Device
4
+ # Position item, defined in a ControlPoint.
5
+ #
6
+ # === Relations
7
+ #
8
+ # * A ControlPoint has many CollimatorSetups.
9
+ #
10
+ class CollimatorSetup
11
+
12
+ # The ControlPoint that the Collimator is defined for.
13
+ attr_reader :control_point
14
+ # Collimator pairs of positions.
15
+ attr_reader :positions
16
+ # Collimator type.
17
+ attr_reader :type
18
+
19
+ # Creates a new CollimatorSetup instance from the RT Beam Limiting Device Position item of the RTPlan file.
20
+ # Returns the CollimatorSetup instance.
21
+ #
22
+ # === Parameters
23
+ #
24
+ # * <tt>coll_item</tt> -- The RT Beam Limiting Device Position item from the DObject of a RTPlan file.
25
+ # * <tt>control_point</tt> -- The ControlPoint instance that this CollimatorSetup belongs to.
26
+ #
27
+ def self.create_from_item(coll_item, control_point)
28
+ raise ArgumentError, "Invalid argument 'coll_item'. Expected DICOM::Item, got #{coll_item.class}." unless coll_item.is_a?(DICOM::Item)
29
+ raise ArgumentError, "Invalid argument 'control_point'. Expected ControlPoint, got #{control_point.class}." unless control_point.is_a?(ControlPoint)
30
+ options = Hash.new
31
+ # Values from the Beam Limiting Device Position Item:
32
+ type = coll_item.value(COLL_TYPE)
33
+ positions = coll_item.value(COLL_POS)
34
+ #positions = coll_item.value(COLL_POS).split("\\").collect {|str| str.to_f}
35
+ # Regroup the positions values so they appear in pairs:
36
+ #positions = positions.each_slice(2).collect{|i| i}.transpose
37
+ # Create the Collimator instance:
38
+ c = self.new(type, positions, control_point)
39
+ return c
40
+ end
41
+
42
+ # Creates a new CollimatorSetup instance.
43
+ #
44
+ # === Parameters
45
+ #
46
+ # * <tt>type</tt> -- String. The RT Beam Limiting Device Type.
47
+ # * <tt>positions</tt> -- Array. The collimator positions, organised in pairs of two values.
48
+ # * <tt>control_point</tt> -- The ControlPoint instance that this CollimatorSetup belongs to.
49
+ #
50
+ def initialize(type, positions, control_point)
51
+ raise ArgumentError, "Invalid argument 'control_point'. Expected ControlPoint, got #{control_point.class}." unless control_point.is_a?(ControlPoint)
52
+ # Set values:
53
+ self.type = type
54
+ self.positions = positions
55
+ # Set references:
56
+ @control_point = control_point
57
+ # Register ourselves with the ControlPoint:
58
+ @control_point.add_collimator(self)
59
+ end
60
+
61
+ # Returns true if the argument is an instance with attributes equal to self.
62
+ #
63
+ def ==(other)
64
+ if other.respond_to?(:to_collimator_setup)
65
+ other.send(:state) == state
66
+ end
67
+ end
68
+
69
+ alias_method :eql?, :==
70
+
71
+ # Generates a Fixnum hash value for this instance.
72
+ #
73
+ def hash
74
+ state.hash
75
+ end
76
+
77
+ # Sets new Leaf/Jaw Positions (an array with pairs of positions).
78
+ #
79
+ # === Parameters
80
+ #
81
+ # * <tt>value</tt> -- Array. The Leaf/Jaw positions (300A,011C).
82
+ #
83
+ def positions=(value)
84
+ raise ArgumentError, "Argument 'value' must be defined (got #{value.class})." unless value
85
+ if value.is_a?(Array)
86
+ @positions = value
87
+ else
88
+ # Split the string, convert to float, and regroup the positions so they appear in pairs:
89
+ positions = value.to_s.split("\\").collect {|str| str.to_f}
90
+ if positions.length == 2
91
+ @positions = [positions]
92
+ else
93
+ @positions = positions.each_slice(2).collect{|i| i}.transpose
94
+ end
95
+ end
96
+ end
97
+
98
+ # Returns self.
99
+ #
100
+ def to_collimator_setup
101
+ self
102
+ end
103
+
104
+ # Creates and returns a Beam Limiting Device Position Sequence Item
105
+ # from the attributes of the CollimatorSetup.
106
+ #
107
+ def to_item
108
+ item = DICOM::Item.new
109
+ item.add(DICOM::Element.new(COLL_TYPE, @type))
110
+ item.add(DICOM::Element.new(COLL_POS, positions_string))
111
+ return item
112
+ end
113
+
114
+ # Sets a new RT Beam Limiting Device Type.
115
+ #
116
+ # === Parameters
117
+ #
118
+ # * <tt>value</tt> -- String. The RT Beam Limiting Device Type (300A,00B8).
119
+ #
120
+ def type=(value)
121
+ raise ArgumentError, "Argument 'value' must be defined (got #{value.class})." unless value
122
+ @type = value.to_s
123
+ end
124
+
125
+
126
+ private
127
+
128
+
129
+ # Returns the position values converted to a '\' separated string with values
130
+ # appearing in the order as specified by the dicom standard.
131
+ #
132
+ def positions_string
133
+ @positions.transpose.join("\\")
134
+ end
135
+
136
+ # Returns the attributes of this instance in an array (for comparison purposes).
137
+ #
138
+ def state
139
+ [@positions, @type]
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,215 @@
1
+ module RTKIT
2
+
3
+ # Instance Creation Date.
4
+ IMAGE_DATE = '0008,0012'
5
+ # Instance Creation Time.
6
+ IMAGE_TIME = '0008,0013'
7
+ # SOP Class UID.
8
+ SOP_CLASS = '0008,0016'
9
+ # SOP Instance UID.
10
+ SOP_UID = '0008,0018'
11
+ # Study Date.
12
+ STUDY_DATE = '0008,0020'
13
+ # Series Date.
14
+ SERIES_DATE = '0008,0021'
15
+ # Study Time.
16
+ STUDY_TIME = '0008,0030'
17
+ # Series Time.
18
+ SERIES_TIME = '0008,0031'
19
+ # Modality.
20
+ MODALITY = '0008,0060'
21
+ # Study description.
22
+ STUDY_DESCR = '0008,1030'
23
+ # Series description.
24
+ SERIES_DESCR = '0008,103E'
25
+ # Referenced SOP Class UID.
26
+ REF_SOP_CLASS_UID = '0008,1150'
27
+ # Referenced SOP Instance UID.
28
+ REF_SOP_UID = '0008,1155'
29
+ # Patient's Name.
30
+ PATIENTS_NAME = '0010,0010'
31
+ # Patient's Name.
32
+ PATIENTS_ID = '0010,0020'
33
+ # Patient's Name.
34
+ BIRTH_DATE = '0010,0030'
35
+ # Patient's Name.
36
+ SEX = '0010,0040'
37
+ # Patient Position.
38
+ PATIENT_POSITION = '0018,5100'
39
+ # Study Instance UID.
40
+ STUDY_UID = '0020,000D'
41
+ # Series Instance UID.
42
+ SERIES_UID = '0020,000E'
43
+ # Study ID.
44
+ STUDY_ID = '0020,0010'
45
+ # Image Position (Patient).
46
+ IMAGE_POSITION = '0020,0032'
47
+ # Image Orientation (Patient):
48
+ IMAGE_ORIENTATION = '0020,0037'
49
+ # Frame of Reference UID.
50
+ FRAME_OF_REF = '0020,0052'
51
+ # Position Reference Indicator.
52
+ POS_REF_INDICATOR = '0020,1040'
53
+ # Number of Frames.
54
+ NR_FRAMES = '0028,0008'
55
+ # Rows.
56
+ ROWS = '0028,0010'
57
+ # Columns.
58
+ COLUMNS = '0028,0011'
59
+ # Pixel Spacing.
60
+ SPACING = '0028,0030'
61
+ # Image Plane Pixel Spacing.
62
+ IMAGE_PLANE_SPACING = '3002,0011'
63
+ # RT Image Position.
64
+ RT_IMAGE_POSITION = '3002,0012'
65
+ # Grid Frame Offset Vector.
66
+ GRID_FRAME_OFFSETS = '3004,000C'
67
+ # Dose Grid Scaling.
68
+ DOSE_GRID_SCALING = '3004,000E'
69
+ # Referenced Frame of Reference Sequence.
70
+ REF_FRAME_OF_REF_SQ = '3006,0010'
71
+ # RT Referenced Study Sequence.
72
+ RT_REF_STUDY_SQ = '3006,0012'
73
+ # RT Referenced Series Sequence.
74
+ RT_REF_SERIES_SQ = '3006,0014'
75
+ # Contour Image Sequence.
76
+ CONTOUR_IMAGE_SQ = '3006,0016'
77
+ # Structure Set ROI Sequence.
78
+ STRUCTURE_SET_ROI_SQ = '3006,0020'
79
+ # ROI Number.
80
+ ROI_NUMBER = '3006,0022'
81
+ # Referenced Frame of Reference UID.
82
+ REF_FRAME_OF_REF = '3006,0024'
83
+ # ROI Name.
84
+ ROI_NAME = '3006,0026'
85
+ # ROI Display Color.
86
+ ROI_COLOR = '3006,002A'
87
+ # ROI Generation Algorithm.
88
+ ROI_ALGORITHM = '3006,0036'
89
+ # ROI Contour Sequence.
90
+ ROI_CONTOUR_SQ = '3006,0039'
91
+ # Contour Sequence.
92
+ CONTOUR_SQ = '3006,0040'
93
+ # Contour Geometric Type.
94
+ CONTOUR_GEO_TYPE = '3006,0042'
95
+ # Number of Contour Points.
96
+ NR_CONTOUR_POINTS = '3006,0046'
97
+ # Contour Number.
98
+ CONTOUR_NUMBER = '3006,0048'
99
+ # Contour Data.
100
+ CONTOUR_DATA = '3006,0050'
101
+ # RT ROI Observations Sequence.
102
+ RT_ROI_OBS_SQ = '3006,0080'
103
+ # Obervation Number.
104
+ OBS_NUMBER = '3006,0082'
105
+ # Referenced ROI Number.
106
+ REF_ROI_NUMBER = '3006,0084'
107
+ # RT ROI Interpreted Type.
108
+ ROI_TYPE = '3006,00A4'
109
+ # ROI Interpreter.
110
+ ROI_INTERPRETER = '3006,00A6'
111
+ # Frame of Reference Relationship Sequence.
112
+ FRAME_OF_REF_REL_SQ = '3006,00C0'
113
+ # Fraction Group Sequence.
114
+ FRACTION_GROUP_SQ = '300A,0070'
115
+ # Fraction Group Number.
116
+ FRACTION_GROUP_NUMBER = '300A,0071'
117
+ # Beam Meterset.
118
+ BEAM_METERSET = '300A,0086'
119
+ # Beam Sequence.
120
+ BEAM_SQ = '300A,00B0'
121
+ # Treatment Machine Name.
122
+ MACHINE_NAME = '300A,00B2'
123
+ # Primary Dosimeter Unit.
124
+ DOSIMETER_UNIT = '300A,00B3'
125
+ # Source-Axis Distance.
126
+ SAD = '300A,00B4'
127
+ # RT Beam Limiting Device Sequence.
128
+ COLL_SQ = '300A,00B6'
129
+ # RT Beam Limiting Device Type.
130
+ COLL_TYPE = '300A,00B8'
131
+ # Number of Leaf/Jaw Pairs.
132
+ NR_COLLIMATORS = '300A,00BC'
133
+ # Leaf Position Boundaries.
134
+ COLL_BOUNDARIES = '300A,00BE'
135
+ # Beam Number.
136
+ BEAM_NUMBER = '300A,00C0'
137
+ # Beam Name.
138
+ BEAM_NAME = '300A,00C2'
139
+ # Beam Description.
140
+ BEAM_DESCR = '300A,00C3'
141
+ # Beam Type.
142
+ BEAM_TYPE = '300A,00C4'
143
+ # Radiation Type.
144
+ RAD_TYPE = '300A,00C6'
145
+ # Treatment Delivery Type.
146
+ DELIVERY_TYPE = '300A,00CE'
147
+ # Final Cumulative Meterset Weight.
148
+ FINAL_METERSET_WEIGHT = '300A,010E'
149
+ # Control Point Sequence.
150
+ CONTROL_POINT_SQ = '300A,0111'
151
+ # Cumulative Meterset Weight.
152
+ CONTROL_POINT_INDEX = '300A,0112'
153
+ # Nominal Beam Energy.
154
+ BEAM_ENERGY = '300A,0114'
155
+ # RT Beam Limiting Device Type Sequence.
156
+ COLL_POS_SQ = '300A,011A'
157
+ # Leaf/Jaw Positions.
158
+ COLL_POS = '300A,011C'
159
+ # Gantry Angle.
160
+ GANTRY_ANGLE = '300A,011E'
161
+ # Gantry Rotation Direction.
162
+ GANTRY_DIRECTION = '300A,011F'
163
+ # Beam Limiting Device Angle.
164
+ COLL_ANGLE = '300A,0120'
165
+ # Beam Limiting Device Rotation Direction.
166
+ COLL_DIRECTION = '300A,0121'
167
+ # Patient Support Angle.
168
+ PEDESTAL_ANGLE = '300A,0122'
169
+ # Patient Support Rotation Direction.
170
+ PEDESTAL_DIRECTION = '300A,0123'
171
+ # Table Top Eccentric Angle.
172
+ TABLE_TOP_ANGLE = '300A,0125'
173
+ # Table Top Eccentric Rotation Direction.
174
+ TABLE_TOP_DIRECTION = '300A,0126'
175
+ # Table Top Vertical Position.
176
+ TABLE_TOP_VERTICAL = '300A,0128'
177
+ # Table Top Longitudinal Position.
178
+ TABLE_TOP_LONGITUDINAL = '300A,0129'
179
+ # Table Top Lateral Position.
180
+ TABLE_TOP_LATERAL = '300A,012A'
181
+ # Isocenter Position.
182
+ ISO_POS = '300A,012C'
183
+ # Source to Surface Distance.
184
+ SSD = '300A,0130'
185
+ # Cumulative Meterset Weight.
186
+ CUM_METERSET_WEIGHT = '300A,0134'
187
+ # Patient Setup Sequence.
188
+ PATIENT_SETUP_SQ = '300A,0180'
189
+ # Patient Setup Number.
190
+ PATIENT_SETUP_NUMBER = '300A,0182'
191
+ # Setup technique.
192
+ SETUP_TECHNIQUE = '300A,01B0'
193
+ # Table Top Vertical Setup Displacement.
194
+ OFFSET_VERTICAL = '300A,01D2'
195
+ # Table Top Longitudinal Setup Displacement.
196
+ OFFSET_LONG = '300A,01D4'
197
+ # Table Top Lateral Setup Displacement.
198
+ OFFSET_LATERAL = '300A,01D6'
199
+ # Referenced RT Plan Sequence.
200
+ REF_PLAN_SQ = '300C,0002'
201
+ # Referenced Beam Sequence.
202
+ REF_BEAM_SQ = '300C,0004'
203
+ # Referenced Beam Number.
204
+ REF_BEAM_NUMBER = '300C,0006'
205
+ # Referenced Structure Set Sequence.
206
+ REF_STRUCT_SQ = '300C,0060'
207
+
208
+ # The modalities that contain multiple images per series.
209
+ IMAGE_SERIES = ['CT', 'MR']
210
+ # The modalities that contain pixel data:
211
+ IMAGE_MODALITIES = ['CT', 'MR', 'RTDOSE', 'RTIMAGE']
212
+ # The modalities supported by RTKIT.
213
+ SUPPORTED_MODALITIES = ['CT', 'MR', 'RTDOSE', 'RTIMAGE', 'RTPLAN', 'RTSTRUCT']
214
+
215
+ end