Class: DICOM::DObject
- Includes:
- Logging
- Defined in:
- lib/dicom/d_object.rb
Overview
The DObject class is the main class for interacting with the DICOM object. Reading from and writing to files is executed from instances of this class.
Inheritance
As the DObject class inherits from the ImageItem class, which itself inherits from the Parent class, all ImageItem and Parent methods are also available to instances of DObject.
Instance Attribute Summary collapse
-
#parent ⇒ Object
readonly
An attribute set as nil.
-
#read_success ⇒ Object
(also: #read?)
A boolean which is set as true if a DICOM file has been successfully read & parsed from a file (or binary string).
-
#source ⇒ Object
The source of the DObject (nil, :str or file name string).
-
#stream ⇒ Object
readonly
The Stream instance associated with this DObject instance (this attribute is mostly used internally).
-
#was_dcm_on_input ⇒ Object
An attribute (used by e.g. DICOM.load) to indicate that a DObject-type instance was given to the load method (instead of e.g. a file).
-
#write_success ⇒ Object
(also: #written?)
readonly
A boolean which is set as true if a DObject instance has been successfully written to file (or successfully encoded).
Class Method Summary collapse
-
.get(link) ⇒ DObject
Creates a DObject instance by downloading a DICOM file specified by a hyperlink, and parsing the retrieved file.
-
.parse(string, options = {}) ⇒ Object
Creates a DObject instance by parsing an encoded binary DICOM string.
-
.read(file, options = {}) ⇒ Object
Creates a DObject instance by reading and parsing a DICOM file.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Checks for equality.
-
#anonymize(a = Anonymizer.new) ⇒ Object
Performs de-identification (anonymization) on the DICOM object.
-
#encode_segments(max_size, transfer_syntax = IMPLICIT_LITTLE_ENDIAN) ⇒ Array<String>
Encodes the DICOM object into a series of binary string segments with a specified maximum length.
-
#hash ⇒ Integer
Computes a hash code for this object.
-
#initialize ⇒ DObject
constructor
Creates a DObject instance (DObject is an abbreviation for “DICOM object”).
-
#print_all ⇒ Object
Prints information of interest related to the DICOM object.
-
#summary ⇒ Array<String>
Gathers key information about the DObject as well as some system data, and prints this information to the screen.
-
#to_dcm ⇒ DObject
Returns self.
-
#transfer_syntax ⇒ String
Gives the transfer syntax string of the DObject.
-
#transfer_syntax=(new_syntax) ⇒ Object
Changes the transfer syntax Element of the DObject instance, and performs re-encoding of all numerical values if a switch of endianness is implied.
-
#write(file_name, options = {}) ⇒ Object
Writes the DICOM object to file.
Methods included from Logging
Methods inherited from ImageItem
#add_element, #add_sequence, #color?, #compression?, #decode_pixels, #delete_sequences, #encode_pixels, #image, #image=, #image_from_file, #image_strings, #image_to_file, #images, #narray, #num_cols, #num_frames, #num_rows, #pixels, #pixels=
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
Constructor Details
#initialize ⇒ DObject
Creates a DObject instance (DObject is an abbreviation for “DICOM object”).
The DObject instance holds references to the different types of objects (Element, Item, Sequence) that makes up a DICOM object. A DObject is typically buildt by reading and parsing a file or a binary string (with DObject::read or ::parse), but can also be buildt from an empty state by this method.
To customize logging behaviour, refer to the Logging module documentation.
182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/dicom/d_object.rb', line 182 def initialize # Initialization of variables that DObject share with other parent elements: initialize_parent # Structural information (default values): @explicit = true @str_endian = false # Control variables: @read_success = nil # Initialize a Stream instance which is used for encoding/decoding: @stream = Stream.new(nil, @str_endian) # The DObject instance is the top of the hierarchy and unlike other elements it has no parent: @parent = nil end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class DICOM::Parent
Instance Attribute Details
#parent ⇒ Object (readonly)
An attribute set as nil. This attribute is included to provide consistency with the other element types which usually have a parent defined.
30 31 32 |
# File 'lib/dicom/d_object.rb', line 30 def parent @parent end |
#read_success ⇒ Object Also known as: read?
A boolean which is set as true if a DICOM file has been successfully read & parsed from a file (or binary string).
32 33 34 |
# File 'lib/dicom/d_object.rb', line 32 def read_success @read_success end |
#source ⇒ Object
The source of the DObject (nil, :str or file name string).
34 35 36 |
# File 'lib/dicom/d_object.rb', line 34 def source @source end |
#stream ⇒ Object (readonly)
The Stream instance associated with this DObject instance (this attribute is mostly used internally).
36 37 38 |
# File 'lib/dicom/d_object.rb', line 36 def stream @stream end |
#was_dcm_on_input ⇒ Object
An attribute (used by e.g. DICOM.load) to indicate that a DObject-type instance was given to the load method (instead of e.g. a file).
38 39 40 |
# File 'lib/dicom/d_object.rb', line 38 def was_dcm_on_input @was_dcm_on_input end |
#write_success ⇒ Object (readonly) Also known as: written?
A boolean which is set as true if a DObject instance has been successfully written to file (or successfully encoded).
40 41 42 |
# File 'lib/dicom/d_object.rb', line 40 def write_success @write_success end |
Class Method Details
.get(link) ⇒ DObject
Highly experimental and un-tested!
Designed for the HTTP protocol only.
Whether this method should be included or removed from ruby-dicom is up for debate.
Creates a DObject instance by downloading a DICOM file specified by a hyperlink, and parsing the retrieved file.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/dicom/d_object.rb', line 55 def self.get(link) raise ArgumentError, "Invalid argument 'link'. Expected String, got #{link.class}." unless link.is_a?(String) raise ArgumentError, "Invalid argument 'link'. Expected a string starting with 'http', got #{link}." unless link.index('http') == 0 require 'open-uri' bin = nil file = nil # Try to open the remote file using open-uri: retrials = 0 begin file = open(link, 'rb') # binary encoding (ASCII-8BIT) rescue Exception => e if retrials > 3 retrials = 0 raise "Unable to retrieve the file. File does not exist?" else logger.warn("Exception in ruby-dicom when loading a dicom file from: #{file}") logger.debug("Retrying... #{retrials}") retrials += 1 retry end end bin = File.open(file, "rb") { |f| f.read } # Parse the file contents and create the DICOM object: if bin dcm = self.parse(bin) else dcm = self.new dcm.read_success = false end dcm.source = link return dcm end |
.parse(string, options = {}) ⇒ Object
Creates a DObject instance by parsing an encoded binary DICOM string.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/dicom/d_object.rb', line 101 def self.parse(string, ={}) raise ArgumentError, "Invalid argument 'string'. Expected String, got #{string.class}." unless string.is_a?(String) raise ArgumentError, "Invalid option :syntax. Expected String, got #{[:syntax].class}." if [:syntax] && ![:syntax].is_a?(String) signature = [:signature].nil? ? true : [:signature] dcm = self.new dcm.send(:read, string, signature, :overwrite => [:overwrite], :syntax => [:syntax]) if dcm.read? logger.debug("DICOM string successfully parsed.") else logger.warn("Failed to parse this string as DICOM.") end dcm.source = :str return dcm end |
.read(file, options = {}) ⇒ Object
Creates a DObject instance by reading and parsing a DICOM file.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 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 166 |
# File 'lib/dicom/d_object.rb', line 125 def self.read(file, ={}) raise ArgumentError, "Invalid argument 'file'. Expected String, got #{file.class}." unless file.is_a?(String) # Read the file content: bin = nil unless File.exist?(file) logger.error("Invalid (non-existing) file: #{file}") else unless File.readable?(file) logger.error("File exists but I don't have permission to read it: #{file}") else if File.directory?(file) logger.error("Expected a file, got a directory: #{file}") else if File.size(file) < 8 logger.error("This file is too small to contain any DICOM information: #{file}.") else bin = File.open(file, "rb") { |f| f.read } end end end end # Parse the file contents and create the DICOM object: if bin dcm = self.parse(bin, ) # If reading failed, and no transfer syntax was detected, we will make another attempt at reading the file while forcing explicit (little endian) decoding. # This will help for some rare cases where the DICOM file is saved (erroneously, Im sure) with explicit encoding without specifying the transfer syntax tag. if !dcm.read? and !dcm.exists?("0002,0010") logger.info("Attempting a second decode pass (assuming Explicit Little Endian transfer syntax).") [:syntax] = EXPLICIT_LITTLE_ENDIAN dcm = self.parse(bin, ) end else dcm = self.new end if dcm.read? logger.info("DICOM file successfully read: #{file}") else logger.warn("Reading DICOM file failed: #{file}") end dcm.source = file return dcm end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Checks for equality.
Other and self are considered equivalent if they are of compatible types and their attributes are equivalent.
204 205 206 207 208 |
# File 'lib/dicom/d_object.rb', line 204 def ==(other) if other.respond_to?(:to_dcm) other.send(:state) == state end end |
#anonymize(a = Anonymizer.new) ⇒ Object
Performs de-identification (anonymization) on the DICOM object.
216 217 218 |
# File 'lib/dicom/d_object.rb', line 216 def anonymize(a=Anonymizer.new) a.to_anonymizer.anonymize(self) end |
#encode_segments(max_size, transfer_syntax = IMPLICIT_LITTLE_ENDIAN) ⇒ Array<String>
Encodes the DICOM object into a series of binary string segments with a specified maximum length.
Returns the encoded binary strings in an array.
230 231 232 233 234 235 |
# File 'lib/dicom/d_object.rb', line 230 def encode_segments(max_size, transfer_syntax=IMPLICIT_LITTLE_ENDIAN) raise ArgumentError, "Invalid argument. Expected an Integer, got #{max_size.class}." unless max_size.is_a?(Integer) raise ArgumentError, "Argument too low (#{max_size}), please specify a bigger Integer." unless max_size > 16 raise "Can not encode binary segments for an empty DICOM object." if children.length == 0 encode_in_segments(max_size, :syntax => transfer_syntax) end |
#hash ⇒ Integer
Two objects with the same attributes will have the same hash code.
Computes a hash code for this object.
243 244 245 |
# File 'lib/dicom/d_object.rb', line 243 def hash state.hash end |
#print_all ⇒ Object
Prints information of interest related to the DICOM object. Calls the Parent#print method as well as DObject#summary.
250 251 252 253 254 |
# File 'lib/dicom/d_object.rb', line 250 def print_all puts "" print(:value_max => 30) summary end |
#summary ⇒ Array<String>
Gathers key information about the DObject as well as some system data, and prints this information to the screen. This information includes properties like encoding, byte order, modality and various image properties.
261 262 263 264 265 266 267 268 269 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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/dicom/d_object.rb', line 261 def summary # FIXME: Perhaps this method should be split up in one or two separate methods # which just builds the information arrays, and a third method for printing this to the screen. sys_info = Array.new info = Array.new # Version of Ruby DICOM used: sys_info << "Ruby DICOM version: #{VERSION}" # System endian: cpu = (CPU_ENDIAN ? "Big Endian" : "Little Endian") sys_info << "Byte Order (CPU): #{cpu}" # Source (file name): if @source if @source == :str source = "Binary string #{@read_success ? '(successfully parsed)' : '(failed to parse)'}" else source = "File #{@read_success ? '(successfully read)' : '(failed to read)'}: #{@source}" end else source = 'Created from scratch' end info << "Source: #{source}" # Modality: modality = (LIBRARY.uid(value('0008,0016')) ? LIBRARY.uid(value('0008,0016')).name : "SOP Class unknown or not specified!") info << "Modality: #{modality}" # Meta header presence (Simply check for the presence of the transfer syntax data element), VR and byte order: ts_status = self['0002,0010'] ? '' : ' (Assumed)' ts = LIBRARY.uid(transfer_syntax) explicit = ts ? ts.explicit? : true endian = ts ? ts.big_endian? : false = ts ? "" : " (But unknown/invalid transfer syntax: #{transfer_syntax})" info << "Meta Header: #{self['0002,0010'] ? 'Yes' : 'No'}#{}" info << "Value Representation: #{explicit ? 'Explicit' : 'Implicit'}#{ts_status}" info << "Byte Order (File): #{endian ? 'Big Endian' : 'Little Endian'}#{ts_status}" # Pixel data: pixels = self[PIXEL_TAG] unless pixels info << "Pixel Data: No" else info << "Pixel Data: Yes" # Image size: cols = (exists?("0028,0011") ? self["0028,0011"].value : "Columns missing") rows = (exists?("0028,0010") ? self["0028,0010"].value : "Rows missing") info << "Image Size: #{cols}*#{rows}" # Frames: frames = value("0028,0008") || "1" unless frames == "1" or frames == 1 # Encapsulated or 3D pixel data: if pixels.is_a?(Element) frames = frames.to_s + " (3D Pixel Data)" else frames = frames.to_s + " (Encapsulated Multiframe Image)" end end info << "Number of frames: #{frames}" # Color: colors = (exists?("0028,0004") ? self["0028,0004"].value : "Not specified") info << "Photometry: #{colors}" # Compression: compression = (ts ? (ts.compressed_pixels? ? ts.name : 'No') : 'No' ) info << "Compression: #{compression}#{ts_status}" # Pixel bits (allocated): bits = (exists?("0028,0100") ? self["0028,0100"].value : "Not specified") info << "Bits per Pixel: #{bits}" end # Print the DICOM object's key properties: separator = "-------------------------------------------" puts "System Properties:" puts separator + "\n" puts sys_info puts "\n" puts "DICOM Object Properties:" puts separator puts info puts separator return info end |
#to_dcm ⇒ DObject
Returns self.
342 343 344 |
# File 'lib/dicom/d_object.rb', line 342 def to_dcm self end |
#transfer_syntax ⇒ String
Gives the transfer syntax string of the DObject.
If a transfer syntax has not been defined in the DObject, a default tansfer syntax is assumed and returned.
352 353 354 |
# File 'lib/dicom/d_object.rb', line 352 def transfer_syntax return value("0002,0010") || IMPLICIT_LITTLE_ENDIAN end |
#transfer_syntax=(new_syntax) ⇒ Object
This method does not change the compressed state of the pixel data element. Changing
Changes the transfer syntax Element of the DObject instance, and performs re-encoding of all numerical values if a switch of endianness is implied.
the transfer syntax between an uncompressed and compressed state will NOT change the pixel data accordingly (this must be taken care of manually).
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/dicom/d_object.rb', line 365 def transfer_syntax=(new_syntax) # Verify old and new transfer syntax: new_uid = LIBRARY.uid(new_syntax) old_uid = LIBRARY.uid(transfer_syntax) raise ArgumentError, "Invalid/unknown transfer syntax specified: #{new_syntax}" unless new_uid && new_uid.transfer_syntax? raise ArgumentError, "Invalid/unknown existing transfer syntax: #{new_syntax} Unable to reliably handle byte order encoding. Modify the transfer syntax element directly instead." unless old_uid && old_uid.transfer_syntax? # Set the new transfer syntax: if exists?("0002,0010") self["0002,0010"].value = new_syntax else add(Element.new("0002,0010", new_syntax)) end # Update our Stream instance with the new encoding: @stream.endian = new_uid.big_endian? # If endianness is changed, re-encode elements (only elements depending on endianness will actually be re-encoded): encode_children(old_uid.big_endian?) if old_uid.big_endian? != new_uid.big_endian? end |
#write(file_name, options = {}) ⇒ Object
The goal of the Ruby DICOM library is to yield maximum conformance with the DICOM
Writes the DICOM object to file.
standard when outputting DICOM files. Therefore, when encoding the DICOM file, manipulation of items such as the meta group, group lengths and header signature may occur. Therefore, the file that is written may not be an exact bitwise copy of the file that was read, even if no DObject manipulation has been done by the user.
398 399 400 401 402 403 |
# File 'lib/dicom/d_object.rb', line 398 def write(file_name, ={}) raise ArgumentError, "Invalid file_name. Expected String, got #{file_name.class}." unless file_name.is_a?(String) @include_empty_parents = [:include_empty_parents] unless [:ignore_meta] write_elements(:file_name => file_name, :signature => true, :syntax => transfer_syntax) end |