Class: RTKIT::BinMatcher

Inherits:
Object
  • Object
show all
Defined in:
lib/rtkit/bin_matcher.rb

Overview

Contains the DICOM data and methods related to the comparison of binary volumes (i.e. segmented volumes).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(volumes = nil, master = nil) ⇒ BinMatcher

Creates a new BinMatcher instance.

Parameters

  • volumes – An array of BinVolume instances to be matched.

  • master – A master BinVolume which the other volumes will be compared against.

Raises:

  • (ArgumentError)


19
20
21
22
23
24
25
# File 'lib/rtkit/bin_matcher.rb', line 19

def initialize(volumes=nil, master=nil)
  raise ArgumentError, "Invalid argument 'volumes'. Expected Array, got #{volumes.class}." if volumes && volumes.class != Array
  raise ArgumentError, "Invalid argument 'master'. Expected BinVolume, got #{master.class}." if master && master.class != BinVolume
  raise ArgumentError, "Invalid argument 'volumes'. Expected array to contain only BinVolume instances, got #{volumes.collect{|i| i.class}.uniq}." if volumes && volumes.collect{|i| i.class}.uniq != [BinVolume]
  @volumes = volumes || Array.new
  @master = master
end

Instance Attribute Details

#masterObject

The reference (master) BinVolume.



8
9
10
# File 'lib/rtkit/bin_matcher.rb', line 8

def master
  @master
end

#volumesObject (readonly)

An array of BinVolumes.



10
11
12
# File 'lib/rtkit/bin_matcher.rb', line 10

def volumes
  @volumes
end

Instance Method Details

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

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



29
30
31
32
33
# File 'lib/rtkit/bin_matcher.rb', line 29

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

#add(volume) ⇒ Object

Adds a BinVolume instance to the matcher.

Raises:

  • (ArgumentError)


39
40
41
42
# File 'lib/rtkit/bin_matcher.rb', line 39

def add(volume)
  raise ArgumentError, "Invalid argument 'volume'. Expected BinVolume, got #{volume.class}." unless volume.is_a?(BinVolume)
  @volumes << volume
end

#by_sensitivityObject

Returns an array of volumes, (reversly) sorted by their sensitivity score. The volumes are sorted in such a way that the best scoring volume (highest sensitivity) appears first. If no volumes are defined, an empty array is returned.



48
49
50
# File 'lib/rtkit/bin_matcher.rb', line 48

def by_sensitivity
  return (@volumes.sort_by {|v| v.sensitivity}).reverse
end

#by_specificityObject

Returns an array of volumes, (reversly) sorted by their specificity score. The volumes are sorted in such a way that the best scoring volume (highest specificity) appears first. If no volumes are defined, an empty array is returned.



56
57
58
# File 'lib/rtkit/bin_matcher.rb', line 56

def by_specificity
  return (@volumes.sort_by {|v| v.specificity}).reverse
end

#fill_blanksObject

Ensures that a valid comparison can be done by making sure that every volume has a BinImage for any image that is referenced among the BinVolumes. If one or more BinVolumes are missing one or more BinImages, empty BinImages will be created for these BinVolumes.

Notes

  • The master volume (if present) is also processed in this method.



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

def fill_blanks
  if @volumes.length > 0
    # Register all unique images referenced by the various volumes:
    images = Set.new
    # Include the master volume (if it is present):
    [@volumes, @master].flatten.compact.each do |volume|
      volume.bin_images.each do |bin_image|
        images << bin_image.image unless images.include?(bin_image.image)
      end
    end
    # Check if any of the volumes have images missing, and if so, create empty images:
    images.each do |image|
      [@volumes, @master].flatten.compact.each do |volume|
        match = false
        volume.bin_images.each do |bin_image|
          match = true if bin_image.image == image
        end
        unless match
          # Create BinImage:
          bin_img = BinImage.new(NArray.byte(image.columns, image.rows), image)
          volume.add(bin_img)
        end
      end
    end
  end
