MaskedLattice.h
Classes
- MaskedLattice -- A templated, abstract base class for array-like objects with masks. (full description)
Interface
- Public Members
- MaskedLattice() : itsDefRegPtr(0)
- MaskedLattice (const MaskedLattice<T>&)
- virtual ~MaskedLattice()
- virtual MaskedLattice<T>* cloneML() const = 0
- virtual Lattice<T>* clone() const
- virtual Bool isMasked() const
- virtual Bool hasPixelMask() const
- virtual const Lattice<Bool>& pixelMask() const
- virtual Lattice<Bool>& pixelMask()
- const LatticeRegion& region() const
- Bool getMask (COWPtr<Array<Bool> >& buffer, Bool removeDegenerateAxes=False) const
- Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const Slicer& section, Bool removeDegenerateAxes=False) const
- Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const
- Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const
- Bool getMask (Array<Bool>& buffer, Bool removeDegenerateAxes=False)
- Bool getMaskSlice (Array<Bool>& buffer, const Slicer& section, Bool removeDegenerateAxes=False)
- Bool getMaskSlice (Array<Bool>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False)
- Bool getMaskSlice (Array<Bool>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False)
- Array<Bool> getMask (Bool removeDegenerateAxes=False) const
- Array<Bool> getMaskSlice (const Slicer& section, Bool removeDegenerateAxes=False) const
- Array<Bool> getMaskSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const
- Array<Bool> getMaskSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const
- virtual Bool doGetMaskSlice (Array<Bool>& buffer, const Slicer& section)
- Protected Members
- MaskedLattice<T>& operator= (const MaskedLattice<T>&)
- virtual const LatticeRegion* getRegionPtr() const = 0
Review Status
- Date Reviewed:
- yyyy/mm/dd
- Programs:
- Demos:
Prerequisite
- IPosition
- Abstract Base class Inheritance - try "Advanced C++" by James
O. Coplien, Ch. 5.
Etymology
Lattice: "A regular, periodic configuration of points, particles,
or objects, throughout an area of a space..." (American Heritage Directory)
This definition matches our own: an n-dimensional arrangement of items,
on regular orthogonal axes.
Synopsis
This pure abstract base class defines the operations which may be performed
on any concrete class derived from it. It has only a few non-pure virtual
member functions.
The fundamental contribution of this class, therefore, is that it
defines the operations derived classes must provide:
- how to extract a "slice" (or sub-array, or subsection) from
a Lattice.
- how to copy a slice in.
- how to get and put a single element
- how to apply a function to all elements
- various shape related functions.
Lattices are always zero origined.
Example
Because Lattice is an abstract base class, an actual instance of this
class cannot be constructed. However the interface it defines can be used
inside a function. This is always recommended as it allows Functions
which have Lattices as arguments to work for any derived class.
I will give a few examples here and then refer the reader to the
ArrayLattice class (a memory resident
Lattice) and the PagedArray class (a
disk based Lattice) which contain further examples with concrete
classes (rather than an abstract one). All the examples shown below are used
in the dLattice.cc demo program.
Example 1:
This example calculates the mean of the Lattice. Because Lattices can be too
large to fit into physical memory it is not good enough to simply use
getSlice to read all the elements into an Array. Instead the
Lattice is accessed in chunks which can fit into memory (the size is
determined by the maxPixels and niceCursorShape
functions). The LatticeIterator::cursor() function then returns
each of these chunks as an Array and the standard Array based functions are
used to calculate the mean on each of these chunks. Functions like this one
are the recommended way to access Lattices as the
LatticeIterator will correctly
setup any required caches.
Complex latMean(const Lattice<Complex>& lat) {
const uInt cursorSize = lat.advisedMaxPixels();
const IPosition cursorShape = lat.niceCursorShape(cursorSize);
const IPosition latticeShape = lat.shape();
Complex currentSum = 0.0f;
uInt nPixels = 0u;
RO_LatticeIterator<Complex> iter(lat,
LatticeStepper(latticeShape, cursorShape));
for (iter.reset(); !iter.atEnd(); iter++){
currentSum += sum(iter.cursor());
nPixels += iter.cursor().nelements();
}
return currentSum/nPixels;
}
Example 2:
Sometimes it will be neccesary to access slices of a Lattice in a nearly
random way. Often this can be done using the subSection commands in the
LatticeStepper class. But it is also
possible to use the getSlice and putSlice functions. The following example
does a two-dimensional Real to Complex Fourier transform. This example is
restricted to four-dimensional Arrays (unlike the previous example) and does
not set up any caches (caching is currently only used with PagedArrays). So
only use getSlice and putSlice when things cannot be done using
LatticeIterators.
void FFT2DReal2Complex(Lattice<Complex>& result,
const Lattice<Float>& input){
AlwaysAssert(input.ndim() == 4, AipsError);
const IPosition shape = input.shape();
const uInt nx = shape(0);
AlwaysAssert (nx > 1, AipsError);
const uInt ny = shape(1);
AlwaysAssert (ny > 1, AipsError);
const uInt npol = shape(2);
const uInt nchan = shape(3);
const IPosition resultShape = result.shape();
AlwaysAssert(resultShape.nelements() == 4, AipsError);
AlwaysAssert(resultShape(3) == nchan, AipsError);
AlwaysAssert(resultShape(2) == npol, AipsError);
AlwaysAssert(resultShape(1) == ny, AipsError);
AlwaysAssert(resultShape(0) == nx/2 + 1, AipsError);
const IPosition inputSliceShape(4,nx,ny,1,1);
const IPosition resultSliceShape(4,nx/2+1,ny,1,1);
COWPtr<Array<Float> >
inputArrPtr(new Array<Float>(inputSliceShape.nonDegenerate()));
Array<Complex> resultArray(resultSliceShape.nonDegenerate());
FFTServer<Float, Complex> FFT2D(inputSliceShape.nonDegenerate());
IPosition start(4,0);
Bool isARef;
for (uInt c = 0; c < nchan; c++){
for (uInt p = 0; p < npol; p++){
isARef = input.getSlice(inputArrPtr,
Slicer(start,inputSliceShape), True);
FFT2D.fft(resultArray, *inputArrPtr);
result.putSlice(resultArray, start);
start(2) += 1;
}
start(2) = 0;
start(3) += 1;
}
}
Example 3:
Occasionally you may want to access a few elements of a Lattice without
all the difficulty involved in setting up Iterators or calling getSlice
and putSlice. This is demonstrated in the example below and uses the
parenthesis operator, along with the LatticeValueRef companion
class. Using these functions to access many elements of a Lattice is not
recommended as this is the slowest access method.
In this example an ideal point spread function will be inserted into an
empty Lattice. As with the previous examples all the action occurs
inside a function because Lattice is an interface (abstract) class.
void makePsf(Lattice<Float>& psf) {
const IPosition centrePos = psf.shape()/2;
psf.set(0.0f); // this sets all the elements to zero
// As it uses a LatticeIterator it is efficient
psf(centrePos) = 1; // This sets just the centre element to one
AlwaysAssert(near(psf(centrePos), 1.0f, 1E-6), AipsError);
AlwaysAssert(near(psf(centrePos*0), 0.0f, 1E-6), AipsError);
}
Motivation
Creating an abstract base class which provides a common interface between
memory and disk based arrays has a number of advantages.
- It allows functions common to all arrays to be written independent
of the way the data is stored. This is illustrated in the three examples
above.
- It reduces the learning curve for new users who only have to become
familiar with one interface (ie. Lattice) rather than distinct interfaces
for different array types.
Member Description
Default constructor.
Copy constructor.
a virtual destructor is needed so that it will use the actual destructor
in the derived class
virtual MaskedLattice<T>* cloneML() const = 0
virtual Lattice<T>* clone() const
Make a copy of the object (reference semantics).
virtual Bool isMasked() const
Has the object really a mask?
The default implementation returns True if the MaskedLattice has
a region with a mask.
Does the lattice have a pixelmask?
The default implementation returns False.
Get access to the pixelmask.
An exception is thrown if the lattice does not have a pixelmask.
Get the region used.
This is in principle the region pointed to by getRegionPtr.
However, if that pointer is 0, it returns a LatticeRegion for the
full image.
Bool getMask (COWPtr<Array<Bool> >& buffer, Bool removeDegenerateAxes=False) const
Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const Slicer& section, Bool removeDegenerateAxes=False) const
Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const
Bool getMaskSlice (COWPtr<Array<Bool> >& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const
Bool getMask (Array<Bool>& buffer, Bool removeDegenerateAxes=False)
Bool getMaskSlice (Array<Bool>& buffer, const Slicer& section, Bool removeDegenerateAxes=False)
Bool getMaskSlice (Array<Bool>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False)
Bool getMaskSlice (Array<Bool>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False)
Array<Bool> getMask (Bool removeDegenerateAxes=False) const
Array<Bool> getMaskSlice (const Slicer& section, Bool removeDegenerateAxes=False) const
Array<Bool> getMaskSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const
Array<Bool> getMaskSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const
Get the mask or a slice from the mask.
This is the mask formed by combination of the possible pixelmask of the
lattice and the possible mask of the region taken from the lattice.
If there is no mask, it still works fine.
In that case it sizes the buffer correctly and sets it to True.
The function (in the derived classes) doing the actual work.
These functions are public, so they can be used internally in the
various Lattice classes.
However, doGetMaskSlice does not call Slicer::inferShapeFromSource
to fill in possible unspecified section values. Therefore one
should normally use one of the getMask(Slice) functions. doGetMaskSlice
should be used with care and only when performance is an issue.
The default implementation gets the mask from the region
and fills the buffer with True values if there is no region.
MaskedLattice<T>& operator= (const MaskedLattice<T>&)
Assignment can only be used by derived classes.
Get a pointer to the region used.
It can return 0 meaning that the MaskedLattice is the full lattice.