Class StreamingApertureProcessor

All Implemented Interfaces:
Cloneable, ArrayProcessor, ArrayProcessorWithContextSwitching
Direct Known Subclasses:
ContinuedStreamingApertureProcessor

public abstract class StreamingApertureProcessor extends AbstractArrayProcessorWithContextSwitching

Streaming aperture matrix processor: an algorithm, processing one or several n-dimensional matrices and returning one resulting matrix, where the value of every element of the resulting matrix depends on (and only on) the source elements in an aperture with the fixed shape "around" the same position. This class allows to optimize such type of processing in all cases, when the source matrix is not direct accessible, for example, if it is very large and created via LargeMemoryModel: this class automatically splits the source matrix into blocks which can fit in Java memory, downloads them into (directly accessible) matrices, created by SimpleMemoryModel, and processes them by the abstract method asProcessed, overridden by the user. It is supposed that the type of matrix elements is one of primitive Java types (boolean, char, byte, short, int, long, float, double) and, so, represents an integer or a real number, according to comments to PFixedArray.getLong(long) and PArray.getDouble(long) methods. See below for more details.

First of all, let's define all terms and specify, what kind of algorithms can be performed by this class.

This class works with some AlgART matrix M, called the source matrix, some list of additional matrices M0, M1, ..., MK−1, called the additional arguments (K can be 0: an empty list), and some pattern P, called the aperture shape. The dimensions of all additional arguments always must be the same as the dimensions of the source matrix: Mk.dimEquals(M); the aperture shape must have the same number of dimensions: P.dimCount()==M.dimCount(). The aperture shape P is supposed to be an integer pattern; if a pattern, passed to the main process method of this class, is not integer, it is automatically rounded to the nearest integer pattern by the call pattern=pattern.round().

  1. For any integer point, or position x = (x0, x1, ..., xn−1), n=M.dimCount(), the aperture of this point, or the aperture at the position x, is a set of points xpi = (x0pi0, x1pi1, ..., xn−1pi,n−1) for all piP (points of the pattern P). We always consider that the point x lies inside M matrix (0≤xk<M.dim(k) for all k), but this condition can be not true for points of the aperture xpi.
     
  2. For every point x' = xpi of the aperture we consider the corresponding value vi of the source matrix M. More formally, vi it is the value of the element (integer: PFixedArray.getLong(long), if the type of the matrix elements is boolean, char, byte, short, int or long, or real: PArray.getDouble(long), if the element type is float or double) of the underlying array M.array() with an index M.pseudoCyclicIndex(x'0, x'1, ..., x'n−1), where x'k = xkpi,k. These values vi form the unordered set of N=P.pointCount() "neighbour" values.
     
  3. Also for the position x we consider an ordered list of K additional values w0, w1, ..., wK−1 — the values of corresponding elements of the additional arguments M0, M1, ..., MK−1. More formally, wk it is the value of the element (integer: PFixedArray.getLong(long), if the type of the matrix elements is boolean, char, byte, short, int or long, or real: PArray.getDouble(long), if the element type is float or double) of the underlying array Mk.array() with an index Mk.index(x0, x1, ..., xn−1).
     
  4. The streaming aperture processor is an algorithm, which transforms the source matrix M and the additional matrices M0, M1, ..., MK−1 to the resulting matrix R, every element r of which at the position x is the result of some processing function g with N+K arguments:
    r = g (v0, v1, ..., vN−1, w0, w1, ..., wK−1).
    The processing function g is a parameter of this object and is specified by the concrete implementation of asProcessed(Class, Matrix, List, Pattern) abstract method. As v0, v1, ..., vN−1 is unordered set with unspecified order of elements, this function must not depend on the order of its first N arguments vi. (But it usually depends on the order of the additional arguments wk.)

This class declares two base methods:

  • asProcessed(Class, Matrix, List, Pattern) abstract method, which fully defines the processing algorithm, i.e. the processing function g, and returns the result of this algorithm as a "lazy" matrix R (i.e. an immutable view of the passed source matrix, such that any reading data from it calculates and returns the resulting r elements);
  • and process(Matrix, Matrix, List, Pattern) non-abstract method, which really performs all calculations, using asProcessed method, and stores the resulting matrix R in its first argument dest.

