Permalink
Browse files

Add some documentation and examples.

  • Loading branch information...
1 parent f46c2c8 commit 0090d867ddb00a83efcd7912c8eca4f93b718e86 Erik Kastman committed Feb 7, 2011
Showing with 791 additions and 47 deletions.
  1. +12 −0 CHANGELOG
  2. +674 −0 COPYING
  3. +62 −27 README.rdoc
  4. +3 −0 lib/nifti.rb
  5. +1 −1 lib/nifti/constants.rb
  6. +7 −7 lib/nifti/n_object.rb
  7. +1 −0 spec/custom_matchers.rb
  8. +26 −12 spec/interactive/compare.rb
  9. +5 −0 spec/nifti/n_object_spec.rb
View
@@ -0,0 +1,12 @@
+= 0.0.1
+
+=== (In Development) February, 2011
+
+First release.
+The library offers bare-bones functionality and should be usable for people interested in working with NIfTI files in Ruby. The code is 98% documented according to rdoc and should hopefully be clear and straightforward to read, use and fork.
+
+Known issues:
+* Speed and Memory usage: This thing is really, really slow when packing and unpacking image data into arrays, and I've seen a single 40MB image explode into ~6GB of in-use memory.
+* Only tested with little-endian, signed short, 3d and 4d images.
+* No interfaces for viewing image data (qt, NImage, etc.). The easiest way right now is to save out the nifti and then use another image viewing program.
+* Only supports .nii (n+1) single image files, not a split .img/.hdr
View
674 COPYING

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -1,18 +1,18 @@
-= RUBY NIFTI
+= RUBY NIfTI
-Ruby NIFTI is a pure-ruby library for handling NIfTI data in Ruby. NIfTI
+Ruby NIfTI is a pure-ruby library for handling NIfTI data in Ruby. NIfTI
[(Neuroimaging Informatics Technology Initiative)](http://nifti.nimh.nih.gov/)
is an image format designed primarily for storing and analyzing MRI & PET
imaging data.
-Ruby NIfTI currently only supports reading NIfTI files, including basic and
-extended header information. It doesn't attempt to touch the image data
-(rotate it, etc.) and is still very much in its infancy. Nonetheless it does
-provide a nice interface to get at NIfTI info from within ruby. Since Ruby
-isn't as widely known as a quantitative scripting language yet (Python is more
-well known, and PyNifti is more mature than NumericalRuby / NArray) I don't
-know how widely this will be used, but hopefully it will be useful for
-somebody.
+Ruby NIfTI currently only supports basic access to NIfTI files, including
+basic and extended header information, as well as image information. It
+doesn't attempt to touch the image data (rotate it, etc.), but it does provide
+access to qform and sform orientation matrices. Nonetheless it does provide a
+nice interface to get at NIfTI info from within ruby. Since Ruby isn't as
+widely seen as a quantitative scripting language yet (Python is more well
+known, and PyNifti is more mature than NumericalRuby / NArray) I don't know
+how widely this will be used, but hopefully it will be useful for somebody.
== INSTALLATION
@@ -25,46 +25,81 @@ somebody.
# Read file:
obj = Nifti::NObject.new("some_file.nii")
# Display some key information about the file:
- obj.header
+ puts obj.header['sform_code_descr']
+ => "NIFTI_XFORM_SCANNER_ANAT"
# Retrieve the pixel data in a Ruby Array:
image = obj.get_image
# Load the pixel data to an NArray image object and display it on the screen:
image = obj.get_image_narray
== LIMITATIONS
-There are plenty of Nifti libraries around (the canonical
+There are plenty of NIfTI libraries around (the canonical
[nifticlib](http://niftilib.sourceforge.net/), which includes c, python and
-matlab interfaces, and matlab interfaces in the Mathworks image processing
-toolbox and [Toolbox for
+matlab interfaces, and Matlab interfaces in the Mathworks image processing
+toolbox (IPT) and [Toolbox for
Nifti](http://www.mathworks.com/matlabcentral/fileexchange/8797-tools-for-nifti-and-analyze-image)
by Jimmy Shen.
-This library is not intending to replace them, but rather to provide a few
-pure ruby convenience methods for quickly getting into imaging data from ruby
-without having to call out to external programs. At current, it doesn't do
-much more than byte parsing and providing access to the header. However,
-[narray](http://narray.rubyforge.org/) is picking up and its possible that
-people would want to have some convenience methods if they have existing ruby
-programs.
+This library does not intend to replace them, but rather to provide a few ruby
+convenience methods for quickly accessing imaging data from ruby without
+having to call out to external programs, as well as write custom extensions to
+the NIfTI header. At current, it doesn't do much more than byte parsing and
+providing access to the header, and its quite slow when compared to existing
+libraries. However, [narray](http://narray.rubyforge.org/) is picking up and
+its possible that people would want to have some easy way to access NIfTI info
+using custom ruby libraries.
+
== CREDIT
-Ruby NIfTI is highly derivative of the wonderful library [Ruby
+Ruby NIfTI is highly derivative of the very good library [Ruby
DICOM](http://dicom.rubyforge.org/), from which all of the design and most of
the code has been cold-heartedly stolen. Many thanks to Chris Lervag - this
wouldn't exist without his examples.
+
== RESOURCES
-* {Official home page}[http://dicom.rubyforge.org/]
-* {Discussion forum}[http://groups.google.com/group/ruby-dicom]
-* {Source code repository}[https://github.com/dicom/ruby-dicom]
+* {Official home page}[http://brainmap.wisc.edu/pages]
+* {Source code repository}[https://github.com/brainmap/ruby-nifti]
-== COPYRIGHT
+== EXAMPLES
+
+=== Using Extended Headers
+
+Each NObject that is successfully read has an array of extended_header hashes.
+Each extended_header hash has 3 keys, :esize, :ecode, and :data corresponding
+to the available fields in the nifti header extension.
+
+Here's a silly example (since you'd probably want to read do this using
+something that's designed for it, like 3dinfo). Suppose you wanted to collect
+the history of an image from inside the AFNI extended header. Since the data
+of the AFNI extended header is just xml, you could easily parse it with an xml
+parser like nokogiri (using the AFNI ecode of 4 to select it from the list of
+extended headers, and assuming that there's only 1 AFNI header per file):
+
+ require 'nifti'; require 'nokogiri'
+ obj = Nifti::NObject.new('./T1.nii')
+ afni_extended_header = obj.extended_header.select{|ext| ext[:ecode] == 4 }[:data]
+ afni_xml = Nokogiri::XML(xml)
+ history = afni_xml.xpath("//AFNI_atr[@atr_name='HISTORY_NOTE']").first.children.first.text
-Copyright 2011 Erik Kastman
+=== Image Summary Statistics
+
+Again, this is a trivial example, but shows one usage of the library. Say you
+want to take find the mean and std dev of the entire image.
+
+ obj = Nifti::NObject.new('./T1.nii', :narray => true)
+ mean = obj.image.mean
+ stddev = obj.image.stddev
+
+If you don't have narray installed, you could still use obj.image as a ruby
+array, but you'd have to collect the summary stats yourself.
+
+
+== COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
View
@@ -5,6 +5,9 @@
# The following classes are meant to be used by users of Ruby DICOM:
# * NObject - for reading, manipulating and writing DICOM files.
+# Nifti is the main namespace for all Ruby NIfTI classes, constants and methods.
+module Nifti; end
+
# Core library:
require 'nifti/n_object'
require 'nifti/n_read'
View
@@ -1,5 +1,5 @@
module Nifti
- # Varaibles used to determine endianness.
+ # Variables used to determine endianness.
x = 0xdeadbeef
endian_type = {
Array(x).pack("V*") => false, # Little
View
@@ -12,11 +12,11 @@ class NObject
# A boolean which is set as true if a DObject instance has been successfully written to file (or successfully encoded).
attr_reader :write_success
# A hash of header information
- attr_reader :header
+ attr_accessor :header
# A hash of extended attributes
- attr_reader :extended_header
+ attr_accessor :extended_header
# An array or narray of image values
- attr_reader :image
+ attr_accessor :image
# Creates an NObject instance (NObject is an abbreviation for "Nifti object").
#
@@ -39,12 +39,12 @@ class NObject
#
# === Examples
#
- # # Load a Nifti file:
+ # # Load a Nifti file's header information:
# require 'nifti'
# obj = Nifti::NObject.new("test.nii")
- # # Read a Nifti file that has already been loaded into memory in a binary string:
- # obj = Nfiti::NObject.new(binary_string, :bin => true)
- # # Create an empty DICOM object & choose non-verbose behaviour:
+ # # Read a Nifti header and image into a numerical-ruby narray:
+ # obj = Nfiti::NObject.new("test.nii", :image => true, :narray => true)
+ # # Create an empty NIfTI object & choose non-verbose behaviour:
# obj = Nifti::NObject.new(nil, :verbose => false)
#
def initialize(string=nil, options={})
View
@@ -6,6 +6,7 @@
md5_hash(actual_file_path).should == md5_hash(exected_file_path)
end
+ # Calculate an md5 hash from a file path
def md5_hash(file_path)
Digest::MD5.hexdigest(File.read(file_path))
end
@@ -1,29 +1,43 @@
+# Reopen File to add a diff calculator for investigating byte differences between fixtures and created files.
class File
+ # Checks if two files contain the same contents. Prints and returns a
+ # position and values of differences, or returns nil if there were no
+ # differnces.
def File.same_contents(p1, p2)
f1 = open(p1).read
f2 = open(p2).read
+
+ # Control variables
+ differences = []
read_length = 1
same = true
index = 0
+
while ((index + read_length) < f1.length) && ((index + read_length) < f2.length)
same = f1.slice(index, read_length) == f2.slice(index, read_length)
unless same
puts index
pp f1.slice(index, read_length).unpack("C*")
pp f2.slice(index, read_length).unpack("C*")
+ differences << {:index => index,
+ :f1_value => f1.slice(index, read_length).unpack("C*"),
+ :f2_value => f2.slice(index, read_length).unpack("C*")
+ }
end
index = index + read_length
- # print '.'
+ differences
end
- # open(p1) do |f1|
- # open(p2) do |f2|
- # puts blocksize = f1.lstat.blksize
- # same = true
- # while same && !f1.eof? && !f2.eof?
- # same = f1.read(blocksize) == f2.read(blocksize)
- # end
- # return same
- # end
- # end
end
-end
+end
+
+# # Original comparison from Ruby Cookbook by Carlson & Richardson
+# open(p1) do |f1|
+# open(p2) do |f2|
+# puts blocksize = f1.lstat.blksize
+# same = true
+# while same && !f1.eof? && !f2.eof?
+# same = f1.read(blocksize) == f2.read(blocksize)
+# end
+# return same
+# end
+# end
@@ -98,6 +98,11 @@
File.exist?(@new_fixture_file_name).should be_true
end
+ it "should be able to assign an image" do
+ obj = NObject.new(@string, :bin => true, :image => true)
+ obj.image = [0] * @fixture_image_length
+ end
+
after :each do
File.delete @new_fixture_file_name if File.exist? @new_fixture_file_name
end

0 comments on commit 0090d86

Please sign in to comment.