dicom 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +20 -1
- data/README.rdoc +26 -17
- data/lib/dicom.rb +32 -28
- data/lib/dicom/anonymizer.rb +187 -49
- data/lib/dicom/audit_trail.rb +117 -0
- data/lib/dicom/constants.rb +1 -2
- data/lib/dicom/d_client.rb +13 -13
- data/lib/dicom/d_object.rb +50 -22
- data/lib/dicom/d_read.rb +9 -9
- data/lib/dicom/d_server.rb +14 -10
- data/lib/dicom/d_write.rb +6 -6
- data/lib/dicom/deprecated.rb +73 -0
- data/lib/dicom/element.rb +37 -5
- data/lib/dicom/elemental.rb +4 -4
- data/lib/dicom/file_handler.rb +10 -10
- data/lib/dicom/image_item.rb +28 -12
- data/lib/dicom/item.rb +36 -4
- data/lib/dicom/link.rb +1 -1
- data/lib/dicom/logging.rb +0 -0
- data/lib/dicom/parent.rb +41 -41
- data/lib/dicom/ruby_extensions.rb +6 -4
- data/lib/dicom/sequence.rb +34 -2
- data/lib/dicom/stream.rb +9 -2
- data/lib/dicom/variables.rb +19 -0
- data/lib/dicom/version.rb +1 -1
- metadata +30 -17
data/lib/dicom/d_write.rb
CHANGED
@@ -6,7 +6,7 @@ module DICOM
|
|
6
6
|
# === Notes
|
7
7
|
#
|
8
8
|
# The philosophy of the Ruby DICOM library is to feature maximum conformance to the DICOM standard.
|
9
|
-
# As such, the class which writes DICOM files may manipulate the meta group,
|
9
|
+
# As such, the class which writes DICOM files may manipulate the meta group, delete/change group lengths and add a header signature.
|
10
10
|
#
|
11
11
|
# Therefore, the file that is written may not be an exact bitwise copy of the file that was read,
|
12
12
|
# even if no DObject manipulation has been done on the part of the user.
|
@@ -32,7 +32,7 @@ module DICOM
|
|
32
32
|
#
|
33
33
|
# === Parameters
|
34
34
|
#
|
35
|
-
# * <tt>
|
35
|
+
# * <tt>dcm</tt> -- A DObject instance which will be used to encode a DICOM string.
|
36
36
|
# * <tt>transfer_syntax</tt> -- String. The transfer syntax used for the encoding settings of the post-meta part of the DICOM string.
|
37
37
|
# * <tt>file_name</tt> -- A string, either specifying the path of a DICOM file to be loaded, or a binary DICOM string to be parsed.
|
38
38
|
# * <tt>options</tt> -- A hash of parameters.
|
@@ -41,8 +41,8 @@ module DICOM
|
|
41
41
|
#
|
42
42
|
# * <tt>:signature</tt> -- Boolean. If set as false, the DICOM header signature will not be written to the DICOM file.
|
43
43
|
#
|
44
|
-
def initialize(
|
45
|
-
@
|
44
|
+
def initialize(dcm, transfer_syntax, file_name=nil, options={})
|
45
|
+
@dcm = dcm
|
46
46
|
@transfer_syntax = transfer_syntax
|
47
47
|
@file_name = file_name
|
48
48
|
# As default, signature will be written and meta header added:
|
@@ -75,7 +75,7 @@ module DICOM
|
|
75
75
|
if body
|
76
76
|
@stream.add_last(body)
|
77
77
|
else
|
78
|
-
elements = @
|
78
|
+
elements = @dcm.children
|
79
79
|
write_data_elements(elements)
|
80
80
|
end
|
81
81
|
# As file has been written successfully, it can be closed.
|
@@ -97,7 +97,7 @@ module DICOM
|
|
97
97
|
init_variables
|
98
98
|
@max_size = max_size
|
99
99
|
@segments = Array.new
|
100
|
-
elements = @
|
100
|
+
elements = @dcm.children
|
101
101
|
# Create a Stream instance to handle the encoding of content to
|
102
102
|
# the binary string that will eventually be saved to file:
|
103
103
|
@stream = Stream.new(nil, @file_endian)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module DICOM
|
2
|
+
|
3
|
+
class Parent
|
4
|
+
include Logging
|
5
|
+
# Deprecated.
|
6
|
+
#
|
7
|
+
def remove(tag, options={})
|
8
|
+
logger.warn("Parent#remove is deprecated. Use #delete instead.")
|
9
|
+
delete(tag, options)
|
10
|
+
end
|
11
|
+
# Deprecated.
|
12
|
+
#
|
13
|
+
def remove_children
|
14
|
+
logger.warn("Parent#remove_children is deprecated. Use #delete_children instead.")
|
15
|
+
delete_children
|
16
|
+
end
|
17
|
+
|
18
|
+
# Deprecated.
|
19
|
+
#
|
20
|
+
def remove_group(group_string)
|
21
|
+
logger.warn("Parent#remove_group is deprecated. Use #delete_group instead.")
|
22
|
+
delete_group(group_string)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Deprecated.
|
26
|
+
#
|
27
|
+
def remove_private
|
28
|
+
logger.warn("Parent#remove_private is deprecated. Use #delete_private instead.")
|
29
|
+
delete_private
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ImageItem < Parent
|
34
|
+
include Logging
|
35
|
+
# Deprecated.
|
36
|
+
#
|
37
|
+
def remove_sequences
|
38
|
+
logger.warn("ImageItem#remove_sequences is deprecated. Use #delete_sequences instead.")
|
39
|
+
delete_sequences
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class DServer
|
44
|
+
# Deprecated.
|
45
|
+
#
|
46
|
+
def remove_abstract_syntax(uid)
|
47
|
+
logger.warn("DServer#remove_abstract_syntax is deprecated. Use #delete_abstract_syntax instead.")
|
48
|
+
delete_abstract_syntax(uid)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Deprecated.
|
52
|
+
#
|
53
|
+
def remove_transfer_syntax(uid)
|
54
|
+
logger.warn("DServer#remove_transfer_syntax is deprecated. Use #delete_transfer_syntax instead.")
|
55
|
+
delete_transfer_syntax(uid)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Deprecated.
|
59
|
+
#
|
60
|
+
def remove_all_abstract_syntaxes
|
61
|
+
logger.warn("DServer#remove_all_abstract_syntaxes is deprecated. Use #clear_abstract_syntaxes instead.")
|
62
|
+
clear_abstract_syntaxes
|
63
|
+
end
|
64
|
+
|
65
|
+
# Deprecated.
|
66
|
+
#
|
67
|
+
def remove_all_transfer_syntaxes
|
68
|
+
logger.warn("DServer#remove_all_transfer_syntaxes is deprecated. Use #clear_transfer_syntaxes instead.")
|
69
|
+
clear_transfer_syntaxes
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/dicom/element.rb
CHANGED
@@ -35,11 +35,11 @@ module DICOM
|
|
35
35
|
# === Examples
|
36
36
|
#
|
37
37
|
# # Create a new data element and connect it to a DObject instance:
|
38
|
-
# patient_name = Element.new("0010,0010", "John Doe", :parent =>
|
38
|
+
# patient_name = Element.new("0010,0010", "John Doe", :parent => dcm)
|
39
39
|
# # Create a "Pixel Data" element and insert image data that you have already encoded elsewhere:
|
40
|
-
# pixel_data = Element.new("7FE0,0010", processed_pixel_data, :encoded => true, :parent =>
|
40
|
+
# pixel_data = Element.new("7FE0,0010", processed_pixel_data, :encoded => true, :parent => dcm)
|
41
41
|
# # Create a private data element:
|
42
|
-
# private_data = Element.new("0011,2102", some_data, :parent =>
|
42
|
+
# private_data = Element.new("0011,2102", some_data, :parent => dcm, :vr => "LO")
|
43
43
|
#
|
44
44
|
def initialize(tag, value, options={})
|
45
45
|
raise ArgumentError, "The supplied tag (#{tag}) is not valid. The tag must be a string of the form 'GGGG,EEEE'." unless tag.is_a?(String) && tag.tag?
|
@@ -63,7 +63,11 @@ module DICOM
|
|
63
63
|
if options[:bin]
|
64
64
|
@bin = options[:bin]
|
65
65
|
else
|
66
|
-
|
66
|
+
if value == ""
|
67
|
+
@bin = ""
|
68
|
+
else
|
69
|
+
@bin = encode(value)
|
70
|
+
end
|
67
71
|
end
|
68
72
|
else
|
69
73
|
# When no value is present, we set the binary as an empty string, unless the binary is specified:
|
@@ -81,6 +85,16 @@ module DICOM
|
|
81
85
|
end
|
82
86
|
end
|
83
87
|
|
88
|
+
# Returns true if the argument is an instance with attributes equal to self.
|
89
|
+
#
|
90
|
+
def ==(other)
|
91
|
+
if other.respond_to?(:to_element)
|
92
|
+
other.send(:state) == state
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
alias_method :eql?, :==
|
97
|
+
|
84
98
|
# Sets the binary string of a Element.
|
85
99
|
#
|
86
100
|
# === Notes
|
@@ -95,7 +109,7 @@ module DICOM
|
|
95
109
|
def bin=(new_bin)
|
96
110
|
raise ArgumentError, "Expected String, got #{new_bin.class}." unless new_bin.is_a?(String)
|
97
111
|
# Add a zero byte at the end if the length of the binary is odd:
|
98
|
-
if new_bin.length
|
112
|
+
if new_bin.length.odd?
|
99
113
|
@bin = new_bin + stream.pad_byte[@vr]
|
100
114
|
else
|
101
115
|
@bin = new_bin
|
@@ -118,6 +132,12 @@ module DICOM
|
|
118
132
|
return stream.str_endian
|
119
133
|
end
|
120
134
|
|
135
|
+
# Generates a Fixnum hash value for this instance.
|
136
|
+
#
|
137
|
+
def hash
|
138
|
+
state.hash
|
139
|
+
end
|
140
|
+
|
121
141
|
# Returns a string containing a human-readable hash representation of the Element.
|
122
142
|
#
|
123
143
|
def inspect
|
@@ -136,6 +156,12 @@ module DICOM
|
|
136
156
|
def to_hash
|
137
157
|
return {self.send(DICOM.key_representation) => value}
|
138
158
|
end
|
159
|
+
|
160
|
+
# Returns self.
|
161
|
+
#
|
162
|
+
def to_element
|
163
|
+
self
|
164
|
+
end
|
139
165
|
|
140
166
|
# Returns a json string containing a human-readable representation of the Element.
|
141
167
|
#
|
@@ -182,6 +208,12 @@ module DICOM
|
|
182
208
|
def encode(formatted_value)
|
183
209
|
return stream.encode_value(formatted_value, @vr)
|
184
210
|
end
|
211
|
+
|
212
|
+
# Returns the attributes of this instance in an array (for comparison purposes).
|
213
|
+
#
|
214
|
+
def state
|
215
|
+
[@tag, @vr, @value, @bin]
|
216
|
+
end
|
185
217
|
|
186
218
|
end
|
187
219
|
end
|
data/lib/dicom/elemental.rb
CHANGED
@@ -41,7 +41,7 @@ module DICOM
|
|
41
41
|
return all_parents
|
42
42
|
end
|
43
43
|
|
44
|
-
# Sets a specified parent instance as this elemental's parent, while taking care to
|
44
|
+
# Sets a specified parent instance as this elemental's parent, while taking care to delete this elemental from any previous parent
|
45
45
|
# as well as adding itself to the new parent (unless new parent is nil).
|
46
46
|
#
|
47
47
|
# === Parameters
|
@@ -52,16 +52,16 @@ module DICOM
|
|
52
52
|
#
|
53
53
|
# # Create a new Sequence and connect it to a DObject instance:
|
54
54
|
# structure_set_roi = Sequence.new("3006,0020")
|
55
|
-
# structure_set_roi.parent =
|
55
|
+
# structure_set_roi.parent = dcm
|
56
56
|
#
|
57
57
|
def parent=(new_parent)
|
58
58
|
# First take care of 'dependencies':
|
59
59
|
if self.parent
|
60
60
|
# Remove ourselves from the previous parent:
|
61
61
|
if self.is_a?(Item)
|
62
|
-
self.parent.
|
62
|
+
self.parent.delete(self.index, :no_follow => true)
|
63
63
|
else
|
64
|
-
self.parent.
|
64
|
+
self.parent.delete(self.tag, :no_follow => true)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
if new_parent
|
data/lib/dicom/file_handler.rb
CHANGED
@@ -32,21 +32,21 @@ module DICOM
|
|
32
32
|
# === Parameters
|
33
33
|
#
|
34
34
|
# * <tt>path_prefix</tt> -- String. Specifies the root path of the DICOM storage.
|
35
|
-
# * <tt>
|
35
|
+
# * <tt>dcm</tt> -- A DObject instance which will be written to file.
|
36
36
|
# * <tt>transfer_syntax</tt> -- String. Specifies the transfer syntax that will be used to write the DICOM file.
|
37
37
|
#
|
38
|
-
def self.save_file(path_prefix,
|
38
|
+
def self.save_file(path_prefix, dcm, transfer_syntax)
|
39
39
|
# File name is set using the SOP Instance UID:
|
40
|
-
file_name =
|
40
|
+
file_name = dcm.value("0008,0018") || "missing_SOP_UID"
|
41
41
|
extension = ".dcm"
|
42
42
|
folders = Array.new(3)
|
43
|
-
folders[0] =
|
44
|
-
folders[1] =
|
45
|
-
folders[2] =
|
43
|
+
folders[0] = dcm.value("0010,0020") || "PatientID"
|
44
|
+
folders[1] = dcm.value("0008,0020") || "StudyDate"
|
45
|
+
folders[2] = dcm.value("0008,0060") || "Modality"
|
46
46
|
local_path = folders.join(File::SEPARATOR) + File::SEPARATOR + file_name
|
47
47
|
full_path = path_prefix + local_path + extension
|
48
48
|
# Save the DICOM object to disk:
|
49
|
-
|
49
|
+
dcm.write(full_path, :transfer_syntax => transfer_syntax)
|
50
50
|
message = [:info, "DICOM file saved to: #{full_path}"]
|
51
51
|
return message
|
52
52
|
end
|
@@ -78,12 +78,12 @@ module DICOM
|
|
78
78
|
server_level = DICOM.logger.level
|
79
79
|
DICOM.logger.level = Logger::FATAL
|
80
80
|
# Parse the received data string and load it to a DICOM object:
|
81
|
-
|
81
|
+
dcm = DObject.parse(objects[i], :no_meta => true, :syntax => transfer_syntaxes[i])
|
82
82
|
# Reset the logg threshold:
|
83
83
|
DICOM.logger.level = server_level
|
84
|
-
if
|
84
|
+
if dcm.read?
|
85
85
|
begin
|
86
|
-
message = self.save_file(path,
|
86
|
+
message = self.save_file(path, dcm, transfer_syntaxes[i])
|
87
87
|
successful += 1
|
88
88
|
rescue
|
89
89
|
handle_fail += 1
|
data/lib/dicom/image_item.rb
CHANGED
@@ -62,8 +62,8 @@ module DICOM
|
|
62
62
|
template = template_string(bit_depth_element.value.to_i)
|
63
63
|
pixels = stream.decode_all(template) if template
|
64
64
|
else
|
65
|
-
raise "The
|
66
|
-
raise "The
|
65
|
+
raise "The Element specifying Bit Depth (0028,0100) is missing. Unable to decode pixel data." unless bit_depth_element
|
66
|
+
raise "The Element specifying Pixel Representation (0028,0103) is missing. Unable to decode pixel data." unless pixel_representation_element
|
67
67
|
end
|
68
68
|
return pixels
|
69
69
|
end
|
@@ -86,8 +86,8 @@ module DICOM
|
|
86
86
|
template = template_string(bit_depth_element.value.to_i)
|
87
87
|
bin = stream.encode(pixels, template) if template
|
88
88
|
else
|
89
|
-
raise "The
|
90
|
-
raise "The
|
89
|
+
raise "The Element specifying Bit Depth (0028,0100) is missing. Unable to encode the pixel data." unless bit_depth_element
|
90
|
+
raise "The Element specifying Pixel Representation (0028,0103) is missing. Unable to encode the pixel data." unless pixel_representation_element
|
91
91
|
end
|
92
92
|
return bin
|
93
93
|
end
|
@@ -170,7 +170,7 @@ module DICOM
|
|
170
170
|
strings.each {|string| pixel_frames << decode_rle(num_cols, num_rows, string)}
|
171
171
|
else
|
172
172
|
images = decompress(strings) || Array.new
|
173
|
-
logger.warn("Decompressing pixel values has failed (unsupported transfer syntax: '#{transfer_syntax}')") unless images
|
173
|
+
logger.warn("Decompressing pixel values has failed (unsupported transfer syntax: '#{transfer_syntax}' - #{LIBRARY.get_syntax_description(transfer_syntax)})") unless images.length > 0
|
174
174
|
end
|
175
175
|
else
|
176
176
|
# Uncompressed: Decode to numbers.
|
@@ -356,7 +356,12 @@ module DICOM
|
|
356
356
|
# Import the pixels to NArray and give it a proper shape:
|
357
357
|
raise "Missing Rows and/or Columns Element. Unable to construct pixel data array." unless num_rows and num_cols
|
358
358
|
if num_frames > 1 or options[:volume]
|
359
|
-
|
359
|
+
# Create an empty 3D NArray. fill it with pixels frame by frame, then reassign the pixels variable to it:
|
360
|
+
narr = NArray.int(num_frames, num_cols, num_rows)
|
361
|
+
num_frames.times do |i|
|
362
|
+
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)
|
363
|
+
end
|
364
|
+
pixels = narr
|
360
365
|
else
|
361
366
|
pixels = NArray.to_na(pixels).reshape!(num_cols, num_rows)
|
362
367
|
end
|
@@ -434,19 +439,30 @@ module DICOM
|
|
434
439
|
#
|
435
440
|
def pixels=(values)
|
436
441
|
raise ArgumentError, "Expected Array or NArray, got #{values.class}." unless [Array, NArray].include?(values.class)
|
437
|
-
|
438
|
-
|
442
|
+
if values.is_a?(NArray)
|
443
|
+
# When NArray, convert to a Ruby Array:
|
444
|
+
if values.shape.length > 2
|
445
|
+
# We need to take care when reshaping this 3D array so that the pixel values falls properly into place:
|
446
|
+
narr = NArray.int(values.shape[1] * values.shape[2], values.shape[0])
|
447
|
+
values.shape[0].times do |i|
|
448
|
+
narr[true, i] = values[i, true, true].reshape(values.shape[1] * values.shape[2])
|
449
|
+
end
|
450
|
+
values = narr.to_a
|
451
|
+
else
|
452
|
+
values = values.to_a
|
453
|
+
end
|
454
|
+
end
|
439
455
|
# Encode the pixel data:
|
440
|
-
bin = encode_pixels(values)
|
456
|
+
bin = encode_pixels(values.flatten)
|
441
457
|
# Write the binary data to the Pixel Data Element:
|
442
458
|
write_pixels(bin)
|
443
459
|
end
|
444
460
|
|
445
|
-
#
|
461
|
+
# Delete all Sequence instances from the DObject or Item instance.
|
446
462
|
#
|
447
|
-
def
|
463
|
+
def delete_sequences
|
448
464
|
@tags.each_value do |element|
|
449
|
-
|
465
|
+
delete(element.tag) if element.is_a?(Sequence)
|
450
466
|
end
|
451
467
|
end
|
452
468
|
|
data/lib/dicom/item.rb
CHANGED
@@ -38,9 +38,9 @@ module DICOM
|
|
38
38
|
# === Examples
|
39
39
|
#
|
40
40
|
# # Create an empty Item and connect it to the "Structure Set ROI Sequence":
|
41
|
-
# item = Item.new(:parent =>
|
41
|
+
# item = Item.new(:parent => dcm["3006,0020"])
|
42
42
|
# # Create a "Pixel Data Item" which carries an encapsulated image frame (a pre-encoded binary):
|
43
|
-
# pixel_item = Item.new(:bin => processed_pixel_data, :parent =>
|
43
|
+
# pixel_item = Item.new(:bin => processed_pixel_data, :parent => dcm["7FE0,0010"][1])
|
44
44
|
#
|
45
45
|
def initialize(options={})
|
46
46
|
# Set common parent variables:
|
@@ -62,6 +62,16 @@ module DICOM
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
# Returns true if the argument is an instance with attributes equal to self.
|
66
|
+
#
|
67
|
+
def ==(other)
|
68
|
+
if other.respond_to?(:to_item)
|
69
|
+
other.send(:state) == state
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :eql?, :==
|
74
|
+
|
65
75
|
# Sets the binary string that the Item will contain.
|
66
76
|
#
|
67
77
|
# === Parameters
|
@@ -71,12 +81,12 @@ module DICOM
|
|
71
81
|
# === Examples
|
72
82
|
#
|
73
83
|
# # Insert a custom jpeg in the (encapsulated) pixel data element, in it's first pixel data item:
|
74
|
-
#
|
84
|
+
# dcm["7FE0,0010"][1].children.first.bin = jpeg_binary_string
|
75
85
|
#
|
76
86
|
def bin=(new_bin)
|
77
87
|
raise ArgumentError, "Invalid parameter type. String was expected, got #{new_bin.class}." unless new_bin.is_a?(String)
|
78
88
|
# Add an empty byte at the end if the length of the binary is odd:
|
79
|
-
if new_bin.length
|
89
|
+
if new_bin.length.odd?
|
80
90
|
@bin = new_bin + "\x00"
|
81
91
|
else
|
82
92
|
@bin = new_bin
|
@@ -85,5 +95,27 @@ module DICOM
|
|
85
95
|
@length = @bin.length
|
86
96
|
end
|
87
97
|
|
98
|
+
# Generates a Fixnum hash value for this instance.
|
99
|
+
#
|
100
|
+
def hash
|
101
|
+
state.hash
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns self.
|
105
|
+
#
|
106
|
+
def to_item
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
|
114
|
+
# Returns the attributes of this instance in an array (for comparison purposes).
|
115
|
+
#
|
116
|
+
def state
|
117
|
+
[@vr, @name, @tags]
|
118
|
+
end
|
119
|
+
|
88
120
|
end
|
89
121
|
end
|
data/lib/dicom/link.rb
CHANGED
@@ -57,7 +57,7 @@ module DICOM
|
|
57
57
|
segments = receive_single_transmission
|
58
58
|
info = segments.first
|
59
59
|
if info[:pdu] != PDU_RELEASE_REQUEST
|
60
|
-
# For some reason we
|
60
|
+
# For some reason we didn't get our expected release request. Determine why:
|
61
61
|
if info[:valid]
|
62
62
|
logger.error("Unexpected message type received (PDU: #{info[:pdu]}). Expected a release request. Closing the connection.")
|
63
63
|
handle_abort(false)
|
data/lib/dicom/logging.rb
CHANGED
File without changes
|
data/lib/dicom/parent.rb
CHANGED
@@ -25,9 +25,9 @@ module DICOM
|
|
25
25
|
# === Examples
|
26
26
|
#
|
27
27
|
# # Extract the "Pixel Data" data element from the DObject instance:
|
28
|
-
# pixel_data_element =
|
28
|
+
# pixel_data_element = dcm["7FE0,0010"]
|
29
29
|
# # Extract the first Item from a Sequence:
|
30
|
-
# first_item =
|
30
|
+
# first_item = dcm["3006,0020"][1]
|
31
31
|
#
|
32
32
|
def [](tag_or_index)
|
33
33
|
formatted = tag_or_index.is_a?(String) ? tag_or_index.upcase : tag_or_index
|
@@ -52,9 +52,9 @@ module DICOM
|
|
52
52
|
# === Examples
|
53
53
|
#
|
54
54
|
# # Set a new patient's name to the DICOM object:
|
55
|
-
#
|
55
|
+
# dcm.add(Element.new("0010,0010", "John_Doe"))
|
56
56
|
# # Add a previously defined element roi_name to the first item in the following sequence:
|
57
|
-
#
|
57
|
+
# dcm["3006,0020"][0].add(roi_name)
|
58
58
|
#
|
59
59
|
def add(element, options={})
|
60
60
|
unless element.is_a?(Item)
|
@@ -96,9 +96,9 @@ module DICOM
|
|
96
96
|
# === Examples
|
97
97
|
#
|
98
98
|
# # Add an empty Item to a specific Sequence:
|
99
|
-
#
|
99
|
+
# dcm["3006,0020"].add_item
|
100
100
|
# # Add an existing Item at the 2nd item position/index in the specific Sequence:
|
101
|
-
#
|
101
|
+
# dcm["3006,0020"].add_item(my_item, :index => 2)
|
102
102
|
#
|
103
103
|
def add_item(item=nil, options={})
|
104
104
|
unless self.is_a?(DObject)
|
@@ -160,7 +160,7 @@ module DICOM
|
|
160
160
|
# === Examples
|
161
161
|
#
|
162
162
|
# # Retrieve all top level data elements in a DICOM object:
|
163
|
-
# top_level_elements =
|
163
|
+
# top_level_elements = dcm.children
|
164
164
|
#
|
165
165
|
def children
|
166
166
|
return @tags.sort.transpose[1] || Array.new
|
@@ -280,7 +280,7 @@ module DICOM
|
|
280
280
|
#
|
281
281
|
# === Examples
|
282
282
|
#
|
283
|
-
# process_name(
|
283
|
+
# process_name(dcm["0010,0010"]) if dcm.exists?("0010,0010")
|
284
284
|
#
|
285
285
|
def exists?(tag)
|
286
286
|
if self[tag]
|
@@ -435,7 +435,7 @@ module DICOM
|
|
435
435
|
# * Returns the element if the method name suggests an element retrieval, and the element exists.
|
436
436
|
# * Returns nil if the method name suggests an element retrieval, but the element doesn't exist.
|
437
437
|
# * Returns a boolean, if the method name suggests a query (?), based on whether the matched element exists or not.
|
438
|
-
# * When the method name suggests assignment (=), an element is created with the supplied arguments, or if the argument is nil, the element is
|
438
|
+
# * When the method name suggests assignment (=), an element is created with the supplied arguments, or if the argument is nil, the element is deleted.
|
439
439
|
#
|
440
440
|
# * When a dynamic method name is not matched against a DICOM element, and the method is not defined by the parent, a NoMethodError is raised.
|
441
441
|
#
|
@@ -462,7 +462,7 @@ module DICOM
|
|
462
462
|
return self.add(Element.new(tag, *args))
|
463
463
|
end
|
464
464
|
else
|
465
|
-
return self.
|
465
|
+
return self.delete(tag)
|
466
466
|
end
|
467
467
|
else
|
468
468
|
# Retrieval:
|
@@ -510,11 +510,11 @@ module DICOM
|
|
510
510
|
# === Examples
|
511
511
|
#
|
512
512
|
# # Print a DObject instance to screen
|
513
|
-
#
|
514
|
-
# # Print the
|
515
|
-
#
|
513
|
+
# dcm.print
|
514
|
+
# # Print the DObject to the screen, but specify a 25 character value cutoff to produce better-looking results:
|
515
|
+
# dcm.print(:value_max => 25)
|
516
516
|
# # Print to a text file the elements that belong to a specific Sequence:
|
517
|
-
#
|
517
|
+
# dcm["3006,0020"].print(:file => "dicom.txt")
|
518
518
|
#
|
519
519
|
#--
|
520
520
|
# FIXME: Perhaps a :children => false option would be a good idea (to avoid lengthy printouts in cases where this would be desirable)?
|
@@ -572,25 +572,25 @@ module DICOM
|
|
572
572
|
return max_name, max_length, max_generations
|
573
573
|
end
|
574
574
|
|
575
|
-
#
|
575
|
+
# Deletes the specified element from this parent.
|
576
576
|
#
|
577
577
|
# === Parameters
|
578
578
|
#
|
579
|
-
# * <tt>tag</tt> -- A tag string which specifies the element to be
|
579
|
+
# * <tt>tag</tt> -- A tag string which specifies the element to be deleted (Exception: In the case of an Item removal, an index (Fixnum) is used instead).
|
580
580
|
# * <tt>options</tt> -- A hash of parameters.
|
581
581
|
#
|
582
582
|
# === Options
|
583
583
|
#
|
584
|
-
# * <tt>:no_follow</tt> -- Boolean. If true, the method does not update the parent attribute of the child that is
|
584
|
+
# * <tt>:no_follow</tt> -- Boolean. If true, the method does not update the parent attribute of the child that is deleted.
|
585
585
|
#
|
586
586
|
# === Examples
|
587
587
|
#
|
588
|
-
# #
|
589
|
-
#
|
590
|
-
# #
|
591
|
-
#
|
588
|
+
# # Delete an Element from a DObject instance:
|
589
|
+
# dcm.delete("0008,0090")
|
590
|
+
# # Delete Item 1 from a specific Sequence:
|
591
|
+
# dcm["3006,0020"].delete(1)
|
592
592
|
#
|
593
|
-
def
|
593
|
+
def delete(tag, options={})
|
594
594
|
if tag.is_a?(String) or tag.is_a?(Integer)
|
595
595
|
raise ArgumentError, "Argument (#{tag}) is not a valid tag string." if tag.is_a?(String) && !tag.tag?
|
596
596
|
raise ArgumentError, "Negative Integer argument (#{tag}) is not allowed." if tag.is_a?(Integer) && tag < 0
|
@@ -605,15 +605,15 @@ module DICOM
|
|
605
605
|
end
|
606
606
|
end
|
607
607
|
|
608
|
-
#
|
608
|
+
# Deletes all child elements from this parent.
|
609
609
|
#
|
610
|
-
def
|
610
|
+
def delete_children
|
611
611
|
@tags.each_key do |tag|
|
612
|
-
|
612
|
+
delete(tag)
|
613
613
|
end
|
614
614
|
end
|
615
615
|
|
616
|
-
#
|
616
|
+
# Deletes all data elements of the specified group from this parent.
|
617
617
|
#
|
618
618
|
# === Parameters
|
619
619
|
#
|
@@ -621,30 +621,30 @@ module DICOM
|
|
621
621
|
#
|
622
622
|
# === Examples
|
623
623
|
#
|
624
|
-
# #
|
625
|
-
#
|
624
|
+
# # Delete the File Meta Group of a DICOM object:
|
625
|
+
# dcm.delete_group("0002")
|
626
626
|
#
|
627
|
-
def
|
627
|
+
def delete_group(group_string)
|
628
628
|
group_elements = group(group_string)
|
629
629
|
group_elements.each do |element|
|
630
|
-
|
630
|
+
delete(element.tag)
|
631
631
|
end
|
632
632
|
end
|
633
633
|
|
634
|
-
#
|
634
|
+
# Deletes all private data elements from the child elements of this parent.
|
635
635
|
#
|
636
636
|
# === Examples
|
637
637
|
#
|
638
|
-
# #
|
639
|
-
#
|
640
|
-
# #
|
641
|
-
#
|
638
|
+
# # Delete all private elements from a DObject instance:
|
639
|
+
# dcm.delete_private
|
640
|
+
# # Delete only private elements belonging to a specific Sequence:
|
641
|
+
# dcm["3006,0020"].delete_private
|
642
642
|
#
|
643
|
-
def
|
644
|
-
# Iterate all children, and repeat recursively if a child itself has children, to
|
643
|
+
def delete_private
|
644
|
+
# Iterate all children, and repeat recursively if a child itself has children, to delete all private data elements:
|
645
645
|
children.each do |element|
|
646
|
-
|
647
|
-
element.
|
646
|
+
delete(element.tag) if element.tag.private?
|
647
|
+
element.delete_private if element.children?
|
648
648
|
end
|
649
649
|
end
|
650
650
|
|
@@ -751,9 +751,9 @@ module DICOM
|
|
751
751
|
# === Examples
|
752
752
|
#
|
753
753
|
# # Get the patient's name value:
|
754
|
-
# name =
|
754
|
+
# name = dcm.value("0010,0010")
|
755
755
|
# # Get the Frame of Reference UID from the first item in the Referenced Frame of Reference Sequence:
|
756
|
-
# uid =
|
756
|
+
# uid = dcm["3006,0010"][0].value("0020,0052")
|
757
757
|
#
|
758
758
|
def value(tag)
|
759
759
|
if tag.is_a?(String) or tag.is_a?(Integer)
|