Class Quadruple3x5ThinningSkeleton2D

java.lang.Object
net.algart.arrays.AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
net.algart.matrices.skeletons.Quadruple3x5ThinningSkeleton2D
All Implemented Interfaces:
ArrayProcessor, IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>, ThinningSkeleton

public class Quadruple3x5ThinningSkeleton2D extends AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>> implements ThinningSkeleton

Algorithm of 2-dimensional skeletonization of binary matrices based on 4 thinning steps, corresponding to 4 directions with the step 90 degree, based on analysis of 3x5 aperture.

It is a stronger version of OctupleThinningSkeleton2D skeletonization, the standard variant without diagonal thinning: OctupleThinningSkeleton2D.getInstance(MemoryModel, Matrix, false, false). This class implements almost the same algorithm as OctupleThinningSkeleton2D, but the thinning method is more aggressive (and slow): it can remove pixels even with breaking connectivity in 3x3 pixel aperture, if the connectivity in 3x5 aperture is not broken. This algorithm also guarantees that 8-connected "objects" (areas filled by 1 elements) always stay 8-connected: see ThinningSkeleton interface about the precise sense of this state.

The typical "bad" cases for OctupleThinningSkeleton2D are successfully skeletonized by this algorithm in the following or similar way:

 . . . . 1 . . . .                     . . . . 1 . . . .
 . . . 1 . 1 . . .                     . . . 1 . 1 . . .
 . . 1 . 1 . 1 . .                     . . 1 . 1 . 1 . .
 . 1 . 1 1 1 . 1 .                     . 1 . . 1 . . 1 .
 1 . 1 1 1 1 1 . 1  is transformed to  1 . 1 1 1 1 1 . 1
 . 1 . 1 1 1 . 1 .                     . 1 . . 1 . . 1 .
 . . 1 . 1 . 1 . .                     . . 1 . 1 . 1 . .
 . . . 1 . 1 . . .                     . . . 1 . 1 . . .
 . . . . 1 . . . .                     . . . . 1 . . . .
 

Examples of the result in the "bad" cases, when some areas cannot be "thinned" by this algorithm:

 . . . . . . . .     . . . . . . .      . . . 1 . . .     . . . . . . . .     1 . . 1 . . 1 .
 . . . . . . . .     . 1 . . 1 . .      . 1 . 1 . 1 .     1 . . 1 . . 1 .     . 1 . 1 . 1 . .
 . . 1 . . 1 . .     . . 1 1 . . .      . . 1 1 1 . .     . 1 . 1 . 1 . .     . . 1 1 1 . . .
 . . . 1 1 . . .     . . 1 1 1 1 .      . . . 1 . . .     . . 1 1 1 . . .     1 1 1 1 1 1 1 .
 . . . 1 1 . . .     . 1 . 1 . . .      . . 1 1 1 . .     . . 1 1 1 . . .     . . 1 1 1 . . .
 . . 1 . . 1 . .     . . . 1 . . .      . 1 . 1 . 1 .     . 1 . 1 . 1 . .     . 1 . 1 . 1 . .
 . . . . . . . .     . . . . . . .      . . . 1 . . .     1 . . 1 . . 1 .     1 . . 1 . . 1 .
 

It is obvious that the left configuration cannot be thinned more without breaking connectivity.

I recommend to run this algorithm after finishing OctupleThinningSkeleton2D and WeakOctupleThinningSkeleton2D algorithms (for example, with help of IterativeArrayProcessor.chain(IterativeArrayProcessor, double) method).

This class is based on Matrices.asShifted method with some elementwise logical operations (AND, OR, NOT). So, the matrix is supposed to be infinitely pseudo-cyclically continued, as well Matrices.asShifted method supposes it. You can change this behavior by appending the source matrix with zero elements by calling Matrix.subMatrix(long[], long[], Matrix.ContinuationMode) method, where the dimensions of the "submatrix" are greater than dimensions of the source one by 1 and the continuationMode argument is Matrix.ContinuationMode.ZERO_CONSTANT.

This class may be applied to a matrix with any number of dimensions, but it is designed for 2-dimensional case: all other dimensions will be ignored.

