Class: RTKIT::Image
Overview
Contains the DICOM data and methods related to an image.
Inheritance
-
As the Image class inherits from the PixelData class, all PixelData methods are available to instances of Image.
Instance Attribute Summary collapse
-
#col_spacing ⇒ Object
The physical distance (in millimeters) between columns in the pixel data (i.e. horisontal spacing).
-
#columns ⇒ Object
The number of columns in the pixel data.
-
#cosines ⇒ Object
The values of the Image Orientation (Patient) element.
-
#date ⇒ Object
readonly
The Instance Creation Date.
-
#dcm ⇒ Object
readonly
The DICOM object of this Image instance.
-
#narray ⇒ Object
The 2d NArray holding the pixel data of this Image instance.
-
#pos_slice ⇒ Object
The physical position (in millimeters) of the image slice.
-
#pos_x ⇒ Object
The physical position (in millimeters) of the first (left) column in the pixel data.
-
#pos_y ⇒ Object
The physical position (in millimeters) of the first (top) row in the pixel data.
-
#row_spacing ⇒ Object
The physical distance (in millimeters) between rows in the pixel data (i.e. vertical spacing).
-
#rows ⇒ Object
The number of rows in the pixel data.
-
#series ⇒ Object
readonly
The Image’s Series (volume) reference.
-
#time ⇒ Object
readonly
The Instance Creation Time.
-
#uid ⇒ Object
readonly
The SOP Instance UID.
Class Method Summary collapse
-
.load(dcm, series) ⇒ Object
Creates a new Image instance by loading image information from the specified DICOM object.
Instance Method Summary collapse
-
#==(other) ⇒ Object
(also: #eql?)
Returns true if the argument is an instance with attributes equal to self.
-
#binary_image(coords_x, coords_y, coords_z) ⇒ Object
Creates and returns a filled, binary NArray image (a ‘segmented’ image) based on the provided contour coordinates.
-
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
-
#initialize(sop_uid, series) ⇒ Image
constructor
Creates a new Image instance.
-
#load_pixel_data(dcm) ⇒ Object
Transfers the pixel data, as well as the related image properties and the DObject instance itself, to the Image instance.
-
#pixel_area ⇒ Object
Calculates the area of a single pixel of this image.
-
#pixel_values(selection) ⇒ Object
Extracts pixel values from the image based on the given indices.
-
#set_resolution(columns, rows, options = {}) ⇒ Object
Sets the resolution of the image.
-
#to_dcm ⇒ Object
Dumps the Image instance to a DObject.
-
#to_image ⇒ Object
Returns self.
-
#write(file_name) ⇒ Object
Writes the Image 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(sop_uid, series) ⇒ Image
Creates a new Image instance. The SOP Instance UID tag value is used to uniquely identify an image.
Parameters
-
sop_uid
– The SOP Instance UID string. -
series
– The Series instance that this Image belongs to.
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rtkit/image.rb', line 65 def initialize(sop_uid, series) raise ArgumentError, "Invalid argument 'sop_uid'. Expected String, got #{sop_uid.class}." unless sop_uid.is_a?(String) raise ArgumentError, "Invalid argument 'series'. Expected Series, got #{series.class}." unless series.is_a?(Series) raise ArgumentError, "Invalid argument 'series'. Expected Series to have an image related modality, got #{series.modality}." unless IMAGE_MODALITIES.include?(series.modality) # Key attributes: @uid = sop_uid @series = series # Register ourselves with the ImageSeries: @series.add_image(self) end |
Instance Attribute Details
#col_spacing ⇒ Object
The physical distance (in millimeters) between columns in the pixel data (i.e. horisontal spacing).
12 13 14 |
# File 'lib/rtkit/image.rb', line 12 def col_spacing @col_spacing end |
#columns ⇒ Object
The number of columns in the pixel data.
14 15 16 |
# File 'lib/rtkit/image.rb', line 14 def columns @columns end |
#cosines ⇒ Object
The values of the Image Orientation (Patient) element.
16 17 18 |
# File 'lib/rtkit/image.rb', line 16 def cosines @cosines end |
#date ⇒ Object (readonly)
The Instance Creation Date.
18 19 20 |
# File 'lib/rtkit/image.rb', line 18 def date @date end |
#dcm ⇒ Object (readonly)
The DICOM object of this Image instance.
20 21 22 |
# File 'lib/rtkit/image.rb', line 20 def dcm @dcm end |
#narray ⇒ Object
The 2d NArray holding the pixel data of this Image instance.
22 23 24 |
# File 'lib/rtkit/image.rb', line 22 def narray @narray end |
#pos_slice ⇒ Object
The physical position (in millimeters) of the image slice.
24 25 26 |
# File 'lib/rtkit/image.rb', line 24 def pos_slice @pos_slice end |
#pos_x ⇒ Object
The physical position (in millimeters) of the first (left) column in the pixel data.
26 27 28 |
# File 'lib/rtkit/image.rb', line 26 def pos_x @pos_x end |
#pos_y ⇒ Object
The physical position (in millimeters) of the first (top) row in the pixel data.
28 29 30 |
# File 'lib/rtkit/image.rb', line 28 def pos_y @pos_y end |
#row_spacing ⇒ Object
The physical distance (in millimeters) between rows in the pixel data (i.e. vertical spacing).
30 31 32 |
# File 'lib/rtkit/image.rb', line 30 def row_spacing @row_spacing end |
#rows ⇒ Object
The number of rows in the pixel data.
32 33 34 |
# File 'lib/rtkit/image.rb', line 32 def rows @rows end |
#series ⇒ Object (readonly)
The Image’s Series (volume) reference.
34 35 36 |
# File 'lib/rtkit/image.rb', line 34 def series @series end |
#time ⇒ Object (readonly)
The Instance Creation Time.
36 37 38 |
# File 'lib/rtkit/image.rb', line 36 def time @time end |
#uid ⇒ Object (readonly)
The SOP Instance UID.
38 39 40 |
# File 'lib/rtkit/image.rb', line 38 def uid @uid end |
Class Method Details
.load(dcm, series) ⇒ Object
Creates a new Image instance by loading image information from the specified DICOM object. The Image object’s SOP Instance UID string value is used to uniquely identify an image.
Parameters
-
dcm
– An instance of a DICOM object (DObject). -
series
– The Series instance that this Image belongs to.
48 49 50 51 52 53 54 55 56 |
# File 'lib/rtkit/image.rb', line 48 def self.load(dcm, series) raise ArgumentError, "Invalid argument 'dcm'. Expected DObject, got #{dcm.class}." unless dcm.is_a?(DICOM::DObject) raise ArgumentError, "Invalid argument 'series'. Expected Series, got #{series.class}." unless series.is_a?(Series) raise ArgumentError, "Invalid argument 'dcm'. Expected an image related modality, got #{dcm.value(MODALITY)}." unless IMAGE_MODALITIES.include?(dcm.value(MODALITY)) sop_uid = dcm.value(SOP_UID) image = self.new(sop_uid, series) image.load_pixel_data(dcm) return image end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
Returns true if the argument is an instance with attributes equal to self.
78 79 80 81 82 |
# File 'lib/rtkit/image.rb', line 78 def ==(other) if other.respond_to?(:to_image) other.send(:state) == state end end |
#binary_image(coords_x, coords_y, coords_z) ⇒ Object
Creates and returns a filled, binary NArray image (a ‘segmented’ image) based on the provided contour coordinates.
Parameters
-
coords_x
– An Array/NArray of a contour’s X coordinates. Must have at least 3 elements. -
coords_y
– An Array/NArray of a contour’s Y coordinates. Must have at least 3 elements. -
coords_z
– An Array/NArray of a contour’s Z coordinates. Must have at least 3 elements.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rtkit/image.rb', line 94 def binary_image(coords_x, coords_y, coords_z) raise ArgumentError, "Invalid argument 'coords_x'. Expected at least 3 elements, got #{coords_x.length}" unless coords_x.length >= 3 raise ArgumentError, "Invalid argument 'coords_y'. Expected at least 3 elements, got #{coords_y.length}" unless coords_y.length >= 3 raise ArgumentError, "Invalid argument 'coords_z'. Expected at least 3 elements, got #{coords_z.length}" unless coords_z.length >= 3 # Values that will be used for image geometry: empty_value = 0 line_value = 1 fill_value = 2 # Convert physical coordinates to image indices: column_indices, row_indices = coordinates_to_indices(NArray.to_na(coords_x), NArray.to_na(coords_y), NArray.to_na(coords_z)) # Create an empty array and fill in the gathered points: empty_array = NArray.byte(@columns, @rows) delineated_array = draw_lines(column_indices.to_a, row_indices.to_a, empty_array, line_value) # Establish starting point indices for the coming flood fill algorithm: # (Using a rather simple approach by finding the average column and row index among the selection of indices) start_col = column_indices.mean start_row = row_indices.mean # Perform a flood fill to enable us to extract all pixels contained in a specific ROI: filled_array = flood_fill(start_col, start_row, delineated_array, fill_value) # Extract the indices of 'ROI pixels': if filled_array[0,0] != fill_value # ROI has been filled as expected. Extract indices of value line_value and fill_value: filled_array[(filled_array.eq line_value).where] = fill_value indices = (filled_array.eq fill_value).where else # An inversion has occured. The entire image except our ROI has been filled. Extract indices of value line_value and empty_value: filled_array[(filled_array.eq line_value).where] = empty_value indices = (filled_array.eq empty_value).where end # Create binary image: bin_image = NArray.byte(@columns, @rows) bin_image[indices] = 1 return bin_image end |
#hash ⇒ Object
Generates a Fixnum hash value for this instance.
151 152 153 |
# File 'lib/rtkit/image.rb', line 151 def hash state.hash end |
#load_pixel_data(dcm) ⇒ Object
Transfers the pixel data, as well as the related image properties and the DObject instance itself, to the Image instance.
Parameters
-
dcm
– A DICOM object containing image data that will be applied to the Image instance.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/rtkit/image.rb', line 162 def load_pixel_data(dcm) raise ArgumentError, "Invalid argument 'dcm'. Expected DObject, got #{dcm.class}." unless dcm.is_a?(DICOM::DObject) raise ArgumentError, "Invalid argument 'dcm'. Expected an image related modality, got #{dcm.value(MODALITY)}." unless IMAGE_MODALITIES.include?(dcm.value(MODALITY)) # Set attributes common for all image modalities, i.e. CT, MR, RTDOSE & RTIMAGE: @dcm = dcm @narray = dcm.narray @date = dcm.value(IMAGE_DATE) @time = dcm.value(IMAGE_TIME) @uid = dcm.value(SOP_UID) @columns = dcm.value(COLUMNS) @rows = dcm.value(ROWS) # Some difference in where we pick our values depending on if we have an RTIMAGE or another type: if @series.modality == 'RTIMAGE' image_position = dcm.value(RT_IMAGE_POSITION).split("\\") raise "Invalid DICOM image: 2 basckslash-separated values expected for RT Image Position (Patient), got: #{image_position}" unless image_position.length == 2 @pos_x = image_position[0].to_f @pos_y = image_position[1].to_f @pos_slice = nil spacing = dcm.value(IMAGE_PLANE_SPACING).split("\\") raise "Invalid DICOM image: 2 basckslash-separated values expected for Image Plane Pixel Spacing, got: #{spacing}" unless spacing.length == 2 @col_spacing = spacing[1].to_f @row_spacing = spacing[0].to_f else image_position = dcm.value(IMAGE_POSITION).split("\\") raise "Invalid DICOM image: 3 basckslash-separated values expected for Image Position (Patient), got: #{image_position}" unless image_position.length == 3 @pos_x = image_position[0].to_f @pos_y = image_position[1].to_f self.pos_slice = image_position[2].to_f spacing = dcm.value(SPACING).split("\\") raise "Invalid DICOM image: 2 basckslash-separated values expected for Pixel Spacing, got: #{spacing}" unless spacing.length == 2 @col_spacing = spacing[1].to_f @row_spacing = spacing[0].to_f raise "Invalid DICOM image: Direction cosines missing (DICOM tag '#{IMAGE_ORIENTATION}')." unless dcm.exists?(IMAGE_ORIENTATION) @cosines = dcm.value(IMAGE_ORIENTATION).split("\\").collect {|val| val.to_f} if dcm.value(IMAGE_ORIENTATION) raise "Invalid DICOM image: 6 values expected for direction cosines (DICOM tag '#{IMAGE_ORIENTATION}'), got #{@cosines.length}." unless @cosines.length == 6 end end |
#pixel_area ⇒ Object
Calculates the area of a single pixel of this image. Returns a float value, in units of millimeters squared.
212 213 214 |
# File 'lib/rtkit/image.rb', line 212 def pixel_area return @row_spacing * @col_spacing end |
#pixel_values(selection) ⇒ Object
Extracts pixel values from the image based on the given indices.
218 219 220 221 |
# File 'lib/rtkit/image.rb', line 218 def pixel_values(selection) raise ArgumentError, "Invalid argument 'selection'. Expected Selection, got #{selection.class}" unless selection.is_a?(Selection) return @narray[selection.indices] end |
#set_resolution(columns, rows, options = {}) ⇒ Object
Sets the resolution of the image. This modifies the pixel data (in the specified way) and the column/row attributes as well. The image 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. -
rows
– Integer. The number of rows applied to the cropped/expanded image.
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)).
270 271 272 273 274 275 276 277 278 279 280 281 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 314 315 316 317 318 319 |
# File 'lib/rtkit/image.rb', line 270 def set_resolution(columns, rows, ={}) [:hor] = :even unless [:hor] [:ver] = :even unless [:ver] old_cols = @narray.shape[0] old_rows = @narray.shape[1] if @narray # Modify the width only if changed: if columns != old_cols self.columns = columns.to_i old_arr = @narray.dup @narray = NArray.int(@columns, @rows) if @columns > old_cols # New array is larger: case [:hor] when :left then @narray[(@columns-old_cols)..(@columns-1), true] = old_arr when :right then @narray[0..(old_cols-1), true] = old_arr when :even then @narray[((@columns-old_cols)/2+(@columns-old_cols).remainder(2))..(@columns-1-(@columns-old_cols)/2), true] = old_arr end else # New array is smaller: case [:hor] when :left then @narray = old_arr[(old_cols-@columns)..(old_cols-1), true] when :right then @narray = old_arr[0..(@columns-1), true] when :even then @narray = old_arr[((old_cols-@columns)/2+(old_cols-@columns).remainder(2))..(old_cols-1-(old_cols-@columns)/2), true] end end end # Modify the height only if changed: if rows != old_rows self.rows = rows.to_i old_arr = @narray.dup @narray = NArray.int(@columns, @rows) if @rows > old_rows # New array is larger: case [:ver] when :top then @narray[true, (@rows-old_rows)..(@rows-1)] = old_arr when :bottom then @narray[true, 0..(old_rows-1)] = old_arr when :even then @narray[true, ((@rows-old_rows)/2+(@rows-old_rows).remainder(2))..(@rows-1-(@rows-old_rows)/2)] = old_arr end else # New array is smaller: case [:ver] when :top then @narray = old_arr[true, (old_rows-@rows)..(old_rows-1)] when :bottom then @narray = old_arr[true, 0..(@rows-1)] when :even then @narray = old_arr[true, ((old_rows-@rows)/2+(old_rows-@rows).remainder(2))..(old_rows-1-(old_rows-@rows)/2)] end end end end end |
#to_dcm ⇒ Object
Dumps the Image instance to a DObject. This overwrites the dcm instance attribute. Returns the DObject instance.
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/rtkit/image.rb', line 325 def to_dcm # Use the original DICOM object as a starting point, # and update all image related parameters: @dcm.add(DICOM::Element.new(IMAGE_DATE, @date)) @dcm.add(DICOM::Element.new(IMAGE_TIME, @time)) @dcm.add(DICOM::Element.new(SOP_UID, @uid)) @dcm.add(DICOM::Element.new(COLUMNS, @columns)) @dcm.add(DICOM::Element.new(ROWS, @rows)) if @series.modality == 'RTIMAGE' @dcm.add(DICOM::Element.new(RT_IMAGE_POSITION, [@pos_x, @pos_y].join("\\"))) @dcm.add(DICOM::Element.new(IMAGE_PLANE_SPACING, [@row_spacing, @col_spacing].join("\\"))) else @dcm.add(DICOM::Element.new(IMAGE_POSITION, [@pos_x, @pos_y, @pos_slice].join("\\"))) @dcm.add(DICOM::Element.new(SPACING, [@row_spacing, @col_spacing].join("\\"))) @dcm.add(DICOM::Element.new(IMAGE_ORIENTATION, [@cosines].join("\\"))) end # Write pixel data: @dcm.pixels = @narray return @dcm end |
#to_image ⇒ Object
Returns self.
348 349 350 |
# File 'lib/rtkit/image.rb', line 348 def to_image self end |
#write(file_name) ⇒ Object
Writes the Image to a DICOM file given by the specified file string.
354 355 356 357 |
# File 'lib/rtkit/image.rb', line 354 def write(file_name) to_dcm @dcm.write(file_name) end |