dicom 0.9.2 → 0.9.3
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.
- 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)
|