This class is not thread-safe, but is thread-compatible and can be synchronized manually, if multithread access is necessary.

Author:
Daniel Alievsky
See Also:
  • Method Details

    • getInstance

      public static Quadruple3x5ThinningSkeleton2D getInstance(ArrayContext context, Matrix<? extends UpdatableBitArray> matrix)
      Creates new instance of this class.
      Parameters:
      context - the context that will be used by this object; may be null, then it will be ignored.
      matrix - the bit matrix that should be processed and returned by IterativeArrayProcessor.result() method.
      Returns:
      new instance of this class.
      Throws:
      NullPointerException - if matrix argument is null.
    • estimatedNumberOfIterations

      public long estimatedNumberOfIterations()
      Description copied from interface: IterativeArrayProcessor
      Estimates the number of iterations, that should be performed from this moment to finish the algorithm. Returns 0 if it is impossible or too difficult to estimate this number: it means that the remaining number of iteration is unknown.

      This method may require some time for its execution.

      You usually don't need to call this method: it is automatically called from time to time by IterativeArrayProcessor.process() method. It is used for creating subcontexts, describing a part of the full task.

      This method must be implemented while creating a new iterative array-processing algorithm.

      Specified by:
      estimatedNumberOfIterations in interface IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Returns:
      the estimated number of iterations, that should be performed from this moment to finish the algorithm.
    • asThinning

      public Matrix<BitArray> asThinning(int directionIndex)
      Returns current result() matrix thinned along the given direction. The result is "lazy": it is only a view of the current matrix.

      The precise algorithm of thinning is not documented. Generally speaking, the "thinning" means removing elements from the boundary of any "object" (area of the matrix filled by 1). directionIndex specifies the "eroded side" of objects, or the direction of thinning:

      • 0 means removing elements from the left, i.e. from the side (x−1,y),
      • 2 means removing elements from the side (x,y−1),
      • 4 means removing elements from the right, i.e. from the side (x+1,y),
      • 6 means removing elements from the side (x,y+1).
      Odd values (1, 3, 5, 7) are ignored by this method: for these values, the reference to the current result() matrix is returned.

      Though the algorithm is not documented, there are the following guarantees:

      • this algorithm never sets zero elements to unit: if the element of the current matrix with some coordinates (x0, y0) is 0, then the element with the same coordinates in the returned matrix is also 0;
      • each element of the returned matrix with coordinates (x0, y0) depends only on the elements in 3x5 or 5x3 aperture of the current matrix:
        • x0−1≤xx0+1, y0−2≤yy0+2, if directionIndex is 0 or 2,
        • x0−2≤xx0+2, y0−1≤yy0+1, if directionIndex is 1 or 3;
      • moreover, the element of the returned matrix with coordinates (x0, y0) does not depend on isolated unit elements in the aperture, specified above, i.e. if there is no 8-connected series of unit elements, connecting the central (x0, y0) unit element with another unit element (x, y) in this aperture, then the result value of the central element (will it be cleared or no) does not depend on (x, y) element of the current matrix. For example, in cases directionIndex=0 and directionIndex=2, if the central element is 1 and yy0, the (x, y) is isolated in the following three situations: a) y=y0+2 and all 3 elements with y=y0+1 are zero; b) y=y0+2, x=x0+1 and 3 elements (x0, y0+2), (x0, y0+1), (x0+1, y0+1) are zero; c) y=y0+2, x=x0−1 and 3 elements (x0, y0+2), (x0, y0+1), (x0−1, y0+1) are zero;
      • if all elements of the current matrix in 3x3 (not 3x5!) aperture x0−1≤xx0+1, y0−1≤yy0+1 are inside the matrix (i.e. 1≤x0dimX−2, 1≤y0dimY−2, dimX and dimY are dimensions of the matrix) and all they are equal to 1, then the element (x0, y0) in the returned matrix will be equal to 1.
      Specified by:
      asThinning in interface ThinningSkeleton
      Parameters:
      directionIndex - the direction of thinning, from 0 to 7.
      Returns:
      the thinned view if the current IterativeArrayProcessor.result() matrix.
      Throws:
      IllegalArgumentException - if directionIndex is not in 0..7 range.
    • toString

      public String toString()
      Returns a brief string description of this object.
      Overrides:
      toString in class Object
      Returns:
      a brief string description of this object.
    • performIteration

      public final void performIteration(ArrayContext context)
      Description copied from interface: IterativeArrayProcessor
      Performs the next iteration of the iterative algorithm. If the algorithm is IterativeArrayProcessor.done(), the results are unspecified: please never call this method if IterativeArrayProcessor.done() returns true.

      You usually don't need to call this method: please call IterativeArrayProcessor.process() instead. If you need to perform only one or n iterations, you may use limitIterations(n) call.

      Warning: this method should ignore the current execution context of this object. Instead, this method should use the context of execution specified by context argument. This method is called by IterativeArrayProcessor.process() method with the argument, describing a subtrask of the full algorithm. The context argument may be null: this method should work properly in this case (ignore the context).

      This method must be implemented while creating a new iterative array-processing algorithm.

      Specified by:
      performIteration in interface IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Specified by:
      performIteration in class AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Parameters:
      context - the context used by this instance for all operations; may be null.
    • done

      public final boolean done()
      Description copied from interface: IterativeArrayProcessor
      Returns true if and only if the algorithm was successfully finished and there is no sense to perform further iterations.

      This method usually does not perform actual calculations and works very quickly (just returns and internal flag). However, this condition is not strict.

      You usually don't need to call this method: it is automatically called by IterativeArrayProcessor.process() method.

      This method must be implemented while creating a new iterative array-processing algorithm.

      Specified by:
      done in interface IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Specified by:
      done in class AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Returns:
      true if and only if the algorithm was successfully finished.
    • result

      public final Matrix<? extends UpdatableBitArray> result()
      Description copied from interface: IterativeArrayProcessor
      Returns the result of the previous iteration. Usually it is UpdatableArray or Matrix<? extends UpdatableArray>. This method returns valid result even if no iterations were performed yet. If IterativeArrayProcessor.done() method returns true, the result of this method is the final result of iterative processing performed by this instance.

      This method may return null. In this case, the concrete implementation of this interface should provide additional methods for returning calculation results.

      This method does not perform actual calculations and works very quickly.

      This method must be implemented while creating a new iterative array-processing algorithm.

      Specified by:
      result in interface IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Specified by:
      result in class AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Returns:
      the result of the previous iteration (may be null).
    • freeResources

      public final void freeResources(ArrayContext context)
      Description copied from interface: IterativeArrayProcessor
      If there are some resources, allocated by this object, which are not controlled by Java garbage collectors — files, streams, sockets, locks, etc. — this method tries to release them (for example, to close any files). The object stays alive: if the resources will be necessary for following operations, they will be automatically re-acquired.

      Usually, this method just calls Array.freeResources(context) and Matrix.freeResources(context) for all temporary arrays and matrices, allocated by this object for storing work data.

      If IterativeArrayProcessor.result() method returns AlgART array or matrix (typical situation), this method calls Array.freeResources(context) / Matrix.freeResources(context) methods for this array / matrix.

      This method may be used in situations when the instance of this object has long time life and will be reused in future.

      This method must be implemented while creating a new iterative array-processing algorithm.

      Specified by:
      freeResources in interface IterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Specified by:
      freeResources in class AbstractIterativeArrayProcessor<Matrix<? extends UpdatableBitArray>>
      Parameters:
      context - the context of execution; may be null, then it will be ignored.
    • isThinningRequired

      public final boolean isThinningRequired(int directionIndex)
      Description copied from interface: ThinningSkeleton
      Returns true if and only if performIteration(ArrayContext) method really calls ThinningSkeleton.asThinning(int directionIndex) for this direction and copies its result to the result matrix. It depends on the implementation of this interface.
      Specified by:
      isThinningRequired in interface ThinningSkeleton
      Parameters:
      directionIndex - the direction of thinning, from 0 to 7.
      Returns:
      whether the matrix should be thinned along this direction.