dicom 0.8 → 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
- # Copyright 2008-2010 Christoffer Lervag
2
-
3
1
  module DICOM
4
2
 
5
3
  # This class contains methods that interact with Ruby DICOM's dictionary.
6
4
  #
7
5
  class DLibrary
8
6
 
7
+ # A hash with element name strings as key and method name symbols as value.
8
+ attr_reader :methods_from_names
9
+ # A hash with element method name symbols as key and name strings as value.
10
+ attr_reader :names_from_methods
9
11
  # A hash containing tags as key and an array as value, where the array contains data element vr and name.
10
12
  attr_reader :tags
11
13
  # A hash containing UIDs as key and an array as value, where the array contains name and type.
@@ -20,6 +22,58 @@ module DICOM
20
22
  # Load UID hash (DICOM unique identifiers), where the keys are UID strings,
21
23
  # and values are two-element arrays [description, type]:
22
24
  @uid = Dictionary.load_uid
25
+ create_method_conversion_tables
26
+ end
27
+
28
+ # Returns the method (symbol) corresponding to the specified string value (which may represent a element tag, name or method).
29
+ # Returns nil if no match is found.
30
+ #
31
+ def as_method(value)
32
+ case true
33
+ when value.tag?
34
+ name, vr = get_name_vr(value)
35
+ @methods_from_names[name]
36
+ when value.dicom_name?
37
+ @methods_from_names[value]
38
+ when value.dicom_method?
39
+ @names_from_methods.has_key?(value.to_sym) ? value.to_sym : nil
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ # Returns the name (string) corresponding to the specified string value (which may represent a element tag, name or method).
46
+ # Returns nil if no match is found.
47
+ #
48
+ def as_name(value)
49
+ case true
50
+ when value.tag?
51
+ name, vr = get_name_vr(value)
52
+ name
53
+ when value.dicom_name?
54
+ @methods_from_names.has_key?(value) ? value.to_s : nil
55
+ when value.dicom_method?
56
+ @names_from_methods[value.to_sym]
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ # Returns the tag (string) corresponding to the specified string value (which may represent a element tag, name or method).
63
+ # Returns nil if no match is found.
64
+ #
65
+ def as_tag(value)
66
+ case true
67
+ when value.tag?
68
+ name, vr = get_name_vr(value)
69
+ name.nil? ? nil : value
70
+ when value.dicom_name?
71
+ get_tag(value)
72
+ when value.dicom_method?
73
+ get_tag(@names_from_methods[value.to_sym])
74
+ else
75
+ nil
76
+ end
23
77
  end
24
78
 
25
79
  # Checks whether a given string is a valid transfer syntax or not.
@@ -64,12 +118,12 @@ module DICOM
64
118
  # * <tt>uid</tt> -- String. A DICOM UID value.
65
119
  #
66
120
  def get_compression(uid)
121
+ raise ArgumentError, "Expected String, got #{uid.class}" unless uid.is_a?(String)
67
122
  result = false
68
- if uid
69
- value = @uid[uid]
70
- if value
71
- result = true if value[1] == "Transfer Syntax" and not value[0].include?("Endian")
72
- end
123
+ value = @uid[uid]
124
+ if value
125
+ first_word = value[0].split(" ").first
126
+ result = true if value[1] == "Transfer Syntax" and not ["Implicit", "Explicit"].include?(first_word)
73
127
  end
74
128
  return result
75
129
  end
@@ -151,9 +205,15 @@ module DICOM
151
205
  #
152
206
  def get_tag(name)
153
207
  tag = nil
208
+ name = name.to_s.downcase
209
+ @tag_name_pairs_cache ||= Hash.new
210
+ return @tag_name_pairs_cache[name] unless @tag_name_pairs_cache[name].nil?
154
211
  @tags.each_pair do |key, value|
155
- tag = key if value[1] == name
212
+ next unless value[1].downcase == name
213
+ tag = key
214
+ break
156
215
  end
216
+ @tag_name_pairs_cache[name]=tag
157
217
  return tag
158
218
  end
159
219
 
@@ -204,5 +264,25 @@ module DICOM
204
264
  return valid, explicit, endian
