Class: RTKIT::ImageSeries

Inherits:
Series
  • Object
show all
Includes:
ImageParent
Defined in:
lib/rtkit/image_series.rb

Overview

The ImageSeries class contains methods that are specific for the slice based image modalites (e.g. CT, MR).

Inheritance

  • ImageSeries inherits all methods and attributes from the Series class.

Instance Attribute Summary collapse

Attributes inherited from Series

#class_uid, #date, #description, #modality, #series_uid, #study, #time

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ImageParent

#slice_spacing, #update_image_position

Methods inherited from Series

#image_modality?, #uid

Constructor Details

#initialize(series_uid, modality, frame, study, options = {}) ⇒ ImageSeries

Creates a new ImageSeries instance.

Parameters

  • series_uid – The Series Instance UID string.

  • modality – The Modality string of the ImageSeries, e.g. ‘CT’ or ‘MR’.

  • frame – The Frame instance that this ImageSeries belongs to.

  • study – The Study instance that this ImageSeries belongs to.

  • options – A hash of parameters.

Options

  • :class_uid – String. The SOP Class UID (DICOM tag ‘0008,0016’).

  • :date – String. The Series Date (DICOM tag ‘0008,0021’).

  • :time – String. The Series Time (DICOM tag ‘0008,0031’).

  • :description – String. The Series Description (DICOM tag ‘0008,103E’).

Raises:

  • (ArgumentError)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rtkit/image_series.rb', line 71

def initialize(series_uid, modality, frame, study, options={})
  raise ArgumentError, "Invalid argument 'series_uid'. Expected String, got #{series_uid.class}." unless series_uid.is_a?(String)
  raise ArgumentError, "Invalid argument 'modality'. Expected String, got #{modality.class}." unless modality.is_a?(String)
  raise ArgumentError, "Invalid argument 'frame'. Expected Frame, got #{frame.class}." unless frame.is_a?(Frame)
  raise ArgumentError, "Invalid argument 'study'. Expected Study, got #{study.class}." unless study.is_a?(Study)
  raise ArgumentError, "Invalid argument 'modality'. Expected an Image Series type modality, got #{modality}." unless IMAGE_SERIES.include?(modality)
  # Pass attributes to Series initialization:
  super(series_uid, modality, study, options)
  # Key attributes:
  @frame = frame
  # Default attributes:
  @slices = Hash.new
  @sop_uids = Hash.new
  @images = Array.new
  @structs = Array.new
  @image_positions = Hash.new
  # A hash with the associated StructureSet's UID as key and the instance of the StructureSet that belongs to this ImageSeries as value:
  @associated_structs = Hash.new
  # Register ourselves with the study & frame:
  @study.add_series(self)
  @frame.add_series(self)
end

Instance Attribute Details

#frameObject

The Frame (of Reference) which this ImageSeries belongs to.



14
15
16
# File 'lib/rtkit/image_series.rb', line 14

def frame
  @frame
end

#imagesObject (readonly)

An array of Image references.



16
17
18
# File 'lib/rtkit/image_series.rb', line 16

def images
  @images
end

#slicesObject (readonly)

A hash containing SOP Instance UIDs as key and Slice Positions as value.



18
19
20
# File 'lib/rtkit/image_series.rb', line 18

def slices
  @slices
end

#sop_uidsObject

A hash containing Slice Positions as key and SOP Instance UIDS as value.



20
21
22
# File 'lib/rtkit/image_series.rb', line 20

def sop_uids
  @sop_uids
end

#structsObject (readonly)

An array of Structure Sets associated with this Image Series.



22
23
24
# File 'lib/rtkit/image_series.rb', line 22

def structs
  @structs
end

Class Method Details

.load(dcm, study) ⇒ Object

Creates a new ImageSeries instance by loading series information from the specified DICOM object. The Series’ UID string value is used to uniquely identify an ImageSeries.

Parameters

  • dcm – An instance of a DICOM object (DICOM::DObject) with an image type modality (e.g. CT or MR).

  • study – The Study instance that this ImageSeries belongs to.

