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/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
|