205
265
  end
206
266
 
267
+
268
+ private
269
+
270
+
271
+ # Creates the instance hashes that are used for name to/from method conversion.
272
+ #
273
+ def create_method_conversion_tables
274
+ if @methods_from_names.nil?
275
+ @methods_from_names = Hash.new
276
+ @names_from_methods = Hash.new
277
+ # Fill the hashes:
278
+ @tags.each_pair do |key, value|
279
+ name = value[1]
280
+ method_name = name.dicom_methodize
281
+ @methods_from_names[name] = method_name.to_sym
282
+ @names_from_methods[method_name.to_sym] = name
283
+ end
284
+ end
285
+ end
286
+
207
287
  end
208
288
  end
@@ -1,4 +1,18 @@
1
- # Copyright 2008-2010 Christoffer Lervag
1
+ # === TODO:
2
+ #
3
+ # * The retrieve file network functionality (get_image() in DClient class) has not been tested.
4
+ # * Make the networking code more intelligent in its handling of unexpected network communication.
5
+ # * Full support for compressed image data.
6
+ # * Read/Write 12 bit image data.
7
+ # * Full color support (RGB and PALETTE COLOR with get_object_magick() already implemented).
8
+ # * Support for extraction of multiple encapsulated pixel data frames in get_image() and get_image_narray().
9
+ # * Image handling currently ignores DICOM tags like Pixel Aspect Ratio, Image Orientation and (to some degree) Photometric Interpretation.
10
+ # * More robust and flexible options for reorienting extracted pixel arrays?
11
+ # * A curious observation: Creating a DLibrary instance is exceptionally slow on Ruby 1.9.1: 0.4 seconds versus ~0.01 seconds on Ruby 1.8.7!
12
+ # * Add these as github issues and remove this list!
13
+
14
+
15
+ # Copyright 2008-2011 Christoffer Lervag
2
16
  #
3
17
  # This program is free software: you can redistribute it and/or modify
4
18
  # it under the terms of the GNU General Public License as published by
@@ -13,20 +27,6 @@
13
27
  # You should have received a copy of the GNU General Public License
14
28
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
29
  #
16
-
17
- # === TODO:
18
- #
19
- # * The retrieve file network functionality (get_image() in DClient class) has not been tested.
20
- # * Make the networking code more intelligent in its handling of unexpected network communication.
21
- # * Full support for compressed image data.
22
- # * Read/Write 12 bit image data.
23
- # * Support for color image data.
24
- # * Complete support for Big endian (Everything but signed short and signed long has been implemented).
25
- # * Complete support for multiple frame image data to NArray and RMagick objects (partial support already featured).
26
- # * Image handling does not take into consideration DICOM tags which specify orientation, samples per pixel and photometric interpretation.
27
- # * More robust and flexible options for reorienting extracted pixel arrays?
28
- # * A curious observation: Creating a DLibrary instance is exceptionally slow on my Ruby 1.9.1 install: 0.4 seconds versus ~0.01 seconds on my Ruby 1.8.7 install!
29
-
30
30
  module DICOM
31
31
 
32
32
  # The DObject class is the main class for interacting with the DICOM object.
@@ -34,10 +34,10 @@ module DICOM
34
34
  #
35
35
  # === Inheritance
36
36
  #
37
- # As the DObject class inherits from the SuperItem class, which itself inherits from the SuperParent class,
38
- # all SuperItem and SuperParent methods are also available to instances of DObject.
37
+ # As the DObject class inherits from the ImageItem class, which itself inherits from the Parent class,
38
+ # all ImageItem and Parent methods are also available to instances of DObject.
39
39
  #
40
- class DObject < SuperItem
40
+ class DObject < ImageItem
41
41
 
42
42
  # An array which contain any notices/warnings/errors that have been recorded for the DObject instance.
43
43
  attr_reader :errors
@@ -50,9 +50,12 @@ module DICOM
50
50
  # A boolean which is set as true if a DObject instance has been successfully written to file (or successfully encoded).
51
51
  attr_reader :write_success
52
52
 
