nifti 0.0.1
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/.gitignore +6 -0
- data/CHANGELOG +13 -0
- data/COPYING +674 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +30 -0
- data/README.markdown +126 -0
- data/Rakefile +2 -0
- data/lib/nifti/constants.rb +223 -0
- data/lib/nifti/n_object.rb +155 -0
- data/lib/nifti/n_read.rb +264 -0
- data/lib/nifti/n_write.rb +142 -0
- data/lib/nifti/stream.rb +373 -0
- data/lib/nifti/version.rb +4 -0
- data/lib/nifti.rb +23 -0
- data/nifti.gemspec +24 -0
- data/spec/custom_matchers.rb +13 -0
- data/spec/fixtures/3plLoc.nii +0 -0
- data/spec/interactive/compare.rb +43 -0
- data/spec/nifti/n_object_spec.rb +111 -0
- data/spec/nifti/n_read_spec.rb +89 -0
- data/spec/nifti/n_write_spec.rb +56 -0
- data/spec/nifti/stream_spec.rb +44 -0
- data/spec/spec_helper.rb +13 -0
- metadata +138 -0
data/lib/nifti/stream.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
module NIFTI
|
2
|
+
# The Stream class handles string operations (encoding to and decoding from binary strings).
|
3
|
+
#
|
4
|
+
class Stream
|
5
|
+
# A boolean which reports the relationship between the endianness of the system and the instance string.
|
6
|
+
attr_reader :equal_endian
|
7
|
+
# Our current position in the instance string (used only for decoding).
|
8
|
+
attr_accessor :index
|
9
|
+
# The instance string.
|
10
|
+
attr_accessor :string
|
11
|
+
# The endianness of the instance string.
|
12
|
+
attr_reader :str_endian
|
13
|
+
# An array of warning/error messages that (may) have been accumulated.
|
14
|
+
attr_reader :errors
|
15
|
+
# A hash of proper strings (based on endianess) to use for unpacking binary strings.
|
16
|
+
attr_reader :format
|
17
|
+
# A File object to write to
|
18
|
+
attr_accessor :file
|
19
|
+
|
20
|
+
# Creates a Stream instance.
|
21
|
+
#
|
22
|
+
# === Parameters
|
23
|
+
#
|
24
|
+
# * <tt>binary</tt> -- A binary string.
|
25
|
+
# * <tt>string_endian</tt> -- Boolean. The endianness of the instance string (true for big endian, false for small endian).
|
26
|
+
# * <tt>options</tt> -- A hash of parameters.
|
27
|
+
#
|
28
|
+
# === Options
|
29
|
+
#
|
30
|
+
# * <tt>:index</tt> -- Fixnum. A position (offset) in the instance string where reading will start.
|
31
|
+
#
|
32
|
+
def initialize(binary, string_endian, options={})
|
33
|
+
@string = binary || ""
|
34
|
+
@index = options[:index] || 0
|
35
|
+
@errors = Array.new
|
36
|
+
self.endian = string_endian
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
# Decodes a section of the instance string and returns the formatted data.
|
42
|
+
# The instance index is offset in accordance with the length read.
|
43
|
+
#
|
44
|
+
# === Notes
|
45
|
+
#
|
46
|
+
# * If multiple numbers are decoded, these are returned in an array.
|
47
|
+
#
|
48
|
+
# === Parameters
|
49
|
+
#
|
50
|
+
# * <tt>length</tt> -- Fixnum. The string length which will be decoded.
|
51
|
+
# * <tt>type</tt> -- String. The type (vr) of data to decode.
|
52
|
+
#
|
53
|
+
def decode(length, type)
|
54
|
+
# Check if values are valid:
|
55
|
+
if (@index + length) > @string.length
|
56
|
+
# The index number is bigger then the length of the binary string.
|
57
|
+
# We have reached the end and will return nil.
|
58
|
+
value = nil
|
59
|
+
else
|
60
|
+
if type == "AT"
|
61
|
+
value = decode_tag
|
62
|
+
else
|
63
|
+
# Decode the binary string and return value:
|
64
|
+
value = @string.slice(@index, length).unpack(vr_to_str(type))
|
65
|
+
# If the result is an array of one element, return the element instead of the array.
|
66
|
+
# If result is contained in a multi-element array, the original array is returned.
|
67
|
+
if value.length == 1
|
68
|
+
value = value[0]
|
69
|
+
# If value is a string, strip away possible trailing whitespace:
|
70
|
+
# Do this using gsub instead of ruby-core #strip to keep trailing carriage
|
71
|
+
# returns, etc., because that is valid whitespace that we want to have included
|
72
|
+
# (i.e. in extended header data, AFNI writes a \n at the end of it's xml info,
|
73
|
+
# and that must be kept in order to not change the file on writing back out).
|
74
|
+
value.gsub!(/\000*$/, "") if value.respond_to? :gsub!
|
75
|
+
end
|
76
|
+
# Update our position in the string:
|
77
|
+
skip(length)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return value
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the length of the binary instance string.
|
84
|
+
#
|
85
|
+
def length
|
86
|
+
return @string.length
|
87
|
+
end
|
88
|
+
|
89
|
+
# Calculates and returns the remaining length of the instance string (from the index position).
|
90
|
+
#
|
91
|
+
def rest_length
|
92
|
+
length = @string.length - @index
|
93
|
+
return length
|
94
|
+
end
|
95
|
+
|
96
|
+
# Extracts and returns the remaining part of the instance string (from the index position to the end of the string).
|
97
|
+
#
|
98
|
+
def rest_string
|
99
|
+
str = @string[@index..(@string.length-1)]
|
100
|
+
return str
|
101
|
+
end
|
102
|
+
|
103
|
+
# Resets the instance string and index.
|
104
|
+
#
|
105
|
+
def reset
|
106
|
+
@string = ""
|
107
|
+
@index = 0
|
108
|
+
end
|
109
|
+
|
110
|
+
# Resets the instance index.
|
111
|
+
#
|
112
|
+
def reset_index
|
113
|
+
@index = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sets an instance file variable.
|
117
|
+
#
|
118
|
+
# === Notes
|
119
|
+
#
|
120
|
+
# For performance reasons, we enable the Stream instance to write directly to file,
|
121
|
+
# to avoid expensive string operations which will otherwise slow down the write performance.
|
122
|
+
#
|
123
|
+
# === Parameters
|
124
|
+
#
|
125
|
+
# * <tt>file</tt> -- A File instance.
|
126
|
+
#
|
127
|
+
def set_file(file)
|
128
|
+
@file = file
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets a new instance string, and resets the index variable.
|
132
|
+
#
|
133
|
+
# === Parameters
|
134
|
+
#
|
135
|
+
# * <tt>binary</tt> -- A binary string.
|
136
|
+
#
|
137
|
+
def set_string(binary)
|
138
|
+
binary = binary[0] if binary.is_a?(Array)
|
139
|
+
@string = binary
|
140
|
+
@index = 0
|
141
|
+
end
|
142
|
+
|
143
|
+
# Applies an offset (positive or negative) to the instance index.
|
144
|
+
#
|
145
|
+
# === Parameters
|
146
|
+
#
|
147
|
+
# * <tt>offset</tt> -- Fixnum. The length to skip (positive) or rewind (negative).
|
148
|
+
#
|
149
|
+
def skip(offset)
|
150
|
+
@index += offset
|
151
|
+
end
|
152
|
+
|
153
|
+
# Converts a data type/vr to an encode/decode string used by the pack/unpack methods, which is returned.
|
154
|
+
#
|
155
|
+
# === Parameters
|
156
|
+
#
|
157
|
+
# * <tt>vr</tt> -- String. A data type (value representation).
|
158
|
+
#
|
159
|
+
def vr_to_str(vr)
|
160
|
+
unless @format[vr]
|
161
|
+
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."
|
162
|
+
return @hex
|
163
|
+
else
|
164
|
+
return @format[vr]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Sets an instance file variable.
|
169
|
+
#
|
170
|
+
# === Notes
|
171
|
+
#
|
172
|
+
# For performance reasons, we enable the Stream instance to write directly to file,
|
173
|
+
# to avoid expensive string operations which will otherwise slow down the write performance.
|
174
|
+
#
|
175
|
+
# === Parameters
|
176
|
+
#
|
177
|
+
# * <tt>file</tt> -- A File instance.
|
178
|
+
#
|
179
|
+
def set_file(file)
|
180
|
+
@file = file
|
181
|
+
end
|
182
|
+
|
183
|
+
# Writes a binary string to the File instance.
|
184
|
+
#
|
185
|
+
# === Parameters
|
186
|
+
#
|
187
|
+
# * <tt>binary</tt> -- A binary string.
|
188
|
+
#
|
189
|
+
def write(binary)
|
190
|
+
@file.write(binary)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Encodes a value and returns the resulting binary string.
|
194
|
+
#
|
195
|
+
# === Parameters
|
196
|
+
#
|
197
|
+
# * <tt>value</tt> -- A custom value (String, Fixnum, etc..) or an array of numbers.
|
198
|
+
# * <tt>type</tt> -- String. The type (vr) of data to encode.
|
199
|
+
#
|
200
|
+
def encode(value, type)
|
201
|
+
value = [value] unless value.is_a?(Array)
|
202
|
+
return value.pack(vr_to_str(type))
|
203
|
+
end
|
204
|
+
|
205
|
+
# Appends a string with trailling spaces to achieve a target length, and encodes it to a binary string.
|
206
|
+
# Returns the binary string. Raises an error if pad option is different than :null or :spaces
|
207
|
+
#
|
208
|
+
# === Parameters
|
209
|
+
#
|
210
|
+
# * <tt>string</tt> -- A string to be processed.
|
211
|
+
# * <tt>target_length</tt> -- Fixnum. The target length of the string that is created.
|
212
|
+
# * <tt>pad</tt> -- Type of desired padding, either :null or :spaces
|
213
|
+
#
|
214
|
+
def encode_string_to_length(string, target_length, pad = :null)
|
215
|
+
if pad == :spaces
|
216
|
+
template = "A#{target_length}"
|
217
|
+
elsif pad == :null
|
218
|
+
template = "a#{target_length}"
|
219
|
+
else
|
220
|
+
raise StandardError, "Could not identify padding type #{pad}"
|
221
|
+
end
|
222
|
+
|
223
|
+
length = string.length
|
224
|
+
if length < target_length
|
225
|
+
return [string].pack(template)
|
226
|
+
elsif length == target_length
|
227
|
+
return [string].pack(@str)
|
228
|
+
else
|
229
|
+
raise "The specified string is longer than the allowed maximum length (String: #{string}, Target length: #{target_length})."
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Following methods are private:
|
234
|
+
private
|
235
|
+
|
236
|
+
|
237
|
+
# Determines the relationship between system and string endianness, and sets the instance endian variable.
|
238
|
+
#
|
239
|
+
def configure_endian
|
240
|
+
if CPU_ENDIAN == @str_endian
|
241
|
+
@equal_endian = true
|
242
|
+
else
|
243
|
+
@equal_endian = false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Sets the pack/unpack format strings that is used for encoding/decoding.
|
248
|
+
# Some of these depends on the endianness of the system and the String.
|
249
|
+
#
|
250
|
+
#--
|
251
|
+
# Note: Surprisingly the Ruby pack/unpack methods lack a format for signed short
|
252
|
+
# and signed long in the network byte order. A hack has been implemented to to ensure
|
253
|
+
# correct behaviour in this case, but it is slower (~4 times slower than a normal pack/unpack).
|
254
|
+
#
|
255
|
+
def set_string_formats
|
256
|
+
if @equal_endian
|
257
|
+
# Native byte order:
|
258
|
+
@us = "S*" # Unsigned short (2 bytes)
|
259
|
+
@ss = "s*" # Signed short (2 bytes)
|
260
|
+
@ul = "I*" # Unsigned long (4 bytes)
|
261
|
+
@sl = "l*" # Signed long (4 bytes)
|
262
|
+
@fs = "e*" # Floating point single (4 bytes)
|
263
|
+
@fd = "E*" # Floating point double ( 8 bytes)
|
264
|
+
else
|
265
|
+
# Network byte order:
|
266
|
+
@us = "n*"
|
267
|
+
@ss = CUSTOM_SS # Custom string for our redefined pack/unpack.
|
268
|
+
@ul = "N*"
|
269
|
+
@sl = CUSTOM_SL # Custom string for our redefined pack/unpack.
|
270
|
+
@fs = "g*"
|
271
|
+
@fd = "G*"
|
272
|
+
end
|
273
|
+
# Format strings that are not dependent on endianness:
|
274
|
+
@by = "C*" # Unsigned char (1 byte)
|
275
|
+
@str = "a*"
|
276
|
+
@hex = "H*" # (this may be dependent on endianness(?))
|
277
|
+
end
|
278
|
+
|
279
|
+
# Sets the hash which is used to convert data element types (VR) to
|
280
|
+
# encode/decode strings accepted by the pack/unpack methods.
|
281
|
+
#
|
282
|
+
def set_format_hash
|
283
|
+
@format = {
|
284
|
+
"BY" => @by, # Byte/Character (1-byte integers)
|
285
|
+
"US" => @us, # Unsigned short (2 bytes)
|
286
|
+
"SS" => @ss, # Signed short (2 bytes)
|
287
|
+
"UL" => @ul, # Unsigned long (4 bytes)
|
288
|
+
"SL" => @sl, # Signed long (4 bytes)
|
289
|
+
"FL" => @fs, # Floating point single (4 bytes)
|
290
|
+
"FD" => @fd, # Floating point double (8 bytes)
|
291
|
+
"OB" => @by, # Other byte string (1-byte integers)
|
292
|
+
"OF" => @fs, # Other float string (4-byte floating point numbers)
|
293
|
+
"OW" => @us, # Other word string (2-byte integers)
|
294
|
+
"AT" => @hex, # Tag reference (4 bytes) NB: This may need to be revisited at some point...
|
295
|
+
"UN" => @hex, # Unknown information (header element is not recognized from local database)
|
296
|
+
"HEX" => @hex, # HEX
|
297
|
+
# We have a number of VRs that are decoded as string:
|
298
|
+
"AE" => @str,
|
299
|
+
"AS" => @str,
|
300
|
+
"CS" => @str,
|
301
|
+
"DA" => @str,
|
302
|
+
"DS" => @str,
|
303
|
+
"DT" => @str,
|
304
|
+
"IS" => @str,
|
305
|
+
"LO" => @str,
|
306
|
+
"LT" => @str,
|
307
|
+
"PN" => @str,
|
308
|
+
"SH" => @str,
|
309
|
+
"ST" => @str,
|
310
|
+
"TM" => @str,
|
311
|
+
"UI" => @str,
|
312
|
+
"UT" => @str,
|
313
|
+
"STR" => @str
|
314
|
+
}
|
315
|
+
end
|
316
|
+
|
317
|
+
# Sets the hash which is used to keep track of which bytes to use for padding
|
318
|
+
# data elements of various vr which have an odd value length.
|
319
|
+
#
|
320
|
+
def set_pad_byte
|
321
|
+
@pad_byte = {
|
322
|
+
# Space character:
|
323
|
+
"AE" => "\x20",
|
324
|
+
"AS" => "\x20",
|
325
|
+
"CS" => "\x20",
|
326
|
+
"DA" => "\x20",
|
327
|
+
"DS" => "\x20",
|
328
|
+
"DT" => "\x20",
|
329
|
+
"IS" => "\x20",
|
330
|
+
"LO" => "\x20",
|
331
|
+
"LT" => "\x20",
|
332
|
+
"PN" => "\x20",
|
333
|
+
"SH" => "\x20",
|
334
|
+
"ST" => "\x20",
|
335
|
+
"TM" => "\x20",
|
336
|
+
"UT" => "\x20",
|
337
|
+
# Zero byte:
|
338
|
+
"AT" => "\x00",
|
339
|
+
"FL" => "\x00",
|
340
|
+
"FD" => "\x00",
|
341
|
+
"OB" => "\x00",
|
342
|
+
"OF" => "\x00",
|
343
|
+
"OW" => "\x00",
|
344
|
+
"SL" => "\x00",
|
345
|
+
"SQ" => "\x00",
|
346
|
+
"SS" => "\x00",
|
347
|
+
"UI" => "\x00",
|
348
|
+
"UL" => "\x00",
|
349
|
+
"UN" => "\x00",
|
350
|
+
"US" => "\x00"
|
351
|
+
}
|
352
|
+
@pad_byte.default = "\20"
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
# Sets the endianness of the instance string. The relationship between the string endianness and
|
357
|
+
# the system endianness, determines which encoding/decoding flags to use.
|
358
|
+
#
|
359
|
+
# === Parameters
|
360
|
+
#
|
361
|
+
# * <tt>string_endian</tt> -- Boolean. The endianness of the instance string (true for big endian, false for small endian).
|
362
|
+
#
|
363
|
+
def endian=(string_endian)
|
364
|
+
@str_endian = string_endian
|
365
|
+
configure_endian
|
366
|
+
set_string_formats
|
367
|
+
set_format_hash
|
368
|
+
set_pad_byte
|
369
|
+
end
|
370
|
+
|
371
|
+
end
|
372
|
+
|
373
|
+
end
|
data/lib/nifti.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# Loads the files that are used by Ruby NIFTI.
|
4
|
+
#
|
5
|
+
# The following classes are meant to be used by users of Ruby DICOM:
|
6
|
+
# * NObject - for reading, manipulating and writing DICOM files.
|
7
|
+
|
8
|
+
# NIFTI is the main namespace for all Ruby NIfTI classes, constants and methods.
|
9
|
+
module NIFTI; end
|
10
|
+
|
11
|
+
# Core library:
|
12
|
+
require 'nifti/n_object'
|
13
|
+
require 'nifti/n_read'
|
14
|
+
require 'nifti/n_write'
|
15
|
+
require 'nifti/stream'
|
16
|
+
require 'nifti/constants'
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'narray'
|
20
|
+
rescue LoadError => e
|
21
|
+
puts "NArray requried for some image visualization options."
|
22
|
+
puts "Run 'gem install narray' or 'bundle install' to get it."
|
23
|
+
end
|
data/nifti.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "nifti/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "nifti"
|
7
|
+
s.version = NIFTI::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Erik Kastman"]
|
10
|
+
s.email = ["ekk@medicine.wisc.edu"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A pure Ruby API to the NIfTI Neuroimaging Format}
|
13
|
+
s.description = %q{A pure Ruby API to the NIfTI Neuroimaging Format}
|
14
|
+
|
15
|
+
s.rubyforge_project = "nifti"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "mocha"
|
23
|
+
s.add_development_dependency "narray"
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
# Source: Matt Wynne; https://gist.github.com/736421
|
4
|
+
RSpec::Matchers.define(:be_same_file_as) do |exected_file_path|
|
5
|
+
match do |actual_file_path|
|
6
|
+
md5_hash(actual_file_path).should == md5_hash(exected_file_path)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Calculate an md5 hash from a file path
|
10
|
+
def md5_hash(file_path)
|
11
|
+
Digest::MD5.hexdigest(File.read(file_path))
|
12
|
+
end
|
13
|
+
end
|
Binary file
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Reopen File to add a diff calculator for investigating byte differences between fixtures and created files.
|
2
|
+
class File
|
3
|
+
# Checks if two files contain the same contents. Prints and returns a
|
4
|
+
# position and values of differences, or returns nil if there were no
|
5
|
+
# differnces.
|
6
|
+
def File.same_contents(p1, p2)
|
7
|
+
f1 = open(p1).read
|
8
|
+
f2 = open(p2).read
|
9
|
+
|
10
|
+
# Control variables
|
11
|
+
differences = []
|
12
|
+
read_length = 1
|
13
|
+
same = true
|
14
|
+
index = 0
|
15
|
+
|
16
|
+
while ((index + read_length) < f1.length) && ((index + read_length) < f2.length)
|
17
|
+
same = f1.slice(index, read_length) == f2.slice(index, read_length)
|
18
|
+
unless same
|
19
|
+
puts index
|
20
|
+
pp f1.slice(index, read_length).unpack("C*")
|
21
|
+
pp f2.slice(index, read_length).unpack("C*")
|
22
|
+
differences << {:index => index,
|
23
|
+
:f1_value => f1.slice(index, read_length).unpack("C*"),
|
24
|
+
:f2_value => f2.slice(index, read_length).unpack("C*")
|
25
|
+
}
|
26
|
+
end
|
27
|
+
index = index + read_length
|
28
|
+
differences
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# # Original comparison from Ruby Cookbook by Carlson & Richardson
|
34
|
+
# open(p1) do |f1|
|
35
|
+
# open(p2) do |f2|
|
36
|
+
# puts blocksize = f1.lstat.blksize
|
37
|
+
# same = true
|
38
|
+
# while same && !f1.eof? && !f2.eof?
|
39
|
+
# same = f1.read(blocksize) == f2.read(blocksize)
|
40
|
+
# end
|
41
|
+
# return same
|
42
|
+
# end
|
43
|
+
# end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NIFTI::NObject do
|
4
|
+
before :all do
|
5
|
+
@string = File.open(NIFTI_TEST_FILE1, 'rb').read
|
6
|
+
@fixture_image_length = 983040
|
7
|
+
@fixture_afni_extension_length = 5661
|
8
|
+
@new_fixture_file_name = '5PlLoc.nii'
|
9
|
+
@valid_header = {
|
10
|
+
"xyzt_units"=>2, "pixdim"=>[1.0, 0.9375, 0.9375, 12.5, 0.0, 0.0, 0.0,
|
11
|
+
0.0], "sform_code"=>1, "aux_file"=>"", "scl_slope"=>0.0,
|
12
|
+
"srow_x"=>[-0.9375, -0.0, -0.0, 119.53125], "glmin"=>0, "freq_dim"=>0,
|
13
|
+
"srow_y"=>[-0.0, -0.9375, -0.0, 159.531005859375], "qform_code"=>1,
|
14
|
+
"slice_duration"=>0.0, "cal_min"=>0.0, "db_name"=>"", "magic"=>"n+1",
|
15
|
+
"srow_z"=>[0.0, 0.0, 12.5, -25.0], "quatern_b"=>0.0, "data_type"=>"",
|
16
|
+
"qform_code_descr"=>"NIFTI_XFORM_SCANNER_ANAT",
|
17
|
+
"sform_code_descr"=>"NIFTI_XFORM_SCANNER_ANAT", "intent_name"=>"",
|
18
|
+
"quatern_c"=>0.0, "slice_end"=>0, "scl_inter"=>0.0, "quatern_d"=>1.0,
|
19
|
+
"slice_code"=>0, "sizeof_hdr"=>348, "slice_dim"=>0,
|
20
|
+
"qoffset_x"=>119.53125, "dim_info"=>0, "phase_dim"=>0,
|
21
|
+
"qoffset_y"=>159.531005859375, "descrip"=>"", "datatype"=>4,
|
22
|
+
"intent_p1"=>0.0, "dim"=>[3, 256, 256, 15, 1, 1, 1, 1],
|
23
|
+
"qoffset_z"=>-25.0, "glmax"=>0, "toffset"=>0.0, "bitpix"=>16,
|
24
|
+
"intent_code"=>0, "intent_p2"=>0.0, "session_error"=>0, "extents"=>0,
|
25
|
+
"cal_max"=>0.0, "vox_offset"=>6032.0, "slice_start"=>0,
|
26
|
+
"intent_p3"=>0.0, "regular"=>"r"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Think of these more as integration tests, since the actual reading
|
31
|
+
# is done and tested in the NRead spec
|
32
|
+
it "should read a nifti file and correctly initialize header and image" do
|
33
|
+
obj = NObject.new(NIFTI_TEST_FILE1)
|
34
|
+
|
35
|
+
obj.header.should == @valid_header
|
36
|
+
obj.extended_header.should_not be_empty
|
37
|
+
obj.extended_header.first[:esize].should == 5680
|
38
|
+
obj.extended_header.first[:ecode].should == 4
|
39
|
+
obj.extended_header.first[:data].length.should == @fixture_afni_extension_length
|
40
|
+
obj.image.should be_nil
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should read a binary string and correctly initialize header and image" do
|
45
|
+
obj = NObject.new(@string, :bin => true)
|
46
|
+
|
47
|
+
obj.header.should == @valid_header
|
48
|
+
obj.extended_header.should_not be_empty
|
49
|
+
obj.extended_header.first[:esize].should == 5680
|
50
|
+
obj.extended_header.first[:ecode].should == 4
|
51
|
+
obj.extended_header.first[:data].length.should == @fixture_afni_extension_length
|
52
|
+
obj.image.should be_nil
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should read a nifti file with image" do
|
57
|
+
obj = NObject.new(NIFTI_TEST_FILE1, :image => true)
|
58
|
+
|
59
|
+
obj.header.should == @valid_header
|
60
|
+
obj.extended_header.should_not be_empty
|
61
|
+
obj.extended_header.first[:esize].should == 5680
|
62
|
+
obj.extended_header.first[:ecode].should == 4
|
63
|
+
obj.extended_header.first[:data].length.should == @fixture_afni_extension_length
|
64
|
+
obj.image.should_not be_nil
|
65
|
+
obj.image.length.should == @fixture_image_length
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should read a nifti file with image as narray" do
|
70
|
+
obj = NObject.new(NIFTI_TEST_FILE1, :image => true, :narray => true)
|
71
|
+
|
72
|
+
obj.header.should == @valid_header
|
73
|
+
obj.extended_header.should_not be_empty
|
74
|
+
obj.extended_header.first[:esize].should == 5680
|
75
|
+
obj.extended_header.first[:ecode].should == 4
|
76
|
+
obj.extended_header.first[:data].length.should == @fixture_afni_extension_length
|
77
|
+
obj.image.should_not be_nil
|
78
|
+
obj.image.class.should == NArray
|
79
|
+
obj.image.dim.should == 3
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should retrieve image data when requested" do
|
84
|
+
obj = NObject.new(NIFTI_TEST_FILE1)
|
85
|
+
obj.get_image.length.should == @fixture_image_length
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
it "should raise an error if initialized with bad argument" do
|
90
|
+
lambda {
|
91
|
+
NObject.new(12345)
|
92
|
+
}.should raise_error ArgumentError, /Invalid argument/
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should sucessfully write a NIfTI file" do
|
96
|
+
obj = NObject.new(NIFTI_TEST_FILE1, :image => true)
|
97
|
+
obj.write(@new_fixture_file_name)
|
98
|
+
File.exist?(@new_fixture_file_name).should be_true
|
99
|
+
obj.write_success.should be_true
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be able to assign an image" do
|
103
|
+
obj = NObject.new(@string, :bin => true, :image => true)
|
104
|
+
obj.image = [0] * @fixture_image_length
|
105
|
+
end
|
106
|
+
|
107
|
+
after :each do
|
108
|
+
File.delete @new_fixture_file_name if File.exist? @new_fixture_file_name
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|