dicom 0.9.3 → 0.9.4
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 +312 -290
- data/COPYING +674 -674
- data/Gemfile +3 -0
- data/dicom.gemspec +31 -0
- data/lib/dicom.rb +53 -51
- data/lib/dicom/anonymizer.rb +98 -123
- data/lib/dicom/audit_trail.rb +104 -116
- data/lib/dicom/constants.rb +219 -170
- data/lib/dicom/d_client.rb +122 -150
- data/lib/dicom/d_library.rb +219 -287
- data/lib/dicom/d_object.rb +451 -539
- data/lib/dicom/d_read.rb +151 -245
- data/lib/dicom/d_server.rb +329 -359
- data/lib/dicom/d_write.rb +327 -395
- data/lib/dicom/deprecated.rb +1 -72
- data/lib/dicom/dictionary/elements.txt +3646 -0
- data/lib/dicom/dictionary/uids.txt +334 -0
- data/lib/dicom/dictionary_element.rb +61 -0
- data/lib/dicom/element.rb +278 -218
- data/lib/dicom/elemental.rb +21 -27
- data/lib/dicom/file_handler.rb +121 -121
- data/lib/dicom/image_item.rb +819 -861
- data/lib/dicom/image_processor.rb +24 -15
- data/lib/dicom/image_processor_mini_magick.rb +21 -23
- data/lib/dicom/image_processor_r_magick.rb +39 -34
- data/lib/dicom/item.rb +133 -120
- data/lib/dicom/link.rb +1531 -1532
- data/lib/dicom/logging.rb +155 -158
- data/lib/dicom/parent.rb +782 -847
- data/lib/dicom/ruby_extensions.rb +248 -229
- data/lib/dicom/sequence.rb +109 -92
- data/lib/dicom/stream.rb +480 -511
- data/lib/dicom/uid.rb +82 -0
- data/lib/dicom/variables.rb +9 -9
- data/lib/dicom/version.rb +5 -5
- data/rakefile.rb +29 -0
- metadata +130 -76
- data/lib/dicom/dictionary.rb +0 -3280
@@ -1,230 +1,249 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
# This file contains extensions to the Ruby library which are used by Ruby DICOM.
|
4
|
-
|
5
|
-
# Extension to the String class. These
|
6
|
-
# A tag string (as used by the
|
7
|
-
# (where G represents a group hexadecimal, and E represents an element hexadecimal).
|
8
|
-
#
|
9
|
-
class String
|
10
|
-
|
11
|
-
# Renames the original unpack method.
|
12
|
-
#
|
13
|
-
alias __original_unpack__ unpack
|
14
|
-
|
15
|
-
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
#
|
35
|
-
def dicom_titleize
|
36
|
-
self.dicom_underscore.gsub(/_/,
|
37
|
-
end
|
38
|
-
|
39
|
-
# Makes an underscored,
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
word.
|
45
|
-
word
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
def group_length
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
#
|
200
|
-
#
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
when
|
226
|
-
return self.collect {|i| i
|
227
|
-
|
228
|
-
|
229
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# This file contains extensions to the Ruby library which are used by Ruby DICOM.
|
4
|
+
|
5
|
+
# Extension to the String class. These mainly facilitate the processing and analysis of element tags.
|
6
|
+
# A tag string (as used by the ruby-dicom library) is 9 characters long and of the form 'GGGG,EEEE'
|
7
|
+
# (where G represents a group hexadecimal, and E represents an element hexadecimal).
|
8
|
+
#
|
9
|
+
class String
|
10
|
+
|
11
|
+
# Renames the original unpack method.
|
12
|
+
#
|
13
|
+
alias __original_unpack__ unpack
|
14
|
+
|
15
|
+
# Checks if a string value LOOKS like a DICOM name - it may still not be valid one.
|
16
|
+
#
|
17
|
+
# @return [Boolean] true if a string looks like a DICOM name, and false if not
|
18
|
+
#
|
19
|
+
def dicom_name?
|
20
|
+
self == self.dicom_titleize
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks if a string value LOOKS like a DICOM method name - it may still not be valid one.
|
24
|
+
#
|
25
|
+
# @return [Boolean] true if a string looks like a DICOM method name, and false if not
|
26
|
+
#
|
27
|
+
def dicom_method?
|
28
|
+
self == self.dicom_underscore
|
29
|
+
end
|
30
|
+
|
31
|
+
# Capitalizes all the words in the string and replaces some characters to make a nicer looking title.
|
32
|
+
#
|
33
|
+
# @return [String] a formatted, capitalized string
|
34
|
+
#
|
35
|
+
def dicom_titleize
|
36
|
+
self.dicom_underscore.gsub(/_/, ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Makes an underscored, lowercased version of the string.
|
40
|
+
#
|
41
|
+
# @return [String] an underscored, lower case string
|
42
|
+
#
|
43
|
+
def dicom_underscore
|
44
|
+
word = self.dup
|
45
|
+
word.tr!('-', '_')
|
46
|
+
word.downcase!
|
47
|
+
word
|
48
|
+
end
|
49
|
+
|
50
|
+
# Divides a string into a number of sub-strings of exactly equal length.
|
51
|
+
#
|
52
|
+
# @note The length of self must be a multiple of parts, or an exception will be raised.
|
53
|
+
# @param [Integer] parts the number of sub-strings to create
|
54
|
+
# @return [Array<String>] the divided sub-strings
|
55
|
+
#
|
56
|
+
def divide(parts)
|
57
|
+
raise ArgumentError, "Expected an integer (Fixnum). Got #{parts.class}." unless parts.is_a?(Fixnum)
|
58
|
+
raise ArgumentError, "Argument must be in the range <1 - self.length (#{self.length})>. Got #{parts}." if parts < 1 or parts > self.length
|
59
|
+
raise ArgumentError, "Length of self (#{self.length}) must be a multiple of parts (#{parts})." unless (self.length/parts).to_f == self.length/parts.to_f
|
60
|
+
if parts > 1
|
61
|
+
sub_strings = Array.new
|
62
|
+
sub_length = self.length/parts
|
63
|
+
parts.times { sub_strings << self.slice!(0..(sub_length-1)) }
|
64
|
+
return sub_strings
|
65
|
+
else
|
66
|
+
return [self]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Extracts the element part of the tag string: The last 4 characters.
|
71
|
+
#
|
72
|
+
# @return [String] the element part of the tag
|
73
|
+
#
|
74
|
+
def element
|
75
|
+
return self[5..8]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the group part of the tag string: The first 4 characters.
|
79
|
+
#
|
80
|
+
# @return [String] the group part of the tag
|
81
|
+
#
|
82
|
+
def group
|
83
|
+
return self[0..3]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the "Group Length" ('GGGG,0000') tag which corresponds to the original tag/group string.
|
87
|
+
# The original string may either be a 4 character group string, or a 9 character custom tag.
|
88
|
+
#
|
89
|
+
# @return [String] a group length tag
|
90
|
+
#
|
91
|
+
def group_length
|
92
|
+
if self.length == 4
|
93
|
+
return self + ',0000'
|
94
|
+
else
|
95
|
+
return self.group + ',0000'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Checks if the string is a "Group Length" tag (its element part is '0000').
|
100
|
+
#
|
101
|
+
# @return [Boolean] true if it is a group length tag, and false if not
|
102
|
+
#
|
103
|
+
def group_length?
|
104
|
+
return (self.element == '0000' ? true : false)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Checks if the string is a private tag (has an odd group number).
|
108
|
+
#
|
109
|
+
# @return [Boolean] true if it is a private tag, and false if not
|
110
|
+
#
|
111
|
+
def private?
|
112
|
+
return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Checks if the string is a valid tag (as defined by ruby-dicom: 'GGGG,EEEE').
|
116
|
+
#
|
117
|
+
# @return [Boolean] true if it is a valid tag, and false if not
|
118
|
+
#
|
119
|
+
def tag?
|
120
|
+
# Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
|
121
|
+
return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Converts the string to a proper DICOM element method name symbol.
|
125
|
+
#
|
126
|
+
# @return [Symbol] a DICOM element method name
|
127
|
+
#
|
128
|
+
def to_element_method
|
129
|
+
self.gsub(/^3/,'three_').gsub(/[#*?!]/,' ').gsub(', ',' ').gsub('&','and').gsub(' - ','_').gsub(' / ','_').gsub(/[\s\-\.\,\/\\]/,'_').gsub(/[\(\)\']/,'').gsub(/\_+/, '_').downcase.to_sym
|
130
|
+
end
|
131
|
+
|
132
|
+
# Redefines the core library unpack method, adding
|
133
|
+
# the ability to decode signed integers in big endian.
|
134
|
+
#
|
135
|
+
# @param [String] format a format string which decides the decoding scheme to use
|
136
|
+
# @return [Array<String, Integer, Float>] the decoded values
|
137
|
+
#
|
138
|
+
def unpack(format)
|
139
|
+
# Check for some custom unpack strings that we've invented:
|
140
|
+
case format
|
141
|
+
when "k*" # SS
|
142
|
+
# Unpack BE US, repack LE US, then finally unpack LE SS:
|
143
|
+
wrongly_unpacked = self.__original_unpack__('n*')
|
144
|
+
repacked = wrongly_unpacked.__original_pack__('S*')
|
145
|
+
correct = repacked.__original_unpack__('s*')
|
146
|
+
when 'r*' # SL
|
147
|
+
# Unpack BE UL, repack LE UL, then finally unpack LE SL:
|
148
|
+
wrongly_unpacked = self.__original_unpack__('N*')
|
149
|
+
repacked = wrongly_unpacked.__original_pack__('I*')
|
150
|
+
correct = repacked.__original_unpack__('l*')
|
151
|
+
else
|
152
|
+
# Call the original method for all other (normal) cases:
|
153
|
+
self.__original_unpack__(format)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Extensions to the Array class.
|
161
|
+
# These mainly deal with encoding integer arrays as well as conversion between
|
162
|
+
# signed and unsigned integers.
|
163
|
+
#
|
164
|
+
class Array
|
165
|
+
|
166
|
+
# Renames the original pack method.
|
167
|
+
#
|
168
|
+
alias __original_pack__ pack
|
169
|
+
|
170
|
+
# Redefines the old pack method, adding the ability to encode signed integers in big endian
|
171
|
+
# (which surprisingly has not been supported out of the box in Ruby until version 1.9.3).
|
172
|
+
#
|
173
|
+
# @param [String] format a format string which decides the encoding scheme to use
|
174
|
+
# @return [String] the encoded binary string
|
175
|
+
#
|
176
|
+
def pack(format)
|
177
|
+
# FIXME: At some time in the future, when Ruby 1.9.3 can be set as required ruby version,
|
178
|
+
# this custom pack (as well as unpack) method can be discarded, and the desired endian
|
179
|
+
# encodings can probably be achieved with the new template strings introduced in 1.9.3.
|
180
|
+
#
|
181
|
+
# Check for some custom pack strings that we've invented:
|
182
|
+
case format
|
183
|
+
when 'k*' # SS
|
184
|
+
# Pack LE SS, re-unpack as LE US, then finally pack BE US:
|
185
|
+
wrongly_packed = self.__original_pack__('s*')
|
186
|
+
reunpacked = wrongly_packed.__original_unpack__('S*')
|
187
|
+
correct = reunpacked.__original_pack__('n*')
|
188
|
+
when 'r*' # SL
|
189
|
+
# Pack LE SL, re-unpack as LE UL, then finally pack BE UL:
|
190
|
+
wrongly_packed = self.__original_pack__('l*')
|
191
|
+
reunpacked = wrongly_packed.__original_unpack__('I*')
|
192
|
+
correct = reunpacked.__original_pack__('N*')
|
193
|
+
else
|
194
|
+
# Call the original method for all other (normal) cases:
|
195
|
+
self.__original_pack__(format)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Packs an array of (unsigned) integers to a binary string (blob).
|
200
|
+
#
|
201
|
+
# @param [Integer] depth the bit depth to be used when encoding the unsigned integers
|
202
|
+
# @return [String] an encoded binary string
|
203
|
+
#
|
204
|
+
def to_blob(depth)
|
205
|
+
raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
|
206
|
+
raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
|
207
|
+
case depth
|
208
|
+
when 8
|
209
|
+
return self.pack('C*') # Unsigned char
|
210
|
+
when 16
|
211
|
+
return self.pack('S*') # Unsigned short, native byte order
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Shifts the integer values of the array to make a signed data set.
|
216
|
+
# The size of the shift is determined by the given bit depth.
|
217
|
+
#
|
218
|
+
# @param [Integer] depth the bit depth of the integers
|
219
|
+
# @return [Array<Integer>] an array of signed integers
|
220
|
+
#
|
221
|
+
def to_signed(depth)
|
222
|
+
raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
|
223
|
+
raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
|
224
|
+
case depth
|
225
|
+
when 8
|
226
|
+
return self.collect {|i| i - 128}
|
227
|
+
when 16
|
228
|
+
return self.collect {|i| i - 32768}
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Shifts the integer values of the array to make an unsigned data set.
|
233
|
+
# The size of the shift is determined by the given bit depth.
|
234
|
+
#
|
235
|
+
# @param [Integer] depth the bit depth of the integers
|
236
|
+
# @return [Array<Integer>] an array of unsigned integers
|
237
|
+
#
|
238
|
+
def to_unsigned(depth)
|
239
|
+
raise ArgumentError, "Expected Integer, got #{depth.class}" unless depth.is_a?(Integer)
|
240
|
+
raise ArgumentError, "Unsupported bit depth #{depth}." unless [8,16].include?(depth)
|
241
|
+
case depth
|
242
|
+
when 8
|
243
|
+
return self.collect {|i| i + 128}
|
244
|
+
when 16
|
245
|
+
return self.collect {|i| i + 32768}
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
230
249
|
end
|