53
+ alias_method :read?, :read_success
54
+ alias_method :written?, :write_success
55
+
53
56
  # Creates a DObject instance (DObject is an abbreviation for "DICOM object").
54
57
  #
55
- # The DObject instance holds references to the different types of objects (DataElement, Item, Sequence)
58
+ # The DObject instance holds references to the different types of objects (Element, Item, Sequence)
56
59
  # that makes up a DICOM object. A DObject is typically buildt by reading and parsing a file or a
57
60
  # binary string, but can also be buildt from an empty state by the user.
58
61
  #
@@ -63,7 +66,7 @@ module DICOM
63
66
  #
64
67
  # === Options
65
68
  #
66
- # * <tt>:bin</tt> -- Boolean. If set to true, string parameter will be interpreted as a binary DICOM string, and not a path string, which is the default behaviour.
69
+ # * <tt>:bin</tt> -- Boolean. If true, the string parameter will be interpreted as a binary DICOM string instead of a path string.
67
70
  # * <tt>:syntax</tt> -- String. If a syntax string is specified, the DRead class will be forced to use this transfer syntax when decoding the file/binary string.
68
71
  # * <tt>:verbose</tt> -- Boolean. If set to false, the DObject instance will run silently and not output warnings and error messages to the screen. Defaults to true.
69
72
  #
@@ -89,15 +92,17 @@ module DICOM
89
92
  @explicit = true
90
93
  @file_endian = false
91
94
  # Control variables:
92
- @read_success = false
95
+ @read_success = nil
93
96
  # Initialize a Stream instance which is used for encoding/decoding:
94
97
  @stream = Stream.new(nil, @file_endian)
95
98
  # The DObject instance is the top of the hierarchy and unlike other elements it has no parent:
96
99
  @parent = nil
97
100
  # For convenience, call the read method if a string has been supplied:
98
- if string.is_a?(String) and string != ""
101
+ if string.is_a?(String)
99
102
  @file = string unless options[:bin]
100
103
  read(string, options)
104
+ elsif string
105
+ raise ArgumentError, "Invalid argument. Expected String (or nil), got #{string.class}."
101
106
  end
102
107
  end
103
108
 
@@ -108,12 +113,16 @@ module DICOM
108
113
  # === Parameters
109
114
  #
110
115
  # * <tt>max_size</tt> -- An integer (Fixnum) which specifies the maximum allowed size of the binary data strings which will be encoded.
116
+ # * <tt>transfer_syntax</tt> -- The transfer syntax string to be used when encoding the DICOM object to string segments. When this method is used for making network packets, the transfer_syntax is not part of the object, and thus needs to be specified. Defaults to the DObject's transfer syntax/Implicit little endian.
111
117
  #
112
118
  # === Examples
113
119
  #
114
120
  # encoded_strings = obj.encode_segments(16384)
115
121
  #
116
- def encode_segments(max_size)
122
+ def encode_segments(max_size, transfer_syntax=transfer_syntax)
123
+ raise ArgumentError, "Invalid argument. Expected an Integer, got #{max_size.class}." unless max_size.is_a?(Integer)
124
+ raise ArgumentError, "Argument too low (#{max_size}), please specify a bigger Integer." unless max_size > 16
125
+ raise "Can not encode binary segments for an empty DICOM object." if children.length == 0
117
126
  w = DWrite.new(self, transfer_syntax, file_name=nil)
118
127
  w.encode_segments(max_size)
119
128
  # Write process succesful?
@@ -123,59 +132,97 @@ module DICOM
123
132
  return w.segments
124
133
  end
125
134
 
