dicom 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.