nifti 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
nifti (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
mocha (0.9.10)
|
11
|
+
rake
|
12
|
+
narray (0.5.9.9)
|
13
|
+
rake (0.8.7)
|
14
|
+
rspec (2.4.0)
|
15
|
+
rspec-core (~> 2.4.0)
|
16
|
+
rspec-expectations (~> 2.4.0)
|
17
|
+
rspec-mocks (~> 2.4.0)
|
18
|
+
rspec-core (2.4.0)
|
19
|
+
rspec-expectations (2.4.0)
|
20
|
+
diff-lcs (~> 1.1.2)
|
21
|
+
rspec-mocks (2.4.0)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
mocha
|
28
|
+
narray
|
29
|
+
nifti!
|
30
|
+
rspec
|
data/README.markdown
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
RUBY NIfTI
|
2
|
+
==========
|
3
|
+
|
4
|
+
Ruby NIfTI is a pure-ruby library for handling NIfTI data in Ruby. NIfTI
|
5
|
+
[(Neuroimaging Informatics Technology Initiative)](http://nifti.nimh.nih.gov/)
|
6
|
+
is an image format designed primarily for storing and analyzing MRI & PET
|
7
|
+
imaging data.
|
8
|
+
|
9
|
+
Ruby NIfTI currently only supports basic access to NIfTI files, including
|
10
|
+
basic and extended header information, as well as image information. It
|
11
|
+
doesn't attempt to touch the image data (rotate it, etc.), but it does provide
|
12
|
+
access to qform and sform orientation matrices. Nonetheless it does provide a
|
13
|
+
nice interface to get at NIfTI info from within ruby. Since Ruby isn't as
|
14
|
+
widely seen as a quantitative scripting language yet (Python is more well
|
15
|
+
known, and PyNifti is more mature than NumericalRuby / NArray) I don't know
|
16
|
+
how widely this will be used, but hopefully it will be useful for somebody.
|
17
|
+
|
18
|
+
INSTALLATION
|
19
|
+
------------
|
20
|
+
|
21
|
+
gem install nifti
|
22
|
+
|
23
|
+
|
24
|
+
BASIC USAGE
|
25
|
+
-----------
|
26
|
+
|
27
|
+
require "nifti"
|
28
|
+
# Read file:
|
29
|
+
obj = NIFTI::NObject.new("some_file.nii")
|
30
|
+
# Display some key information about the file:
|
31
|
+
puts obj.header['sform_code_descr']
|
32
|
+
=> "NIFTI_XFORM_SCANNER_ANAT"
|
33
|
+
# Retrieve the pixel data in a Ruby Array:
|
34
|
+
image = obj.get_image
|
35
|
+
# Load the pixel data to an NArray image object and display it on the screen:
|
36
|
+
image = obj.get_image_narray
|
37
|
+
|
38
|
+
LIMITATIONS
|
39
|
+
-----------
|
40
|
+
|
41
|
+
There are plenty of NIfTI libraries around (the canonical
|
42
|
+
[nifticlib](http://niftilib.sourceforge.net/), which includes c, python and
|
43
|
+
matlab interfaces, and Matlab interfaces in the Mathworks image processing
|
44
|
+
toolbox (IPT) and [Toolbox for
|
45
|
+
NIfTI](http://www.mathworks.com/matlabcentral/fileexchange/8797-tools-for-nifti-and-analyze-image)
|
46
|
+
by Jimmy Shen.
|
47
|
+
|
48
|
+
This library does not intend to replace them, but rather to provide a few ruby
|
49
|
+
convenience methods for quickly accessing imaging data from ruby without
|
50
|
+
having to call out to external programs, as well as write custom extensions to
|
51
|
+
the NIfTI header. At current, it doesn't do much more than byte parsing and
|
52
|
+
providing access to the header, and its quite slow when compared to existing
|
53
|
+
libraries. However, [narray](http://narray.rubyforge.org/) is picking up and
|
54
|
+
its possible that people would want to have some easy way to access NIfTI info
|
55
|
+
using custom ruby libraries.
|
56
|
+
|
57
|
+
|
58
|
+
CREDIT
|
59
|
+
------
|
60
|
+
|
61
|
+
Ruby NIfTI is highly derivative of the very good library [Ruby
|
62
|
+
DICOM](http://dicom.rubyforge.org/), from which all of the design and most of
|
63
|
+
the code has been cold-heartedly stolen. Many thanks to Chris Lervag - this
|
64
|
+
wouldn't exist without his examples.
|
65
|
+
|
66
|
+
|
67
|
+
RESOURCES
|
68
|
+
---------
|
69
|
+
|
70
|
+
* [Official home page](http://brainmap.wisc.edu/pages/10-RUBY-NIFTI)
|
71
|
+
* [Development / Source code repository](https://github.com/brainmap/ruby-nifti)
|
72
|
+
* [Documentation](http://rdoc.info/github/brainmap/ruby-nifti/master/frames)
|
73
|
+
|
74
|
+
|
75
|
+
Examples
|
76
|
+
--------
|
77
|
+
|
78
|
+
### Using Extended Headers ###
|
79
|
+
|
80
|
+
Each NObject that is successfully read has an array of extended_header hashes.
|
81
|
+
Each extended_header hash has 3 keys, :esize, :ecode, and :data corresponding
|
82
|
+
to the available fields in the nifti header extension.
|
83
|
+
|
84
|
+
Here's a silly example (since you'd probably want to read do this using
|
85
|
+
something that's designed for it, like 3dinfo). Suppose you wanted to collect
|
86
|
+
the history of an image from inside the AFNI extended header. Since the data
|
87
|
+
of the AFNI extended header is just xml, you could easily parse it with an xml
|
88
|
+
parser like nokogiri (using the AFNI ecode of 4 to select it from the list of
|
89
|
+
extended headers, and assuming that there's only 1 AFNI header per file):
|
90
|
+
|
91
|
+
require 'nifti'; require 'nokogiri'
|
92
|
+
obj = NIFTI::NObject.new('./T1.nii')
|
93
|
+
afni_extended_header = obj.extended_header.select{|ext| ext[:ecode] == 4 }[:data]
|
94
|
+
afni_xml = Nokogiri::XML(xml)
|
95
|
+
history = afni_xml.xpath("//AFNI_atr[@atr_name='HISTORY_NOTE']").first.children.first.text
|
96
|
+
|
97
|
+
### Image Summary Statistics ###
|
98
|
+
|
99
|
+
Again, this is a trivial example, but shows one usage of the library. Say you
|
100
|
+
want to take find the mean and std dev of the entire image.
|
101
|
+
|
102
|
+
obj = NIFTI::NObject.new('./T1.nii', :narray => true)
|
103
|
+
mean = obj.image.mean
|
104
|
+
stddev = obj.image.stddev
|
105
|
+
|
106
|
+
If you don't have narray installed, you could still use obj.image as a ruby
|
107
|
+
array, but you'd have to collect the summary stats yourself.
|
108
|
+
|
109
|
+
|
110
|
+
COPYRIGHT
|
111
|
+
---------
|
112
|
+
|
113
|
+
Copyright 2011 Erik Kastman
|
114
|
+
|
115
|
+
This program is free software: you can redistribute it and/or modify
|
116
|
+
it under the terms of the GNU General Public License as published by
|
117
|
+
the Free Software Foundation, either version 3 of the License, or
|
118
|
+
(at your option) any later version.
|
119
|
+
|
120
|
+
This program is distributed in the hope that it will be useful,
|
121
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
122
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
123
|
+
GNU General Public License for more details.
|
124
|
+
|
125
|
+
You should have received a copy of the GNU General Public License
|
126
|
+
along with this program. If not, see http://www.gnu.org/licenses/ .
|
data/Rakefile
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
module NIFTI
|
2
|
+
# Variables used to determine endianness.
|
3
|
+
x = 0xdeadbeef
|
4
|
+
endian_type = {
|
5
|
+
Array(x).pack("V*") => false, # Little
|
6
|
+
Array(x).pack("N*") => true # Big
|
7
|
+
}
|
8
|
+
# System (CPU) Endianness.
|
9
|
+
CPU_ENDIAN = endian_type[Array(x).pack("L*")]
|
10
|
+
|
11
|
+
# Custom string used for (un)packing big endian signed short.
|
12
|
+
CUSTOM_SS = "k*"
|
13
|
+
# Custom string used for (un)packing big endian signed long.
|
14
|
+
CUSTOM_SL = "r*"
|
15
|
+
|
16
|
+
# Q/S Form Transform codes defined in the nifti header
|
17
|
+
# Reference: http://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields_pages/qsform.html
|
18
|
+
XFORM_CODES = {
|
19
|
+
0 => 'NIFTI_XFORM_UNKNOWN', # Arbitrary coordinates (Method 1).
|
20
|
+
1 => 'NIFTI_XFORM_SCANNER_ANAT', # Scanner-based anatomical coordinates
|
21
|
+
2 => 'NIFTI_XFORM_ALIGNED_ANAT', # Coordinates aligned to another file's, or to anatomical "truth".
|
22
|
+
3 => 'NIFTI_XFORM_TALAIRACH', # Coordinates aligned to Talairach-Tournoux Atlas; (0,0,0)=AC, etc.
|
23
|
+
4 => 'NIFTI_XFORM_MNI_152' # MNI 152 normalized coordinates
|
24
|
+
}
|
25
|
+
|
26
|
+
# Take a NIFTI TypeCode and return datatype and bitpix
|
27
|
+
#
|
28
|
+
# From Jimmy Shen:
|
29
|
+
# Set bitpix according to datatype
|
30
|
+
# /*Acceptable values for datatype are*/
|
31
|
+
#
|
32
|
+
# 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN
|
33
|
+
# 1 Binary (ubit1, bitpix=1) % DT_BINARY
|
34
|
+
# 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8
|
35
|
+
# 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16
|
36
|
+
# 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32
|
37
|
+
# 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32
|
38
|
+
# 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64
|
39
|
+
# 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64
|
40
|
+
# 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24
|
41
|
+
# 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8
|
42
|
+
# 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96
|
43
|
+
# 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16
|
44
|
+
# 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32
|
45
|
+
# 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64
|
46
|
+
# 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64
|
47
|
+
# 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128
|
48
|
+
# 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
|
49
|
+
# 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128
|
50
|
+
NIFTI_DATATYPES = {
|
51
|
+
0 => "Unknown",
|
52
|
+
# 1 => "", Can't find a single bit encoding in ruby's unpack method?
|
53
|
+
2 => "OB",
|
54
|
+
4 => "SS",
|
55
|
+
8 => "SL",
|
56
|
+
16 => "FL",
|
57
|
+
32 => "FD",
|
58
|
+
64 => "FD",
|
59
|
+
128 => "RGBUnknown",
|
60
|
+
256 => "BY",
|
61
|
+
511 => "RGBUnknown",
|
62
|
+
512 => "US",
|
63
|
+
768 => "UL"
|
64
|
+
# 1024 => "",
|
65
|
+
# 1280 => "",
|
66
|
+
# 1536 => "",
|
67
|
+
# 1792 => "",
|
68
|
+
# 2048 => ""
|
69
|
+
}
|
70
|
+
|
71
|
+
# The NIFTI signature is hardcoded by bytes. This consists of arrays for
|
72
|
+
# each item in the signature, where item[0] is the name of the item,
|
73
|
+
# item[1] is the length in bytes of the item, and dim[2] is the Format
|
74
|
+
# string to use in packing/unpacking the item.
|
75
|
+
HEADER_SIGNATURE = [
|
76
|
+
# /**********************/ /**********************/ /***************/
|
77
|
+
# struct nifti_1_header { /* NIFTI-1 usage */ /*ANALYZE 7.5 field(s)*/ /* Byte offset */
|
78
|
+
# /**********************/ /**********************/ /***************/
|
79
|
+
# int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ /* 0 */
|
80
|
+
['sizeof_hdr', 4, 'SL'],
|
81
|
+
|
82
|
+
# char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ /* 4 */
|
83
|
+
['data_type', 10, 'STR'],
|
84
|
+
|
85
|
+
# char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ /* 14 */
|
86
|
+
['db_name', 18, 'STR'],
|
87
|
+
|
88
|
+
# int extents; /*!< ++UNUSED++ */ /* int extents; */ /* 32 */
|
89
|
+
['extents', 4, "SL"],
|
90
|
+
|
91
|
+
# short session_error; /*!< ++UNUSED++ */ /* short session_error; */ /* 36 */
|
92
|
+
['session_error', 2, "SS"],
|
93
|
+
|
94
|
+
# char regular; /*!< ++UNUSED++ */ /* char regular; */ /* 38 */
|
95
|
+
['regular', 1, "STR"],
|
96
|
+
|
97
|
+
# char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /* 39 */
|
98
|
+
['dim_info', 1, "BY"],
|
99
|
+
|
100
|
+
|
101
|
+
# /*--- was image_dimension substruct ---*/
|
102
|
+
# short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ /* 40 */
|
103
|
+
['dim', 16, "US"],
|
104
|
+
|
105
|
+
# float intent_p1; /*!< 1st intent parameter. */ /* short unused8; */ /* 56 */
|
106
|
+
['intent_p1', 4, "FL"],
|
107
|
+
|
108
|
+
# float intent_p2; /*!< 2nd intent parameter. */ /* short unused10; */ /* 60 */
|
109
|
+
['intent_p2', 4, "FL"],
|
110
|
+
# /* short unused11; */
|
111
|
+
# float intent_p3; /*!< 3rd intent parameter. */ /* short unused12; */ /* 64 */
|
112
|
+
['intent_p3', 4, "FL"],
|
113
|
+
|
114
|
+
# short intent_code; /*!< NIFTIINTENT code. */ /* short unused14; */ /* 68 */
|
115
|
+
['intent_code', 2, "US"],
|
116
|
+
|
117
|
+
# short datatype; /*!< Defines data type! */ /* short datatype; */ /* 70 */
|
118
|
+
['datatype', 2, "US"],
|
119
|
+
|
120
|
+
# short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ /* 72 */
|
121
|
+
['bitpix', 2, "US"],
|
122
|
+
|
123
|
+
# short slice_start; /*!< First slice index. */ /* short dim_un0; */ /* 74 */
|
124
|
+
['slice_start', 2, "US"],
|
125
|
+
|
126
|
+
# float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ /* 76 */
|
127
|
+
['pixdim', 32, "FL"],
|
128
|
+
|
129
|
+
# float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ /* 108 */
|
130
|
+
['vox_offset', 4, "FL"],
|
131
|
+
|
132
|
+
# float scl_slope; /*!< Data scaling: slope. */ /* float funused1; */ /* 112 */
|
133
|
+
['scl_slope', 4, "FL"],
|
134
|
+
|
135
|
+
# float scl_inter; /*!< Data scaling: offset. */ /* float funused2; */ /* 116 */
|
136
|
+
['scl_inter', 4, "FL"],
|
137
|
+
|
138
|
+
# short slice_end; /*!< Last slice index. */ /* float funused3; */ /* 120 */
|
139
|
+
['slice_end', 2, "US"],
|
140
|
+
|
141
|
+
# char slice_code; /*!< Slice timing order. */ /* 122 */
|
142
|
+
['slice_code', 1, "BY"],
|
143
|
+
|
144
|
+
# char xyzt_units; /*!< Units of pixdim[1..4] */ /* 123 */
|
145
|
+
['xyzt_units', 1, "BY"],
|
146
|
+
|
147
|
+
# float cal_max; /*!< Max display intensity */ /* float cal_max; */ /* 124 */
|
148
|
+
['cal_max', 4, "FL"],
|
149
|
+
|
150
|
+
# float cal_min; /*!< Min display intensity */ /* float cal_min; */ /* 128 */
|
151
|
+
['cal_min', 4, "FL"],
|
152
|
+
|
153
|
+
# float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ /* 132 */
|
154
|
+
['slice_duration', 4, "FL"],
|
155
|
+
|
156
|
+
# float toffset; /*!< Time axis shift. */ /* float verified; */ /* 136 */
|
157
|
+
['toffset', 4, "FL"],
|
158
|
+
|
159
|
+
# int glmax; /*!< ++UNUSED++ */ /* int glmax; */ /* 140 */
|
160
|
+
['glmax', 4, "UL"],
|
161
|
+
|
162
|
+
# int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /* 144 */
|
163
|
+
['glmin', 4, "UL"],
|
164
|
+
|
165
|
+
# /*--- was data_history substruct ---*/
|
166
|
+
# char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ /* 148 */
|
167
|
+
['descrip', 80, "STR"],
|
168
|
+
|
169
|
+
# char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ /* 228 */
|
170
|
+
['aux_file', 24, "STR"],
|
171
|
+
|
172
|
+
#
|
173
|
+
# short qform_code; /*!< NIFTIXFORM code. */ /*-- all ANALYZE 7.5 ---*/ /* 252 */
|
174
|
+
['qform_code', 2, "US"], # /* fields below here */
|
175
|
+
# /* are replaced */
|
176
|
+
# short sform_code; /*!< NIFTIXFORM code. */ /* 254 */
|
177
|
+
['sform_code', 2, "US"],
|
178
|
+
#
|
179
|
+
# float quatern_b; /*!< Quaternion b param. */ /* 256 */
|
180
|
+
['quatern_b', 4, "FL"],
|
181
|
+
|
182
|
+
# float quatern_c; /*!< Quaternion c param. */ /* 260 */
|
183
|
+
['quatern_c', 4, "FL"],
|
184
|
+
|
185
|
+
# float quatern_d; /*!< Quaternion d param. */ /* 264 */
|
186
|
+
['quatern_d', 4, "FL"],
|
187
|
+
|
188
|
+
|
189
|
+
# float qoffset_x; /*!< Quaternion x shift. */ /* 268 */
|
190
|
+
['qoffset_x', 4, "FL"],
|
191
|
+
|
192
|
+
# float qoffset_y; /*!< Quaternion y shift. */ /* 272 */
|
193
|
+
['qoffset_y', 4, "FL"],
|
194
|
+
|
195
|
+
# float qoffset_z; /*!< Quaternion z shift. */ /* 276 */
|
196
|
+
['qoffset_z', 4, "FL"],
|
197
|
+
|
198
|
+
# float srow_x[4]; /*!< 1st row affine transform. */ /* 280 */
|
199
|
+
['srow_x', 16, "FL"],
|
200
|
+
|
201
|
+
# float srow_y[4]; /*!< 2nd row affine transform. */ /* 296 */
|
202
|
+
['srow_y', 16, "FL"],
|
203
|
+
|
204
|
+
# float srow_z[4]; /*!< 3rd row affine transform. */ /* 312 */
|
205
|
+
['srow_z', 16, "FL"],
|
206
|
+
|
207
|
+
#
|
208
|
+
#
|
209
|
+
# char intent_name[16];/*!< name or meaning of data. */ /* 328 */
|
210
|
+
['intent_name', 16, "STR"],
|
211
|
+
|
212
|
+
#
|
213
|
+
#
|
214
|
+
# char magic[4]; /*!< MUST be "ni1\0" or "n+1\0". */ /* 344 */
|
215
|
+
['magic', 4, "STR"]
|
216
|
+
|
217
|
+
# } ; /** 348 bytes total **/
|
218
|
+
#
|
219
|
+
#
|
220
|
+
|
221
|
+
|
222
|
+
]
|
223
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module NIFTI
|
2
|
+
# The NObject class is the main class for interacting with the NIFTI object.
|
3
|
+
# Reading from and writing to files is executed from instances of this class.
|
4
|
+
#
|
5
|
+
class NObject
|
6
|
+
# An array which contain any notices/warnings/errors that have been recorded for the NObject instance.
|
7
|
+
attr_reader :errors
|
8
|
+
# A boolean which is set as true if a NIFTI file has been successfully read & parsed from a file (or binary string).
|
9
|
+
attr_reader :read_success
|
10
|
+
# The Stream instance associated with this DObject instance (this attribute is mostly used internally).
|
11
|
+
attr_reader :stream
|
12
|
+
# A boolean which is set as true if a DObject instance has been successfully written to file (or successfully encoded).
|
13
|
+
attr_reader :write_success
|
14
|
+
# A hash of header information
|
15
|
+
attr_accessor :header
|
16
|
+
# A hash of extended attributes
|
17
|
+
attr_accessor :extended_header
|
18
|
+
# An array or narray of image values
|
19
|
+
attr_accessor :image
|
20
|
+
|
21
|
+
# Creates an NObject instance (NObject is an abbreviation for "NIFTI object").
|
22
|
+
#
|
23
|
+
# The NObject instance holds references to the NIFTI Header and Image
|
24
|
+
# A NObject is typically built by reading and parsing a file or a
|
25
|
+
# binary string, but can also be built from an empty state by the user.
|
26
|
+
#
|
27
|
+
# === Parameters
|
28
|
+
#
|
29
|
+
# * <tt>string</tt> -- A string which specifies either the path of a DICOM file to be loaded, or a binary DICOM string to be parsed. The parameter defaults to nil, in which case an empty DObject instance is created.
|
30
|
+
# * <tt>options</tt> -- A hash of parameters.
|
31
|
+
#
|
32
|
+
# === Options
|
33
|
+
#
|
34
|
+
# * <tt>:bin</tt> -- Boolean. If set to true, string parameter will be interpreted as a binary DICOM string, and not a path string, which is the default behaviour.
|
35
|
+
# * <tt>:syntax</tt> -- String. If a syntax string is specified, the DRead class will be forced to use this transfer syntax when decoding the file/binary string.
|
36
|
+
# * <tt>:verbose</tt> -- Boolean. If set to false, the NObject instance will run silently and not output warnings and error messages to the screen. Defaults to true.
|
37
|
+
# * <tt>:image</tt> -- Boolean. If set to true, automatically load the image into @image, otherwise only a header is collected and you can get an image from #get_image
|
38
|
+
# * <tt>:narray</tt> -- Boolean. If set to true, the NObject will build a properly shaped narray from the image data.
|
39
|
+
#
|
40
|
+
# === Examples
|
41
|
+
#
|
42
|
+
# # Load a NIFTI file's header information:
|
43
|
+
# require 'nifti'
|
44
|
+
# obj = NIFTI::NObject.new("test.nii")
|
45
|
+
# # Read a NIFTI header and image into a numerical-ruby narray:
|
46
|
+
# obj = Nfiti::NObject.new("test.nii", :image => true, :narray => true)
|
47
|
+
# # Create an empty NIfTI object & choose non-verbose behaviour:
|
48
|
+
# obj = NIFTI::NObject.new(nil, :verbose => false)
|
49
|
+
#
|
50
|
+
def initialize(string=nil, options={})
|
51
|
+
# Process option values, setting defaults for the ones that are not specified:
|
52
|
+
# Default verbosity is true if verbosity hasn't been specified (nil):
|
53
|
+
@verbose = (options[:verbose] == false ? false : true)
|
54
|
+
# Messages (errors, warnings or notices) will be accumulated in an array:
|
55
|
+
@errors = Array.new
|
56
|
+
# Structural information (default values):
|
57
|
+
@file_endian = false
|
58
|
+
# Control variables:
|
59
|
+
@read_success = nil
|
60
|
+
|
61
|
+
# Call the read method if a string has been supplied:
|
62
|
+
if string.is_a?(String)
|
63
|
+
@file = string unless options[:bin]
|
64
|
+
read(string, options)
|
65
|
+
elsif not string == nil
|
66
|
+
raise ArgumentError, "Invalid argument. Expected String (or nil), got #{string.class}."
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# Reopen the NIFTI File and retrieve image data
|
72
|
+
def get_image
|
73
|
+
r = NRead.new(@string, :image => true)
|
74
|
+
if r.success
|
75
|
+
@image = r.image_rubyarray
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Passes the NObject to the DWrite class, which writes out the header and image to the specified file.
|
80
|
+
#
|
81
|
+
# === Parameters
|
82
|
+
#
|
83
|
+
# * <tt>file_name</tt> -- A string which identifies the path & name of the NIfTI file which is to be written to disk.
|
84
|
+
# * <tt>options</tt> -- A hash of parameters.
|
85
|
+
#
|
86
|
+
# === Options
|
87
|
+
#
|
88
|
+
# === Examples
|
89
|
+
#
|
90
|
+
# obj.write(path + "test.dcm")
|
91
|
+
#
|
92
|
+
def write(file_name, options={})
|
93
|
+
if file_name.is_a?(String)
|
94
|
+
w = NWrite.new(self, file_name, options)
|
95
|
+
w.write
|
96
|
+
# Write process succesful?
|
97
|
+
@write_success = w.success
|
98
|
+
# If any messages has been recorded, send these to the message handling method:
|
99
|
+
add_msg(w.msg) if w.msg.length > 0
|
100
|
+
else
|
101
|
+
raise ArgumentError, "Invalid file_name. Expected String, got #{file_name.class}."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Following methods are private:
|
106
|
+
private
|
107
|
+
|
108
|
+
# Returns a NIFTI object by reading and parsing the specified file.
|
109
|
+
# This is accomplished by initializing the NRead class, which loads NIFTI information.
|
110
|
+
#
|
111
|
+
# === Notes
|
112
|
+
#
|
113
|
+
# This method is called automatically when initializing the NObject class with a file parameter,
|
114
|
+
# and in practice should not be called by users.
|
115
|
+
#
|
116
|
+
def read(string, options={})
|
117
|
+
if string.is_a?(String)
|
118
|
+
@string = string
|
119
|
+
r = NRead.new(string, options)
|
120
|
+
# Store the data to the instance variables if the readout was a success:
|
121
|
+
if r.success
|
122
|
+
@read_success = true
|
123
|
+
# Update instance variables based on the properties of the NRead object:
|
124
|
+
@header = r.hdr
|
125
|
+
@extended_header = r.extended_header
|
126
|
+
if r.image_narray
|
127
|
+
@image = r.image_narray
|
128
|
+
elsif r.image_rubyarray
|
129
|
+
@image = r.image_rubyarray
|
130
|
+
end
|
131
|
+
else
|
132
|
+
@read_success = false
|
133
|
+
end
|
134
|
+
# If any messages have been recorded, send these to the message handling method:
|
135
|
+
add_msg(r.msg) if r.msg.length > 0
|
136
|
+
else
|
137
|
+
raise ArgumentError, "Invalid argument. Expected String, got #{string.class}."
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Adds one or more status messages to the instance array holding messages, and if the verbose instance variable
|
142
|
+
# is true, the status message(s) are printed to the screen as well.
|
143
|
+
#
|
144
|
+
# === Parameters
|
145
|
+
#
|
146
|
+
# * <tt>msg</tt> -- Status message string, or an array containing one or more status message strings.
|
147
|
+
#
|
148
|
+
def add_msg(msg)
|
149
|
+
puts msg if @verbose
|
150
|
+
@errors << msg
|
151
|
+
@errors.flatten
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|