end

#hashObject

Generates a Fixnum hash value for this instance.



98
99
100
# File 'lib/rtkit/bin_matcher.rb', line 98

def hash
  state.hash
end

#narrays(sort_slices = true) ⇒ Object

Returns an array of NArrays from the BinVolumes of this instance. Returns an empty array if no volumes are defined.

Notes

  • Only the volumes are returned. The master volume (if present) is not returned with this method.



116
117
118
119
120
121
122
# File 'lib/rtkit/bin_matcher.rb', line 116

def narrays(sort_slices=true)
  n = Array.new
  @volumes.each do |volume|
    n << volume.narray(sort_slices)
  end
  return n
end

#score_diceObject

Scores the volumes of the BinMatcher instance against the reference (master) volume, by using Dice’s coeffecient.

For more information, see: en.wikipedia.org/wiki/Dice%27s_coefficient



166
167
168
169
170
171
172
173
174
175
176
# File 'lib/rtkit/bin_matcher.rb', line 166

def score_dice
  if @master
    # Find the voxel-indices for the master where the decisions are 1 and 0:
    pos_indices_master = (@master.narray.eq 1).where
    @volumes.each do |bin_vol|
      pos_indices_vol = (bin_vol.narray.eq 1).where
      num_common = (@master.narray[pos_indices_vol].eq 1).where.length
      bin_vol.dice = 2 * num_common / (pos_indices_master.length + pos_indices_vol.length).to_f
    end
  end
end

#score_ssObject

Scores the volumes of the BinMatcher instance against the reference (master) volume, by using Sensitivity and Specificity.

For more information, see: en.wikipedia.org/wiki/Sensitivity_and_specificity



184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rtkit/bin_matcher.rb', line 184

def score_ss
  if @master
    # Find the voxel-indices for the master where the decisions are 1 and 0:
    pos_indices, neg_indices = (@master.narray.eq 1).where2
    @volumes.each do |bin_vol|
      narr = bin_vol.narray
      bin_vol.sensitivity = (narr[pos_indices].eq 1).where.length / pos_indices.length.to_f
      bin_vol.specificity = (narr[neg_indices].eq 0).where.length / neg_indices.length.to_f
    end
  end
end

#sort_volumesObject

Rearranges the BinImages belonging to the BinVolumes of this instance, by matching the BinImages by their Image instance references, to ensure that the NArrays extracted from these volumes are truly comparable.

Notes

  • The master volume (if present) is not processed in this method.

  • Raises an exception if any irregularities in number of BinImages or Image references occurs.



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/rtkit/bin_matcher.rb', line 205

def sort_volumes
  # It only makes sense to sort if we have at least two volumes:
  if @volumes.length > 1
    raise "All volumes of the BinMatcher isntance must have the same number of BinImages (got lengths #{@volumes.collect {|v| v.bin_images.length}})." if @volumes.collect {|v| v.bin_images.length}.uniq.length > 1
    # Collect the Image references of the BinImage's of the first volume:
    desired_image_order = Array.new
    @volumes.first.bin_images.collect {|bin_image| desired_image_order << bin_image.image}
    raise "One (or more) Image references were nil in the first BinVolume instance of the BinMatcher. Unable to sort BinImages when they lack Image reference." if desired_image_order.compact.length != desired_image_order.length
    # Sort the BinImages of the remaining volumes so that their order of images are equal to that of the first volume:
    (1...@volumes.length).each do |i|
      current_image_order = Array.new
      @volumes[i].bin_images.collect {|bin_image| current_image_order << bin_image.image}
      sort_order = current_image_order.compare_with(desired_image_order)
      @volumes[i].bin_images.sort_by_order!(sort_order)
      #@volumes[i].reorder_images(sort_order)
    end
  end
end

#to_bin_matcherObject

Returns self.



226
227
228
# File 'lib/rtkit/bin_matcher.rb', line 226

def to_bin_matcher
  self
end