135
+ # Prints information of interest related to the DICOM object.
136
+ # Calls the print() method of Parent as well as the information() method of DObject.
137
+ #
138
+ def print_all
139
+ puts ""
140
+ print(:value_max => 30)
141
+ summary
142
+ end
143
+
144
+ # Fills a DICOM object by reading and parsing the specified DICOM file,
145
+ # and transfers the DICOM data to the DICOM object (self).
146
+ #
147
+ # === Notes
148
+ #
149
+ # This method is called automatically when initializing the DObject class with a file parameter.
150
+ # In practice this method is rarely called by the user.
151
+ #
152
+ # === Parameters
153
+ #
154
+ # * <tt>string</tt> -- A string which specifies either the path of a DICOM file to be loaded, or a binary DICOM string to be parsed.
155
+ # * <tt>options</tt> -- A hash of parameters.
156
+ #
157
+ # === Options
158
+ #
159
+ # * <tt>:bin</tt> -- Boolean. If true, the string parameter will be interpreted as a binary DICOM string instead of a path string.
160
+ # * <tt>:syntax</tt> -- String. If a syntax string is specified, the DRead class will be forced to use this transfer syntax when decoding the file/binary string.
161
+ #
162
+ def read(string, options={})
163
+ raise ArgumentError, "Invalid argument. Expected String, got #{string.class}." unless string.is_a?(String)
164
+ # Clear any existing DObject tags, then read:
165
+ @tags = Hash.new
166
+ r = DRead.new(self, string, options)
167
+ # If reading failed, and no transfer syntax was detected, we will make another attempt at reading the file while forcing explicit (little endian) decoding.
168
+ # 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.
169
+ if !r.success and !exists?("0002,0010")
170
+ # Clear the existing DObject tags:
171
+ @tags = Hash.new
172
+ r_explicit = DRead.new(self, string, :bin => options[:bin], :syntax => EXPLICIT_LITTLE_ENDIAN)
173
+ # Only extract information from this new attempt if it was successful:
174
+ r = r_explicit if r_explicit.success
175
+ end
176
+ # Store the data to the instance variables if the readout was a success:
177
+ if r.success
178
+ @read_success = true
179
+ # Update instance variables based on the properties of the DICOM object:
180
+ @explicit = r.explicit
181
+ @file_endian = r.file_endian
182
+ @signature = r.signature
183
+ @stream.endian = @file_endian
184
+ else
185
+ @read_success = false
186
+ end
187
+ # If any messages has been recorded, send these to the message handling method:
188
+ add_msg(r.msg) if r.msg.length > 0
189
+ end
190
+
126
191
  # Gathers key information about the DObject as well as some system data, and prints this information to the screen.
127
192
  #
128
193
  # This information includes properties like encoding, byte order, modality and various image properties.
129
194
  #
130
195
  #--
131
- # FIXME: Perhaps this method should be split up in one or two separate methods which just builds the information arrays,
132
- # and a third method for printing this to the screen.
196
+ # FIXME: Perhaps this method should be split up in one or two separate methods
197
+ # which just builds the information arrays, and a third method for printing this to the screen.
133
198
  #
134
- def information
199
+ def summary
135
200
  sys_info = Array.new
136
201
  info = Array.new
137
202
  # Version of Ruby DICOM used:
138
203
  sys_info << "Ruby DICOM version: #{VERSION}"
139
204
  # System endian:
140
- if CPU_ENDIAN
141
- cpu = "Big Endian"
142
- else
143
- cpu = "Little Endian"
144
- end
205
+ cpu = (CPU_ENDIAN ? "Big Endian" : "Little Endian")
145
206
  sys_info << "Byte Order (CPU): #{cpu}"
146
207
  # File path/name:
147
208
  info << "File: #{@file}"
148
209
  # Modality:
149
- sop_class_uid = self["0008,0016"]
150
- if sop_class_uid
151
- modality = LIBRARY.get_syntax_description(sop_class_uid.value) || "Unknown UID!"
152
- else
153
- modality = "SOP Class not specified!"
154
- end
210
+ modality = (exists?("0008,0016") ? LIBRARY.get_syntax_description(self["0008,0016"].value) : "SOP Class unknown or not specified!")
155
211
  info << "Modality: #{modality}"
156
212
  # Meta header presence (Simply check for the presence of the transfer syntax data element), VR and byte order:
157
213
  transfer_syntax = self["0002,0010"]
158
214
  if transfer_syntax
159
215
  syntax_validity, explicit, endian = LIBRARY.process_transfer_syntax(transfer_syntax.value)
160
216
  if syntax_validity
