Neuroimaging data comes in various formats, among of the more prominent ones are
Additionally, several other formats are often required to work with neuroimaging data (e.g. to have the capability to load in physiological measurements).
xff is meant to be a central hub to access those files in Matlab.
xff (Object Class) This class allows users to read and write diverse fileformats, as well as altering their contents in memory through a struct-like access. vmr = xff('new:hdr'); % create an SPM-compatible Analyze/NIftI header vmr = xff('colin.vmr'); % load the bundled 'colin.vmr' VMR object from disk vmr = xff('*.head' [, titleornumber]); % show file selector The storage is made in one global variable, xffcont, to keep only one copy of elements (full call-by-reference implementation), as of version v0.7b. To clear the storage, it is thus required to call one of the clearing methods: object.ClearObject; clearxffobjects({object, ...}); Properties in the objects are then accessible like struct fields: dimx = vmr.DimX; Methods are equally accessible and, if available, overload properties. Some methods work for all (several) object / file types: bbox = vmr.BoundingBox; unused.ClearObject; copied = vmr.CopyObject; filename = vmr.FilenameOnDisk; filetype = vmr.Filetype; firstvolume = vtc.GetVolume(1); vmr.ReloadFromDisk; vmr.Save; vmr.SaveAs(newfilename); vmr.SaveAs('*.vmr' [, title]); Alternatively, you can use the functional form: Call(vmr, 'MethodName' [, arguments]); To obtain help on object methods issue the call object.Help - or - object.Help('Method') Additionally, certain commands / functions can be executed via xff(0, command [, arguments]); where the following commands are supported xff(0, 'clearallobjects') clear the object storage (like clear in Matlab for objects WS) xff(0, 'clearobj', h) clear the objects with internal handle(s) h xff(0, 'config', globalsettingname [, globalsettingvalue]]); xff(0, 'config' [, type [, settingname [, settingvalue]]]); e.g. xff(0, 'config', 'vmr', 'autov16', false|true); make global class configuration (on types) xff(0, 'copyobject', h) make a copy of object with internal handle h xff(0, 'extensions') return supported extensions (struct with cell array fields) xff(0, 'formats') return file formats (struct with fields bff, tff, extensions, magic) xff(0, 'isobject', var) implementation of isxff function xff(0, 'magic') return just the file magic array (1xN struct) xff(0, 'makeobject', obj_struct) return the classified object (struct -> class constructor) xff(0, 'methods') return a struct with 1x1 struct field for each file type and sub fields for each method supported by this type xff(0, 'newcont', type) return the resulting struct of NewFileCode of Xff files xff(0, 'object', h) return the occording object with handle h (implements a check) xff(0, 'transiosize'); retrieve current transio size setting for all BFF (struct) xff(0, 'transiosize', tiostruct); sets transiosize (see last transiosize command below) xff(0, 'transiosize', 'vtc'); retrieve current transio size setting for one BFF (see next command) xff(0, 'transiosize', 'vtc', 1048576 [, boolean:updatecache]); this sets the minimum number of bytes before , instead of loading the data in a property field, creating a @transio object xff(0, 'unwindstack'); perform garbage collection (remove objects with unmatching dbstack) xff(0, 'updatecache'); updates the cache.mat file (with current transiosize settings) xff(0, 'updatedisable' [, type]); xff(0, 'updateenable' [, type]); xff(0, 'updatestate' [, type [, truefalse]]); xff(0, 'updatestate' [, truefalsestruct]]); disable/enable automatic object updating (on type field) for "undatestate" call can work on a 1x1 struct with type fields Note: the default constructor (xff without arguments) produces the so-called ROOT xff object with content fields Extensions, Formats, Magic, Methods containing the supported features of the class
To load a file from disk, it is often convenient to use the system's default file locator:
object = xff('*.hdr');
will open a file selector and expect the user to pick the desired file. Naturally, if known, a relative or absolute filename can be provided as well to load an object (e.g. within a script).
The class itself uses so-called binary and text format specification files which are stored in the folder NeuroElf_vXY/_core/formats folder as information about the file formats. These specification files are human readable, so they can also be used to code a specific reader in virtually any other programming language. The information from the same specification files is also used to write the files, which makes this a much more flexible and easier to maintain library of parsing information than using distinct reading and writing functions. Most notably excluded from this logic are DICOM and FIF files, which use their own IO function, mostly for speed issues.
The access to properties is using the syntax as for structs – the underlying data is, in fact, stored as a struct, and in principle all syntax valid for a struct is also valid for xff objects with one notable exception: if the field contents of several structs is accessed, Matlab does not (and cannot) say at runtime how many outputs this will produce; hence this will not work, and instead the field must first be accessed and then the subfield. Here are some examples:
To access the VoxelData associated with an Analyze/NIftI header object you can either access the entire field:
braindata = hdr.VoxelData;
or also just access part of the data:
brainslice = hdr.VoxelData(:, 30, :);
The exception kicks in when the field in question is a non-singleton struct, as is the case for BrainVoyager's VMP objects. If the object were a regular struct, the following syntax would work:
mapnames = {vmp.Map(:).Name};
This is the exception, for which a work-around is required! Instead use:
maps = vmp.Map; mapnames = {maps(:).Name};
(The object indexing operator, (:), is not required, but usually improves readability of the code, as the user then is aware that the accessed variable or field is not a singleton struct!)
With all file types, new objects can be created with default settings in place by using the following syntax:
vmr = BVQXfile('new:vmr');
Many files (e.g. especially BrainVoyager formats) are created with standard dimensions (e.g. TAL box for VTCs), and some will have data allocated already (e.g. VMRs) whereas others will be completely empty for not to allocate memory uselessly (e.g. VTCs).
For the VMR example above, it would be easy to create a random VMR:
vmr = xff('new:vmr'); vmr.VMRData = uint8(225 * rand([256, 256, 256]));
As a convenience, some fields (such as number of volumes for VTCs) are checked/replaced immediately before writing the file to disk, so as to make sure that the file content is valid.
In many (more object-oriented) programming languages, the concept of methods is used. That means that each “type” of object has a list of associated functions that can be called on that particular type. While Matlab (by now) fully supports deriving classes from super-classes and defining unique methods for each type, the syntax is still that of a regular function call.
xff instead uses the same overloading principle to read/write access object properties for calling functions. If, for example, a specific object type (say, the surface format of BrainVoyager, SRF) supports a method called Smooth
, the syntax is not Smooth(srf, …);
but instead srf.Smooth(…);
.
While this is, at first, rather unusual, it allows a more transparent access to the resulting variable. In case the return value of a method is struct, as in:
bb = vmr.BoundingBox;
with the struct overloading, it is perfectly valid to say:
res = vmr.BoundingBox.QuatT2B;
which otherwise would not work:
res = BoundingBox(vmr).QuatT2B; ??? some error message...
To write an object back to file, the common Save and SaveAs methods can be used:
vmr.Save; % save under the currently related filename vmr.SaveAs('colin_masked.glm'); % use a new filename
As a convenience function (and mostly useful to access files for which handles have been lost, i.e. in case of a memory leak) but also to get a list of objects of a given type, two additional features are available.
Creating an object of type xff without any argument will result in the so-called ROOT object. Using Matlab's internal display function on this object will show some stats about the class itself:
>> x = xff Extensions: [1x1 struct] Formats: [1x1 struct] Magic: [1x55 struct] Methods: [1x1 struct] List of currently loaded objects: -------------------------------------------------------------------------------- # | Type | Filename -------------------------------------------------------------------------------- 1 | ntt | /Volumes/Immersion/Analysis/Physio/10_4137.txt 2 | vmr | 3 | srf | /Applications/NeuroElf/NeuroElf_v09a/_files/colin/colin_RH_SPH.srf -------------------------------------------------------------------------------- Total MB occupied: 165.793
To access one of these objects, this ROOT object offers the .Documents
method:
handles = x.Documents
handles = '/Volumes/Immersion/Analysis/Physio/10_4137.txt' [ 2] '/Applications/NeuroElf/NeuroElf_v09a/_files/colin/colin_RH_SPH.srf'
whereas character handles in this cell array represent unique (first occurrence) filenames, and numeric values will allow access to unnamed or not-by-name specifiable objects. Access to one of these objects is then given via the .Document method:
srf = x.Document(handles{3});
Please be aware that numeric indices possibly change between several calls! For example, if the first object (NTT object) is removed from the class memory (using the ClearObject
on its object variable), then the numbered indices for handle 2 and 4 will be 1 afterwards!
Furthermore, to access only objects of a specific (list of) type(s), you can add a type specification to the root.Documents call:
vmrs = root.Documents('vmr'); static = root.Documents('vmr|srf');
A list of types can be created by chaining several types with the OR (pipe) character.
A concept that I took up from the Visual Basic family of languages is the Default property. If an object (e.g. a VTC = voxel-based time course) has one main property (in this case VTCData), and the object variable is singleton (almost always the case), the property name can be dropped. So, these two lines of code produce the same result:
sliceddata = vtc.VTCData(:, :, :, 20); % is the same as sliceddata = vtc(:, :, :, 20);
Matlab automatically refuses this syntax for objects without a default property or in case the variable (vtc) is, in itself, not of size 1×1.
In addition to pre-defined fields, it is also possible to store arbitrary information (text, variables, contrast definitions, etc.) with xff objects by creating new fields in the .RunTimeVars field. This data is not automatically saved when the object is written to disk, but only so upon calling the .SaveRunTimeVars
method. Then, all fields therein will be saved into a file with the same name but the “.rtv” extension (in fact a MAT-file). If several xff-accessible files share the same name (e.g. for PRT/SDM/VTC combinations of BrainVoyager projects), each object has its own “sub-space” in the .rtv-file, so information will not be overwritten by using the .RunTimeVars concept in more than one of these objects! Here is an example:
vmr = BVQXfile('subject_01.vmr'); vmr.RunTimeVars.Comments = 'Subject has seemingly deformed brain.'; vmr.SaveRunTimeVars;
The next time this VMR will be opened, the comments are available again.
To access information about available methods for any given type, use the .Help
method. This produces a list of methods and their syntax, e.g.:
>> vmr.Help ans = VMR::ApplyTRF - apply transformation to VMR / Syntax: newvmr = vmr.ApplyTRF(trf [, opts]) VMR::BoundingBox - get bounding box / Syntax: bbox = vmr.BoundingBox; VMR::Browse - add variable to GUI / Syntax: vmr.Browse; VMR::CleanVMR - try automatic cleaning (experimental !) / Syntax: cleaned = vmr.CleanVMR([opts]) VMR::ConvertToNLF - convert an object to NLF (if appropriate) / Syntax: vmr.ConvertToNLF([opts]); VMR::CoordinateFrame - generate coordinate matrices of VMR / Syntax: vmrc = vmr.CoordinateFrame([opts]); VMR::CreateVTC - mimick BV's VTC creation / Syntax: [vtc] = vmr.CreateVTC(fmr, afs, vtcfile, res, meth, bbox, dt); VMR::DBReco - direct border reconstruction / Syntax: srf = vmr.DBReco([bcol, scol]) VMR::GetVolume - get one volume from a (multi-volume) dataset / Syntax: y = vmr.GetVolume([volnum]) VMR::GetVolumeSize - get volume size from a (multi-volume) object / Syntax: ys = vmr.GetVolumeSize; VMR::GradientVMR - compute gradient VMR(s) / Syntax: [gvmr, gvmrx, gvmry, gvmrz] = vmr.GradientVMR([gdir]); VMR::HiResRescale - upsample ISO-Voxel VMR to higher resolution / Syntax: hresvmr = vmr.HiResRescale([res, imeth]) VMR::InhomogeneityCorrect - attempt automatic inhomogeneity correction / Syntax: [vmr = ] vmr.InhomogeneityCorrect([opts]) VMR::IntensityMask - mask a data object based on intensity values / Syntax: [nvox = ] vmr.IntensityMask(minvalue [, maxvalue [, opts]]) VMR::Layout - get layout signature / Syntax: layout = vmr.Layout; VMR::LimitVMR - apply intensity limits to V16 of VMR / Syntax: [vmr = ] vmr.LimitVMR([opts]) VMR::LoadV16 - load matching V16 file into VMRData16 / Syntax: [vmr] = vmr.LoadV16([v16fname]) VMR::LoResRescale - bring 0.5mm hires back to VMR space / Syntax: lores = vmr.LoResRescale([cutoff]) VMR::MarkWhiteMatter - mark white matter (e.g. for IC) / Syntax: wmvmr = vmr.MarkWhiteMatter([opts]) VMR::MaskWithVMR - zero values where VMR value beyond threshold / Syntax: [obj = ] vmr.MaskWithVMR(maskvmr [, threshold]) VMR::OverlaySRF - overlay an SRF into the VMR (data changed!) / Syntax: [vmr] = vmr.OverlaySRF(srf [, ocolor]) VMR::OverlayVMP - overlay a VMP into the VMR (data changed!) / Syntax: [vmr] = vmr.OverlayVMP(vmp [, vmpnum, vmpi]) VMR::PeelBrain - cheap try to peel the brain / Syntax: peeled = vmr.PeelBrain([thresh, nre, nrd]) VMR::PrepareForReco - prepare border for reconstruction / Syntax: [vmr] = vmr.PrepareForReco([sval, bval]) VMR::Reframe - reframe the VMR / Syntax: vmr.Reframe(bbox) VMR::SampleData3D - sample spatial data / Syntax: data = vmr.SampleData3D(crd [, opts]); VMR::SaveV16 - save matching V16 file into 16-bit VMR file / Syntax: [vmr] = vmr.SaveV16([v16fname]) VMR::SetConvention - set radiological/neurological convention / Syntax: [vmr = ] vmr.SetConvention(cnv) VMR::SetOrientation - set orientation/origin of file / Syntax: [mat44 = ] vmr.SetOrientation([opts]) VMR::SliceData3D - slice spatial data / Syntax: [sag, cor, tra, sc, data] = vmr.SliceData3D(crd [, opts]); VMR::SliceToTransimg - slice spatial data into transimg object(s) / Syntax: [sval, scrd = ] vmr.SliceToTransimg(crd, ti [, opts]); VMR::Talairach - Un/Talairachize VMR / Syntax: talvmr = vmr.Talairach(imeth, tal [, acpc, inverse]); VMR::Update - called after subsasgn for VMRs / Syntax: vmr.Update.m(...); VMR::WriteAnalyzeVol - write an Analyze image from the VMR volume / Syntax: [success = ] vmr.WriteAnalyzeVol(filename)
And, naturally, there are more details available. To get those, add the name of the method to the .Help call:
>> vmr.Help('GradientVMR') ans = VMR::GradientVMR - compute gradient VMR(s) FORMAT: [gvmr, gvmrx, gvmry, gvmrz] = vmr.GradientVMR([gdir]); Input fields: gdir flag whether to create directional VMRs too Output fields: gvmr gradient VMR (intensity of gradient) gvmrx X-gradient VMR (TAL X axis!) gvmry Y-gradient VMR (TAL Y axis!) gvmrz Z-gradient VMR (TAL Z axis!)
A complete list of all available methods (on all object types) can be produced by a call to Help using the root object:
x = xff; x.Help
The xff class interoperates with the transio class (see there for more information). To configure the thresholds for which data fields will not be read but rather a transio access point is created, use the syntax described in the help:
xff(0, 'transiosize', 'vtc', 65536);
With this command, every VTC that is opened afterwards will contain transio objects instead of large data arrays for fields that surpass the 64kB threshold. To set this threshold for all (binary) files, leave out the filetype:
xff(0, 'transiosize', 5e5);
This would set the threshold to roughly 500kb for all filetypes.