Raises:

  • (ArgumentError)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rtkit/image_series.rb', line 32

def self.load(dcm, study)
  raise ArgumentError, "Invalid argument 'dcm'. Expected DObject, got #{dcm.class}." unless dcm.is_a?(DICOM::DObject)
  raise ArgumentError, "Invalid argument 'study'. Expected Study, got #{study.class}." unless study.is_a?(Study)
  raise ArgumentError, "Invalid argument 'dcm'. Expected DObject with an Image Series type modality, got #{dcm.value(MODALITY)}." unless IMAGE_SERIES.include?(dcm.value(MODALITY))
  # Required attributes:
  modality = dcm.value(MODALITY)
  series_uid = dcm.value(SERIES_UID)
  # Optional attributes:
  class_uid = dcm.value(SOP_CLASS)
  date = dcm.value(SERIES_DATE)
  time = dcm.value(SERIES_TIME)
  description = dcm.value(SERIES_DESCR)
  # Check if a Frame with the given UID already exists, and if not, create one:
  frame = study.patient.dataset.frame(dcm.value(FRAME_OF_REF)) || frame = study.patient.create_frame(dcm.value(FRAME_OF_REF), dcm.value(POS_REF_INDICATOR))
  # Create the ImageSeries instance:
  is = self.new(series_uid, modality, frame, study, :class_uid => class_uid, :date => date, :time => time, :description => description)
  is.add(dcm)
  # Add our ImageSeries instance to its corresponding Frame:
  frame.add_series(is)
  return is
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?

Returns true if the argument is an instance with attributes equal to self.



96
97
98
99
100
# File 'lib/rtkit/image_series.rb', line 96

def ==(other)
  if other.respond_to?(:to_image_series)
    other.send(:state) == state
  end
end

#add(dcm) ⇒ Object

Adds a DICOM object (Image) to the ImageSeries, by creating a new Image instance linked to this ImageSeries.



106
107
108
# File 'lib/rtkit/image_series.rb', line 106

def add(dcm)
  Image.load(dcm, self)
end

#add_image(image) ⇒ Object

Adds an Image to this ImageSeries.

Raises:

  • (ArgumentError)


112
113
114
115
116
117
118
119
# File 'lib/rtkit/image_series.rb', line 112

def add_image(image)
  raise ArgumentError, "Invalid argument 'image'. Expected Image, got #{image.class}." unless image.is_a?(Image)
  @images << image unless @frame.image(image.uid)
  @slices[image.uid] = image.pos_slice
  @sop_uids[image.pos_slice] = image.uid
  # The link between image uid and image instance is kept in the Frame, instead of the ImageSeries:
  @frame.add_image(image) unless @frame.image(image.uid)
end

#add_struct(struct) ⇒ Object

Adds a StructureSet to this ImageSeries.

Raises:

  • (ArgumentError)


123
124
125
126
127
128
# File 'lib/rtkit/image_series.rb', line 123

def add_struct(struct)
  raise ArgumentError, "Invalid argument 'struct'. Expected StructureSet, got #{struct.class}." unless struct.is_a?(StructureSet)
  # Do not add it again if the struct already belongs to this instance:
  @structs << struct unless @associated_structs[struct.uid]
  @associated_structs[struct.uid] = struct
end

#hashObject

Generates a Fixnum hash value for this instance.



155
156
157
# File 'lib/rtkit/image_series.rb', line 155

def hash
  state.hash
end

#image(*args) ⇒ Object

Returns the Image 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 Image instance associated with the ImageSeries is returned.

Parameters

  • uid – String. The value of the SOP Instance UID element of the Image.

Raises:

  • (ArgumentError)


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/rtkit/image_series.rb', line 167

def image(*args)
  raise ArgumentError, "Expected one or none arguments, got #{args.length}." unless [0, 1].include?(args.length)
  if args.length == 1
    if args.first.is_a?(Float)
      # Presumably an image position:
      return @image_positions[args.first]
    else
      # Presumably a uid string:
      return @frame.image(args.first)
    end
  else
    # No argument used, therefore we return the first Image instance:
    return @images.first
  end