161
- meta_comment = ""
162
- explicit_comment = ""
163
- encoding_comment = ""
217
+ meta_comment, explicit_comment, encoding_comment = "", "", ""
164
218
  else
165
219
  meta_comment = " (But unknown/invalid transfer syntax: #{transfer_syntax})"
166
220
  explicit_comment = " (Assumed)"
167
221
  encoding_comment = " (Assumed)"
168
222
  end
169
- if explicit
170
- explicitness = "Explicit"
171
- else
172
- explicitness = "Implicit"
173
- end
174
- if endian
175
- encoding = "Big Endian"
176
- else
177
- encoding = "Little Endian"
178
- end
223
+ explicitness = (explicit ? "Explicit" : "Implicit")
224
+ encoding = (endian ? "Big Endian" : "Little Endian")
225
+ meta = "Yes#{meta_comment}"
179
226
  else
180
227
  meta = "No"
181
228
  explicitness = (@explicit == true ? "Explicit" : "Implicit")
@@ -183,11 +230,9 @@ module DICOM
183
230
  explicit_comment = " (Assumed)"
184
231
  encoding_comment = " (Assumed)"
185
232
  end
186
- meta = "Yes#{meta_comment}"
187
- explicit = "#{explicitness}#{explicit_comment}"
188
- encoding = "#{encoding}#{encoding_comment}"
189
- info << "Value Representation: #{explicit}"
190
- info << "Byte Order (File): #{encoding}"
233
+ info << "Meta Header: #{meta}"
234
+ info << "Value Representation: #{explicitness}#{explicit_comment}"
235
+ info << "Byte Order (File): #{encoding}#{encoding_comment}"
191
236
  # Pixel data:
192
237
  pixels = self[PIXEL_TAG]
193
238
  unless pixels
@@ -195,23 +240,23 @@ module DICOM
195
240
  else
196
241
  info << "Pixel Data: Yes"
197
242
  # Image size:
198
- cols = self["0028,0011"] || "Columns missing"
199
- rows = self["0028,0010"] || "Rows missing"
200
- info << "Image Size: #{cols.value}*#{rows.value}"
243
+ cols = (exists?("0028,0011") ? self["0028,0011"].value : "Columns missing")
244
+ rows = (exists?("0028,0010") ? self["0028,0010"].value : "Rows missing")
245
+ info << "Image Size: #{cols}*#{rows}"
201
246
  # Frames:
202
- frames = self["0028,0008"] || "1"
203
- if frames != "1"
247
+ frames = value("0028,0008") || "1"
248
+ unless frames == "1" or frames == 1
204
249
  # Encapsulated or 3D pixel data:
205
- if pixels.is_a?(DataElement)
206
- frames = frames.value + " (3D Pixel Data)"
250
+ if pixels.is_a?(Element)
251
+ frames = frames.to_s + " (3D Pixel Data)"
207
252
  else
208
- frames = frames.value + " (Encapsulated Multiframe Image)"
253
+ frames = frames.to_s + " (Encapsulated Multiframe Image)"
209
254
  end
210
255
  end
211
256
  info << "Number of frames: #{frames}"
212
257
  # Color:
213
- colors = self["0028,0004"] || "Not specified"
214
- info << "Photometry: #{colors.value}"
258
+ colors = (exists?("0028,0004") ? self["0028,0004"].value : "Not specified")
259
+ info << "Photometry: #{colors}"
215
260
  # Compression:
216
261
  if transfer_syntax
217
262
  compression = LIBRARY.get_compression(transfer_syntax.value)
@@ -225,14 +270,13 @@ module DICOM
225
270
  end
226
271
  info << "Compression: #{compression}"
227
272
  # Pixel bits (allocated):
228
- bits = self["0028,0100"] || "Not specified"
229
- info << "Bits per Pixel: #{bits.value}"
273
+ bits = (exists?("0028,0100") ? self["0028,0100"].value : "Not specified")
274
+ info << "Bits per Pixel: #{bits}"
230
275
  end
231
276
  # Print the DICOM object's key properties:
232
277
  separator = "-------------------------------------------"
