Class: RTKIT::BinImage
Overview
Contains the DICOM data and methods related to a binary image.
Inheritance
-
As the BinImage class inherits from the PixelData class, all PixelData methods are available to instances of BinImage.
Instance Attribute Summary collapse
-
#image ⇒ Object
readonly
The BinImage’s Image reference.
-
#narray ⇒ Object
The binary numerical image array.
-
#narray_indices ⇒ Object
readonly
A narray containing pixel indices.
Class Method Summary collapse
-
.from_contours(contours, image, bin_volume) ⇒ Object
Creates a new BinImage instance from an array of contours.
Instance Method Summary collapse
-
#==(other) ⇒ Object
(also: #eql?)
Returns true if the argument is an instance with attributes equal to self.
-
#add(pixels) ⇒ Object
Adds a binary image array to the image array of this instance.
-
#area(type = true) ⇒ Object
Calculates the area defined by true/false (1/0) pixels.
-
#col_spacing ⇒ Object
Returns the col_spacing attribute from the Image reference.
-
#columns ⇒ Object
Returns the number of columns in the binary array.
-
#contour_image ⇒ Object
Applies the contour indices of this instance to an empty image (2D NArray) to create a ‘contour image’.
-
#contour_indices ⇒ Object
Extracts the contour indices of the (filled) structures contained in the BinImage, by performing a contour tracing algorithm on the binary image.
-
#cosines ⇒ Object
Returns the cosines attribute from the Image reference.
-
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
-
#initialize(narray, image) ⇒ BinImage
constructor
Creates a new BinImage instance.
-
#pos_slice ⇒ Object
Returns the pos_slice attribute from the Image reference.
-
#pos_x ⇒ Object
Returns the pos_x attribute from the Image reference.
-
#pos_y ⇒ Object
Returns the pos_y attribute from the Image reference.
-
#row_spacing ⇒ Object
Returns the row_spacing attribute from the Image reference.
-
#rows ⇒ Object
Returns the number of rows in the binary array.
-
#selection ⇒ Object
Creates a Selection containing all ‘segmented’ indices of this instance, i.e.
-
#to_bin_image ⇒ Object
Returns self.
-
#to_bin_volume(series, source = nil) ⇒ Object
Converts the BinImage instance to a single image BinVolume instance.
-
#to_contours(slice) ⇒ Object
Creates an array of Contour instances from the segmentation of this BinImage.
-
#to_dcm ⇒ Object
Dumps the BinImage instance to a DObject.
-
#to_slice(roi) ⇒ Object
Creates a Slice instance from the segmentation of this BinImage.
-
#write(path) ⇒ Object
Writes the BinImage to a DICOM file given by the specified file string.
Methods inherited from PixelData
#coordinates_from_indices, #coordinates_to_indices, #draw_lines, #flood_fill, #indices_general_to_specific, #indices_specific_to_general, #print_img
Constructor Details
#initialize(narray, image) ⇒ BinImage
Creates a new BinImage instance.
Parameters
-
narray
– A binary, two-dimensional NArray. -
image
– The Image instance that this BinImage is associated with.
53 54 55 56 57 58 59 60 61 |
# File 'lib/rtkit/bin_image.rb', line 53 def initialize(narray, image) raise ArgumentError, "Invalid argument 'narray'. Expected NArray, got #{narray.class}." unless narray.is_a?(NArray) raise ArgumentError, "Invalid argument 'image'. Expected Image, got #{image.class}." unless image.is_a?(Image) raise ArgumentError, "Invalid argument 'narray'. Expected two-dimensional NArray, got #{narray.shape.length} dimensions." unless narray.shape.length == 2 raise ArgumentError, "Invalid argument 'narray'. Expected NArray of element size 1 byte, got #{narray.element_size} bytes (per element)." unless narray.element_size == 1 raise ArgumentError, "Invalid argument 'narray'. Expected binary NArray with max value 1, got #{narray.max} as max." if narray.max > 1 self.narray = narray @image = image end |
Instance Attribute Details
#image ⇒ Object (readonly)
The BinImage’s Image reference.
12 13 14 |
# File 'lib/rtkit/bin_image.rb', line 12 def image @image end |
#narray ⇒ Object
The binary numerical image array.
14 15 16 |
# File 'lib/rtkit/bin_image.rb', line 14 def narray @narray end |
#narray_indices ⇒ Object (readonly)
A narray containing pixel indices.
16 17 18 |
# File 'lib/rtkit/bin_image.rb', line 16 def narray_indices @narray_indices end |
Class Method Details
.from_contours(contours, image, bin_volume) ⇒ Object
Creates a new BinImage instance from an array of contours. The BinVolume is typically defined from a ROI delineation against an image series, but it may also be applied to an rtdose ‘image’ series. Returns the BinVolume instance.
Parameters
-
contours
– An array of contours from which to fill in a binary image. -
image
– The image that this BinImage instance will be based on. -
bin_volume
– The BinVolume instance that this bin_image belongs to.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/rtkit/bin_image.rb', line 29 def self.from_contours(contours, image, bin_volume) raise ArgumentError, "Invalid argument 'contours'. Expected Array, got #{contours.class}." unless contours.is_a?(Array) raise ArgumentError, "Invalid argument 'image'. Expected Image, got #{image.class}." unless image.is_a?(Image) raise ArgumentError, "Invalid argument 'bin_volume'. Expected BinVolume, got #{bin_volume.class}." unless bin_volume.is_a?(BinVolume) # Create the narray to be used: narr = NArray.byte(image.columns, image.rows) # Create the BinImage instance: bi = self.new(narr, image) # Delineate and fill for each contour: contours.each do |contour| x, y, z = contour.coords bi.add(image.binary_image(x, y, z)) end bin_volume.add(bi) return bi end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
Returns true if the argument is an instance with attributes equal to self.
65 66 67 68 69 |
# File 'lib/rtkit/bin_image.rb', line 65 def ==(other) if other.respond_to?(:to_bin_image) other.send(:state) == state end end |
#add(pixels) ⇒ Object
Adds a binary image array to the image array of this instance. Any segmented pixels in the new array (value = 1), is added (value set eql to 1) to the instance array.
76 77 78 79 80 81 82 |
# File 'lib/rtkit/bin_image.rb', line 76 def add(pixels) raise ArgumentError, "Invalid argument 'pixels'. Expected NArray, got #{pixels.class}." unless pixels.is_a?(NArray) raise ArgumentError, "Invalid argument 'pixels'. Expected NArray of element size 1 byte, got #{pixels.element_size} bytes (per element)." unless pixels.element_size == 1 raise ArgumentError, "Invalid argument 'pixels'. Expected binary NArray with max value 1, got #{pixels.max} as max." if pixels.max > 1 raise ArgumentError, "Invalid argument 'pixels'. Expected NArray to have same dimension as the instance array. Got #{pixels.shape}, expected #{@narray.shape}." unless pixels.shape == @narray.shape @narray[(pixels > 0).where] = 1 end |
#area(type = true) ⇒ Object
Calculates the area defined by true/false (1/0) pixels. By default, the area of the true pixels are returned. Returns a float value, in units of millimeters squared.
Parameters
-
type
– Boolean. Pixel type of interest.
92 93 94 95 96 97 98 99 100 |
# File 'lib/rtkit/bin_image.rb', line 92 def area(type=true) if type number = (@narray.eq 1).where.length else number = (@narray.eq 0).where.length end # Total area is number of pixels times the area per pixel: return number * @image.pixel_area end |
#col_spacing ⇒ Object
Returns the col_spacing attribute from the Image reference. This attribute defines the physical distance (in millimeters) between columns in the pixel data (i.e. horisontal spacing).
105 106 107 |
# File 'lib/rtkit/bin_image.rb', line 105 def col_spacing return @image.col_spacing end |
#columns ⇒ Object
Returns the number of columns in the binary array.
111 112 113 |
# File 'lib/rtkit/bin_image.rb', line 111 def columns return @narray.shape[0] end |
#contour_image ⇒ Object
Applies the contour indices of this instance to an empty image (2D NArray) to create a ‘contour image’. Each separate contour is indicated by individual integers (e.g. 1,2,3 etc).
119 120 121 122 123 124 125 |
# File 'lib/rtkit/bin_image.rb', line 119 def contour_image img = NArray.byte(columns, rows) contour_indices.each_with_index do |contour, i| img[contour.indices] = i + 1 end return img end |
#contour_indices ⇒ Object
Extracts the contour indices of the (filled) structures contained in the BinImage, by performing a contour tracing algorithm on the binary image. Returns an array filled with contour Selection instances, with length equal to the number of separated structures in the image.
Notes
-
The contours are established using a contour tracing algorithm called “Radial Sweep”:
Restrictions
-
Does not detect inner contour of hollow structures (holes).
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/rtkit/bin_image.rb', line 141 def contour_indices # Create the array to be returned: contours = Array.new # Initialize the contour extraction process if indicated: if @narray.segmented? # Initialize some variables used by the contour algorithm: initialize_contour_reorder_structures unless @reorder # The contour algorithm needs the image to be padded with a border of zero-pixels: original_image = @narray padded_image = NArray.byte(columns + 2, rows + 2) padded_image[1..-2, 1..-2] = @narray # Temporarily replace our instance image with the padded image: self.narray = padded_image # Get the contours: padded_contours = extract_contours # Convert from padded indices to proper indices: padded_contours.each do |padded_contour| padded_contour.shift_and_crop(-1, -1) contours << padded_contour end # Restore the instance image: self.narray = original_image end return contours end |
#cosines ⇒ Object
Returns the cosines attribute from the Image reference.
169 170 171 |
# File 'lib/rtkit/bin_image.rb', line 169 def cosines return @image.cosines end |
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
175 176 177 |
# File 'lib/rtkit/bin_image.rb', line 175 def hash state.hash end |
#pos_slice ⇒ Object
Returns the pos_slice attribute from the Image reference. This attribute defines the physical position (in millimeters) of the image slice. Returns nil if there is no Image reference.
195 196 197 |
# File 'lib/rtkit/bin_image.rb', line 195 def pos_slice return @image ? @image.pos_slice : nil end |
#pos_x ⇒ Object
Returns the pos_x attribute from the Image reference. This attribute defines the physical position (in millimeters) of the first (left) column in the pixel data.
202 203 204 |
# File 'lib/rtkit/bin_image.rb', line 202 def pos_x return @image.pos_x end |
#pos_y ⇒ Object
Returns the pos_y attribute from the Image reference. This attribute defines the physical position (in millimeters) of the first (top) row in the pixel data.
209 210 211 |
# File 'lib/rtkit/bin_image.rb', line 209 def pos_y return @image.pos_y end |
#row_spacing ⇒ Object
Returns the row_spacing attribute from the Image reference. This attribute defines the physical distance (in millimeters) between rows in the pixel data (i.e. vertical spacing).
216 217 218 |
# File 'lib/rtkit/bin_image.rb', line 216 def row_spacing return @image.row_spacing end |
#rows ⇒ Object
Returns the number of rows in the binary array.
222 223 224 |
# File 'lib/rtkit/bin_image.rb', line 222 def rows return @narray.shape[1] end |
#selection ⇒ Object
Creates a Selection containing all ‘segmented’ indices of this instance, i.e. indices of all pixels with a value of 1. Returns the Selection instance.
230 231 232 233 234 |
# File 'lib/rtkit/bin_image.rb', line 230 def selection s = Selection.new(self) s.add_indices((@narray.eq 1).where) return s end |
#to_bin_image ⇒ Object
Returns self.
238 239 240 |
# File 'lib/rtkit/bin_image.rb', line 238 def to_bin_image self end |
#to_bin_volume(series, source = nil) ⇒ Object
Converts the BinImage instance to a single image BinVolume instance.
Parameters
-
series
– The image series (e.g. ImageSeries or DoseVolume) which forms the reference data of the BinVolume. -
source
– The object which is the source of the binary (segmented) data (i.e. ROI or Dose/Hounsfield threshold).
249 250 251 |
# File 'lib/rtkit/bin_image.rb', line 249 def to_bin_volume(series, source=nil) bin_volume = BinVolume.new(series, :images => [self], :source => source) end |
#to_contours(slice) ⇒ Object
Creates an array of Contour instances from the segmentation of this BinImage. Returns the array of Contours. Returns an empty array if no Contours are created (empty BinImage).
Parameters
-
slice
– A Slice instance which the Contours will be connected to.
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/rtkit/bin_image.rb', line 261 def to_contours(slice) raise ArgumentError, "Invalid argument 'slice. Expected Slice, got #{slice.class}." unless slice.is_a?(Slice) contours = Array.new # Iterate the extracted collection of contour indices and convert to Contour instances: contour_indices.each do |contour| # Convert column and row indices to X, Y and Z coordinates: x, y, z = coordinates_from_indices(NArray.to_na(contour.columns), NArray.to_na(contour.rows)) # Convert NArray to Array and round the coordinate floats: x = x.to_a.collect {|f| f.round(1)} y = y.to_a.collect {|f| f.round(1)} z = z.to_a.collect {|f| f.round(3)} contours << Contour.create_from_coordinates(x, y, z, slice) end return contours end |
#to_dcm ⇒ Object
Dumps the BinImage instance to a DObject. This is achieved by copying the Elements of the DICOM object of the Image instance referenced by this BinImage, and replacing its pixel data with the NArray of this instance. Returns the DObject instance.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/rtkit/bin_image.rb', line 282 def to_dcm # Use the original DICOM object as a starting point (keeping all non-sequence elements): # Note: Something like dcm.dup doesn't work here because that only performs a shallow copy on the DObject instance. dcm = DICOM::DObject.new @image.dcm.each_element do |element| # A bit of a hack to regenerate the DICOM elements: begin if element.value # ATM this fails for tags with integer values converted to a backslash-separated string: DICOM::Element.new(element.tag, element.value, :parent => dcm) else # Transfer the binary content as long as it is not the pixel data string: DICOM::Element.new(element.tag, element.bin, :encoded => true, :parent => dcm) end rescue DICOM::Element.new(element.tag, element.value.split("\\").collect {|val| val.to_i}, :parent => dcm) if element.value end end dcm.delete_group('0002') # Format the DICOM image ensure good contrast amongst the binary pixel values: # Window Center: DICOM::Element.new('0028,1050', '128', :parent => dcm) # Window Width: DICOM::Element.new('0028,1051', '256', :parent => dcm) # Rescale Intercept: DICOM::Element.new('0028,1052', '0', :parent => dcm) # Rescale Slope: DICOM::Element.new('0028,1053', '1', :parent => dcm) # Pixel data: dcm.pixels = @narray*255 return dcm end |
#to_slice(roi) ⇒ Object
Creates a Slice instance from the segmentation of this BinImage. This method also creates and connects any child structures as indicated in the item (e.g. Contours). Returns the Slice instance.
Parameters
-
roi
– A ROI instance which the Slice will be connected to.
323 324 325 326 327 328 329 330 |
# File 'lib/rtkit/bin_image.rb', line 323 def to_slice(roi) raise ArgumentError, "Invalid argument 'roi'. Expected ROI, got #{roi.class}." unless roi.is_a?(ROI) # Create the Slice: s = Slice.new(@image.uid, roi) # Create Contours: to_contours(s) return s end |
#write(path) ⇒ Object
Writes the BinImage to a DICOM file given by the specified file string.
334 335 336 337 |
# File 'lib/rtkit/bin_image.rb', line 334 def write(path) dcm = to_dcm dcm.write(path) end |