Class: RTKIT::ROI
- Inherits:
-
Object
- Object
- RTKIT::ROI
- Defined in:
- lib/rtkit/roi.rb
Overview
Contains DICOM data and methods related to a Region of Interest, defined in a Structure Set.
Relations
-
An image series has many ROIs, defined through a Structure Set.
-
An image slice has only the ROIs which are contoured in that particular slice in the Structure Set.
-
A ROI has many Slices.
Instance Attribute Summary collapse
-
#algorithm ⇒ Object
ROI Generation Algorithm.
-
#color ⇒ Object
ROI Display Color.
-
#frame ⇒ Object
readonly
The Frame which this ROI belongs to.
-
#interpreter ⇒ Object
ROI Interpreter.
-
#name ⇒ Object
ROI Name.
-
#number ⇒ Object
ROI Number (Integer).
-
#slices ⇒ Object
readonly
An array containing the Slices that the ROI is defined in.
-
#struct ⇒ Object
readonly
The StructureSet that the ROI is defined in.
-
#type ⇒ Object
RT ROI Interpreted Type.
Class Method Summary collapse
-
.create_from_items(roi_item, contour_item, rt_item, struct) ⇒ Object
Creates a new ROI instance from the three items of the structure set which contains the information related to a particular ROI.
Instance Method Summary collapse
-
#==(other) ⇒ Object
(also: #eql?)
Returns true if the argument is an instance with attributes equal to self.
-
#add_slice(slice) ⇒ Object
Adds a Slice instance to this ROI.
-
#attach_to(series) ⇒ Object
Attaches a ROI to a specified ImageSeries, by setting the ROIs frame reference to the Frame which the ImageSeries belongs to, and setting the Image reference of each of the Slices belonging to the ROI to an Image instance which matches the coordinates of the Slice’s Contour(s).
-
#bin_volume(image_volume = @struct.image_series.first) ⇒ Object
Creates a binary volume object consisting of a series of binary (segmented) images, extracted from the contours defined for the slices of this ROI.
-
#contour_item ⇒ Object
Creates and returns a ROI Contour Sequence Item from the attributes of the ROI.
-
#create_slices(contour_sequence) ⇒ Object
Iterates the Contour Sequence Items, collects Contour Items for each slice and passes them along to the Slice class.
-
#distribution(dose_volume = @struct.plan.rt_dose.sum) ⇒ Object
Creates a DoseDistribution based on the delineation of this ROI in the specified RTDose series.
-
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
-
#image_series ⇒ Object
Returns the ImageSeries instance that this ROI is defined in.
-
#initialize(name, number, frame, struct, options = {}) ⇒ ROI
constructor
Creates a new ROI instance.
-
#num_contours ⇒ Object
Returns the number of Contours belonging to this ROI through its Slices.
-
#obs_item ⇒ Object
Creates and returns a RT ROI Obervations Sequence Item from the attributes of the ROI.
-
#remove_references ⇒ Object
Removes the parent references of the ROI (StructureSet and Frame).
-
#size ⇒ Object
Calculates the size (volume) of the ROI by evaluating the ROI’s delination in the referenced image series.
-
#slice(*args) ⇒ Object
Returns the Slice instance mathcing the specified SOP Instance UID (if an argument is used).
-
#ss_item ⇒ Object
Creates and returns a Structure Set ROI Sequence Item from the attributes of the ROI.
-
#to_roi ⇒ Object
Returns self.
Constructor Details
#initialize(name, number, frame, struct, options = {}) ⇒ ROI
Creates a new ROI instance.
Parameters
-
name
– String. The ROI Name. -
number
– Integer. The ROI Number. -
frame
– The Frame instance that this ROI belongs to. -
struct
– The StructureSet instance that this ROI belongs to. -
options
– A hash of parameters.
Options
-
:algorithm
– String. The ROI Generation Algorithm. Defaults to ‘Automatic’. -
:color
– String. The ROI Display Color. Defaults to a random color string (format: ‘xyz’ where [x,y,z] is a byte (0-255)). -
:interpreter
– String. The ROI Interpreter. Defaults to ‘RTKIT’. -
:type
– String. The ROI Interpreted Type. Defaults to ‘CONTROL’.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/rtkit/roi.rb', line 87 def initialize(name, number, frame, struct, ={}) raise ArgumentError, "Invalid argument 'name'. Expected String, got #{name.class}." unless name.is_a?(String) raise ArgumentError, "Invalid argument 'number'. Expected Integer, got #{number.class}." unless number.is_a?(Integer) raise ArgumentError, "Invalid argument 'frame'. Expected Frame, got #{frame.class}." unless frame.is_a?(Frame) raise ArgumentError, "Invalid argument 'struct'. Expected StructureSet, got #{struct.class}." unless struct.is_a?(StructureSet) raise ArgumentError, "Invalid option :algorithm. Expected String, got #{[:algorithm].class}." if [:algorithm] && ![:algorithm].is_a?(String) raise ArgumentError, "Invalid option :color. Expected String, got #{[:color].class}." if [:color] && ![:color].is_a?(String) raise ArgumentError, "Invalid option :interpreter. Expected String, got #{[:interpreter].class}." if [:interpreter] && ![:interpreter].is_a?(String) raise ArgumentError, "Invalid option :type. Expected String, got #{[:type].class}." if [:type] && ![:type].is_a?(String) @slices = Array.new @associated_instance_uids = Hash.new # Set values: @number = number @name = name @algorithm = [:algorithm] || 'Automatic' @type = [:type] || 'CONTROL' @interpreter = [:interpreter] || 'RTKIT' @color = [:color] || random_color # Set references: @frame = frame @struct = struct # Register ourselves with the Frame and StructureSet: @frame.add_roi(self) @struct.add_roi(self) end |
Instance Attribute Details
#algorithm ⇒ Object
ROI Generation Algorithm.
14 15 16 |
# File 'lib/rtkit/roi.rb', line 14 def algorithm @algorithm end |
#color ⇒ Object
ROI Display Color.
16 17 18 |
# File 'lib/rtkit/roi.rb', line 16 def color @color end |
#frame ⇒ Object (readonly)
The Frame which this ROI belongs to.
18 19 20 |
# File 'lib/rtkit/roi.rb', line 18 def frame @frame end |
#interpreter ⇒ Object
ROI Interpreter.
20 21 22 |
# File 'lib/rtkit/roi.rb', line 20 def interpreter @interpreter end |
#name ⇒ Object
ROI Name.
22 23 24 |
# File 'lib/rtkit/roi.rb', line 22 def name @name end |
#number ⇒ Object
ROI Number (Integer).
24 25 26 |
# File 'lib/rtkit/roi.rb', line 24 def number @number end |
#slices ⇒ Object (readonly)
An array containing the Slices that the ROI is defined in.
26 27 28 |
# File 'lib/rtkit/roi.rb', line 26 def slices @slices end |
#struct ⇒ Object (readonly)
The StructureSet that the ROI is defined in.
28 29 30 |
# File 'lib/rtkit/roi.rb', line 28 def struct @struct end |
#type ⇒ Object
RT ROI Interpreted Type.
30 31 32 |
# File 'lib/rtkit/roi.rb', line 30 def type @type end |
Class Method Details
.create_from_items(roi_item, contour_item, rt_item, struct) ⇒ Object
Creates a new ROI instance from the three items of the structure set which contains the information related to a particular ROI. This method also creates and connects any child structures as indicated in the items (e.g. Slices). Returns the ROI instance.
Parameters
-
roi_item
– The ROI’s Item from the Structure Set ROI Sequence in the DObject of a Structure Set. -
contour_item
– The ROI’s Item from the ROI Contour Sequence in the DObject of a Structure Set. -
rt_item
– The ROI’s Item from the RT ROI Observations Sequence in the DObject of a Structure Set. -
struct
– The StructureSet instance that this ROI belongs to.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/rtkit/roi.rb', line 44 def self.create_from_items(roi_item, contour_item, rt_item, struct) raise ArgumentError, "Invalid argument 'roi_item'. Expected DICOM::Item, got #{roi_item.class}." unless roi_item.is_a?(DICOM::Item) raise ArgumentError, "Invalid argument 'contour_item'. Expected DICOM::Item, got #{contour_item.class}." unless contour_item.is_a?(DICOM::Item) raise ArgumentError, "Invalid argument 'rt_item'. Expected DICOM::Item, got #{rt_item.class}." unless rt_item.is_a?(DICOM::Item) raise ArgumentError, "Invalid argument 'struct'. Expected StructureSet, got #{struct.class}." unless struct.is_a?(StructureSet) # Values from the Structure Set ROI Sequence Item: number = roi_item.value(ROI_NUMBER).to_i frame_of_ref = roi_item.value(REF_FRAME_OF_REF) name = roi_item.value(ROI_NAME) || '' algorithm = roi_item.value(ROI_ALGORITHM) || '' # Values from the RT ROI Observations Sequence Item: type = rt_item.value(ROI_TYPE) || '' interpreter = rt_item.value(ROI_INTERPRETER) || '' # Values from the ROI Contour Sequence Item: color = contour_item.value(ROI_COLOR) # Get the frame: frame = struct.study.patient.dataset.frame(frame_of_ref) # If the frame didnt exist, create it (assuming the frame belongs to our patient): frame = struct.study.patient.create_frame(frame_of_ref) unless frame # Create the ROI instance: roi = self.new(name, number, frame, struct, :algorithm => algorithm, :color => color, :interpreter => interpreter, :type => type) # Create the Slices in which the ROI has contours defined: roi.create_slices(contour_item[CONTOUR_SQ]) if contour_item[CONTOUR_SQ] return roi end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
Returns true if the argument is an instance with attributes equal to self.
115 116 117 118 119 |
# File 'lib/rtkit/roi.rb', line 115 def ==(other) if other.respond_to?(:to_roi) other.send(:state) == state end end |
#add_slice(slice) ⇒ Object
Adds a Slice instance to this ROI.
125 126 127 128 129 |
# File 'lib/rtkit/roi.rb', line 125 def add_slice(slice) raise ArgumentError, "Invalid argument 'slice'. Expected Slice, got #{slice.class}." unless slice.is_a?(Slice) @slices << slice unless @associated_instance_uids[slice.uid] @associated_instance_uids[slice.uid] = slice end |
#attach_to(series) ⇒ Object
Attaches a ROI to a specified ImageSeries, by setting the ROIs frame reference to the Frame which the ImageSeries belongs to, and setting the Image reference of each of the Slices belonging to the ROI to an Image instance which matches the coordinates of the Slice’s Contour(s). Raises an exception if a suitable match is not found for any Slice.
Notes
This method can be useful when you have multiple segmentations based on the same image series from multiple raters (perhaps as part of a comparison study), and the rater’s software has modified the UIDs of the original image series, so that the references of the returned Structure Set does not match your original image series. This method uses coordinate information to calculate plane equations, which allows it to identify the corresponding image slice even in the case of slice geometry being non-perpendicular with respect to the patient geometry (direction cosine values != [0,1]).
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/rtkit/roi.rb', line 151 def attach_to(series) raise ArgumentError, "Invalid argument 'series'. Expected ImageSeries, got #{series.class}." unless series.is_a?(Series) # Change struct association if indicated: if series.struct != @struct @struct.remove_roi(self) series.struct.add_roi(self) @struct = series.struct end # Change Frame if different: if @frame != series.frame @frame = series.frame end # Update slices: @slices.each do |slice| slice.attach_to(series) end end |
#bin_volume(image_volume = @struct.image_series.first) ⇒ Object
Creates a binary volume object consisting of a series of binary (segmented) images, extracted from the contours defined for the slices of this ROI. Returns a BinVolume instance with binary image references equal to the number of slices defined for this ROI.
Parameters
-
image_volume
– By default the BinVolume is created against the ImageSeries of the ROI’s StructureSet. Optionally, a DoseVolume can be specified.
178 179 180 |
# File 'lib/rtkit/roi.rb', line 178 def bin_volume(image_volume=@struct.image_series.first) return BinVolume.from_roi(self, image_volume) end |
#contour_item ⇒ Object
Creates and returns a ROI Contour Sequence Item from the attributes of the ROI.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/rtkit/roi.rb', line 202 def contour_item item = DICOM::Item.new item.add(DICOM::Element.new(ROI_COLOR, @color)) s = DICOM::Sequence.new(CONTOUR_SQ) item.add(s) item.add(DICOM::Element.new(REF_ROI_NUMBER, @number.to_s)) # Add Contour items to the Contour Sequence (one or several items per Slice): @slices.each do |slice| slice.contours.each do |contour| s.add_item(contour.to_item) end end return item end |
#create_slices(contour_sequence) ⇒ Object
Iterates the Contour Sequence Items, collects Contour Items for each slice and passes them along to the Slice class.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/rtkit/roi.rb', line 219 def create_slices(contour_sequence) raise ArgumentError, "Invalid argument 'contour_sequence'. Expected DICOM::Sequence, got #{contour_sequence.class}." unless contour_sequence.is_a?(DICOM::Sequence) # Sort the contours by slices: slice_collection = Hash.new contour_sequence.each do |slice_contour_item| sop_uid = slice_contour_item[CONTOUR_IMAGE_SQ][0].value(REF_SOP_UID) slice_collection[sop_uid] = Array.new unless slice_collection[sop_uid] slice_collection[sop_uid] << slice_contour_item end # Create slices: slice_collection.each_pair do |sop_uid, items| Slice.create_from_items(sop_uid, items, self) end end |
#distribution(dose_volume = @struct.plan.rt_dose.sum) ⇒ Object
Creates a DoseDistribution based on the delineation of this ROI in the specified RTDose series.
Parameters
-
dose_volume
– The DoseVolume to extract the dose distribution from. Defaults to the sum of the dose volumes of the first RTDose of the first plan of the parent StructureSet.
241 242 243 244 245 246 247 248 249 |
# File 'lib/rtkit/roi.rb', line 241 def distribution(dose_volume=@struct.plan.rt_dose.sum) raise ArgumentError, "Invalid argument 'dose_volume'. Expected DoseVolume, got #{dose_volume.class}." unless dose_volume.is_a?(DoseVolume) raise ArgumentError, "Invalid argument 'dose_volume'. The specified DoseVolume does not belong to this ROI's StructureSet." unless dose_volume.dose_series.plan.struct == @struct # Extract a binary volume from the ROI, based on the dose data: bin_vol = bin_volume(dose_volume) # Create a DoseDistribution from the BinVolume: dose_distribution = DoseDistribution.create(bin_vol) return dose_distribution end |
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
253 254 255 |
# File 'lib/rtkit/roi.rb', line 253 def hash state.hash end |
#image_series ⇒ Object
Returns the ImageSeries instance that this ROI is defined in.
259 260 261 |
# File 'lib/rtkit/roi.rb', line 259 def image_series return @struct.image_series.first end |
#num_contours ⇒ Object
Returns the number of Contours belonging to this ROI through its Slices.
277 278 279 280 281 282 283 |
# File 'lib/rtkit/roi.rb', line 277 def num_contours num = 0 @slices.each do |slice| num += slice.contours.length end return num end |
#obs_item ⇒ Object
Creates and returns a RT ROI Obervations Sequence Item from the attributes of the ROI.
293 294 295 296 297 298 299 300 |
# File 'lib/rtkit/roi.rb', line 293 def obs_item item = DICOM::Item.new item.add(DICOM::Element.new(OBS_NUMBER, @number.to_s)) item.add(DICOM::Element.new(REF_ROI_NUMBER, @number.to_s)) item.add(DICOM::Element.new(ROI_TYPE, @type)) item.add(DICOM::Element.new(ROI_INTERPRETER, @interpreter)) return item end |
#remove_references ⇒ Object
Removes the parent references of the ROI (StructureSet and Frame).
304 305 306 307 |
# File 'lib/rtkit/roi.rb', line 304 def remove_references @frame = nil @struct = nil end |
#size ⇒ Object
Calculates the size (volume) of the ROI by evaluating the ROI’s delination in the referenced image series. Returns a float, giving the volume in units of cubic centimeters,
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/rtkit/roi.rb', line 313 def size volume = 0.0 # Iterate each slice: @slices.each_index do |i| # Get the contoured area in this slice, convert it to volume and add to our total. # If the slice is the first or last, only multiply by half of the slice thickness: if i == 0 or i == @slices.length-1 volume += slice.area * image_series.slice_spacing * 0.5 else volume += slice.area * image_series.slice_spacing end end # Convert from mm^3 to cm^3: return volume / 1000.0 end |
#slice(*args) ⇒ Object
Returns the Slice instance mathcing the specified SOP Instance UID (if an argument is used). If a specified UID doesn’t match, nil is returned. If no argument is passed, the first Slice instance associated with the ROI is returned.
Parameters
-
uid
– String. The value of the SOP Instance UID element.
337 338 339 340 341 342 343 344 345 346 |
# File 'lib/rtkit/roi.rb', line 337 def slice(*args) raise ArgumentError, "Expected one or none arguments, got #{args.length}." unless [0, 1].include?(args.length) if args.length == 1 raise ArgumentError, "Invalid argument 'uid'. Expected String (or nil), got #{args.first.class}." unless [String, NilClass].include?(args.first.class) return @associated_instance_uids[args.first] else # No argument used, therefore we return the first Image instance: return @slices.first end end |
#ss_item ⇒ Object
Creates and returns a Structure Set ROI Sequence Item from the attributes of the ROI.
350 351 352 353 354 355 356 357 |
# File 'lib/rtkit/roi.rb', line 350 def ss_item item = DICOM::Item.new item.add(DICOM::Element.new(ROI_NUMBER, @number.to_s)) item.add(DICOM::Element.new(REF_FRAME_OF_REF, @frame.uid)) item.add(DICOM::Element.new(ROI_NAME, @name)) item.add(DICOM::Element.new(ROI_ALGORITHM, @algorithm)) return item end |
#to_roi ⇒ Object
Returns self.
361 362 363 |
# File 'lib/rtkit/roi.rb', line 361 def to_roi self end |