class NIFTI::NRead
Constants
- MAGIC
Valid Magic codes for the
NIFTI
Header
Attributes
An array of nifti header extension hashes with keys esize, ecode and data.
A hash containing header attributes.
A narray of image values reshapred to image dimensions
An array of decoded image values
An array which records any status messages that are generated while parsing the DICOM string.
A boolean which reports whether the NIFTI
string was parsed successfully (true) or not (false).
Public Class Methods
Create a NRead
object to parse a nifti file or binary string and set header and image info instance variables.
The nifti header will be checked for validity (header size and magic number) and will raise an IOError if invalid.
NIFTI
header extensions are not yet supported and are not included in the header.
The header and image are accessible via the hdr and image instance variables. An optional narray matrix may also be available in image_narray
if desired by passing in :narray => true as an option.
Parameters¶ ↑
-
source
– A string which specifies either the path of aNIFTI
file to be loaded, or a binaryNIFTI
string to be parsed. -
options
– A hash of parameters.
Options¶ ↑
-
:bin
– Boolean. If set to true, string parameter will be interpreted as a binaryNIFTI
string, and not a path string, which is the default behaviour. -
:image
– Boolean. If set to true, automatically load the image into @image, otherwise only a header is collected and you can get an image -
:narray
– Boolean. If set to true, a properly shaped narray matrix will be set in the instance variable @image_narray. Automatically sets :image => true
# File lib/nifti/n_read.rb, line 42 def initialize(source=nil, options={}) options[:image] = true if options[:narray] @msg = [] @success = false set_stream(source, options) parse_header(options) return self end
Public Instance Methods
Create an narray if the NArray is available Tests if a file is readable, and if so, opens it.
Parameters¶ ↑
-
image_array
– Array. A vector of image data. -
dim
– Array. The dim array from the nifti header, specifing number of dimensions (dim) and dimension length of other dimensions to reshape narray into.
# File lib/nifti/n_read.rb, line 76 def get_image_narray(image_array, dim) if Object.const_defined?('NArray') @image_narray = pixel_data = NArray.to_na(image_array).reshape!(*dim[1..dim[0]]) else add_msg "Can't find NArray, no image_narray created. Please `gem install narray`" end end
Unpack an image array from vox_offset to the end of a nifti file.
Parameters¶ ↑
There are no parameters - this reads from the binary string in the @string instance variable.
This sets @image_rubyarray to the image data vector and also returns it.
# File lib/nifti/n_read.rb, line 60 def read_image raw_image = [] @stream.index = @hdr['vox_offset'] type = NIFTI_DATATYPES[@hdr['datatype']] format = @stream.format[type] @image_rubyarray = @stream.decode(@stream.rest_length, type) end
Private Instance Methods
Add a message (TODO: and maybe print to screen if verbose)
# File lib/nifti/n_read.rb, line 265 def add_msg(msg) @msg << msg end
NIFTI
uses the header length (first 4 bytes) to be 348 number of “ni10” or “n+10” as the last 4 bytes to be magic numbers to validate the header.
The header is usually checked before any data is read, but can be checked at any point in the process as the stream index is reset to its original position after validation.
There are no options - the method will raise an IOError if any of the magic numbers are not valid.
# File lib/nifti/n_read.rb, line 134 def check_header begin starting_index = @stream.index # Check sizeof_hdr @stream.index = 0; sizeof_hdr = @stream.decode(4, "UL") raise IOError, "Bad Header Length #{sizeof_hdr}" unless sizeof_hdr == 348 # Check magic @stream.index = 344; magic = @stream.decode(4, "STR") raise IOError, "Bad Magic Code #{magic} (should be ni1 or n+1)" unless MAGIC.include?(magic) rescue IOError => e raise IOError, "Header appears to be malformed: #{e}" else @stream.index = starting_index end end
Bitwise Operator to extract Frequency Dimension
# File lib/nifti/n_read.rb, line 238 def dim_info_to_freq_dim(dim_info) extract_dim_info(dim_info, 0) end
Bitwise Operator to extract Phase Dimension
# File lib/nifti/n_read.rb, line 243 def dim_info_to_phase_dim(dim_info) extract_dim_info(dim_info, 2) end
Bitwise Operator to extract Slice Dimension
# File lib/nifti/n_read.rb, line 248 def dim_info_to_slice_dim(dim_info) extract_dim_info(dim_info, 4) end
Bitwise Operator to extract Frequency, Phase & Slice Dimensions from 2byte diminfo
# File lib/nifti/n_read.rb, line 253 def extract_dim_info(dim_info, offset = 0) (dim_info >> offset) & 0x03 end
Bitwise Operator to encode Freq, Phase & Slice into Diminfo
# File lib/nifti/n_read.rb, line 258 def fps_into_dim_info(frequency_dim, phase_dim, slice_dim) ((frequency_dim & 0x03 ) << 0 ) | ((phase_dim & 0x03) << 2 ) | ((slice_dim & 0x03) << 4 ) end
Tests if a file is readable, and if so, opens it.
Parameters¶ ↑
-
file
– A path/file string.
# File lib/nifti/n_read.rb, line 213 def open_file(file) if File.exist?(file) if File.readable?(file) if not File.directory?(file) if File.size(file) > 8 begin @file = Zlib::GzipReader.new(File.new(file, "rb")) rescue Zlib::GzipFile::Error @file = File.new(file, "rb") end else @msg << "Error! File is too small to contain DICOM information (#{file})." end else @msg << "Error! File is a directory (#{file})." end else @msg << "Error! File exists but I don't have permission to read it (#{file})." end else @msg << "Error! The file you have supplied does not exist (#{file})." end end
Read the nifti header according to its byte signature. The file stream will be left open and should be positioned at the end of the 348 byte header.
# File lib/nifti/n_read.rb, line 157 def parse_basic_header # The HEADER_SIGNATURE is defined in NIFTI::Constants and used for both reading and writing. header = {} HEADER_SIGNATURE.each do |header_item| name, length, type = *header_item header[name] = @stream.decode(length, type) end # Extract Freq, Phase & Slice Dimensions from diminfo if header['dim_info'] header['freq_dim'] = dim_info_to_freq_dim(header['dim_info']) header['phase_dim'] = dim_info_to_phase_dim(header['dim_info']) header['slice_dim'] = dim_info_to_slice_dim(header['dim_info']) end header['sform_code_descr'] = XFORM_CODES[header['sform_code']] header['qform_code_descr'] = XFORM_CODES[header['qform_code']] return header end
Read any extended header information. The file stream will be left at imaging data itself, taking vox_offset into account for NIFTI
Header Extended Attributes. Pass in the voxel offset so the extended header knows when to stop reading.
# File lib/nifti/n_read.rb, line 181 def parse_extended_header extended = [] extension = @stream.decode(4, "BY") # "After the end of the 348 byte header (e.g., after the magic field), # the next 4 bytes are an byte array field named extension. By default, # all 4 bytes of this array should be set to zero. In a .nii file, these 4 # bytes will always be present, since the earliest start point for the # image data is byte #352. In a separate .hdr file, these bytes may or may # not be present (i.e., a .hdr file may only be 348 bytes long). If not # present, then a NIfTI-1.1 compliant program should use the default value # of extension={0,0,0,0}. The first byte (extension[0]) is the only value # of this array that is specified at present. The other 3 bytes are # reserved for future use." if extension[0] != 0 while @stream.index < @hdr['vox_offset'] esize, ecode = *@stream.decode(8, "UL") data = @stream.decode(esize - 8, "STR") extended << {:esize => esize, :ecode => ecode, :data => data} end # stream.decode(header['vox_offset'] - stream.index, "STR") # stream.skip header['vox_offset'] - stream.index end return extended end
Parse the NIFTI
Header.
# File lib/nifti/n_read.rb, line 113 def parse_header(options = {}) check_header @hdr = parse_basic_header @extended_header = parse_extended_header # Optional image gathering read_image if options[:image] get_image_narray(@image_rubyarray, @hdr['dim']) if options[:narray] @success = true end
Initializes @stream from a binary string or filename
# File lib/nifti/n_read.rb, line 87 def set_stream(source, options) # Are we going to read from a file, or read from a binary string? if options[:bin] # Read from the provided binary string: @str = source else # Read from file: open_file(source) # Read the initial header of the file: if @file == nil # File is not readable, so we return: @success = false return else # Extract the content of the file to a binary string: @str = @file.read @file.close end end # Create a Stream instance to handle the decoding of content from this binary string: @file_endian = false @stream = Stream.new(@str, false) end