end

#match_image(plane) ⇒ Object

Analyses the Image instances belonging to this ImageSeries to determine if there is an Image which matches the specified Plane. Returns the Image if a match is found, nil if not.

Parameters

  • plane – The Plane instance which images will be matched against.

Raises:

  • (ArgumentError)


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rtkit/image_series.rb', line 191

def match_image(plane)
  raise ArgumentError, "Invalid argument 'plane'. Expected Plane, got #{plane.class}." unless plane.is_a?(Plane)
  matching_image = nil
  planes_in_series = Array.new
  @images.each do |image|
    # Get three coordinates from the image:
    col_indices = NArray.to_na([0,image.columns/2, image.columns-1])
    row_indices = NArray.to_na([image.rows/2, image.rows-1, 0])
    x, y, z = image.coordinates_from_indices(col_indices, row_indices)
    coordinates = Array.new
    x.length.times do |i|
      coordinates << Coordinate.new(x[i], y[i], z[i])
    end
    # Determine the image plane:
    planes_in_series << Plane.calculate(coordinates[0], coordinates[1], coordinates[2])
  end
  # Search for a match amongst the planes of this series:
  index = plane.match(planes_in_series)
  matching_image = @images[index] if index
  return matching_image
end

#roisObject

Returns all ROIs having the same Frame of Reference as this image series from the structure set(s) belonging to this series. Returns the ROIs in an Array. If no ROIs are matched, an empty array is returned.



217
218
219
220
221
222
223
# File 'lib/rtkit/image_series.rb', line 217

def rois
  frame_rois = Array.new
  structs.each do |struct|
    frame_rois << struct.rois_in_frame(@frame.uid)
  end
  return frame_rois.flatten
end

#set_resolution(columns, rows, options = {}) ⇒ Object

Sets the resolution of all images in this image series. The images will either be expanded or cropped depending on whether the specified resolution is bigger or smaller than the existing one.

Parameters

  • columns – Integer. The number of columns applied to the cropped/expanded image series.

  • rows – Integer. The number of rows applied to the cropped/expanded image series.

Options

  • :hor – Symbol. The side (in the horisontal image direction) to apply the crop/border (:left, :right or :even (default)).

  • :ver – Symbol. The side (in the vertical image direction) to apply the crop/border (:bottom, :top or :even (default)).



239
240
241
242
243
# File 'lib/rtkit/image_series.rb', line 239

def set_resolution(columns, rows, options={})
  @images.each do |img|
    img.set_resolution(columns, rows, options)
  end
end

#struct(*args) ⇒ Object

Returns the StructureSet 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 StructureSet instance associated with the ImageSeries is returned.

Parameters

  • uid – String. The value of the SOP Instance UID element.

Raises:

  • (ArgumentError)


253
254
255
256
257
258
259
260
261
262
# File 'lib/rtkit/image_series.rb', line 253

def struct(*args)
  raise ArgumentError, "Expected one or none arguments, got #{args.length}." unless [0, 1].include?(args.length)
  if args.length == 1
    raise ArgumentError, "Expected String (or nil), got #{args.first.class}." unless [String, NilClass].include?(args.first.class)
    return @associated_structs[args.first]
  else
    # No argument used, therefore we return the first StructureSet instance:
    return @structs.first
  end
end

#to_image_seriesObject

Returns self.



266
267
268
# File 'lib/rtkit/image_series.rb', line 266

def to_image_series
  self
end

#write(path) ⇒ Object

Writes all images in this image series to DICOM files in the specified folder. The file names are set by the image’s UID string, followed by a ‘.dcm’ extension.



273
274
275
276
277
# File 'lib/rtkit/image_series.rb', line 273

def write(path)
  @images.each do |img|
    img.write(path + img.uid + '.dcm')
  end
end