dicom 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,110 +1,110 @@
1
- module DICOM
2
-
3
- # The AuditTrail class handles key/value storage for the Anonymizer.
4
- # When using the advanced Anonymization options such as enumeration
5
- # and UID replacement, the AuditTrail class keeps track of key/value
6
- # pairs and dumps this information to a text file using the json format.
7
- # This enables us to ensure a unique relationship between the anonymized
8
- # values and the original values, as well as preserving this relationship
9
- # for later restoration of original values.
10
- #
11
- class AuditTrail
12
-
13
- # The hash used for storing the key/value pairs of this instace.
14
- attr_reader :dictionary
15
-
16
- # Creates a new AuditTrail instance by loading the information stored
17
- # in the specified file.
18
- #
19
- # @param [String] file_name the path to a file containing a previously stored audit trail
20
- # @return [AuditTrail] the created AuditTrail instance
21
- #
22
- def self.read(file_name)
23
- audit_trail = AuditTrail.new
24
- audit_trail.load(file_name)
25
- return audit_trail
26
- end
27
-
28
- # Creates a new AuditTrail instance.
29
- #
30
- def initialize
31
- # Define the key/value hash used for tag records:
32
- @dictionary = Hash.new
33
- end
34
-
35
- # Adds a tag record to the log.
36
- #
37
- # @param [String] tag the tag string (e.q. '0010,0010')
38
- # @param [String, Integer, Float] original the original value (e.q. 'John Doe')
39
- # @param [String, Integer, Float] replacement the replacement value (e.q. 'Patient1')
40
- #
41
- def add_record(tag, original, replacement)
42
- @dictionary[tag] = Hash.new unless @dictionary.key?(tag)
43
- @dictionary[tag][original] = replacement
44
- end
45
-
46
- # Loads the key/value dictionary hash from a specified file.
47
- #
48
- # @param [String] file_name the path to a file containing a previously stored audit trail
49
- #
50
- def load(file_name)
51
- @dictionary = JSON.load(File.new(file_name, "r:UTF-8"))
52
- end
53
-
54
- # Retrieves the original value used for the given combination of tag & replacement value.
55
- #
56
- # @param [String] tag the tag string (e.q. '0010,0010')
57
- # @param [String, Integer, Float] replacement the replacement value (e.q. 'Patient1')
58
- # @return [String, Integer, Float] the original value of the given tag
59
- #
60
- def original(tag, replacement)
61
- original = nil
62
- if @dictionary.key?(tag)
63
- original = @dictionary[tag].key(replacement)
64
- end
65
- return original
66
- end
67
-
68
- # Gives the key/value pairs for a specific tag.
69
- #
70
- # @param [String] tag the tag string (e.q. '0010,0010')
71
- # @return [Hash] the key/value pairs of a specific tag
72
- #
73
- def records(tag)
74
- if @dictionary.key?(tag)
75
- return @dictionary[tag]
76
- else
77
- return Hash.new
78
- end
79
- end
80
-
81
- # Retrieves the replacement value used for the given combination of tag & original value.
82
- #
83
- # @param [String] tag the tag string (e.q. '0010,0010')
84
- # @param [String, Integer, Float] original the original value (e.q. 'John Doe')
85
- # @return [String, Integer, Float] the replacement value of the given tag
86
- #
87
- def replacement(tag, original)
88
- replacement = nil
89
- replacement = @dictionary[tag][original] if @dictionary.key?(tag)
90
- return replacement
91
- end
92
-
93
- # Dumps the key/value pairs to a json string which is written to the specified file.
94
- #
95
- # @param [String] file_name the path to be used for storing key/value pairs on disk
96
- #
97
- def write(file_name)
98
- # Encode json string:
99
- str = JSON.pretty_generate(@dictionary)
100
- # Create directory if needed:
101
- unless File.directory?(File.dirname(file_name))
102
- require 'fileutils'
103
- FileUtils.mkdir_p(File.dirname(file_name))
104
- end
105
- # Write to file:
106
- File.open(file_name, 'w') {|f| f.write(str) }
107
- end
108
-
109
- end
1
+ module DICOM
2
+
3
+ # The AuditTrail class handles key/value storage for the Anonymizer.
4
+ # When using the advanced Anonymization options such as enumeration
5
+ # and UID replacement, the AuditTrail class keeps track of key/value
6
+ # pairs and dumps this information to a text file using the json format.
7
+ # This enables us to ensure a unique relationship between the anonymized
8
+ # values and the original values, as well as preserving this relationship
9
+ # for later restoration of original values.
10
+ #
11
+ class AuditTrail
12
+
13
+ # The hash used for storing the key/value pairs of this instace.
14
+ attr_reader :dictionary
15
+
16
+ # Creates a new AuditTrail instance by loading the information stored
17
+ # in the specified file.
18
+ #
19
+ # @param [String] file_name the path to a file containing a previously stored audit trail
20
+ # @return [AuditTrail] the created AuditTrail instance
21
+ #
22
+ def self.read(file_name)
23
+ audit_trail = AuditTrail.new
24
+ audit_trail.load(file_name)
25
+ return audit_trail
26
+ end
27
+
28
+ # Creates a new AuditTrail instance.
29
+ #
30
+ def initialize
31
+ # Define the key/value hash used for tag records:
32
+ @dictionary = Hash.new
33
+ end
34
+
35
+ # Adds a tag record to the log.
36
+ #
37
+ # @param [String] tag the tag string (e.q. '0010,0010')
38
+ # @param [String, Integer, Float] original the original value (e.q. 'John Doe')
39
+ # @param [String, Integer, Float] replacement the replacement value (e.q. 'Patient1')
40
+ #
41
+ def add_record(tag, original, replacement)
42
+ @dictionary[tag] = Hash.new unless @dictionary.key?(tag)
43
+ @dictionary[tag][original] = replacement
44
+ end
45
+
46
+ # Loads the key/value dictionary hash from a specified file.
47
+ #
48
+ # @param [String] file_name the path to a file containing a previously stored audit trail
49
+ #
50
+ def load(file_name)
51
+ @dictionary = JSON.load(File.new(file_name, "r:UTF-8"))
52
+ end
53
+
54
+ # Retrieves the original value used for the given combination of tag & replacement value.
55
+ #
56
+ # @param [String] tag the tag string (e.q. '0010,0010')
57
+ # @param [String, Integer, Float] replacement the replacement value (e.q. 'Patient1')
58
+ # @return [String, Integer, Float] the original value of the given tag
59
+ #
60
+ def original(tag, replacement)
61
+ original = nil
62
+ if @dictionary.key?(tag)
63
+ original = @dictionary[tag].key(replacement)
64
+ end
65
+ return original
66
+ end
67
+
68
+ # Gives the key/value pairs for a specific tag.
69
+ #
70
+ # @param [String] tag the tag string (e.q. '0010,0010')
71
+ # @return [Hash] the key/value pairs of a specific tag
72
+ #
73
+ def records(tag)
74
+ if @dictionary.key?(tag)
75
+ return @dictionary[tag]
76
+ else
77
+ return Hash.new
78
+ end
79
+ end
80
+
81
+ # Retrieves the replacement value used for the given combination of tag & original value.
82
+ #
83
+ # @param [String] tag the tag string (e.q. '0010,0010')
84
+ # @param [String, Integer, Float] original the original value (e.q. 'John Doe')
85
+ # @return [String, Integer, Float] the replacement value of the given tag
86
+ #
87
+ def replacement(tag, original)
88
+ replacement = nil
89
+ replacement = @dictionary[tag][original] if @dictionary.key?(tag)
90
+ return replacement
91
+ end
92
+
93
+ # Dumps the key/value pairs to a json string which is written to the specified file.
94
+ #
95
+ # @param [String] file_name the path to be used for storing key/value pairs on disk
96
+ #
97
+ def write(file_name)
98
+ # Encode json string:
99
+ str = JSON.pretty_generate(@dictionary)
100
+ # Create directory if needed:
101
+ unless File.directory?(File.dirname(file_name))
102
+ require 'fileutils'
103
+ FileUtils.mkdir_p(File.dirname(file_name))
104
+ end
105
+ # Write to file:
106
+ File.open(file_name, 'w') {|f| f.write(str) }
107
+ end
108
+
109
+ end
110
110
  end