233
- puts "\n"
234
278
  puts "System Properties:"
235
- puts separator
279
+ puts separator + "\n"
236
280
  puts sys_info
237
281
  puts "\n"
238
282
  puts "DICOM Object Properties:"
@@ -242,52 +286,6 @@ module DICOM
242
286
  return info
243
287
  end
244
288
 
245
- # Prints information of interest related to the DICOM object.
246
- # Calls the print() method of SuperParent as well as the information() method of DObject.
247
- #
248
- def print_all
249
- puts ""
250
- print(:value_max => 30)
251
- information
252
- end
253
-
254
- # Returns a DICOM object by reading and parsing the specified file.
255
- # This is accomplished by initializing the DRead class, which loads DICOM information to arrays.
256
- #
257
- # === Notes
258
- #
259
- # This method is called automatically when initializing the DObject class with a file parameter,
260
- # and in practice should not be called by users.
261
- #
262
- #--
263
- # FIXME: It should be considered whether this should be a private method.
264
- #
265
- def read(string, options={})
266
- r = DRead.new(self, string, options)
267
- # If reading failed, and no transfer syntax was detected, we will make another attempt at reading the file while forcing explicit (little endian) decoding.
268
- # 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.
269
- unless r.success or exists?("0002,0010")
270
- # Clear the existing DObject tags:
271
- @tags = Hash.new
272
- r_explicit = DRead.new(self, string, :bin => options[:bin], :syntax => EXPLICIT_LITTLE_ENDIAN)
273
- # Only extract information from this new attempt if it was successful:
274
- r = r_explicit if r_explicit.success
275
- end
276
- # Store the data to the instance variables if the readout was a success:
277
- if r.success
278
- @read_success = true
279
- # Update instance variables based on the properties of the DICOM object:
280
- @explicit = r.explicit
281
- @file_endian = r.file_endian
282
- @signature = r.signature
283
- @stream.endian = @file_endian
284
- else
285
- @read_success = false
286
- end
287
- # If any messages has been recorded, send these to the message handling method:
288
- add_msg(r.msg) if r.msg.length > 0
289
- end
290
-
291
289
  # Returns the transfer syntax string of the DObject.
292
290
  #
293
291
  # If a transfer syntax has not been defined in the DObject, a default tansfer syntax is assumed and returned.
@@ -296,7 +294,7 @@ module DICOM
296
294
  return value("0002,0010") || IMPLICIT_LITTLE_ENDIAN
297
295
  end
298
296
 
299
- # Changes the transfer syntax DataElement of the DObject instance, and performs re-encoding of all
297
+ # Changes the transfer syntax Element of the DObject instance, and performs re-encoding of all
300
298
  # numerical values if a switch of endianness is implied.
301
299
  #
302
300
  # === Restrictions
@@ -309,28 +307,20 @@ module DICOM
309
307
  # * <tt>new_syntax</tt> -- The new transfer syntax string which will be applied to the DObject.
310
308
  #
311
309
  def transfer_syntax=(new_syntax)
312
- valid, new_explicit, new_endian = LIBRARY.process_transfer_syntax(new_syntax)
313
- if valid
314
- # Get the old transfer syntax and write the new one to the DICOM object:
315
- old_syntax = transfer_syntax
316
- valid, old_explicit, old_endian = LIBRARY.process_transfer_syntax(old_syntax)
317
- if exists?("0002,0010")
318
- self["0002,0010"].value = new_syntax
319
- else
320
- add(DataElement.new("0002,0010", new_syntax))
321
- end
322
- # Update our Stream instance with the new encoding:
323
- @stream.endian = new_endian
324
- # Determine if re-encoding is needed:
325
- if old_endian != new_endian
326
- # Re-encode all Data Elements with number values:
327
- encode_children(old_endian)
328
- else
329
- add_msg("New transfer syntax #{new_syntax} does not change encoding: No re-encoding needed.")
330
- end
310
+ valid_ts, new_explicit, new_endian = LIBRARY.process_transfer_syntax(new_syntax)
311
+ raise ArgumentError, "Invalid transfer syntax specified: #{new_syntax}" unless valid_ts
312
+ # Get the old transfer syntax and write the new one to the DICOM object:
313
+ old_syntax = transfer_syntax
314
+ valid_ts, old_explicit, old_endian = LIBRARY.process_transfer_syntax(old_syntax)
315
+ if exists?("0002,0010")
316
+ self["0002,0010"].value = new_syntax
331
317
  else
