dicom 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +47 -25
- data/DOCUMENTATION +72 -43
- data/README +20 -20
- data/lib/Anonymizer.rb +61 -56
- data/lib/DLibrary.rb +47 -24
- data/lib/DObject.rb +457 -461
- data/lib/DRead.rb +131 -130
- data/lib/DWrite.rb +87 -89
- data/lib/Dictionary.rb +14 -12
- data/lib/dicom.rb +1 -0
- data/lib/ruby_extensions.rb +25 -0
- metadata +12 -9
data/lib/DRead.rb
CHANGED
@@ -1,29 +1,26 @@
|
|
1
|
-
# Copyright 2008-2009 Christoffer
|
1
|
+
# Copyright 2008-2009 Christoffer Lervag
|
2
2
|
|
3
3
|
# Some notes about this DICOM file reading class:
|
4
|
-
# In addition to reading files that are compliant to DICOM 3 Part 10,
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# to take advantage of this information for advanced querying of the
|
10
|
-
# DICOM object afterwards.
|
4
|
+
# In addition to reading files that are compliant to DICOM 3 Part 10, the philosophy of this library
|
5
|
+
# is to have maximum compatibility, and as such it will read most 'DICOM' files that deviate from the standard.
|
6
|
+
# While reading files, this class will also analyse the hierarchy of elements for those DICOM files that
|
7
|
+
# feature sequences and items, enabling the user to take advantage of this information for advanced
|
8
|
+
# querying of the DICOM object afterwards.
|
11
9
|
|
12
10
|
module DICOM
|
13
11
|
# Class for reading the data from a DICOM file:
|
14
12
|
class DRead
|
15
13
|
|
16
|
-
attr_reader :success,:names,:
|
14
|
+
attr_reader :success,:names,:tags,:types,:lengths,:values,:raw,:levels,:explicit,:file_endian,:msg
|
17
15
|
|
18
16
|
# Initialize the DRead instance.
|
19
17
|
def initialize(file_name=nil, opts={})
|
20
18
|
# Process option values, setting defaults for the ones that are not specified:
|
21
19
|
@lib = opts[:lib] || DLibrary.new
|
22
20
|
@sys_endian = opts[:sys_endian] || false
|
23
|
-
|
24
21
|
# Initiate the variables that are used during file reading:
|
25
22
|
init_variables()
|
26
|
-
|
23
|
+
|
27
24
|
# Test if file is readable and open it to the @file variable:
|
28
25
|
open_file(file_name)
|
29
26
|
|
@@ -34,32 +31,35 @@ module DICOM
|
|
34
31
|
else
|
35
32
|
# Read and verify the DICOM header:
|
36
33
|
header = check_header()
|
37
|
-
# If
|
34
|
+
# If the file didnt have the expected header, we will attempt to read data elements from the very start of the file:
|
38
35
|
if header == false
|
39
36
|
@file.close()
|
40
37
|
@file = File.new(file_name, "rb")
|
41
38
|
@header_length = 0
|
39
|
+
elsif header == nil
|
40
|
+
# Not a valid DICOM file, return:
|
41
|
+
return
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
45
|
-
# Run a loop to read the
|
46
|
-
# (
|
47
|
-
|
48
|
-
while
|
49
|
-
|
44
|
+
|
45
|
+
# Run a loop to read the data elements:
|
46
|
+
# (Data element information is stored in arrays by the method process_data_element)
|
47
|
+
data_element = true
|
48
|
+
while data_element != false do
|
49
|
+
data_element = process_data_element()
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
# Post processing:
|
53
53
|
# Close the file as we are finished reading it:
|
54
54
|
@file.close()
|
55
55
|
# Assume file has been read successfully:
|
56
56
|
@success = true
|
57
|
-
# Check if the last
|
57
|
+
# Check if the last element was read out correctly (that the length of its data (@raw.last.length)
|
58
58
|
# corresponds to that expected by the length specified in the DICOM file (@lengths.last)).
|
59
|
-
# We only run this test if the last
|
59
|
+
# We only run this test if the last element has a positive expectation value, obviously.
|
60
60
|
if @lengths.last.to_i > 0
|
61
61
|
if @raw.last.length != @lengths.last
|
62
|
-
@msg += ["Error! The data content read from file does not match the length specified for the tag #{@
|
62
|
+
@msg += ["Error! The data content read from file does not match the length specified for the tag #{@tags.last}. It seems this is either an invalid or corrupt DICOM file. Returning."]
|
63
63
|
@success = false
|
64
64
|
return
|
65
65
|
end
|
@@ -78,16 +78,20 @@ module DICOM
|
|
78
78
|
# Apparently, some providers seems to skip this in their DICOM files.
|
79
79
|
bin1 = @file.read(128)
|
80
80
|
@header_length += 128
|
81
|
-
#filler = bin1.unpack('a' * 128).to_s
|
82
81
|
# Next 4 bytes should spell 'DICM':
|
83
82
|
bin2 = @file.read(4)
|
84
83
|
@header_length += 4
|
85
|
-
|
84
|
+
# Check if this binary was successfully read (if not, this short file is not a valid DICOM file and we will return):
|
85
|
+
if bin2
|
86
|
+
dicm = bin2.unpack('a' * 4).join
|
87
|
+
else
|
88
|
+
return nil
|
89
|
+
end
|
86
90
|
if dicm != 'DICM' then
|
87
91
|
# Header is not valid (we will still try to read it is a DICOM file though):
|
88
92
|
@msg += ["Warning: The specified file does not contain the official DICOM header. Will try to read the file anyway, as some sources are known to skip this header."]
|
89
93
|
# As the file is not conforming to the DICOM standard, it is possible that it does not contain a
|
90
|
-
# transfer syntax
|
94
|
+
# transfer syntax element, and as such, we attempt to choose the most probable encoding values here:
|
91
95
|
@explicit = false
|
92
96
|
return false
|
93
97
|
else
|
@@ -97,127 +101,125 @@ module DICOM
|
|
97
101
|
end # of method check_header
|
98
102
|
|
99
103
|
|
100
|
-
# Governs the process of reading
|
101
|
-
|
102
|
-
#going on here in all cases. Perhaps some day I will get the courage to have a go at it again.)
|
103
|
-
def process_tag()
|
104
|
+
# Governs the process of reading data elements from the DICOM file.
|
105
|
+
def process_data_element()
|
104
106
|
#STEP 1: ------------------------------------------------------
|
105
|
-
# Attempt to read tag
|
106
|
-
|
107
|
-
if
|
108
|
-
# End of file, no more
|
107
|
+
# Attempt to read data element tag, but abort if we have reached end of file:
|
108
|
+
tag = read_tag()
|
109
|
+
if tag == false
|
110
|
+
# End of file, no more elements.
|
109
111
|
return false
|
110
|
-
end
|
112
|
+
end
|
111
113
|
# STEP 2: ------------------------------------------------------
|
112
|
-
# Access library to retrieve the
|
113
|
-
lib_data = @lib.get_name_vr(
|
114
|
+
# Access library to retrieve the data element name and type (VR) from the tag we just read:
|
115
|
+
lib_data = @lib.get_name_vr(tag)
|
114
116
|
name = lib_data[0]
|
115
117
|
vr = lib_data[1]
|
116
118
|
# (Note: VR will be overwritten if the DICOM file contains VR)
|
117
|
-
|
119
|
+
|
118
120
|
# STEP 3: ----------------------------------------------------
|
119
|
-
# Read
|
120
|
-
tag_info = read_type_length(vr,
|
121
|
+
# Read type (VR) (if it exists) and the length value:
|
122
|
+
tag_info = read_type_length(vr,tag)
|
121
123
|
type = tag_info[0]
|
122
124
|
level_type = type
|
123
125
|
length = tag_info[1]
|
124
|
-
|
126
|
+
|
125
127
|
# STEP 4: ----------------------------------------
|
126
|
-
# Reading value of
|
128
|
+
# Reading value of data element.
|
127
129
|
# Special handling needed for items in encapsulated image data:
|
128
|
-
if @enc_image and
|
129
|
-
# The first item appearing after the image
|
130
|
+
if @enc_image and tag == "FFFE,E000"
|
131
|
+
# The first item appearing after the image element is a 'normal' item, the rest hold image data.
|
130
132
|
# Note that the first item will contain data if there are multiple images, and so must be read.
|
131
133
|
type = "OW" # how about alternatives like OB?
|
132
134
|
# Modify name of item if this is an item that holds pixel data:
|
133
|
-
if @
|
135
|
+
if @tags.last != "7FE0,0010"
|
134
136
|
name = "Pixel Data Item"
|
135
137
|
end
|
136
138
|
end
|
137
|
-
# Read the value of the
|
139
|
+
# Read the value of the element (if it contains data, and it is not a sequence or ordinary item):
|
138
140
|
if length.to_i > 0 and type != "SQ" and type != "()"
|
139
|
-
# Read the
|
140
|
-
|
141
|
-
value =
|
142
|
-
raw =
|
141
|
+
# Read the element's value (data):
|
142
|
+
data = read_value(type,length)
|
143
|
+
value = data[0]
|
144
|
+
raw = data[1]
|
143
145
|
else
|
144
|
-
#
|
145
|
-
# Special case: Check if pixel data
|
146
|
-
if
|
147
|
-
# Change name and type of pixel data
|
146
|
+
# Data element has no value (data).
|
147
|
+
# Special case: Check if pixel data element is sequenced:
|
148
|
+
if tag == "7FE0,0010"
|
149
|
+
# Change name and type of pixel data element if it does not contain data itself:
|
148
150
|
name = "Encapsulated Pixel Data"
|
149
151
|
level_type = "SQ"
|
150
152
|
@enc_image = true
|
151
153
|
end
|
152
154
|
end # of if length.to_i > 0
|
153
|
-
# Set the hiearchy level of this
|
154
|
-
set_level(level_type, length,
|
155
|
+
# Set the hiearchy level of this data element:
|
156
|
+
set_level(level_type, length, tag, name)
|
155
157
|
# Transfer the gathered data to arrays and return true:
|
156
158
|
@names += [name]
|
157
|
-
@
|
159
|
+
@tags += [tag]
|
158
160
|
@types += [type]
|
159
161
|
@lengths += [length]
|
160
162
|
@values += [value]
|
161
163
|
@raw += [raw]
|
162
164
|
return true
|
163
|
-
end # of method
|
165
|
+
end # of method process_data_element
|
164
166
|
|
165
167
|
|
166
|
-
# Reads and returns TAG
|
167
|
-
def
|
168
|
+
# Reads and returns the data element's TAG (4 first bytes of element).
|
169
|
+
def read_tag()
|
168
170
|
bin1 = @file.read(2)
|
169
171
|
bin2 = @file.read(2)
|
170
172
|
# Do not proceed if we have reached end of file:
|
171
173
|
if bin2 == nil
|
172
174
|
return false
|
173
175
|
end
|
174
|
-
# Add the length of the tag
|
176
|
+
# Add the length of the data element tag. If this was the first element read from file, we need to add the header length too:
|
175
177
|
if @integrated_lengths.length == 0
|
176
178
|
# Increase the array with the length of the header + the 4 bytes:
|
177
179
|
@integrated_lengths += [@header_length + 4]
|
178
180
|
else
|
179
|
-
# For the remaining
|
181
|
+
# For the remaining elements, increase the array with the integrated length of the previous elements + the 4 bytes:
|
180
182
|
@integrated_lengths += [@integrated_lengths[@integrated_lengths.length-1] + 4]
|
181
183
|
end
|
182
184
|
# Unpack the blobs:
|
183
|
-
|
184
|
-
|
185
|
+
tag1 = bin1.unpack('h*')[0].reverse.upcase
|
186
|
+
tag2 = bin2.unpack('h*')[0].reverse.upcase
|
185
187
|
# Whether DICOM file is big or little endian, the first 0002 group is always little endian encoded.
|
186
188
|
# In case of big endian system:
|
187
189
|
if @sys_endian
|
188
190
|
# Rearrange the numbers (# This has never been tested btw.):
|
189
|
-
|
190
|
-
|
191
|
+
tag1 = tag1[2..3]+tag1[0..1]
|
192
|
+
tag2 = tag2[2..3]+tag2[0..1]
|
191
193
|
end
|
192
194
|
# When we shift from group 0002 to another group we need to update our endian/explicitness variables:
|
193
|
-
if
|
195
|
+
if tag1 != "0002" and @switched == false
|
194
196
|
switch_syntax()
|
195
197
|
end
|
196
|
-
# Perhaps we need to rearrange the
|
198
|
+
# Perhaps we need to rearrange the tag strings?
|
197
199
|
if not @endian
|
198
200
|
# Need to rearrange the first and second part of each string:
|
199
|
-
|
200
|
-
|
201
|
+
tag1 = tag1[2..3]+tag1[0..1]
|
202
|
+
tag2 = tag2[2..3]+tag2[0..1]
|
201
203
|
end
|
202
|
-
# Join the
|
203
|
-
return
|
204
|
-
end # of method
|
204
|
+
# Join the tag group & element part together to form the final, complete string:
|
205
|
+
return tag1+","+tag2
|
206
|
+
end # of method read_tag
|
205
207
|
|
206
208
|
|
207
|
-
# Reads and returns
|
208
|
-
def read_type_length(type,
|
209
|
+
# Reads and returns data element TYPE (VR) (2 bytes) and data element LENGTH (Varying length).
|
210
|
+
def read_type_length(type,tag)
|
209
211
|
# Structure will differ, dependent on whether we have explicit or implicit encoding:
|
210
212
|
# *****EXPLICIT*****:
|
211
213
|
if @explicit == true
|
212
214
|
# Step 1: Read VR (if it exists)
|
213
|
-
unless
|
214
|
-
# Read
|
215
|
+
unless tag == "FFFE,E000" or tag == "FFFE,E00D" or tag == "FFFE,E0DD"
|
216
|
+
# Read the element's type (2 bytes - since we are not dealing with an item related element):
|
215
217
|
bin = @file.read(2)
|
216
218
|
@integrated_lengths[@integrated_lengths.length-1] += 2
|
217
|
-
type = bin.unpack('a*').
|
219
|
+
type = bin.unpack('a*').join
|
218
220
|
end
|
219
221
|
# Step 2: Read length
|
220
|
-
# Three possible structures for value length here, dependent on
|
222
|
+
# Three possible structures for value length here, dependent on element type:
|
221
223
|
case type
|
222
224
|
when "OB","OW","SQ","UN"
|
223
225
|
# 6 bytes total:
|
@@ -230,49 +232,48 @@ module DICOM
|
|
230
232
|
length = bin.unpack(@ul)[0]
|
231
233
|
when "()"
|
232
234
|
# 4 bytes:
|
233
|
-
# For
|
235
|
+
# For elements "FFFE,E000", "FFFE,E00D" and "FFFE,E0DD"
|
234
236
|
bin = @file.read(4)
|
235
237
|
@integrated_lengths[@integrated_lengths.length-1] += 4
|
236
238
|
length = bin.unpack(@ul)[0]
|
237
239
|
else
|
238
240
|
# 2 bytes:
|
239
|
-
# For all the other
|
241
|
+
# For all the other element types, value length is 2 bytes:
|
240
242
|
bin = @file.read(2)
|
241
243
|
@integrated_lengths[@integrated_lengths.length-1] += 2
|
242
244
|
length = bin.unpack(@us)[0]
|
243
245
|
end
|
244
246
|
else
|
245
247
|
# *****IMPLICIT*****:
|
246
|
-
# No VR (retrieved from library based on the
|
248
|
+
# No VR (retrieved from library based on the data element's tag)
|
247
249
|
# Reading value length (4 bytes):
|
248
250
|
bin = @file.read(4)
|
249
251
|
@integrated_lengths[@integrated_lengths.length-1] += 4
|
250
252
|
length = bin.unpack(@ul)[0]
|
251
253
|
end
|
252
|
-
# For encapsulated data, the
|
254
|
+
# For encapsulated data, the element length will not be defined. To convey this,
|
253
255
|
# the hex sequence 'ff ff ff ff' is used (-1 converted to signed long, 4294967295 converted to unsigned long).
|
254
256
|
if length == 4294967295
|
255
257
|
length = @undef
|
256
258
|
elsif length%2 >0
|
257
|
-
# According to the DICOM standard, all
|
259
|
+
# According to the DICOM standard, all data element lengths should be an even number.
|
258
260
|
# If it is not, it may indicate a file that is not standards compliant or it might even not be a DICOM file.
|
259
|
-
@msg += ["Warning: Odd number of bytes in
|
261
|
+
@msg += ["Warning: Odd number of bytes in data element's length occured. This is a violation of the DICOM standard, but program will attempt to read the rest of the file anyway."]
|
260
262
|
end
|
261
263
|
return [type, length]
|
262
264
|
end # of method read_type_length
|
263
265
|
|
264
266
|
|
265
|
-
# Reads and returns
|
266
|
-
def
|
267
|
+
# Reads and returns data element VALUE (Of varying length - which is determined at an earlier stage).
|
268
|
+
def read_value(type, length)
|
267
269
|
# Read the data:
|
268
270
|
bin = @file.read(length)
|
269
271
|
@integrated_lengths[@integrated_lengths.size-1] += length
|
270
272
|
# Decoding of content will naturally depend on what kind of content (VR) we have.
|
271
273
|
case type
|
272
274
|
|
273
|
-
# Normally the "number
|
274
|
-
#
|
275
|
-
# them all in a string separated by "/".
|
275
|
+
# Normally the "number elements" will contain just one number, but in some cases, they contain
|
276
|
+
# multiple numbers. In these cases we will read each number and store them all in a string separated by "/".
|
276
277
|
# Unsigned long: (4 bytes)
|
277
278
|
when "UL"
|
278
279
|
if length <= 4
|
@@ -321,33 +322,33 @@ module DICOM
|
|
321
322
|
data = bin.unpack(@fd).join("/")
|
322
323
|
end
|
323
324
|
|
324
|
-
# The
|
325
|
+
# The data element contains a tag as its value (4 bytes):
|
325
326
|
when "AT"
|
326
327
|
# Bytes read in following order: 1 0 , 3 2 (And Hex nibbles read in this order: Hh)
|
327
328
|
# NB! This probably needs to be modified when dealing with something other than little endian.
|
328
|
-
#
|
329
|
+
# Value is unpacked to a string in the format GGGGEEEE.
|
329
330
|
data = (bin.unpack("xHXhX2HXh").join + bin.unpack("x3HXhX2HXh").join).upcase
|
330
331
|
#data = (bin.unpack("xHXhX2HXh").join + "," + bin.unpack("x3HXhX2HXh").join).upcase
|
331
332
|
|
332
333
|
# We have a number of VRs that are decoded as string:
|
333
334
|
when 'AE','AS','CS','DA','DS','DT','IS','LO','LT','PN','SH','ST','TM','UI','UT' #,'VR'
|
334
|
-
data = bin.unpack('a*').
|
335
|
-
|
336
|
-
# NB!
|
337
|
-
# FOLLOWING
|
335
|
+
data = bin.unpack('a*').join
|
336
|
+
|
337
|
+
# NB!
|
338
|
+
# FOLLOWING ELEMENT TYPES WILL NOT BE DECODED.
|
338
339
|
# DECODING OF PIXEL DATA IS MOVED TO DOBJECT FOR PERFORMANCE REASONS.
|
339
|
-
|
340
|
-
# Unknown information, header element is not
|
340
|
+
|
341
|
+
# Unknown information, header element is not recognized from local database:
|
341
342
|
when "UN"
|
342
343
|
#data=bin.unpack('H*')[0]
|
343
344
|
|
344
345
|
# Other byte string, 1-byte integers
|
345
346
|
when "OB"
|
346
347
|
#data = bin.unpack('H*')[0]
|
347
|
-
|
348
|
+
|
348
349
|
# Other float string, 4-byte floating point numbers
|
349
350
|
when "OF"
|
350
|
-
# NB! This
|
351
|
+
# NB! This element type has not been tested yet with an actual DICOM file.
|
351
352
|
#data = bin.unpack(@fs)
|
352
353
|
|
353
354
|
# Image data:
|
@@ -357,26 +358,26 @@ module DICOM
|
|
357
358
|
|
358
359
|
# Unknown VR:
|
359
360
|
else
|
360
|
-
@msg += ["Warning:
|
361
|
+
@msg += ["Warning: Element type #{type} does not have a reading method assigned to it. Please check the validity of the DICOM file."]
|
361
362
|
#data = bin.unpack('H*')[0]
|
362
363
|
end # of case type
|
363
|
-
|
364
|
+
|
364
365
|
# Return the data:
|
365
366
|
return [data, bin]
|
366
|
-
end # of method
|
367
|
+
end # of method read_value
|
367
368
|
|
368
369
|
|
369
|
-
# Sets the level of the current
|
370
|
+
# Sets the level of the current element in the hiearchy.
|
370
371
|
# The default (top) level is zero.
|
371
|
-
def set_level(type, length,
|
372
|
-
# Set the level of this
|
372
|
+
def set_level(type, length, tag, name)
|
373
|
+
# Set the level of this element:
|
373
374
|
@levels += [@current_level]
|
374
|
-
# Determine if there is a level change for the following
|
375
|
-
# If
|
376
|
-
# If
|
375
|
+
# Determine if there is a level change for the following element:
|
376
|
+
# If element is a sequence, the level of the following elements will be increased by one.
|
377
|
+
# If element is an item, the level of the following elements will likewise be increased by one.
|
377
378
|
# Note the following exception:
|
378
|
-
# If
|
379
|
-
# not in its sub-
|
379
|
+
# If data element is an "Item", and it contains data (image fragment) directly, which is to say,
|
380
|
+
# not in its sub-elements, we should not increase the level. (This is fixed in the process_data_element method.)
|
380
381
|
if type == "SQ"
|
381
382
|
increase = true
|
382
383
|
elsif name == "Item"
|
@@ -386,7 +387,7 @@ module DICOM
|
|
386
387
|
end
|
387
388
|
if increase == true
|
388
389
|
@current_level = @current_level + 1
|
389
|
-
# If length of sequence/item is specified, we must note this length + the current
|
390
|
+
# If length of sequence/item is specified, we must note this length + the current element position in the arrays:
|
390
391
|
if length.to_i != 0
|
391
392
|
@hierarchy += [[length,@integrated_lengths.last]]
|
392
393
|
else
|
@@ -395,7 +396,7 @@ module DICOM
|
|
395
396
|
end
|
396
397
|
# Need to check whether a previous sequence or item has ended, if so the level must be decreased by one:
|
397
398
|
# In the case of tag specification:
|
398
|
-
if (
|
399
|
+
if (tag == "FFFE,E00D") or (tag == "FFFE,E0DD")
|
399
400
|
@current_level = @current_level - 1
|
400
401
|
end
|
401
402
|
# In the case of sequence and item length specification:
|
@@ -405,15 +406,15 @@ module DICOM
|
|
405
406
|
if @hierarchy.size > 0
|
406
407
|
# Do not perform this check for Pixel Data Items or Sequence Delimitation Items:
|
407
408
|
# (If performed, it will give false errors for the case when we have Encapsulated Pixel Data)
|
408
|
-
check_level_end() unless name == "Pixel Data Item" or
|
409
|
+
check_level_end() unless name == "Pixel Data Item" or tag == "FFFE,E0DD"
|
409
410
|
end
|
410
411
|
end # of method set_level
|
411
|
-
|
412
|
-
|
412
|
+
|
413
|
+
|
413
414
|
# Checks how far we've read in the DICOM file to determine if we have reached a point
|
414
415
|
# where sub-levels are ending. This method is recursive, as multiple sequences/items might end at the same point.
|
415
416
|
def check_level_end()
|
416
|
-
# The test is only meaningful to perform if we are not expecting an 'end of sequence/item'
|
417
|
+
# The test is only meaningful to perform if we are not expecting an 'end of sequence/item' element to signal the level-change.
|
417
418
|
if (@hierarchy.last).is_a?(Array)
|
418
419
|
described_length = (@hierarchy.last)[0]
|
419
420
|
previous_length = (@hierarchy.last)[1]
|
@@ -433,7 +434,7 @@ module DICOM
|
|
433
434
|
elsif current_diff > described_length
|
434
435
|
# Only register this type of error one time per file to avoid a spamming effect:
|
435
436
|
if not @hierarchy_error
|
436
|
-
@msg += ["Unexpected hierarchy incident: Current length difference is greater than the expected value, which should not occur. This will not pose any problems unless you intend to query the object for
|
437
|
+
@msg += ["Unexpected hierarchy incident: Current length difference is greater than the expected value, which should not occur. This will not pose any problems unless you intend to query the object for elements based on hierarchy."]
|
437
438
|
@hierarchy_error = true
|
438
439
|
end
|
439
440
|
end
|
@@ -465,7 +466,7 @@ module DICOM
|
|
465
466
|
|
466
467
|
# Changes encoding variables as the file reading proceeds past the initial 0002 group of the DICOM file.
|
467
468
|
def switch_syntax()
|
468
|
-
# The information read from the Transfer syntax
|
469
|
+
# The information read from the Transfer syntax element (if present), needs to be processed:
|
469
470
|
process_transfer_syntax()
|
470
471
|
# We only plan to run this method once:
|
471
472
|
@switched = true
|
@@ -481,12 +482,12 @@ module DICOM
|
|
481
482
|
end
|
482
483
|
|
483
484
|
|
484
|
-
# Checks the Transfer Syntax UID
|
485
|
+
# Checks the Transfer Syntax UID element and updates class variables to prepare for correct reading of DICOM file.
|
485
486
|
# A lot of code here is duplicated in DWrite class. Should move as much of this code as possible to DLibrary I think.
|
486
487
|
def process_transfer_syntax()
|
487
|
-
ts_pos = @
|
488
|
+
ts_pos = @tags.index("0002,0010")
|
488
489
|
if ts_pos != nil
|
489
|
-
ts_value = @raw[ts_pos].unpack('a*').
|
490
|
+
ts_value = @raw[ts_pos].unpack('a*').join.rstrip
|
490
491
|
valid = @lib.check_ts_validity(ts_value)
|
491
492
|
if not valid
|
492
493
|
@msg+=["Warning: Invalid/unknown transfer syntax! Will try reading the file, but errors may occur."]
|
@@ -548,9 +549,9 @@ module DICOM
|
|
548
549
|
# Initiates the variables that are used during file reading.
|
549
550
|
def init_variables()
|
550
551
|
# Variables that hold data that will be available to the DObject class.
|
551
|
-
# Arrays that will hold information from the
|
552
|
+
# Arrays that will hold information from the elements of the DICOM file:
|
552
553
|
@names = Array.new()
|
553
|
-
@
|
554
|
+
@tags = Array.new()
|
554
555
|
@types = Array.new()
|
555
556
|
@lengths = Array.new()
|
556
557
|
@values = Array.new()
|
@@ -566,13 +567,13 @@ module DICOM
|
|
566
567
|
@file_endian = false
|
567
568
|
# Variable used to tell whether file was read succesfully or not:
|
568
569
|
@success = false
|
569
|
-
|
570
|
+
|
570
571
|
# Variables used internally when reading through the DICOM file:
|
571
|
-
# Array for keeping track of how many bytes have been read from the file up to and including each
|
572
|
+
# Array for keeping track of how many bytes have been read from the file up to and including each data element:
|
572
573
|
# (This is necessary for tracking the hiearchy in some DICOM files)
|
573
574
|
@integrated_lengths = Array.new()
|
574
575
|
@header_length = 0
|
575
|
-
# Array to keep track of the hierarchy of
|
576
|
+
# Array to keep track of the hierarchy of elements (this will be used to determine when a sequence or item is finished):
|
576
577
|
@hierarchy = Array.new()
|
577
578
|
@hierarchy_error = false
|
578
579
|
# Explicitness of the remaining groups after the initial 0002 group:
|
@@ -589,15 +590,15 @@ module DICOM
|
|
589
590
|
end
|
590
591
|
# Set which format strings to use when unpacking numbers:
|
591
592
|
set_unpack_strings
|
592
|
-
# A length variable will be used at the end to check whether the last
|
593
|
+
# A length variable will be used at the end to check whether the last element was read correctly, or whether the file endend unexpectedly:
|
593
594
|
@data_length = 0
|
594
|
-
# Keeping track of the
|
595
|
+
# Keeping track of the data element's level while reading through the file:
|
595
596
|
@current_level = 0
|
596
597
|
# This variable's string will be inserted as the length of items/sq that dont have a specified length:
|
597
598
|
@undef = "UNDEFINED"
|
598
|
-
# Items contained under the pixel data
|
599
|
+
# Items contained under the pixel data element may contain data directly, so we need a variable to keep track of this:
|
599
600
|
@enc_image = false
|
600
601
|
end
|
601
602
|
|
602
603
|
end # End of class
|
603
|
-
end # End of module
|
604
|
+
end # End of module
|