dicom 0.7 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +55 -0
- data/README +51 -29
- data/init.rb +1 -0
- data/lib/dicom.rb +35 -21
- data/lib/dicom/{Anonymizer.rb → anonymizer.rb} +178 -80
- data/lib/dicom/constants.rb +121 -0
- data/lib/dicom/d_client.rb +888 -0
- data/lib/dicom/d_library.rb +208 -0
- data/lib/dicom/d_object.rb +424 -0
- data/lib/dicom/d_read.rb +433 -0
- data/lib/dicom/d_server.rb +397 -0
- data/lib/dicom/d_write.rb +420 -0
- data/lib/dicom/data_element.rb +175 -0
- data/lib/dicom/{Dictionary.rb → dictionary.rb} +390 -398
- data/lib/dicom/elements.rb +82 -0
- data/lib/dicom/file_handler.rb +116 -0
- data/lib/dicom/item.rb +87 -0
- data/lib/dicom/{Link.rb → link.rb} +749 -388
- data/lib/dicom/ruby_extensions.rb +44 -35
- data/lib/dicom/sequence.rb +62 -0
- data/lib/dicom/stream.rb +493 -0
- data/lib/dicom/super_item.rb +696 -0
- data/lib/dicom/super_parent.rb +615 -0
- metadata +25 -18
- data/DOCUMENTATION +0 -469
- data/lib/dicom/DClient.rb +0 -584
- data/lib/dicom/DLibrary.rb +0 -194
- data/lib/dicom/DObject.rb +0 -1579
- data/lib/dicom/DRead.rb +0 -532
- data/lib/dicom/DServer.rb +0 -304
- data/lib/dicom/DWrite.rb +0 -410
- data/lib/dicom/FileHandler.rb +0 -50
- data/lib/dicom/Stream.rb +0 -354
@@ -1,47 +1,56 @@
|
|
1
|
-
# This file contains extensions to the Ruby library which are used by Ruby
|
1
|
+
# This file contains extensions to the Ruby library which are used by Ruby-DICOM.
|
2
2
|
|
3
|
-
class
|
3
|
+
# Extension to the String class. These extensions are focused on processing/analysing Data Element tags.
|
4
|
+
# A tag string (as used by the Ruby-DICOM library) is 9 characters long and of the form "GGGG,EEEE"
|
5
|
+
# (where G represents a group hexadecimal, and E represents an element hexadecimal).
|
6
|
+
#
|
7
|
+
class String
|
4
8
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
def
|
8
|
-
|
9
|
-
self.each do |pos|
|
10
|
-
result << pos if array[pos] == value
|
11
|
-
end
|
12
|
-
return result
|
9
|
+
# Returns the element part of the tag string: The last 4 characters.
|
10
|
+
#
|
11
|
+
def element
|
12
|
+
return self[5..8]
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
def
|
18
|
-
|
19
|
-
self.each do |pos|
|
20
|
-
result << pos if array[pos].include?(value)
|
21
|
-
end
|
22
|
-
return result
|
15
|
+
# Returns the group part of the tag string: The first 4 characters.
|
16
|
+
#
|
17
|
+
def group
|
18
|
+
return self[0..3]
|
23
19
|
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
# Returns the "Group Length" ("GGGG,0000") tag which corresponds to the original tag/group string.
|
22
|
+
# This string may either be a 4 character group string, or a 9 character custom tag.
|
23
|
+
#
|
24
|
+
def group_length
|
25
|
+
if self.length == 4
|
26
|
+
return self + ",0000"
|
27
|
+
else
|
28
|
+
return self.group + ",0000"
|
29
|
+
end
|
30
|
+
end
|
28
31
|
|
29
|
-
#
|
30
|
-
#
|
31
|
-
|
32
|
-
def
|
33
|
-
|
34
|
-
#result = true if self =~ /\A\h{4},\h{4}\z/ # (turns out the hex reference '\h' isnt compatible with ruby 1.8)
|
35
|
-
result = true if self =~ /\A[a-fA-F\d]{4},[a-fA-F\d]{4}\z/
|
36
|
-
return result
|
32
|
+
# Checks if the string is a "Group Length" tag (its element part is "0000").
|
33
|
+
# Returns true if it is and false if not.
|
34
|
+
#
|
35
|
+
def group_length?
|
36
|
+
return (self.element == "0000" ? true : false)
|
37
37
|
end
|
38
38
|
|
39
|
-
#
|
39
|
+
# Checks if the string is a private tag (has an odd group number).
|
40
|
+
# Returns true if it is, false if not.
|
41
|
+
#
|
40
42
|
def private?
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
#return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true) # (incompatible with ruby 1.8)
|
44
|
+
return ((self.upcase =~ /\A[a-fA-F\d]{3}[1,3,5,7,9,B,D,F],[a-fA-F\d]{4}\z/) == nil ? false : true)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Checks if the string is a valid tag (as defined by Ruby-DICOM: "GGGG,EEEE").
|
48
|
+
# Returns true if it is a valid tag, false if not.
|
49
|
+
#
|
50
|
+
def tag?
|
51
|
+
# Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
|
52
|
+
#return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true) # (It turns out the hex reference '\h' isnt compatible with ruby 1.8)
|
53
|
+
return ((self.upcase =~ /\A[a-fA-F\d]{4},[a-fA-F\d]{4}\z/) == nil ? false : true)
|
45
54
|
end
|
46
55
|
|
47
|
-
end
|
56
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright 2010 Christoffer Lervag
|
2
|
+
|
3
|
+
module DICOM
|
4
|
+
|
5
|
+
# The Sequence class handles information related to Sequence elements.
|
6
|
+
#
|
7
|
+
class Sequence < SuperParent
|
8
|
+
|
9
|
+
# Include the Elements mix-in module:
|
10
|
+
include Elements
|
11
|
+
|
12
|
+
# Creates a Sequence instance.
|
13
|
+
#
|
14
|
+
# === Notes
|
15
|
+
#
|
16
|
+
# * Private sequences will have their names listed as "Private".
|
17
|
+
# * Non-private sequences that are not found in the dictionary will be listed as "Unknown".
|
18
|
+
#
|
19
|
+
# === Parameters
|
20
|
+
#
|
21
|
+
# * <tt>tag</tt> -- A string which identifies the tag of the sequence.
|
22
|
+
# * <tt>options</tt> -- A hash of parameters.
|
23
|
+
#
|
24
|
+
# === Options
|
25
|
+
#
|
26
|
+
# * <tt>:length</tt> -- Fixnum. The Sequence length, which refers to the length of the encoded string of children of this Sequence.
|
27
|
+
# * <tt>:name</tt> - String. The name of the Sequence may be specified upon creation. If it is not, the name will be retrieved from the dictionary.
|
28
|
+
# * <tt>:parent</tt> - Item or DObject instance which the Sequence instance shall belong to.
|
29
|
+
# * <tt>:vr</tt> -- String. The value representation of the Sequence may be specified upon creation. If it is not, a default vr is chosen.
|
30
|
+
#
|
31
|
+
# === Examples
|
32
|
+
#
|
33
|
+
# # Create a new Sequence and connect it to a DObject instance:
|
34
|
+
# structure_set_roi = Sequence.new("3006,0020", :parent => obj)
|
35
|
+
# # Create an "Encapsulated Pixel Data" Sequence:
|
36
|
+
# encapsulated_pixel_data = Sequence.new("7FE0,0010", :name => "Encapsulated Pixel Data", :parent => obj, :vr => "OW")
|
37
|
+
#
|
38
|
+
def initialize(tag, options={})
|
39
|
+
# Set common parent variables:
|
40
|
+
initialize_parent
|
41
|
+
# Set instance variables:
|
42
|
+
@tag = tag
|
43
|
+
@value = nil
|
44
|
+
@bin = nil
|
45
|
+
# We may beed to retrieve name and vr from the library:
|
46
|
+
if options[:name] and options[:vr]
|
47
|
+
@name = options[:name]
|
48
|
+
@vr = options[:vr]
|
49
|
+
else
|
50
|
+
name, vr = LIBRARY.get_name_vr(tag)
|
51
|
+
@name = options[:name] || name
|
52
|
+
@vr = options[:vr] || "SQ"
|
53
|
+
end
|
54
|
+
@length = options[:length] || -1
|
55
|
+
if options[:parent]
|
56
|
+
@parent = options[:parent]
|
57
|
+
@parent.add(self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/dicom/stream.rb
ADDED
@@ -0,0 +1,493 @@
|
|
1
|
+
# Copyright 2009-2010 Christoffer Lervag
|
2
|
+
|
3
|
+
module DICOM
|
4
|
+
|
5
|
+
# The Stream class handles string operations (encoding to and decoding from binary strings).
|
6
|
+
# It is used by the various classes of Ruby DICOM for tasks such as reading and writing from/to files or network packets.
|
7
|
+
# These methods have been gathered in this single class in an attempt to minimize code duplication.
|
8
|
+
#
|
9
|
+
class Stream
|
10
|
+
|
11
|
+
# A boolean which reports the relationship between the endianness of the system and the instance string.
|
12
|
+
attr_reader :equal_endian
|
13
|
+
# Our current position in the instance string (used only for decoding).
|
14
|
+
attr_accessor :index
|
15
|
+
# The instance string.
|
16
|
+
attr_accessor :string
|
17
|
+
# An array of warning/error messages that (may) have been accumulated.
|
18
|
+
attr_reader :errors
|
19
|
+
# A hash with vr as key and its corresponding pad byte as value.
|
20
|
+
attr_reader :pad_byte
|
21
|
+
|
22
|
+
# Creates a Stream instance.
|
23
|
+
#
|
24
|
+
# === Parameters
|
25
|
+
#
|
26
|
+
# * <tt>binary</tt> -- A binary string.
|
27
|
+
# * <tt>string_endian</tt> -- Boolean. The endianness of the instance string (true for big endian, false for small endian).
|
28
|
+
# * <tt>options</tt> -- A hash of parameters.
|
29
|
+
#
|
30
|
+
# === Options
|
31
|
+
#
|
32
|
+
# * <tt>:index</tt> -- Fixnum. A position (offset) in the instance string where reading will start.
|
33
|
+
#
|
34
|
+
def initialize(binary, string_endian, options={})
|
35
|
+
@string = binary || ""
|
36
|
+
@index = options[:index] || 0
|
37
|
+
@errors = Array.new
|
38
|
+
self.endian = string_endian
|
39
|
+
end
|
40
|
+
|
41
|
+
# Prepends a pre-encoded string to the instance string (inserts at the beginning).
|
42
|
+
#
|
43
|
+
# === Parameters
|
44
|
+
#
|
45
|
+
# * <tt>binary</tt> -- A binary string.
|
46
|
+
#
|
47
|
+
def add_first(binary)
|
48
|
+
@string = binary + @string if binary
|
49
|
+
end
|
50
|
+
|
51
|
+
# Appends a pre-encoded string to the instance string (inserts at the end).
|
52
|
+
#
|
53
|
+
# === Parameters
|
54
|
+
#
|
55
|
+
# * <tt>binary</tt> -- A binary string.
|
56
|
+
#
|
57
|
+
def add_last(binary)
|
58
|
+
@string = @string + binary if binary
|
59
|
+
end
|
60
|
+
|
61
|
+
# Decodes a section of the instance string and returns the formatted data.
|
62
|
+
# The instance index is offset in accordance with the length read.
|
63
|
+
#
|
64
|
+
# === Notes
|
65
|
+
#
|
66
|
+
# * If multiple numbers are decoded, these are returned in an array.
|
67
|
+
#
|
68
|
+
# === Parameters
|
69
|
+
#
|
70
|
+
# * <tt>length</tt> -- Fixnum. The string length which will be decoded.
|
71
|
+
# * <tt>type</tt> -- String. The type (vr) of data to decode.
|
72
|
+
#
|
73
|
+
def decode(length, type)
|
74
|
+
# Check if values are valid:
|
75
|
+
if (@index + length) > @string.length
|
76
|
+
# The index number is bigger then the length of the binary string.
|
77
|
+
# We have reached the end and will return nil.
|
78
|
+
value = nil
|
79
|
+
else
|
80
|
+
# Decode the binary string and return value:
|
81
|
+
value = @string.slice(@index, length).unpack(vr_to_str(type))
|
82
|
+
# If the result is an array of one element, return the element instead of the array.
|
83
|
+
# If result is contained in a multi-element array, the original array is returned.
|
84
|
+
if value.length == 1
|
85
|
+
value = value[0]
|
86
|
+
# If value is a string, strip away possible trailing whitespace:
|
87
|
+
value = value.rstrip if value.is_a?(String)
|
88
|
+
end
|
89
|
+
# Update our position in the string:
|
90
|
+
skip(length)
|
91
|
+
end
|
92
|
+
return value
|
93
|
+
end
|
94
|
+
|
95
|
+
# Decodes the entire instance string and returns the formatted data.
|
96
|
+
# Typically used for decoding image data.
|
97
|
+
#
|
98
|
+
# === Notes
|
99
|
+
#
|
100
|
+
# * If multiple numbers are decoded, these are returned in an array.
|
101
|
+
#
|
102
|
+
# === Parameters
|
103
|
+
#
|
104
|
+
# * <tt>type</tt> -- String. The type (vr) of data to decode.
|
105
|
+
#
|
106
|
+
def decode_all(type)
|
107
|
+
length = @string.length
|
108
|
+
value = @string.slice(@index, length).unpack(vr_to_str(type))
|
109
|
+
skip(length)
|
110
|
+
return value
|
111
|
+
end
|
112
|
+
|
113
|
+
# Decodes 4 bytes of the instance string as a tag.
|
114
|
+
# Returns the tag string as a Ruby DICOM type tag ("GGGG,EEEE").
|
115
|
+
# Returns nil if no tag could be decoded (end of string).
|
116
|
+
#
|
117
|
+
def decode_tag
|
118
|
+
length = 4
|
119
|
+
# Check if values are valid:
|
120
|
+
if (@index + length) > @string.length
|
121
|
+
# The index number is bigger then the length of the binary string.
|
122
|
+
# We have reached the end and will return nil.
|
123
|
+
tag = nil
|
124
|
+
else
|
125
|
+
# Decode and process:
|
126
|
+
string = @string.slice(@index, length).unpack(@hex)[0].upcase
|
127
|
+
if @equal_endian
|
128
|
+
tag = string[2..3] + string[0..1] + "," + string[6..7] + string[4..5]
|
129
|
+
else
|
130
|
+
tag = string[0..3] + "," + string[4..7]
|
131
|
+
end
|
132
|
+
# Update our position in the string:
|
133
|
+
skip(length)
|
134
|
+
end
|
135
|
+
return tag
|
136
|
+
end
|
137
|
+
|
138
|
+
# Encodes a value and returns the resulting binary string.
|
139
|
+
#
|
140
|
+
# === Parameters
|
141
|
+
#
|
142
|
+
# * <tt>value</tt> -- A custom value (String, Fixnum, etc..) or an array of numbers.
|
143
|
+
# * <tt>type</tt> -- String. The type (vr) of data to encode.
|
144
|
+
#
|
145
|
+
def encode(value, type)
|
146
|
+
value = [value] unless value.is_a?(Array)
|
147
|
+
return value.pack(vr_to_str(type))
|
148
|
+
end
|
149
|
+
|
150
|
+
# Encodes a value to a binary string and prepends it to the instance string.
|
151
|
+
#
|
152
|
+
# === Parameters
|
153
|
+
#
|
154
|
+
# * <tt>value</tt> -- A custom value (String, Fixnum, etc..) or an array of numbers.
|
155
|
+
# * <tt>type</tt> -- String. The type (vr) of data to encode.
|
156
|
+
#
|
157
|
+
def encode_first(value, type)
|
158
|
+
value = [value] unless value.is_a?(Array)
|
159
|
+
bin = value.pack(vr_to_str(type))
|
160
|
+
@string = bin + @string
|
161
|
+
end
|
162
|
+
|
163
|
+
# Encodes a value to a binary string and appends it to the instance string.
|
164
|
+
#
|
165
|
+
# === Parameters
|
166
|
+
#
|
167
|
+
# * <tt>value</tt> -- A custom value (String, Fixnum, etc..) or an array of numbers.
|
168
|
+
# * <tt>type</tt> -- String. The type (vr) of data to encode.
|
169
|
+
#
|
170
|
+
def encode_last(value, type)
|
171
|
+
value = [value] unless value.is_a?(Array)
|
172
|
+
bin = value.pack(vr_to_str(type))
|
173
|
+
@string = @string + bin
|
174
|
+
end
|
175
|
+
|
176
|
+
# Appends a string with trailling spaces to achieve a target length, and encodes it to a binary string.
|
177
|
+
# Returns the binary string.
|
178
|
+
#
|
179
|
+
# === Parameters
|
180
|
+
#
|
181
|
+
# * <tt>string</tt> -- A string to be processed.
|
182
|
+
# * <tt>target_length</tt> -- Fixnum. The target length of the string that is created.
|
183
|
+
#
|
184
|
+
def encode_string_with_trailing_spaces(string, target_length)
|
185
|
+
length = string.length
|
186
|
+
if length < target_length
|
187
|
+
return [string].pack(@str)+["20"*(target_length-length)].pack(@hex)
|
188
|
+
elsif length == target_length
|
189
|
+
return [string].pack(@str)
|
190
|
+
else
|
191
|
+
raise "The specified string is longer than the allowed maximum length (String: #{string}, Target length: #{target_length})."
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Encodes a tag from the Ruby DICOM format ("GGGG,EEEE"), to a proper binary string, and returns it.
|
196
|
+
#
|
197
|
+
# === Parameters
|
198
|
+
#
|
199
|
+
# * <tt>string</tt> -- A string to be processed.
|
200
|
+
#
|
201
|
+
def encode_tag(tag)
|
202
|
+
if @equal_endian
|
203
|
+
clean_tag = tag[2..3] + tag[0..1] + tag[7..8] + tag[5..6]
|
204
|
+
else
|
205
|
+
clean_tag = tag[0..3] + tag[5..8]
|
206
|
+
end
|
207
|
+
return [clean_tag].pack(@hex)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Encodes a value, and if the the resulting binary string has an odd length, appends a proper padding byte.
|
211
|
+
# Returns the processed binary string (which will always be of even length).
|
212
|
+
#
|
213
|
+
# === Parameters
|
214
|
+
#
|
215
|
+
# * <tt>value</tt> -- A custom value (String, Fixnum, etc..) or an array of numbers.
|
216
|
+
# * <tt>vr</tt> -- String. The type of data to encode.
|
217
|
+
#
|
218
|
+
def encode_value(value, vr)
|
219
|
+
# Make sure the value is in an array:
|
220
|
+
value = [value] unless value.is_a?(Array)
|
221
|
+
# Get the proper pack string:
|
222
|
+
type = vr_to_str(vr)
|
223
|
+
# Encode:
|
224
|
+
bin = value.pack(type)
|
225
|
+
# Add an empty byte if the resulting binary has an odd length:
|
226
|
+
bin = bin + @pad_byte[vr] if bin.length[0] == 1
|
227
|
+
return bin
|
228
|
+
end
|
229
|
+
|
230
|
+
# Sets the endianness of the instance string. The relationship between the string endianness and
|
231
|
+
# the system endianness, determines which encoding/decoding flags to use.
|
232
|
+
#
|
233
|
+
# === Parameters
|
234
|
+
#
|
235
|
+
# * <tt>string_endian</tt> -- Boolean. The endianness of the instance string (true for big endian, false for small endian).
|
236
|
+
#
|
237
|
+
def endian=(string_endian)
|
238
|
+
@str_endian = string_endian
|
239
|
+
configure_endian
|
240
|
+
set_pad_byte
|
241
|
+
set_string_formats
|
242
|
+
set_format_hash
|
243
|
+
end
|
244
|
+
|
245
|
+
# Extracts and returns the entire instance string, or optionally,
|
246
|
+
# just the first part of it if a length is specified.
|
247
|
+
# The extracted string is removed from the instance string, and returned.
|
248
|
+
#
|
249
|
+
# === Parameters
|
250
|
+
#
|
251
|
+
# * <tt>length</tt> -- Fixnum. The length of the string which will be cut out. If nil, the entire string is exported.
|
252
|
+
#
|
253
|
+
def export(length=nil)
|
254
|
+
if length
|
255
|
+
string = @string.slice!(0, length)
|
256
|
+
else
|
257
|
+
string = @string
|
258
|
+
reset
|
259
|
+
end
|
260
|
+
return string
|
261
|
+
end
|
262
|
+
|
263
|
+
# Extracts and returns a binary string of the given length, starting at the index position.
|
264
|
+
# The instance index is offset in accordance with the length read.
|
265
|
+
#
|
266
|
+
# === Parameters
|
267
|
+
#
|
268
|
+
# * <tt>length</tt> -- Fixnum. The length of the string which will extracted.
|
269
|
+
#
|
270
|
+
def extract(length)
|
271
|
+
str = @string.slice(@index, length)
|
272
|
+
skip(length)
|
273
|
+
return str
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns the length of the binary instance string.
|
277
|
+
#
|
278
|
+
def length
|
279
|
+
return @string.length
|
280
|
+
end
|
281
|
+
|
282
|
+
# Calculates and returns the remaining length of the instance string (from the index position).
|
283
|
+
#
|
284
|
+
def rest_length
|
285
|
+
length = @string.length - @index
|
286
|
+
return length
|
287
|
+
end
|
288
|
+
|
289
|
+
# Extracts and returns the remaining part of the instance string (from the index position to the end of the string).
|
290
|
+
#
|
291
|
+
def rest_string
|
292
|
+
str = @string[@index..(@string.length-1)]
|
293
|
+
return str
|
294
|
+
end
|
295
|
+
|
296
|
+
# Resets the instance string and index.
|
297
|
+
#
|
298
|
+
def reset
|
299
|
+
@string = ""
|
300
|
+
@index = 0
|
301
|
+
end
|
302
|
+
|
303
|
+
# Resets the instance index.
|
304
|
+
#
|
305
|
+
def reset_index
|
306
|
+
@index = 0
|
307
|
+
end
|
308
|
+
|
309
|
+
# Sets an instance file variable.
|
310
|
+
#
|
311
|
+
# === Notes
|
312
|
+
#
|
313
|
+
# For performance reasons, we enable the Stream instance to write directly to file,
|
314
|
+
# to avoid expensive string operations which will otherwise slow down the write performance.
|
315
|
+
#
|
316
|
+
# === Parameters
|
317
|
+
#
|
318
|
+
# * <tt>file</tt> -- A File instance.
|
319
|
+
#
|
320
|
+
def set_file(file)
|
321
|
+
@file = file
|
322
|
+
end
|
323
|
+
|
324
|
+
# Sets a new instance string, and resets the index variable.
|
325
|
+
#
|
326
|
+
# === Parameters
|
327
|
+
#
|
328
|
+
# * <tt>binary</tt> -- A binary string.
|
329
|
+
#
|
330
|
+
def set_string(binary)
|
331
|
+
binary = binary[0] if binary.is_a?(Array)
|
332
|
+
@string = binary
|
333
|
+
@index = 0
|
334
|
+
end
|
335
|
+
|
336
|
+
# Applies an offset (positive or negative) to the instance index.
|
337
|
+
#
|
338
|
+
# === Parameters
|
339
|
+
#
|
340
|
+
# * <tt>offset</tt> -- Fixnum. The length to skip (positive) or rewind (negative).
|
341
|
+
#
|
342
|
+
def skip(offset)
|
343
|
+
@index += offset
|
344
|
+
end
|
345
|
+
|
346
|
+
# Writes a binary string to the File instance.
|
347
|
+
#
|
348
|
+
# === Parameters
|
349
|
+
#
|
350
|
+
# * <tt>binary</tt> -- A binary string.
|
351
|
+
#
|
352
|
+
def write(binary)
|
353
|
+
@file.write(binary)
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
# Following methods are private:
|
358
|
+
private
|
359
|
+
|
360
|
+
|
361
|
+
# Determines the relationship between system and string endianness, and sets the instance endian variable.
|
362
|
+
#
|
363
|
+
def configure_endian
|
364
|
+
if CPU_ENDIAN == @str_endian
|
365
|
+
@equal_endian = true
|
366
|
+
else
|
367
|
+
@equal_endian = false
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# Converts a data type/vr to an encode/decode string used by the pack/unpack methods, which is returned.
|
372
|
+
#
|
373
|
+
# === Parameters
|
374
|
+
#
|
375
|
+
# * <tt>vr</tt> -- String. A data type (value representation).
|
376
|
+
#
|
377
|
+
def vr_to_str(vr)
|
378
|
+
unless @format[vr]
|
379
|
+
errors << "Warning: Element type #{vr} does not have a reading method assigned to it. Something is not implemented correctly or the DICOM data analyzed is invalid."
|
380
|
+
return @hex
|
381
|
+
else
|
382
|
+
return @format[vr]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Sets the hash which is used to convert data element types (VR) to
|
387
|
+
# encode/decode strings accepted by the pack/unpack methods.
|
388
|
+
#
|
389
|
+
def set_format_hash
|
390
|
+
@format = {
|
391
|
+
"BY" => @by, # Byte/Character (1-byte integers)
|
392
|
+
"US" => @us, # Unsigned short (2 bytes)
|
393
|
+
"SS" => @ss, # Signed short (2 bytes)
|
394
|
+
"UL" => @ul, # Unsigned long (4 bytes)
|
395
|
+
"SL" => @sl, # Signed long (4 bytes)
|
396
|
+
"FL" => @fs, # Floating point single (4 bytes)
|
397
|
+
"FD" => @fd, # Floating point double (8 bytes)
|
398
|
+
"OB" => @by, # Other byte string (1-byte integers)
|
399
|
+
"OF" => @fs, # Other float string (4-byte floating point numbers)
|
400
|
+
"OW" => @us, # Other word string (2-byte integers)
|
401
|
+
"AT" => @hex, # Tag reference (4 bytes) NB: This may need to be revisited at some point...
|
402
|
+
"UN" => @hex, # Unknown information (header element is not recognized from local database)
|
403
|
+
"HEX" => @hex, # HEX
|
404
|
+
# We have a number of VRs that are decoded as string:
|
405
|
+
"AE" => @str,
|
406
|
+
"AS" => @str,
|
407
|
+
"CS" => @str,
|
408
|
+
"DA" => @str,
|
409
|
+
"DS" => @str,
|
410
|
+
"DT" => @str,
|
411
|
+
"IS" => @str,
|
412
|
+
"LO" => @str,
|
413
|
+
"LT" => @str,
|
414
|
+
"PN" => @str,
|
415
|
+
"SH" => @str,
|
416
|
+
"ST" => @str,
|
417
|
+
"TM" => @str,
|
418
|
+
"UI" => @str,
|
419
|
+
"UT" => @str,
|
420
|
+
"STR" => @str
|
421
|
+
}
|
422
|
+
end
|
423
|
+
|
424
|
+
# Sets the hash which is used to keep track of which bytes to use for padding
|
425
|
+
# data elements of various vr which have an odd value length.
|
426
|
+
#
|
427
|
+
def set_pad_byte
|
428
|
+
@pad_byte = {
|
429
|
+
# Space character:
|
430
|
+
"AE" => "\x20",
|
431
|
+
"AS" => "\x20",
|
432
|
+
"CS" => "\x20",
|
433
|
+
"DA" => "\x20",
|
434
|
+
"DS" => "\x20",
|
435
|
+
"DT" => "\x20",
|
436
|
+
"IS" => "\x20",
|
437
|
+
"LO" => "\x20",
|
438
|
+
"LT" => "\x20",
|
439
|
+
"PN" => "\x20",
|
440
|
+
"SH" => "\x20",
|
441
|
+
"ST" => "\x20",
|
442
|
+
"TM" => "\x20",
|
443
|
+
"UT" => "\x20",
|
444
|
+
# Zero byte:
|
445
|
+
"AT" => "\x00",
|
446
|
+
"FL" => "\x00",
|
447
|
+
"FD" => "\x00",
|
448
|
+
"OB" => "\x00",
|
449
|
+
"OF" => "\x00",
|
450
|
+
"OW" => "\x00",
|
451
|
+
"SL" => "\x00",
|
452
|
+
"SQ" => "\x00",
|
453
|
+
"SS" => "\x00",
|
454
|
+
"UI" => "\x00",
|
455
|
+
"UL" => "\x00",
|
456
|
+
"UN" => "\x00",
|
457
|
+
"US" => "\x00"
|
458
|
+
}
|
459
|
+
end
|
460
|
+
|
461
|
+
# Sets the pack/unpack format strings that is used for encoding/decoding.
|
462
|
+
# Some of these depends on the endianness of the system and the String.
|
463
|
+
#
|
464
|
+
#--
|
465
|
+
# FIXME: Apparently the Ruby pack/unpack methods lacks a format for signed short
|
466
|
+
# and signed long in the network byte order. A solution needs to be found for this.
|
467
|
+
#
|
468
|
+
def set_string_formats
|
469
|
+
if @equal_endian
|
470
|
+
# Native byte order:
|
471
|
+
@us = "S*" # Unsigned short (2 bytes)
|
472
|
+
@ss = "s*" # Signed short (2 bytes)
|
473
|
+
@ul = "I*" # Unsigned long (4 bytes)
|
474
|
+
@sl = "l*" # Signed long (4 bytes)
|
475
|
+
@fs = "e*" # Floating point single (4 bytes)
|
476
|
+
@fd = "E*" # Floating point double ( 8 bytes)
|
477
|
+
else
|
478
|
+
# Network byte order:
|
479
|
+
@us = "n*"
|
480
|
+
@ss = "n*" # Not correct (gives US)
|
481
|
+
@ul = "N*"
|
482
|
+
@sl = "N*" # Not correct (gives UL)
|
483
|
+
@fs = "g*"
|
484
|
+
@fd = "G*"
|
485
|
+
end
|
486
|
+
# Format strings that are not dependent on endianness:
|
487
|
+
@by = "C*" # Unsigned char (1 byte)
|
488
|
+
@str = "a*"
|
489
|
+
@hex = "H*" # (this may be dependent on endianness(?))
|
490
|
+
end
|
491
|
+
|
492
|
+
end
|
493
|
+
end
|