Usually, this class really represents a streaming aperture processor, according the rules listed above. Such implementations of this class are called standard implementations. In standard implementations, it is enough to implement only the abstract method asProcessed. But it is also allowed this class to represent any other algorithm, that converts M matrix and a set of M0, M1, ..., MK−1 additional matrices, according to the given aperture shape P, to the resulting matrix R. Such implementations of this class are called non-standard implementations. In non-standard implementations, you must override both methods asProcessed and process. You can detect, is this implementation standard or no, with help of isStandardImplementation() method.

Note 1: the definition of the aperture above always supposes the pseudo-cyclic continuation of the source matrix, as described in Matrix.pseudoCyclicIndex(long...) method. You can use ContinuedStreamingApertureProcessor (an example of non-standard implementation of this class) to change this behaviour to another continuation way.

Note 2: it is easy to see that all basic morphology, defined in Morphology interface (in a simple case when the number of morphology pattern dimensions is equal to the number of the source matrix dimensions M.dimCount(), as this class requires), and all rank operations, defined in RankMorphology interface, are streaming aperture processors, when they use standard pseudo-cyclic continuation of the matrix (ContinuedMorphology and ContinuedRankMorphology work in other way). The basic morphology operations have no additional arguments Mk, as well as aperture sum rank operation. The percentile has 1 additional argument r, the rank has 1 additional argument v, the mean between percentiles and mean between values have 2 additional arguments. Most of filters, using in image processing, like the traditional linear filters, are also streaming aperture processors.

The main task, solved by this class, is a ready implementation of the second (process) method on the base of the first method (asProcessed) for a case of the standard implementations of this class. Namely, the implementation, offered by this class, performs the necessary calculations via one or more calls of asProcessed method, and it tries to optimize them by downloading parts of the source matrix M into quick accessible (i.e. direct accessible) temporary matrices, created by SimpleMemoryModel, and applying asProcessed to them. The idea of this optimization is the following. Usually, calculating the result of asProcessed for every aperture position x requires accessing to N elements of the source matrix in the aperture. If the source matrix is not direct accessible, it can require essential time, especially if it is created by LargeMemoryModel. But if you are using this class, you can call process method and be almost sure, that it will call asProcessed method with relatively little direct accessible temporary matrices. Such preloading usually increases performance in times even without any additional efforts; but if you want to provide maximal performance, you should check (in your implementation of asProcessed), whether the passed src matrix is direct accessible, and, probably, provide a special optimized branch of the algorithm which works with the internal Java array.

The amount of Java memory, which process method may spend for the optimization, is specified via maxTempBufferSize(PArray) method and usually corresponds to Arrays.SystemSettings.maxTempJavaMemory() limit.

This optimization is possible only in standard implementations, when your asProcessed method really represents some streaming aperture processor. It means that it calculates and returns the results of some processing function g (not depending on the order of first N arguments), as described in the definition above. If these conditions are not fulfilled, the result of process method can be incorrect. For example, a typical possible error while implementing asProcessed is using the value w of the element of the source matrix M at the position x. It can lead to error, because process method sometimes shifts the passed pattern and correspondingly shifts the source matrix M for optimization goals — so, your asProcessed method, while trying to access to the element of the source matrix M at the position x, will really read the value of another element instead. If you need to implement an algorithm, where the result element r at the position x depends also on the element of the source matrix at the same position x, you should pass the source matrix M also as one of additional Mk matrices. You can find an example of this technique in comments to RankProcessors.getPercentilePairProcessor method, where the usage of it for implementing the corresponding BasicRankMorphology methods is described.

