Class: DICOM::ImageItem
- Includes:
- ImageProcessor
- Defined in:
- lib/dicom/image_item.rb
Overview
Super class which contains common code for both the DObject and Item classes. This class includes the image related methods, since images may be stored either directly in the DObject, or in items (encapsulated items in the “Pixel Data” element or in “Icon Image Sequence” items).
Inheritance
As the ImageItem class inherits from the Parent class, all Parent methods are also available to objects which has inherited ImageItem.
Instance Method Summary collapse
-
#add_element(tag, value, options = {}) ⇒ Object
Creates an Element with the given arguments and connects it to self.
-
#add_sequence(tag, options = {}) ⇒ Object
Creates a Sequence with the given arguments and connects it to self.
-
#color? ⇒ Boolean
Checks if colored pixel data is present.
-
#compression? ⇒ Boolean
Checks if compressed pixel data is present.
-
#decode_pixels(bin, stream = @stream) ⇒ Array<Integer>
Unpacks pixel values from a binary pixel string.
-
#delete_sequences ⇒ Object
Delete all Sequence instances from the DObject or Item instance.
-
#encode_pixels(pixels, stream = @stream) ⇒ String
Packs a pixel value array to a binary pixel string.
-
#image(options = {}) ⇒ MagickImage, ...
Extracts a single image object, created from the encoded pixel data using the image related elements in the DICOM object.
-
#image=(image) ⇒ Object
Encodes pixel data from a (Magick) image object and writes it to the pixel data element (7FE0,0010).
-
#image_from_file(file) ⇒ Object
Reads a binary string from a specified file and writes it to the value field of the pixel data element (7FE0,0010).
-
#image_strings(split = false) ⇒ Array<String, NilClass>
Extracts the pixel data binary string(s) in an array.
-
#image_to_file(file) ⇒ Object
Dumps the binary content of the Pixel Data element to the specified file.
-
#images(options = {}) ⇒ Array<MagickImage, NilClass>
Extracts an array of image objects, created from the encoded pixel data using the image related elements in the DICOM object.
-
#narray(options = {}) ⇒ NArray, ...
Creates an NArray containing the pixel data.
-
#num_cols ⇒ Integer, NilClass
Gives the number of columns in the pixel data.
-
#num_frames ⇒ Integer
Gives the number of frames in the pixel data.
-
#num_rows ⇒ Integer, NilClass
Gives the number of rows in the pixel data.
-
#pixels(options = {}) ⇒ Array, ...
Extracts the Pixel Data values in an ordinary Ruby Array.
-
#pixels=(values) ⇒ Object
Encodes pixel data from a Ruby Array or NArray, and writes it to the pixel data element (7FE0,0010).
Methods included from ImageProcessor
#decompress, #export_pixels, #import_pixels, #valid_image_objects
Methods inherited from Parent
#[], #add, #children, #children?, #count, #count_all, #delete, #delete_children, #delete_group, #delete_private, #delete_retired, #each, #each_element, #each_item, #each_sequence, #each_tag, #elements, #elements?, #encode_children, #exists?, #group, #handle_print, #inspect, #is_parent?, #items, #items?, #length=, #max_lengths, #method_missing, #parse, #print, #representation, #reset_length, #respond_to?, #sequences, #sequences?, #to_hash, #to_json, #to_yaml, #value
Methods included from Logging
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class DICOM::Parent
Instance Method Details
#add_element(tag, value, options = {}) ⇒ Object
Creates an Element with the given arguments and connects it to self.
23 24 25 26 |
# File 'lib/dicom/image_item.rb', line 23 def add_element(tag, value, ={}) add(e = Element.new(tag, value, )) e end |
#add_sequence(tag, options = {}) ⇒ Object
Creates a Sequence with the given arguments and connects it to self.
33 34 35 36 |
# File 'lib/dicom/image_item.rb', line 33 def add_sequence(tag, ={}) add(s = Sequence.new(tag, )) s end |
#color? ⇒ Boolean
Checks if colored pixel data is present.
42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/dicom/image_item.rb', line 42 def color? # "Photometric Interpretation" is contained in the data element "0028,0004": begin photometric = photometry if photometric.include?('COLOR') or photometric.include?('RGB') or photometric.include?('YBR') return true else return false end rescue return false end end |
#compression? ⇒ Boolean
Checks if compressed pixel data is present.
60 61 62 63 64 65 66 67 |
# File 'lib/dicom/image_item.rb', line 60 def compression? # If compression is used, the pixel data element is a Sequence (with encapsulated elements), instead of a Element: if self[PIXEL_TAG].is_a?(Sequence) return true else return false end end |
#decode_pixels(bin, stream = @stream) ⇒ Array<Integer>
Unpacks pixel values from a binary pixel string. The decode is performed using values defined in the image related elements of the DObject instance.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/dicom/image_item.rb', line 76 def decode_pixels(bin, stream=@stream) raise ArgumentError, "Expected String, got #{bin.class}." unless bin.is_a?(String) pixels = false # We need to know what kind of bith depth and integer type the pixel data is saved with: bit_depth_element = self['0028,0100'] pixel_representation_element = self['0028,0103'] if bit_depth_element and pixel_representation_element # Load the binary pixel data to the Stream instance: stream.set_string(bin) template = template_string(bit_depth_element.value.to_i) pixels = stream.decode_all(template) if template else raise "The Element specifying Bit Depth (0028,0100) is missing. Unable to decode pixel data." unless bit_depth_element raise "The Element specifying Pixel Representation (0028,0103) is missing. Unable to decode pixel data." unless pixel_representation_element end return pixels end |
#delete_sequences ⇒ Object
Delete all Sequence instances from the DObject or Item instance.
445 446 447 448 449 |
# File 'lib/dicom/image_item.rb', line 445 def delete_sequences @tags.each_value do |element| delete(element.tag) if element.is_a?(Sequence) end end |
#encode_pixels(pixels, stream = @stream) ⇒ String
Packs a pixel value array to a binary pixel string. The encoding is performed using values defined in the image related elements of the DObject instance.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/dicom/image_item.rb', line 101 def encode_pixels(pixels, stream=@stream) raise ArgumentError, "Expected Array, got #{pixels.class}." unless pixels.is_a?(Array) bin = false # We need to know what kind of bith depth and integer type the pixel data is saved with: bit_depth_element = self['0028,0100'] pixel_representation_element = self['0028,0103'] if bit_depth_element and pixel_representation_element template = template_string(bit_depth_element.value.to_i) bin = stream.encode(pixels, template) if template else raise "The Element specifying Bit Depth (0028,0100) is missing. Unable to encode the pixel data." unless bit_depth_element raise "The Element specifying Pixel Representation (0028,0103) is missing. Unable to encode the pixel data." unless pixel_representation_element end return bin end |
#image(options = {}) ⇒ MagickImage, ...
Creates an image object in accordance with the selected image processor. Available processors are :rmagick and :mini_magick.
Extracts a single image object, created from the encoded pixel data using the image related elements in the DICOM object. If the object contains multiple image frames, the first image frame is returned, unless the :frame option is used.
136 137 138 139 140 141 |
# File 'lib/dicom/image_item.rb', line 136 def image(={}) [:frame] = [:frame] || 0 image = images().first image = false if image.nil? && exists?(PIXEL_TAG) return image end |
#image=(image) ⇒ Object
Encodes pixel data from a (Magick) image object and writes it to the pixel data element (7FE0,0010).
Because of pixel value issues related to image objects (images don’t like signed integers), and the possible difference between presentation values and raw pixel values, the use of image=() may result in pixel data where the integer values differs somewhat from what is expected. Use with care! For precise pixel value processing, use the Array and NArray based pixel data methods instead.
286 287 288 289 290 291 292 |
# File 'lib/dicom/image_item.rb', line 286 def image=(image) raise ArgumentError, "Expected one of the supported image classes: #{valid_image_objects} (got #{image.class})" unless valid_image_objects.include?(image.class.to_s) # Export to pixels using the proper image processor: pixels = export_pixels(image, photometry) # Encode and write to the Pixel Data Element: self.pixels = pixels end |
#image_from_file(file) ⇒ Object
Reads a binary string from a specified file and writes it to the value field of the pixel data element (7FE0,0010).
207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/dicom/image_item.rb', line 207 def image_from_file(file) raise ArgumentError, "Expected #{String}, got #{file.class}." unless file.is_a?(String) f = File.new(file, 'rb') bin = f.read(f.stat.size) if bin.length > 0 # Write the binary data to the Pixel Data Element: write_pixels(bin) else logger.info("The specified file (#{file}) is empty. Nothing to transfer.") end end |
#image_strings(split = false) ⇒ Array<String, NilClass>
Extracts the pixel data binary string(s) in an array.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/dicom/image_item.rb', line 224 def image_strings(split=false) # Pixel data may be a single binary string in the pixel data element, # or located in several encapsulated item elements: pixel_element = self[PIXEL_TAG] strings = Array.new if pixel_element.is_a?(Element) if split strings = pixel_element.bin.dup.divide(num_frames) else strings << pixel_element.bin end elsif pixel_element.is_a?(Sequence) pixel_items = pixel_element.children.first.children pixel_items.each {|item| strings << item.bin} end return strings end |
#image_to_file(file) ⇒ Object
Dumps the binary content of the Pixel Data element to the specified file.
If the DICOM object contains multi-fragment pixel data, each fragment will be dumped to separate files (e.q. ‘fragment-0.dat’, ‘fragment-1.dat’).
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/dicom/image_item.rb', line 251 def image_to_file(file) raise ArgumentError, "Expected #{String}, got #{file.class}." unless file.is_a?(String) # Split the file name in case of multiple fragments: parts = file.split('.') if parts.length > 1 base = parts[0..-2].join extension = '.' + parts.last else base = file extension = '' end # Get the binary image strings and dump them to the file(s): images = image_strings images.each_index do |i| if images.length == 1 f = File.new(file, 'wb') else f = File.new("#{base}-#{i}#{extension}", 'wb') end f.write(images[i]) f.close end end |
#images(options = {}) ⇒ Array<MagickImage, NilClass>
Creates an array of image objects in accordance with the selected image processor. Available processors are :rmagick and :mini_magick.
Extracts an array of image objects, created from the encoded pixel data using the image related elements in the DICOM object.
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/dicom/image_item.rb', line 164 def images(={}) images = Array.new if exists?(PIXEL_TAG) # Gather the pixel data strings, and pick a single frame if indicated by options: strings = image_strings(split_to_frames=true) strings = [strings[[:frame]]] if [:frame] if compression? # Decompress, either to numbers (RLE) or to an image object (image based compressions): if [TXS_RLE].include?(transfer_syntax) pixel_frames = Array.new strings.each {|string| pixel_frames << decode_rle(num_cols, num_rows, string)} else images = decompress(strings) || Array.new logger.warn("Decompressing pixel values has failed (unsupported transfer syntax: '#{transfer_syntax}' - #{LIBRARY.uid(transfer_syntax) ? LIBRARY.uid(transfer_syntax).name : 'Unknown transfer syntax!'})") unless images.length > 0 end else # Uncompressed: Decode to numbers. pixel_frames = Array.new strings.each {|string| pixel_frames << decode_pixels(string)} end if pixel_frames images = Array.new pixel_frames.each do |pixels| # Pixel values and pixel order may need to be rearranged if we have color data: pixels = process_colors(pixels) if color? if pixels images << read_image(pixels, num_cols, num_rows, ) else logger.warn("Processing pixel values for this particular color mode failed, unable to construct image(s).") end end end end return images end |
#narray(options = {}) ⇒ NArray, ...
To call this method you need to have loaded the NArray library in advance (require ‘narray’).
Creates an NArray containing the pixel data. If the pixel data is an image (single frame), a 2-dimensional NArray is returned [columns, rows]. If the pixel data is 3-dimensional (more than one frame), a 3-dimensional NArray is returned [frames, columns, rows].
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/dicom/image_item.rb', line 337 def narray(={}) pixels = nil if exists?(PIXEL_TAG) unless color? # Decode the pixel values: For now we only support returning pixel data of the first frame (if the image is located in multiple pixel data items). if compression? pixels = decompress(image_strings.first) else pixels = decode_pixels(image_strings.first) end if pixels # Import the pixels to NArray and give it a proper shape: raise "Missing Rows and/or Columns Element. Unable to construct pixel data array." unless num_rows and num_cols if num_frames > 1 or [:volume] # Create an empty 3D NArray. fill it with pixels frame by frame, then reassign the pixels variable to it: narr = Numo::Int16.zeros(num_frames, num_cols, num_rows) num_frames.times do |i| narr[i, true, true] = Numo::NArray[*pixels[(i * num_cols * num_rows)..((i + 1) * num_cols * num_rows - 1)]].reshape!(num_cols, num_rows) end pixels = narr else pixels = Numo::NArray[*pixels].reshape!(num_cols, num_rows) end # Remap the image from pixel values to presentation values if the user has requested this: pixels = process_presentation_values_narray(pixels, -65535, 65535, [:level]) if [:remap] or [:level] else logger.warn("Decompressing the Pixel Data failed. Pixel values can not be extracted.") end else logger.warn("The DICOM object contains colored pixel data. Retrieval of colored pixels is not supported by this method yet.") pixels = false end end return pixels end |
#num_cols ⇒ Integer, NilClass
Gives the number of columns in the pixel data.
298 299 300 |
# File 'lib/dicom/image_item.rb', line 298 def num_cols self['0028,0011'].value rescue nil end |
#num_frames ⇒ Integer
Assumes and gives 1 if the number of frames value is not defined.
Gives the number of frames in the pixel data.
307 308 309 |
# File 'lib/dicom/image_item.rb', line 307 def num_frames (self['0028,0008'].is_a?(Element) == true ? self['0028,0008'].value.to_i : 1) end |
#num_rows ⇒ Integer, NilClass
Gives the number of rows in the pixel data.
315 316 317 |
# File 'lib/dicom/image_item.rb', line 315 def num_rows self['0028,0010'].value rescue nil end |
#pixels(options = {}) ⇒ Array, ...
Extracts the Pixel Data values in an ordinary Ruby Array. Returns nil if no pixel data is present, and false if it fails to retrieve pixel data which is present.
The returned array does not carry the dimensions of the pixel data: It is put in a one dimensional Array (vector).
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/dicom/image_item.rb', line 392 def pixels(={}) pixels = nil if exists?(PIXEL_TAG) # For now we only support returning pixel data of the first frame, if the image is located in multiple pixel data items: if compression? pixels = decompress(image_strings.first) else pixels = decode_pixels(image_strings.first) end if pixels # Remap the image from pixel values to presentation values if the user has requested this: if [:remap] or [:level] if [:narray] # Use numerical array (faster): pixels = process_presentation_values_narray(pixels, -65535, 65535, [:level]).to_a else # Use standard Ruby array (slower): pixels = process_presentation_values(pixels, -65535, 65535, [:level]) end end else logger.warn("Decompressing the Pixel Data failed. Pixel values can not be extracted.") end end return pixels end |
#pixels=(values) ⇒ Object
Encodes pixel data from a Ruby Array or NArray, and writes it to the pixel data element (7FE0,0010).
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
# File 'lib/dicom/image_item.rb', line 423 def pixels=(values) raise ArgumentError, "The given argument does not respond to #to_a (got an argument of class #{values.class})" unless values.respond_to?(:to_a) if values.class.ancestors.to_s.include?('NArray') # With an NArray argument, make sure that it gets properly converted to an Array: if values.shape.length > 2 # For a 3D NArray we need to rearrange to ensure that the pixels get their # proper order when converting to an ordinary Array instance: narr = NArray.int(values.shape[1] * values.shape[2], values.shape[0]) values.shape[0].times do |i| narr[true, i] = values[i, true, true].reshape(values.shape[1] * values.shape[2]) end values = narr end end # Encode the pixel data: bin = encode_pixels(values.to_a.flatten) # Write the binary data to the Pixel Data Element: write_pixels(bin) end |