@@ -1,265 +1,269 @@
1
- module DICOM
2
-
3
- # The DLibrary class contains methods for interacting with ruby-dicom's dictionary data.
4
- #
5
- # In practice, the library is for internal use and not accessed by the user. However, a
6
- # a library instance is available through the DICOM::LIBRARY constant.
7
- #
8
- # @example Get a dictionary element corresponding to the given tag
9
- # element = DICOM::LIBRARY.element('0010,0010')
10
- #
11
- class DLibrary
12
-
13
- # A hash with element name strings as key and method name symbols as value.
14
- attr_reader :methods_from_names
15
- # A hash with element method name symbols as key and name strings as value.
16
- attr_reader :names_from_methods
17
-
18
- # Creates a DLibrary instance.
19
- #
20
- def initialize
21
- # Create instance hashes used for dictionary data and method conversion:
22
- @elements = Hash.new
23
- @uids = Hash.new
24
- @methods_from_names = Hash.new
25
- @names_from_methods = Hash.new
26
- # Load the elements dictionary:
27
- add_element_dictionary("#{ROOT_DIR}/dictionary/elements.tsv")
28
- # Load the unique identifiers dictionary:
29
- add_uid_dictionary("#{ROOT_DIR}/dictionary/uids.tsv")
30
- end
31
-
32
- # Adds a custom DictionaryElement to the ruby-dicom element dictionary.
33
- #
34
- # @param [DictionaryElement] element the custom dictionary element to be added
35
- #
36
- def add_element(element)
37
- raise ArgumentError, "Invalid argument 'element'. Expected DictionaryElement, got #{element.class}" unless element.is_a?(DictionaryElement)
38
- # We store the elements in a hash with tag as key and the element instance as value:
39
- @elements[element.tag] = element
40
- # Populate the method conversion hashes with element data:
41
- method = element.name.to_element_method
42
- @methods_from_names[element.name] = method
43
- @names_from_methods[method] = element.name
44
- end
45
-
46
- # Adds a custom dictionary file to the ruby-dicom element dictionary.
47
- #
48
- # @note The format of the dictionary is a tab-separated text file with 5 columns:
49
- # * Tag, Name, VR, VM & Retired status
50
- # * For samples check out ruby-dicom's element dictionaries in the git repository
51
- # @param [String] file the path to the dictionary file to be added
52
- #
53
- def add_element_dictionary(file)
54
- File.open(file, :encoding => 'utf-8').each do |record|
55
- fields = record.split("\t")
56
- add_element(DictionaryElement.new(fields[0], fields[1], fields[2].split(","), fields[3].rstrip, fields[4].rstrip))
57
- end
58
- end
59
-
60
- # Adds a custom uid (e.g. SOP Class, Transfer Syntax) to the ruby-dicom uid dictionary.
61
- #
62
- # @param [UID] uid the custom uid instance to be added
63
- #
64
- def add_uid(uid)
65
- raise ArgumentError, "Invalid argument 'uid'. Expected UID, got #{uid.class}" unless uid.is_a?(UID)
66
- # We store the uids in a hash with uid-value as key and the uid instance as value:
67
- @uids[uid.value] = uid
68
- end
69
-
70
- # Adds a custom dictionary file to the ruby-dicom uid dictionary.
71
- #
72
- # @note The format of the dictionary is a tab-separated text file with 4 columns:
73
- # * Value, Name, Type & Retired status
74
- # * For samples check out ruby-dicom's uid dictionaries in the git repository
75
- # @param [String] file the path to the dictionary file to be added
76
- #
77
- def add_uid_dictionary(file)
78
- File.open(file, :encoding => 'utf-8').each do |record|
79
- fields = record.split("\t")
80
- add_uid(UID.new(fields[0], fields[1], fields[2].rstrip, fields[3].rstrip))
81
- end
82
- end
83
-
84
-
85
- # Gives the method (symbol) corresponding to the specified element string value.
86
- #
87
- # @param [String] value an element tag, element name or an element's method name
88
- # @return [Symbol, NilClass] the matched element method, or nil if no match is made
89
- #
90
- def as_method(value)
91
- case true
92
- when value.tag?
93
- @methods_from_names[element(value).name]
94
- when value.dicom_name?
95
- @methods_from_names[value]
96
- when value.dicom_method?
97
- @names_from_methods.has_key?(value.to_sym) ? value.to_sym : nil
98
- else
99
- nil
100
- end
101
- end
102
-
103
- # Gives the name corresponding to the specified element string value.
104
- #
105
- # @param [String] value an element tag, element name or an element's method name
106
- # @return [String, NilClass] the matched element name, or nil if no match is made
107
- #
108
- def as_name(value)
109
- case true
110
- when value.tag?
111
- element(value).name
112
- when value.dicom_name?
113
- @methods_from_names.has_key?(value) ? value.to_s : nil
114
- when value.dicom_method?
115
- @names_from_methods[value.to_sym]
116
- else
117
- nil
118
- end
119
- end
120
-
121
- # Gives the tag corresponding to the specified element string value.
122
- #
123
- # @param [String] value an element tag, element name or an element's method name
124
- # @return [String, NilClass] the matched element tag, or nil if no match is made
125
- #
126
- def as_tag(value)
127
- case true
128
- when value.tag?
129
- element(value) ? value : nil
130
- when value.dicom_name?
131
- get_tag(value)
132
- when value.dicom_method?
133
- get_tag(@names_from_methods[value.to_sym])
134
- else
135
- nil
136
- end
137
- end
138
-
139
- # Identifies the DictionaryElement that corresponds to the given tag.
140
- #
141
- # @note If a given tag doesn't return a dictionary match, a new DictionaryElement is created.
142
- # * For private tags, a name 'Private' and VR 'UN' is assigned
143
- # * For unknown tags, a name 'Unknown' and VR 'UN' is assigned
144
- # @param [String] tag the tag of the element
145
- # @return [DictionaryElement] a corresponding DictionaryElement
146
- #
147
- def element(tag)
148
- element = @elements[tag]
149
- unless element
150
- if tag.group_length?
151
- element = DictionaryElement.new(tag, 'Group Length', ['UL'], '1', '')
152
- else
153
- if tag.private?
154
- element = DictionaryElement.new(tag, 'Private', ['UN'], '1', '')
155
- else
156
- element = unknown_or_range_element(tag)
157
- end
158
- end
159
- end
160
- element
161
- end
162
-
163
- # Extracts, and returns, all transfer syntaxes and SOP Classes from the dictionary.
164
- #
165
- # @return [Array<Hash, Hash>] transfer syntax and sop class hashes, each with uid as key and name as value
166
- #
167
- def extract_transfer_syntaxes_and_sop_classes
168
- transfer_syntaxes = Hash.new
169
- sop_classes = Hash.new
170
- @uids.each_value do |uid|
171
- if uid.transfer_syntax?
172
- transfer_syntaxes[uid.value] = uid.name
173
- elsif uid.sop_class?
174
- sop_classes[uid.value] = uid.name
175
- end
176
- end
177
- return transfer_syntaxes, sop_classes
178
- end
179
-
180
- # Gives the tag that matches the supplied data element name, by searching the element dictionary.
181
- #
182
- # @param [String] name a data element name
183
- # @return [String, NilClass] the corresponding element tag, or nil if no match is made
184
- #
185
- def get_tag(name)
186
- tag = nil
187
- name = name.to_s.downcase
188
- @tag_name_pairs_cache ||= Hash.new
189
- return @tag_name_pairs_cache[name] unless @tag_name_pairs_cache[name].nil?
190
- @elements.each_value do |element|
191
- next unless element.name.downcase == name
192
- tag = element.tag
193
- break
194
- end
195
- @tag_name_pairs_cache[name]=tag
196
- return tag
197
- end
198
-
199
- # Determines the name and vr of the element which the specified tag belongs to,
200
- # based on a lookup in the element data dictionary.
201
- #
202
- # @note If a given tag doesn't return a dictionary match, the following values are assigned:
203
- # * For private tags: name 'Private' and VR 'UN'
204
- # * For unknown (non-private) tags: name 'Unknown' and VR 'UN'
205
- # @param [String] tag an element's tag
206
- # @return [Array<String, String>] the name and value representation corresponding to the given tag
207
- #
208
- def name_and_vr(tag)
209
- de = element(tag)
210
- return de.name, de.vr
211
- end
212
-
213
- # Identifies the UID that corresponds to the given value.
214
- #
215
- # @param [String] value the unique identifier value
216
- # @return [UID, NilClass] a corresponding UID instance, or nil (if no match is made)
217
- #
218
- def uid(value)
219
- @uids[value]
220
- end
221
-
222
-
223
- private
224
-
225
-
226
- # Creates a list of possible 'range' tag candidates based on the given tag.
227
- # Usually tags are uniquely defined in the DICOM dictionary, and the given
228
- # tag can be matched directly. However, for a small set of known tags, the
229
- # dictionary allows a range of tags to be associated with a specific
230
- # entry. This method creates an array of candidate tags which are processed
231
- # in order to match against these ranges.
232
- #
233
- # @param [String] tag the element tag
234
- # @return [Array<String>] processed candidate tags
235
- #
236
- def range_candidates(tag)
237
- [
238
- "#{tag[0..3]},xxx#{tag[8]}", # 1000,xxxh
239
- "#{tag[0..3]},xxxx", # 1010,xxxx
240
- "#{tag[0..1]}xx,#{tag[5..8]}", # hhxx,hhhh
241
- "#{tag[0..6]}x#{tag[8]}" # 0028,hhxh
242
- ]
243
- end
244
-
245
- # Matches a tag against the possible range tag candidates, and if no match
246
- # is found, returns a dictionary element representing an unknown tag.
247
- #
248
- # @param [String] tag the element tag
249
- # @return [DictionaryElement] a matched range element or an unknown element
250
- #
251
- def unknown_or_range_element(tag)
252
- element = nil
253
- range_candidates(tag).each do |range_candidate_tag|
254
- if de = @elements[range_candidate_tag]
255
- element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
256
- break
257
- end
258
- end
259
- # If nothing was matched, we are facing an unknown (but not private) tag:
260
- element ||= DictionaryElement.new(tag, 'Unknown', ['UN'], '1', '')
261
- end
262
-
263
- end
264
-
265
- end
1
+ module DICOM
2
+
3
+ # The DLibrary class contains methods for interacting with ruby-dicom's dictionary data.
4
+ #
5
+ # In practice, the library is for internal use and not accessed by the user. However, a
6
+ # a library instance is available through the DICOM::LIBRARY constant.
7
+ #
8
+ # @example Get a dictionary element corresponding to the given tag
9
+ # element = DICOM::LIBRARY.element('0010,0010')
10
+ #
11
+ class DLibrary
12
+
13
+ # A hash with element tag strings as keys and DicitonaryElement instances as values.
14
+ attr_reader :elements
15
+ # A hash with element name strings as key and method name symbols as value.
16
+ attr_reader :methods_from_names
17
+ # A hash with element method name symbols as key and name strings as value.
18
+ attr_reader :names_from_methods
19
+ # A hash with uid strings as keys and UID instances as values.
20
+ attr_reader :uids
21
+
22
+ # Creates a DLibrary instance.
23
+ #
24
+ def initialize
25
+ # Create instance hashes used for dictionary data and method conversion:
26
+ @elements = Hash.new
27
+ @uids = Hash.new
28
+ @methods_from_names = Hash.new
29
+ @names_from_methods = Hash.new
30
+ # Load the elements dictionary:
31
+ add_element_dictionary("#{ROOT_DIR}/dictionary/elements.tsv")
32
+ # Load the unique identifiers dictionary:
33
+ add_uid_dictionary("#{ROOT_DIR}/dictionary/uids.tsv")
34
+ end
35
+
36
+ # Adds a custom DictionaryElement to the ruby-dicom element dictionary.
37
+ #
38
+ # @param [DictionaryElement] element the custom dictionary element to be added
39
+ #
40
+ def add_element(element)
41
+ raise ArgumentError, "Invalid argument 'element'. Expected DictionaryElement, got #{element.class}" unless element.is_a?(DictionaryElement)
42
+ # We store the elements in a hash with tag as key and the element instance as value:
43
+ @elements[element.tag] = element
44
+ # Populate the method conversion hashes with element data:
45
+ method = element.name.to_element_method
46
+ @methods_from_names[element.name] = method
47
+ @names_from_methods[method] = element.name
48
+ end
49
+
50
+ # Adds a custom dictionary file to the ruby-dicom element dictionary.
51
+ #
52
+ # @note The format of the dictionary is a tab-separated text file with 5 columns:
53
+ # * Tag, Name, VR, VM & Retired status
54
+ # * For samples check out ruby-dicom's element dictionaries in the git repository
55
+ # @param [String] file the path to the dictionary file to be added
56
+ #
57
+ def add_element_dictionary(file)
58
+ File.open(file, :encoding => 'utf-8').each do |record|
59
+ fields = record.split("\t")
60
+ add_element(DictionaryElement.new(fields[0], fields[1], fields[2].split(","), fields[3].rstrip, fields[4].rstrip))
61
+ end
62
+ end
63
+
64
+ # Adds a custom uid (e.g. SOP Class, Transfer Syntax) to the ruby-dicom uid dictionary.
65
+ #
66
+ # @param [UID] uid the custom uid instance to be added
67
+ #
68
+ def add_uid(uid)
69
+ raise ArgumentError, "Invalid argument 'uid'. Expected UID, got #{uid.class}" unless uid.is_a?(UID)
70
+ # We store the uids in a hash with uid-value as key and the uid instance as value:
71
+ @uids[uid.value] = uid
72
+ end
73
+
74
+ # Adds a custom dictionary file to the ruby-dicom uid dictionary.
75
+ #
76
+ # @note The format of the dictionary is a tab-separated text file with 4 columns:
77
+ # * Value, Name, Type & Retired status
78
+ # * For samples check out ruby-dicom's uid dictionaries in the git repository
79
+ # @param [String] file the path to the dictionary file to be added
80
+ #
81
+ def add_uid_dictionary(file)
82
+ File.open(file, :encoding => 'utf-8').each do |record|
83
+ fields = record.split("\t")
84
+ add_uid(UID.new(fields[0], fields[1], fields[2].rstrip, fields[3].rstrip))
85
+ end
86
+ end
87
+
88
+
89
+ # Gives the method (symbol) corresponding to the specified element string value.
90
+ #
91
+ # @param [String] value an element tag, element name or an element's method name
92
+ # @return [Symbol, NilClass] the matched element method, or nil if no match is made
93
+ #
94
+ def as_method(value)
95
+ case true
96
+ when value.tag?
97
+ @methods_from_names[element(value).name]
98
+ when value.dicom_name?
99
+ @methods_from_names[value]
100
+ when value.dicom_method?
101
+ @names_from_methods.has_key?(value.to_sym) ? value.to_sym : nil
102
+ else
103
+ nil
104
+ end
105
+ end
106
+
107
+ # Gives the name corresponding to the specified element string value.
108
+ #
109
+ # @param [String] value an element tag, element name or an element's method name
110
+ # @return [String, NilClass] the matched element name, or nil if no match is made
111
+ #
112
+ def as_name(value)
113
+ case true
114
+ when value.tag?
115
+ element(value).name
116
+ when value.dicom_name?
117
+ @methods_from_names.has_key?(value) ? value.to_s : nil
118
+ when value.dicom_method?
119
+ @names_from_methods[value.to_sym]
120
+ else
121
+ nil
122
+ end
123
+ end
124
+
125
+ # Gives the tag corresponding to the specified element string value.
126
+ #
127
+ # @param [String] value an element tag, element name or an element's method name
128
+ # @return [String, NilClass] the matched element tag, or nil if no match is made
129
+ #
130
+ def as_tag(value)
131
+ case true
132
+ when value.tag?
133
+ element(value) ? value : nil
134
+ when value.dicom_name?
135
+ get_tag(value)
136
+ when value.dicom_method?
137
+ get_tag(@names_from_methods[value.to_sym])
138
+ else
139
+ nil
140
+ end
141
+ end
142
+
143
+ # Identifies the DictionaryElement that corresponds to the given tag.
144
+ #
145
+ # @note If a given tag doesn't return a dictionary match, a new DictionaryElement is created.
146
+ # * For private tags, a name 'Private' and VR 'UN' is assigned
147
+ # * For unknown tags, a name 'Unknown' and VR 'UN' is assigned
148
+ # @param [String] tag the tag of the element
149
+ # @return [DictionaryElement] a corresponding DictionaryElement
150
+ #
151
+ def element(tag)
152
+ element = @elements[tag]
153
+ unless element
154
+ if tag.group_length?
155
+ element = DictionaryElement.new(tag, 'Group Length', ['UL'], '1', '')
156
+ else
157
+ if tag.private?
158
+ element = DictionaryElement.new(tag, 'Private', ['UN'], '1', '')
159
+ else
160
+ element = unknown_or_range_element(tag)
161
+ end
162
+ end
163
+ end
164
+ element
165
+ end
166
+
167
+ # Extracts, and returns, all transfer syntaxes and SOP Classes from the dictionary.
168
+ #
169
+ # @return [Array<Hash, Hash>] transfer syntax and sop class hashes, each with uid as key and name as value
170
+ #
171
+ def extract_transfer_syntaxes_and_sop_classes
172
+ transfer_syntaxes = Hash.new
173
+ sop_classes = Hash.new
174
+ @uids.each_value do |uid|
175
+ if uid.transfer_syntax?
176
+ transfer_syntaxes[uid.value] = uid.name
177
+ elsif uid.sop_class?
178
+ sop_classes[uid.value] = uid.name
179
+ end
180
+ end
181
+ return transfer_syntaxes, sop_classes
182
+ end
183
+
184
+ # Gives the tag that matches the supplied data element name, by searching the element dictionary.
185
+ #
186
+ # @param [String] name a data element name
187
+ # @return [String, NilClass] the corresponding element tag, or nil if no match is made
188
+ #
189
+ def get_tag(name)
190
+ tag = nil
191
+ name = name.to_s.downcase
192
+ @tag_name_pairs_cache ||= Hash.new
193
+ return @tag_name_pairs_cache[name] unless @tag_name_pairs_cache[name].nil?
194
+ @elements.each_value do |element|
195
+ next unless element.name.downcase == name
196
+ tag = element.tag
197
+ break
198
+ end
199
+ @tag_name_pairs_cache[name]=tag
200
+ return tag
201
+ end
202
+
203
+ # Determines the name and vr of the element which the specified tag belongs to,
204
+ # based on a lookup in the element data dictionary.
205
+ #
206
+ # @note If a given tag doesn't return a dictionary match, the following values are assigned:
207
+ # * For private tags: name 'Private' and VR 'UN'
208
+ # * For unknown (non-private) tags: name 'Unknown' and VR 'UN'
209
+ # @param [String] tag an element's tag
210
+ # @return [Array<String, String>] the name and value representation corresponding to the given tag
211
+ #
212
+ def name_and_vr(tag)
213
+ de = element(tag)
214
+ return de.name, de.vr
215
+ end
216
+
217
+ # Identifies the UID that corresponds to the given value.
218
+ #
219
+ # @param [String] value the unique identifier value
220
+ # @return [UID, NilClass] a corresponding UID instance, or nil (if no match is made)
221
+ #
222
+ def uid(value)
223
+ @uids[value]
224
+ end
225
+
226
+
227
+ private
228
+
229
+
230
+ # Creates a list of possible 'range' tag candidates based on the given tag.
231
+ # Usually tags are uniquely defined in the DICOM dictionary, and the given
232
+ # tag can be matched directly. However, for a small set of known tags, the
233
+ # dictionary allows a range of tags to be associated with a specific
234
+ # entry. This method creates an array of candidate tags which are processed
235
+ # in order to match against these ranges.
236
+ #
237
+ # @param [String] tag the element tag
238
+ # @return [Array<String>] processed candidate tags
239
+ #
240
+ def range_candidates(tag)
241
+ [
242
+ "#{tag[0..3]},xxx#{tag[8]}", # 1000,xxxh
243
+ "#{tag[0..3]},xxxx", # 1010,xxxx
244
+ "#{tag[0..1]}xx,#{tag[5..8]}", # hhxx,hhhh
245
+ "#{tag[0..6]}x#{tag[8]}" # 0028,hhxh
246
+ ]
247
+ end
248
+
249
+ # Matches a tag against the possible range tag candidates, and if no match
250
+ # is found, returns a dictionary element representing an unknown tag.
251
+ #
252
+ # @param [String] tag the element tag
253
+ # @return [DictionaryElement] a matched range element or an unknown element
254
+ #
255
+ def unknown_or_range_element(tag)
256
+ element = nil
257
+ range_candidates(tag).each do |range_candidate_tag|
258
+ if de = @elements[range_candidate_tag]
259
+ element = DictionaryElement.new(tag, de.name, de.vrs, de.vm, de.retired)
260
+ break
261
+ end
262
+ end
263
+ # If nothing was matched, we are facing an unknown (but not private) tag:
264
+ element ||= DictionaryElement.new(tag, 'Unknown', ['UN'], '1', '')
265
+ end
266
+
267
+ end
268
+
269
+ end