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, #add_item, #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, #print, #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.
447 448 449 450 451 |
# File 'lib/dicom/image_item.rb', line 447 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.
When calling this method the corresponding image processor gem must have been loaded in advance (example: require ‘RMagick’).
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.
137 138 139 140 141 142 |
# File 'lib/dicom/image_item.rb', line 137 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.
288 289 290 291 292 293 294 |
# File 'lib/dicom/image_item.rb', line 288 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).
209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/dicom/image_item.rb', line 209 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.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/dicom/image_item.rb', line 226 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’).
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/dicom/image_item.rb', line 253 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.
When calling this method the corresponding image processor gem must have been loaded in advance (example: require ‘RMagick’).
Extracts an array of image objects, created from the encoded pixel data using the image related elements in the DICOM object.
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 199 200 |
# File 'lib/dicom/image_item.rb', line 166 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].
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 372 373 |
# File 'lib/dicom/image_item.rb', line 339 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 = NArray.int(num_frames, num_cols, num_rows) num_frames.times do |i| narr[i, true, true] = NArray.to_na(pixels[(i * num_cols * num_rows)..((i + 1) * num_cols * num_rows - 1)]).reshape!(num_cols, num_rows) end pixels = narr else pixels = NArray.to_na(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.
300 301 302 |
# File 'lib/dicom/image_item.rb', line 300 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.
309 310 311 |
# File 'lib/dicom/image_item.rb', line 309 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.
317 318 319 |
# File 'lib/dicom/image_item.rb', line 317 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).
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/dicom/image_item.rb', line 394 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).
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/dicom/image_item.rb', line 425 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 |