In AIPS++, we have used the ability to call many things by one generic name (Lattice) to create a number of classes which have different storage techniques (e.g. core memory, disk, etc...). The name Lattice should make the user think of a class interface (or member functions) which all Lattice objects have in common. If functions require a Lattice argument, the classes described here may be used interchangeably, even though their actual internal workings are very different.
// Make an Array of shape 3x4x5 Array<Float> simpleArray(IPosition(3,3,4,5)); // fill it with a gradient for (Int k=0; k<5; k++) for (Int j=0; j<4; j++) for (Int i=0; i<3; i++) simpleArray(IPosition(3,i,j,k)) = i+j+k; // use the array to create an ArrayLattice. ArrayLattice<Float> lattice(simpleArray);
// Create a PagedArray from a Table already existing on disk. PagedArray<Float> lattice(fileName); // Create a LatticeIterator to access the Lattice in optimal tile // shaped chunks. LatticeIterator<Float> iter(lattice); // Iterate through and do something simple; here we just // sum up all the values in the Lattice Float dSum = 0; for(iter.reset(); !iter.atEnd(); iter++) { dSum += sum(iter.cursor()); }
// simple route - define a cursor shape that is the xy plane of our lattice. IPosition cursorShape(2, lattice.shape()(0), lattice.shape()(1)); LatticeIterator<Float> iter(lattice, cursorShape); for (iter.reset(); !iter.atEnd(); iter++) { minMax(iter.cursor(), min, max); }
// make an empty PagedArray and fill it. The Table that stores the // PagedArray is deleted when the PagedArray goes out of scope PagedArray<Float> lattice(IPosition(4,100,200,300,50)); LatticeIterator<Float> iter(lattice, IPosition(2, 100, 200)); // fill each plane with the "distance" of the iterator from the origin for(iter.reset();!iter.atEnd(); iter++) { iter.woCursor() = iter.nsteps(); }
// The shape of our Lattice - a 4 dimensional image of shape (x,y,z,t) - // and the shape of the cursor IPosition latticeShape(image.shape()); IPosition cursorShape(3, lattticeShape(0), 1, latticeShape(2)); // Define the path the cursor should follow, we list x and z first, even though // no iterations will be done along those axes since the cursor is an // integral subshape of the Lattice. The cursor will move along the y-axis // and then increment the t-axis. The construct the Navigator and Iterator IPosition order(4,0,2,1,3); LatticeStepper nav(latticeShape, cursorShape, order); LatticeIterator<Float> iter(image, nav);
// Set up a TiledLineStepper to return profiles along the specified // axis from a PagedArray (not all Lattices have the tileShape member // function). Then create the iterator as well. TiledLineStepper nav(lattice.shape(), lattice.tileShape(), axis); LatticeIterator<Complex> nav(lattice, nav);
There is a rich variety of region
classes which can be used to define a LatticeRegion in pixel coordinates.
The elementary ones are box,
ellipsoid,
polygon,
pixelset, and
good/bad mask.
Compound region classes can be used to make a
union,
intersection,
difference,
concatenation,
complement, or
extension
from one or more regions.
Apart from these region classes, class
LCSlicer can be used to define
a box with optional strides. It also offers the opportunity to
define the box in fractions or to define it relative to the
center of the lattice or relative to a reference pixel.
The final, and most general way, to define regions is by
means of the world coordinates region classes in the
Images module, in particular
the WCRegion class.
However, world coordinate regions can only be used with images.
When the expression consists of images, the result can also be treated as an image using class ImageExpr. With the command function in ImageExprParse it is possible to parse and execute a LEL expression given as as a string.
The following are listed for low-level programmers. Lattice users need not understand them.The Lattice directory contains several files relevant only to implementation.