332
- raise "Invalid transfer syntax specified: #{new_syntax}"
318
+ add(Element.new("0002,0010", new_syntax))
333
319
  end
320
+ # Update our Stream instance with the new encoding:
321
+ @stream.endian = new_endian
322
+ # If endianness is changed, re-encode elements (only elements depending on endianness will actually be re-encoded):
323
+ encode_children(old_endian) if old_endian != new_endian
334
324
  end
335
325
 
336
326
  # Passes the DObject to the DWrite class, which traverses the data element
@@ -350,6 +340,7 @@ module DICOM
350
340
  # obj.write(path + "test.dcm")
351
341
  #
352
342
  def write(file_name, options={})
343
+ raise ArgumentError, "Invalid file_name. Expected String, got #{file_name.class}." unless file_name.is_a?(String)
353
344
  insert_missing_meta unless options[:add_meta] == false
354
345
  w = DWrite.new(self, transfer_syntax, file_name, options)
355
346
  w.write
@@ -374,7 +365,7 @@ module DICOM
374
365
  def add_msg(msg)
375
366
  puts msg if @verbose
376
367
  @errors << msg
377
- @errors.flatten
368
+ @errors.flatten!
378
369
  end
379
370
 
380
371
  # Adds any missing meta group (0002,xxxx) data elements to the DICOM object,
@@ -382,23 +373,24 @@ module DICOM
382
373
  #
383
374
  def insert_missing_meta
384
375
  # File Meta Information Version:
385
- DataElement.new("0002,0001", [0,1], :parent => self) unless exists?("0002,0001")
376
+ Element.new("0002,0001", [0,1], :parent => self) unless exists?("0002,0001")
386
377
  # Media Storage SOP Class UID:
387
- DataElement.new("0002,0002", value("0008,0016"), :parent => self) unless exists?("0002,0002")
378
+ Element.new("0002,0002", value("0008,0016"), :parent => self) unless exists?("0002,0002")
388
379
  # Media Storage SOP Instance UID:
389
- DataElement.new("0002,0003", value("0008,0018"), :parent => self) unless exists?("0002,0003")
380
+ Element.new("0002,0003", value("0008,0018"), :parent => self) unless exists?("0002,0003")
390
381
  # Transfer Syntax UID:
391
- DataElement.new("0002,0010", transfer_syntax, :parent => self) unless exists?("0002,0010")
392
- # Implementation Class UID:
393
- DataElement.new("0002,0012", UID, :parent => self) unless exists?("0002,0012")
394
- # Implementation Version Name:
395
- DataElement.new("0002,0013", NAME, :parent => self) unless exists?("0002,0013")
382
+ Element.new("0002,0010", transfer_syntax, :parent => self) unless exists?("0002,0010")
383
+ if !exists?("0002,0012") and !exists?("0002,0013")
384
+ # Implementation Class UID:
385
+ Element.new("0002,0012", UID, :parent => self)
386
+ # Implementation Version Name:
387
+ Element.new("0002,0013", NAME, :parent => self)
388
+ end
396
389
  # Source Application Entity Title:
397
- DataElement.new("0002,0016", SOURCE_APP_TITLE, :parent => self) unless exists?("0002,0016")
398
- # Group length:
399
- # Remove old group length (if it exists) before creating a new one:
390
+ Element.new("0002,0016", DICOM.source_app_title, :parent => self) unless exists?("0002,0016")
391
+ # Group Length: Remove the old one (if it exists) before creating a new one.
400
392
  remove("0002,0000")
401
- DataElement.new("0002,0000", meta_group_length, :parent => self)
393
+ Element.new("0002,0000", meta_group_length, :parent => self)
402
394
  end
403
395
 
404
396
  # Determines and returns the length of the meta group in the DObject instance.