Teem 1.10.0 released

After 3 years of development and tweaking, Teem 1.10.0 has been released. There are some API changes; see below for full details.

Teem now builds well with CMake, see instructions at http://teem.sourceforge.net/build.html

It has been about three years since the last release 1.9. Since that
time, Gordon has been working on getting publications and a job (bye
Red Sox, hello White Sox), but Teem development has continued. Here
is a summary of the changes that people might notice since Teem 1.9.
The major API changes are likely to affect Teem-using software, the
smaller API changes less so. Only the most important API additions
are listed.

It is unlikely that there are functionality changes that are not
accompanied by an API change. So teem-using code should break first
because of failures to link, not because of subtler behavioral chages.
Best of luck, and remember to email teem-users:


in case of problems or questions.

Over-all Changes ===================================================

Thanks to the efforts of James Bigler and Andy Cedilnik, The CMake
build system for Teem is mature, and should most certainly be used
instead of the old GNUMakefiles.

In particular, CMake now builds for Teem a single "mega" library that
puts all the sub-libraries (air, biff, hest, etc) into a single place.
There is currently no single "mega" *header* file.

Thanks to Steve Pieper, there's a new demo of how CMake projects can
use Teem in teem/UseTeemCMakeDemo

Also thanks to Steve Pieper, Teem can now link with VTK's name-mangled
versions of the PNG and zlib libraries, via the TEEM_VTK_* options in

Teem's CMake now generally refers to Teem as "Teem" instead of "TEEM".
This may break existing things that used CMake to build and use Teem.

Teem now optionally uses the "levmar" library from
as a new "external" for non-linear least-squares fitting.

Anticipating the eventual removal of the GNUMakefile-based system for
building Teem, all the architecture-specific directories (darwin.32,
irix6.64, linux.32, linux.ia64, cygwin, darwin.64, irix6.n32,
linux.amd64, solaris) have beem moved from the top-level teem
directory into a new teem/arch subdirectory. Perhaps confusingly,
this includes the win32 directory, even though that was (directly)
handled by GNUMake.

Major API Change ==================================================-

gage ---------

gage_t is gone. Use "double" instead.

GAGE_ITEM_PREREQ_NUM changed from 5 to 6, then to 8, and also

If a volume doesn't identify itself as cell- or node-centered, gage
now assumes cell-centered (in Teem 1.9 gaged assumed node-centered).

GAGE_KERNEL_NUM #define gone, now GAGE_KERNEL_MAX+1 plays its role


compile-time limit on the number of pervolumes in a context is
gone. The pvl[] array (and ctx->pvlNum) is now dynamically managed
with an airArray (ctx->pvlArr). As a result, the stackFslw[] array is
now also allocated dynamically (whenever the stack is attached)