Note that this class does not try to preload necessary parts of the additional Mk matrices into Java memory (i.e. temporary matrices, created by SimpleMemoryModel). The reason is that the aperture size N is usually much greater than the number of additional matrices K (in most applications K is from 0 to 2..3), so it is not too important to optimize access to additional matrices. But, if necessary, you can do it yourself in your asProcessed method by good implementing Array.getData(long, Object, int, int) method in the "lazy" built-in array of the resulting matrix. To do this, you need to load the corresponding subarrays of the additional arguments into your own Java arrays by the corresponding Array.getData(long, Object, int, int) calls for all arrays Mk.array() and then to process these Java arrays. The JArrayPool class can help you to minimize necessary memory allocations.

This package provides a set of methods for creating objects, extending this class for all basic rank operations, in RankProcessors class.

Warning: this class can process only patterns where pointCount()≤Integer.MAX_VALUE. More precisely, any methods of this class, which have Pattern argument, can throw TooManyPointsInPatternError or OutOfMemoryError in the same situations as Pattern.points() method.

The classes, implementing this interface, are usually immutable and thread-safe: there are no ways to modify settings of the created instance. It is not guaranteed for any classes, but it is guaranteed for all instances created by the methods of the classes of this package.

Author:
Daniel Alievsky
  • Constructor Details

    • StreamingApertureProcessor

      protected StreamingApertureProcessor(ArrayContext context)
      Creates an instance of this class with the given context.
      Parameters:
      context - the context used by this instance for all operations.
  • Method Details

    • context

      public StreamingApertureProcessor context(ArrayContext newContext)
      Description copied from class: AbstractArrayProcessorWithContextSwitching

      This method is implemented here via cloning this object (by standard clone() call) and replacing the value of the field, where a reference to the current context is stored, with newContext value. This technique is suitable for most implementation. However, if you need, you can override this method; maybe, it is enough to override clone() instead.

      Specified by:
      context in interface ArrayProcessorWithContextSwitching
      Overrides:
      context in class AbstractArrayProcessorWithContextSwitching
      Parameters:
      newContext - another context, used by the returned instance; may be null.
      Returns:
      new instance with another context.
    • isStandardImplementation

      public boolean isStandardImplementation()
      Returns true if there is a guarantee that this object is a standard implementations of this class. For non-standard implementation, this method usually returns false. See comments to StreamingApertureProcessor class for more details.
      Returns:
      whether this implementation is standard.
    • asProcessed

      public final <T extends PArray> Matrix<T> asProcessed(Class<? extends T> requiredType, Matrix<? extends PArray> src, Pattern pattern)
      Equivalent to asProcessed(requiredType, src, Matrices.several(PArray.class), pattern).
      Parameters:
      requiredType - desired type of the built-in array in the returned matrix.
      src - the source matrix M.
      pattern - the aperture shape P.
      Returns:
      the "lazy" matrix containing the result of this algorithm.
      Throws:
      NullPointerException - if one of the arguments is null.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires at least 1 additional matrix.
    • asProcessed

      public final <T extends PArray> Matrix<T> asProcessed(Class<? extends T> requiredType, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix, Pattern pattern)
      Equivalent to asProcessed(requiredType, src, Matrices.several(PArray.class, additionalMatrix), pattern).
      Parameters:
      requiredType - desired type of the built-in array in the returned matrix.
      src - the source matrix M.
      additionalMatrix - the additional matrix M0.
      pattern - the aperture shape P.
      Returns:
      the "lazy" matrix containing the result of this algorithm.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 1 additional matrix.
    • asProcessed

      public final <T extends PArray> Matrix<T> asProcessed(Class<? extends T> requiredType, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix1, Matrix<? extends PArray> additionalMatrix2, Pattern pattern)
      Equivalent to asProcessed(requiredType, src, Matrices.several(PArray.class, additionalMatrix1, additionalMatrix2), pattern).
      Parameters:
      requiredType - desired type of the built-in array in the returned matrix.
      src - the source matrix M.
      additionalMatrix1 - the additional matrix M0.
      additionalMatrix2 - the additional matrix M1.
      pattern - the aperture shape P.
      Returns:
      the "lazy" matrix containing the result of this algorithm.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 2 additional matrices.
    • asProcessed

      public final <T extends PArray> Matrix<T> asProcessed(Class<? extends T> requiredType, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix1, Matrix<? extends PArray> additionalMatrix2, Matrix<? extends PArray> additionalMatrix3, Pattern pattern)
      Equivalent to asProcessed(requiredType, src, Matrices.several(PArray.class, additionalMatrix1, additionalMatrix2, additionalMatrix3), pattern).
      Parameters:
      requiredType - the desired type of the built-in array in the returned matrix.
      src - the source matrix M.
      additionalMatrix1 - the additional matrix M0.
      additionalMatrix2 - the additional matrix M1.
      additionalMatrix3 - the additional matrix M2.
      pattern - the aperture shape P.
      Returns:
      the "lazy" matrix containing the result of this algorithm.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 3 additional matrices.
    • asProcessed

      public abstract <T extends PArray> Matrix<T> asProcessed(Class<? extends T> requiredType, Matrix<? extends PArray> src, List<? extends Matrix<? extends PArray>> additionalMatrices, Pattern pattern)
      Returns an immutable view of the passed source matrix M=src and the passed additional matrices Mk=additionalMatrices.get(k), such that any reading data from it calculates and returns the result R of this streaming aperture processor. See the comments to this class for more details.

      The matrix, returned by this method, is immutable, and the class of its built-in array implements one of the basic interfaces BitArray, CharArray, ByteArray, ShortArray, IntArray, LongArray, FloatArray or DoubleArray. The class of desired interface (one of 8 possible classes) must be passed as requiredType argument. So, it defines the element type of the returned matrix. The rules of casting the floating-point result of the processing function g to the desired element type depend on implementation. In many (but not all) implementations they are the same as in Arrays.asFuncArray(boolean, net.algart.math.functions.Func, Class, PArray...) method with the argument truncateOverflows=true.

      The concrete algorithm, implementing by this class, can require some number of additional arguments Mk. If the number of matrices in the passed list additionalMatrices is less than the required one, this method throws IllegalArgumentException. If the number of passed matrices is greater than the required one, it is not an error: the extra arguments are ignored.

      Usually you should use process method, which work faster than this method.

      Parameters:
      requiredType - the desired type of the built-in array in the returned matrix.
      src - the source matrix M.
      additionalMatrices - the additional matrices M0, M1, ..., MK−1.
      pattern - the aperture shape P.
      Returns:
      the "lazy" matrix containing the result of this algorithm.
      Throws:
      NullPointerException - if one of the arguments is null or if one of additionalMatrices elements is null.
      SizeMismatchException - if some passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if the number of additional matrices additionalMatrices.size() is less than the number of arguments, required by this implementation.
    • process

      public final void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src, Pattern pattern)
      Equivalent to process(dest, src, Matrices.several(PArray.class), pattern).
      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires at least 1 additional matrix.
    • process

      public final void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix, Pattern pattern)
      Equivalent to process(dest, src, Matrices.several(PArray.class, additionalMatrix1), pattern).
      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      additionalMatrix - the additional matrix M0.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 1 additional matrix.
    • process

      public final void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix1, Matrix<? extends PArray> additionalMatrix2, Pattern pattern)
      Equivalent to process(dest, src, Matrices.several(PArray.class, additionalMatrix1, additionalMatrix2), pattern).
      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      additionalMatrix1 - the additional matrix M0.
      additionalMatrix2 - the additional matrix M1.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 2 additional matrices.
    • process

      public final void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src, Matrix<? extends PArray> additionalMatrix1, Matrix<? extends PArray> additionalMatrix2, Matrix<? extends PArray> additionalMatrix3, Pattern pattern)
      Equivalent to process(dest, src, Matrices.several(PArray.class, additionalMatrix1, additionalMatrix2, additionalMatrix3), pattern).
      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      additionalMatrix1 - the additional matrix M0.
      additionalMatrix2 - the additional matrix M1.
      additionalMatrix3 - the additional matrix M2.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null.
      SizeMismatchException - if the passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if this implementation requires more than 3 additional matrices.
    • process

      public void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src, List<? extends Matrix<? extends PArray>> additionalMatrices, Pattern pattern)
      Processes the passed source matrix M=src and the passed additional matrices Mk=additionalMatrices.get(k) by this streaming aperture processor and stores the result R in dest argument. See the comments to this class for more details.

      This default implementations is based on one or more calls of asProcessed method and copying its result into dest matrix. The requiredType argument of asProcessed method is chosen as dest.type(PArray.class). If you need to create a non-standard implementation (a class which does not represent a streaming aperture processor, complying with strict definition from the comments to this class), you must override this method. You also may override this method, if it is possible to provide better performance than the default implementation, for example, for some specific variants of the aperture shape pattern.

      If the element type of dest matrix is not floating-point, then this method casts the floating-point result of the processing function g to the types of dest elements. The rules of casting depend on implementation and usually are the same as in asProcessed method. The default implementation does not need casting, because all necessary casting is already performed by asProcessed method.

      The concrete algorithm, implementing by this class, can require some number of additional arguments Mk. If the number of matrices in the passed list additionalMatrices is less than the required one, this method throws IllegalArgumentException. If the number of passed matrices is greater than the required one, it is not an error: the extra arguments are ignored.

      The aperture shape pattern, passed to this method, is automatically rounded to the nearest integer pattern by the operators pattern = pattern.round() in the very beginning of this method. In other words, this class is designed for processing integer aperture shapes only. It is the a normal situation for most aperture matrix processing algorithms.

      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      additionalMatrices - the additional matrices M0, M1, ..., MK−1.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null or if one of additionalMatrices elements is null.
      SizeMismatchException - if some passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount() or if the number of additional matrices additionalMatrices.size() is less than the number of arguments, required by this implementation.
    • maxTempBufferSize

      protected long maxTempBufferSize(PArray src)
      Specifies the maximal amount of usual Java memory, measured in elements of temporary arrays, that process method may freely use for optimization needs. The src arguments is M.array(), where M is the source processed matrix, passed to process method.

      By default, this method returns Math.round(maxTempJavaMemory/elementSize), where maxTempJavaMemory = Math.max(Arrays.SystemSettings.MIN_OPTIMIZATION_JAVA_MEMORY, Arrays.SystemSettings.maxTempJavaMemory()) and elementSize is the number of bytes, required for each element of src array (i.e. src.bitsPerElement()/8.0).

      You may override this method if you want to change this behaviour. For example, it can be necessary if your implementation of asProcessed method allocates some Java memory itself: in this case, you should correct the result of this method in such a way, that the total amount of allocated temporary Java memory will not exceed maxTempJavaMemory limit.

      Parameters:
      src - the built-in array of src argument of process method, from which this method is called.
      Returns:
      maximal amount of Java memory, in array elements, allowed for allocation by process method for optimization needs.
    • checkArguments

      protected static void checkArguments(Matrix<? extends PArray> dest, Matrix<? extends PArray> src, List<? extends Matrix<? extends PArray>> additionalMatrices, Pattern pattern)
      Checks whether the passed arguments are allowed arguments for process(Matrix, Matrix, List, Pattern) method and throws the corresponding exception if it is not so. Does nothing if the arguments are correct.

      More precisely, this method checks that all arguments are not null, all elements of additionalMatrices (if this list is not empty) are not null, dest and src matrices and elements of additionalMatrices (if this list is not empty) have the same dimensions and pattern.dimCount()==src.dimCount(). This method is called in the beginning of process method. It also can be used in the beginning of asProcessed method, with passing src argument of that method in a role of both dest and src arguments of this one.

      Parameters:
      dest - the resulting matrix R.
      src - the source matrix M.
      additionalMatrices - the additional matrices M0, M1, ..., MK−1.
      pattern - the aperture shape P.
      Throws:
      NullPointerException - if one of the arguments is null or if one of additionalMatrices elements is null.
      SizeMismatchException - if some passed matrices have different dimensions.
      IllegalArgumentException - if the number of the pattern dimensions pattern.dimCount() is not equal to src.dimCount().