FIXED long-standing bug (how long I don't know) in how the derivatives
were normalized in volumes with only per-axis spacing (that is,
lacking full orientation info)- some users have noticed this change

added matrices ItoWSubInvTransp and ItoWSubInv to gageShape, to handle
more general transforms from index to world space (now gage correctly
handles shear from gantry tilt, for example). Thus, REMOVED fwScale[]
from gageShape, because its role has been taken over by
ItoWSubInvTransp and ItoWSubInv.

gageScl3PFilter2, gageScl3PFilter4, and gageScl3PFilterN now take a
gageShape* as a mandatory first argument, because this stores the
ItoWSubInvTransp and ItoWSubInv matrices used to transform gradients
and hessians from index space to world space. Note that this also
shifts the responsibility for this transform from something that is
strictly internal to gage, to something that is not strictly internal:
each gageKind is now responsible for doing this transform as part of
its filter() callback. In practice, there is no real change, because
all existing kinds ended calling the same gageScl3PFilter2,
gageScl3PFilter4, and gageScl3PFilterN functions, since they all
operate per-component.

ten -----------

renamed enum value tenEstimateMethod* -> tenEstimate1Method*

tenFiberTypeEvec1 now refers to the medium eigenvector NOT the
principal one (that's tenFiberTypeEvec0)

tenInvariantGradients_d() gone, replaced by more specific
tenInvariantGradientsK_d() and tenInvariantGradientsR_d()

The tenAnisoCalc_f function has been removed. It was one of the earliest
and stupidest functions in the ten library, it was incapable of high accuracy
for mode-related invariants, and it has been superceded by other functions.

Specifically, use tenAnisoEval_f() or tenAnisoEval_d() to go from pre-computed
eigenvalues to a particular anisotropy value, or use tenAnisoTen_f() or
tenAnisoTen_d() to go from the full tensor value to an anisotropy value.
Some functions (like tenAniso_Cl1 and related) make more sense computed from
the individual eigenvalues, but others (like tenAniso_FA) can be quickly
computed directly from the tensor value, no eigensolution required.

The tenAniso_* enum and tenAniso airEnum are alive and well, and are used
with the tenAniso{Eval,Ten}_{f,d} to identify which anisotropy to compute.

Minor API change, no one probably notices ==========================

air ---------
airRandMTStateGlobal is no longer allocated at compile time. Now it is
both allocated and initialized as needed at run time. This will create
a memory-in-use-at-exit situation in valgrind.

airBesselI0Scaled --> airBesselI0ExpScaled
airBesselI1Scaled --> airBesselI1ExpScaled
airBesselInScaled --> airBesselInExpScaled

nrrd ------
removed nrrdSpaceGet() (or its public declaration)- Teem's structs
are open for reading.

changed nrrdResample_t from float to double; whatever is released
should use higher precision by default; other people can make it lower
precision for their own builds if they want.

changed NrrdIoState->byteSkip from int to long int, in keeping with
type of offset argument to fseek

changed _nrrdContainsPercentDAndMore(const char *str) to the slightly
more general purpose: _nrrdContainsPercentThisAndMore(const char *str,
char this) The only reason this was a public function was so that
unrrdu/dice could use it, but unrrdu/dice should be changed to use the
new nrrdSaveMulti anyway.

As part of cleaning up kernels, some single-letter kernel
identifications were removed from _nrrdKernelStrToKern(), because they
were too clever, and too apt to be confused:

- if (!strcmp("z", str)) return nrrdKernelZero;
- if (!strcmp("b", str)) return nrrdKernelBox;
- if (!strcmp("t", str)) return nrrdKernelTent;
- if (!strcmp("c", str)) return nrrdKernelBCCubic;
- if (!strcmp("cd", str)) return nrrdKernelBCCubicD;
- if (!strcmp("cdd", str)) return nrrdKernelBCCubicDD;
- if (!strcmp("q", str)) return nrrdKernelAQuartic;
- if (!strcmp("qd", str)) return nrrdKernelAQuarticD;
- if (!strcmp("qdd", str)) return nrrdKernelAQuarticDD;
- if (!strcmp("g", str)) return nrrdKernelGaussian;

unrrdu / unu -------

nrrdStateKeyValuePairsPropagate is now set to AIR_TRUE if user hasn't
already tried to set it via environment variable

gage --------

GAGE_QUERY_BYTES_NUM changed from 8 to 16, then to 24

Some variables in the gageContext that were "int" have now been
changed to "unsigned int"; should have been done a lot time ago.
* gageContext->fd
* gagePoint->xi, yi, zi

removed gageShapeUnitWtoI() and gageShapeUnitItoW() (the latter is
still used internally as _gageShapeUnitItoW) because they were never
outside of gage

tweaked the logic of how continuous index-space space positions are
mapped to discrete index space and offsets (in _gageLocationSet),
and added test/indx function for debugging it.

nixed the _gageStandardPadder() and _gageStandardNixer() functions
that have long since been made obsolete (ever since gage stopped doing
its own padding prior to probing)

limn ---------
limnPolyData completely reorganized to have multiple per-attribute
arrays, but hopefully no one is using this as yet.

limn now depends on gage. In previous Teem versions, this was because
of how the feaure extraction needed gage, but now it is simply because
there is some plan of having more gage-specific per-vertex attribute
information stored in the limnPolyData

limnObjectOFFRead -> limnObjectReadOFF
limnObjectOFFWrite -> limnObjectWriteOFF
limnPolyDataIVWrite -> limnPolyDataWriteIV
limnPolyDataLMPDRead -> limnPolyDataReadLMPD
limnPolyDataLMPDWrite -> limnPolyDataWriteLMPD
limnPolyDataVTKWrite -> limnPolyDataWriteVTK
limnPolyDataOFFRead -> limnPolyDataReadOFF

ten ---------

tenGradientParm no longer has "srand" flag, and now has an explicit
"seed" value, to allow reproducible results. "tend grads" has different
command-line options accordingly

tenAnisoPlot(Nrrd *nout, int aniso, unsigned int res,
int hflip, int whole, int nanout) now has the "hflip" horizontal
flip flag.

Removed the tenAniso_Cz anisotropy measure. It was never used in Teem,
its not used in the literature, and its odd because its unbounded.

tenEvqOne --> tenEvqSingle
tenMakeOne_f --> tenMakeSingle_f
tenMakeOne_d --> tenMakeSingle_d
tenSimulateOne_f --> tenSimulateSingle_f
various tenPath... -> tenInterp...

push -------
The whole library is still under major revision, may get nixed
entirely if "pull" does everythings its supposed to

Major API New ======================================================

nrrd -------
negative byteskip now compatible with gzip encoding. Functionality
contributed by Katharina Quintus

nrrdDistanceL2() and nrrdDistanceL2Signed() are fast separable
Euclidean distance transforms, based on code by Pedro Felzenszwalb,
accessible in "unu dist"

added nrrdKernelDiscreteGaussian, which implements the discrete
Gaussian described by Lindeberg. Thanks to Raul San Jose.

added nrrdKernelSpecSprint(), which is like the opposite of
nrrdKernelSpecParse(): it prints the kernel specification to a
string. New code added to test/tkernel.c to demo this (as well as
adding airMop use). To make the sprinted result parse'able, it was
discovered that sometimes the kernel's self-identification wasn't
actually recognized as identifying that kernel by
nrrdKernelParse(). This has been fixed

gage -------

added the ability to probe in "stacks"- where a single volume is
replaced by an array of volumes, with some kind of kernel-based
interpolation between volumes. This could be used for generic 4-D
volumes, but more importantly it allows probing in scale-space.
Implementing this functionality was made possibly only because gage
has always allowed multiple per-volumes to be probed simultaneously
in a single context. This needs to be documented elsewhere in detail,
but briefly:
gageStackBlur() pre-computes the stack with different blurring levels,
gageStackPerVolumeNew() allocates the per-volume array
gageStackPerVolumeAttach() attaches the per-volume array
gageStackProbe() does the probing at given 4-D position
gageStackProbeSpace() does 4-D probing, with extra tricks

Internal change that shouldn't be much of an API change if at all: the
value of the first, unknown, item (e.g. gageSclUnknown) changes from
-1 to 0, and gageKinds have a new dynamicAlloc member to indicate if
the kind is dynamically allocated. The semantics of other gageKind
members has been clarified relative to this, and the initialization of
the item tables (e.g. _gageSclTable in teem/src/gage/scl.c) is simpler
now that the pre-requisite lists don't have to be filled out with -1s
(because C guarantees that uninitialized struct members will be
zero). If gage users assumed that the unknown item had value -1 (and
in fact, vprobe and pprobe stupidly did make this assumption), then
your code has to change.

The reason that gageSclUnknown used to be -1 was that in the earliest
days of gage (when its brains were actually part of the bane library),
queries were represented by bitflags stored in int or a long long int,
and I didn't want to waste a bit (the first one) on representing an
item value that would never be used.

limn ----------

limnPolyDataSave() does auto-magic format detection based on filename

ten --------

tenDwiGageKind is the first dynamic gageKind, meaning that information
is actually allocated in the gageKind, rather than being completely
set at compile time (as is gageKindScl, gageKindScl, and tenGage).
This is because the number of DWIs is set at run-time. You currently
create a tenDwiGageKind with tenDwiGageKindNew() and kill it with
tenDwiGageKindNix(). You set all the stuff that needs to be set at
run-time with tenDwiGageKindSet(), but the API of this function is
pretty much guaranteed to change in future releases. tenDwiGageKind
also represents the first use of gageKind->data and
gagePerVolume->data, which were added to gage long ago, but never
exploited until now.

added tenFiberSingle, a new struct (based on a class previously in
Deft) for handling all information about a single tractography result
(following a single direction from a single seedpoint). Also added
functions for this: tenFiberSingleInit(), tenFiberSingleDone(),
tenFiberSingleNew(), tenFiberSingleNix(), and most importantly,
tenFiberSingleTrace(), which actually does tractography.

added tenFiberMulti, a new struct for containing an array of
tenFiberSingles (basically an airArray), which is the output of
tenFiberMultiTrace(). The main purpose of this function is to break
the existing one-to-one correspondance between seed points and fibers,
which happens with multi-fiber tractography.

added tenFiberMultiPolyData(), which converts an airArray of
tenFiberSingle's into limnPolyData. Like the tenFiberSingle, this is
another example of code moving from Deft into Teem.

"tend fiber" has been enhanced to make more of this functionality
available, so it is finally able to do multiple tracts from a
given list of seedpoints, instead of being merely a one-track-pony.

There are many, many more tenGage items now, including some from
Thomas Schultz, see teem/src/ten/ten.h for the full details. The
proliferation of these has actually led to some changes in gage's
compile-time limits, and in the future we may have to move to a more
modular system where items are added dynamically at run-time. How to
do this without breaking the existing API will be tricky.

seek --------
New library for doing things like isosurfacing and ridge surface
extraction. API will be in flux for some time.

pull --------
New library for particle systems. API will be in flux.

Minor API New, not such a big deal =================================

air ---------
addded airRandMTSanity() to check return of airUIrandMT_r against
sequence of known values

airInsane_AIR_NAN_f, and airInsane_AIR_NAN_d represent new ways that
airSanity() can fail

airMode3_d() returns the "mode" of three values- really the skewness
scaled to fit in [-1,1]

airEqvAdd() and airEqvSettle() might be useful for managing information
about equivalence classes

airIntPow(v,p) == pow(v,p), but optimized for integral p

airEnumLast() returns the last (valid) value in an airEnum

airFastExp(), a fast but crude (and buggy?) approximation to exp()

nrrd --------

added nrrdUnaryOpIf and "unu 1op if"

nrrdFormatEPS (an output-only format) now understands 4-channel
images as CMYK, exploiting the same implicit semantics as the
PostScript colorimage operator, it turns out.

nrrdClampConvert() is like nrrdConvert() (which is essentially per-value
casting), but now values are clamped so that they don't wrap-around
due to casting. "unu convert -clamp" calls this.

Fixed memory leak in nrrdProject(): scan-line buffer wasn't being
freed (reported by Jorik Blaas)

added nrrdKernelC4Hexic, nrrdKernelC4HexicD, nrrdKernelC4HexicDD- the
unique 6-sample support C4 3rd-order accurate 6th-order polynomial

added nrrdSaveMulti(), which is like nrrdSave(), but saves
an array of Nrrds to some set of files, based on a sprintf-style
formatted string. Eventually "unu dice" will use this.

added nrrdBinaryOpNormalRandScaleAdd, for adding scaled gaussian noise
to given data. Accessible with "unu 2op nrand"

gage --------

gageDeconvolve is a super-dumb way to deconvolve a given volume
(of any gageKind) with a given kernel. Can be used to pre-sharpen
an image so that convolution with, e.g., cubic:1,0 is interpolating.

"vprobe" program now has TEEM_VPROBE_HACK_ZI environmental variable
hack which allows probing to only be done on a single Z slice

added gageScl3PFilter_t typedef, which simplifies the declaration of
gageScl3PFilter2 and gageScl3PFilter4 and: added gageScl3PFilter6,
gageScl3PFilter8, which were easy to implement now that the body of
these functions is in the new file scl3pfilterbody.c, which is
included as if its a macro (bad hack!)

added gageErr airEnum, and now gage's ctx->errNum takes values from
the gageErr* enum

ell --------

added ell_3v_angle_f() and ell_3v_angle_d() for properly measuring
angle between two unit vectors

more stupid macros

added ell_6m_mul_d, and ell_6ms_eigensolve_d for doing Jacobi-based
eigensolves of symmetric 6-by-6 matrices

limn --------

limnPolyDataVertexWindingFix() might work to fix vertex winding problems

limnPolyDataVertexNormals() might compute per-vertex normals

limnPolyDataCCFind() might find connected components in polydata

limnPolyDataWriteIV() can kind of save polydata to Inventor files

limnPolyDataLMPDRead() and limnPolyDataLMPDWrite() read and write a
stupid new "LMPD" polydata format, which will probably change a lot,
but I needed a way of saving limnPolyData that took a long time to compute

limnPolyDataVTKWrite() is mostly a VTK writer for limnPolyData

limnPolyDataOFFRead() is mostly a OFF reader of limnPolyData

limnPolyDataClip(), limnPolyDataVertexWindingSplit() do what the names
suggest, but badly

limnPolyDataSpiralTubeWrap() is new Teem home for the clever single
tri-strip tube wrapping that used to be in Deft.

ten ---------

"tend grads" is now based on a much faster gradient descent algorithm

added teem/src/ten/test/tensorDotDat, which is designed to generate
the information that can replace GE's broken tensor.dat DWI gradient
list file

tenEstimateContext functionality can now handle skipping some of the

various strings associated with key/value pairs in DWMRI headers
TEN_EXPORT const char *tenDWMRIModalityKey;
TEN_EXPORT const char *tenDWMRIModalityVal;
TEN_EXPORT const char *tenDWMRIBValueKey;
TEN_EXPORT const char *tenDWMRIGradKeyFmt;
TEN_EXPORT const char *tenDWMRIBmatKeyFmt;
TEN_EXPORT const char *tenDWMRINexKeyFmt;

tenFiber: added tenFiberStopRadius termination criterion based on
radius of curvature of fiber tracks, and tenFiberStopStub to represent
the fact that although the seedpoint was okay, neither direction of
the fiber went far enough to matter.

Added midpoint integration (AKA 2nd order Runge-Kutta)
tenFiberIntgMidpoint, and an airEnum for tenFiberIntg

"tend satin" teem/src/ten/tendSatin.c now uses full orientation,
instead of just axis-aligned spacing

tenExp() and tenLog() take exp() and log() of tensors

added enum values tenEstimate2Method*

more macros:

Added the _tenAnisoTen_f[] and _tenAnisoTen_d[] arrays of functions,
and the associated tenAnisoTen_f() and tenAnisoTen_d() function wrappers,
to complement tenAnisoEval_{f,d}(): these provide a way of evaluating a
given anisotropy measure given the tensor, whereas tenAnisoEval_{f,d}()
provide a way of evaluating a given aniosotropy given the soted eigenvalues.

added tenPath* functions and enums, including geodesic-loxodrome
functionality. All still very much under construction...

tenFiberContextDwiNew() returns a fiber context wrapped around a DWI
volume, instead of a tensor volume, for some new functionality of
deterministic tractography on DWIs instead of single tensors.

added tenTripleConvert_d() and tenTripleConvert_f() for converting
between the various triples of scalars that come up in DTI
(J, R, and K invariants, eigenvalues)

tenDoubleContract_d() for double contraction of a fourth-order
tensor with two 2nd-order tensors

added tenFiberStopAnisoSet(), tenFiberStopDoubleSet(),
tenFiberStopUIntSet() so that stop criteria can be set without using

Posted by Gordon Kindlmann 2008-12-10

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

No, thanks