Class Boundary2DScanner
- Direct Known Subclasses:
Boundary2DWrapper
2-dimensional object boundaries scanner: the class allowing to trace boundaries of objects, "drawn" at some 2-dimensional bit matrix.
More precisely, let's consider some 2-dimensional AlgART bit matrix
Matrix
<? extends BitArray
>.
Below we shall designate this matrix as M.
Let's define a pixel with integer coordinates (x, y)
as a set of points of the plane with such coordinates (x', y') that
Every unit element of the matrix M with coordinates
(x, y) corresponds to a pixel (x, y) at the plane.
Let's designate IM the figure (point set) consisting of all points of all pixels
(squares with the side 1.0), corresponding to unit (1) elements of our matrix M.
We can consider IM as an image (figure), "drawn" at the matrix M.
Every unit element in M represents a little square 1x1 (a pixel) in the image (figure) IM,
and the center of the pixel has integer coordinates in ranges
dimX()
−1,dimY()
−1.
Then, let's consider a connected object at the matrix M, defined in the same terms
as in ConnectedObjectScanner
class, and corresponding connected figure
in the image IM. As well as in that class, a connected object can have straight-and-diagonal
connectivity (8-connected object) or straight connectivity (4-connected object).
The first case corresponds to a usual connected area in the image IM,
the second case — to a connected area in the figure, coming out from IM by removing
all points with half-integer coordinates.
We define the boundary of some connected object as the geometrical boundary of the corresponding connected figure in the image IM. More precisely, the boundary of the connected object is a connected component of the full set of the boundary points (not pixels, but infinitesimal points of the plane) of the corresponding connected figure. So, the connected object can have several boundaries, if there are some "holes" in it. Any boundary is a chain of horizontal or vertical segments with the length 1.0, that separate pixels from each others. The ends of each segment have half-integer coordinates, and the 2nd end of the last segment coincides with the 1st end of the first segment.
We define the main boundary of the connected object as its boundary containing whole this object inside it.
We define the completion of the connected object as the sets of all points lying at or inside its main boundary. In other words, the completion is the object, where all internal "holes" ("pores") are filled. If the connected object has no "holes", its completion is identical to it.
Each segment with length 1.0 in any object boundary is a boundary of some pixel, belonging to the image IM. These pixels can lie 1) inside the boundary, and then it is true for all segments of the boundary, or 2) outside the boundary, and then it is true for all segments of the boundary.
In the first case we shall call the boundary as external, and in the second case we shall call it as internal. A connected object always have only one external boundary, namely, its main boundary. But a connected object can have several internal boundaries: these are boundaries of all its "holes".
This class represents a boundary scanner: an iterator allowing to trace all segments
of one boundary — in the clockwise order for external boundaries, in the anticlockwise order
for internal boundaries (if the x axis is directed rightwards and
the y axis is directed downwards). The basic method of this iterator is next()
.
In addition, this class allows to sequentially visit all boundaries or all main boundaries
of all connected objects; it is performed by the method nextBoundary()
.
The boundary scanner always has the current position. The position consists of:
x()
-coordinate of the current pixel;y()
-coordinate of the current pixel;- the current pixel
side()
: index of one of 4 sides of the square 1x1, represented byBoundary2DScanner.Side
enumeration class.
There is the only exception, when the scanner has no any position —
directly after creating new instance. The first call of nextBoundary
or goTo
method sets some position.
In other words, the current position specifies some segment with length 1.0. This segment can be an element of some object boundary, but also can be a random pixel side in the image.
There are two basic methods of this class, changing the current position. The first method is
nextBoundary()
: it moves the current position to the nearest next object boundary,
according to some rules depending on a concrete kind of scanner. After calling this method you may be sure
that the current position specifies a segment of some object boundary.
The second method is next()
: it supposes that the current position specifies a segment of a boundary
and, if it's true, moves the current position to the next segment of this boundary.
So, you can find the next object boundary by nextBoundary()
method
and then scan it by sequential calls of next()
method.
Instead of manual loop of next()
calls, you can use scanBoundary(ArrayContext)
method.
We suppose that all possible positions are sorted in the following "natural" order: the position x1, y1, side1 is "less" than the position x2, y2, side2,
- if y1<y2,
- or if y1=y2 and x1<x2,
- or if y1=y2, x1=x2
and
side1. (i.e., for the same coordinates,ordinal()
<side2.ordinal()
X_MINUS
<Y_MINUS
<X_PLUS
<Y_PLUS
).
We also suppose that the "undefined" position, when the scanner is newly created and
nextBoundary
or goTo
methods
were not called yet, is "less" than all other positions.
This order is used by nextBoundary()
method.
There are the following ways to create an instance of this class:
getSingleBoundaryScanner(Matrix, ConnectivityType)
,getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
,getMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
,- extending
Boundary2DWrapper
class, - using some ready wrappers like
Boundary2DSimpleMeasurer
orBoundary2DProjectionMeasurer
.
The difference between instances, created by first 3 methods, is in the behavior of
nextBoundary()
and next()
: see comments to these instantiation methods.
The Boundary2DWrapper
class and its inheritors just call some parent boundary scanner and,
maybe, do some additional work (for example, measure the objects).
The instance of this class always works with some concrete matrix and some concrete connectivity type, specified while creating the instance, and you cannot switch an instance of this class to another bit matrix. But this class is lightweight: there is no problem to create new instances for different matrices.
You must not use this instance after any modifications in the scanned matrix, performed by an external code. If you modify the matrix, you must create new instance of this class after this.
Below is a typical example of using this class:
Boundary2DScanner
scanner =Boundary2DScanner.getAllBoundariesScanner
(m, um1, um2, connectivityType);Boundary2DSimpleMeasurer
measurer =Boundary2DSimpleMeasurer.getInstance
(scanner, EnumSet.of(Boundary2DSimpleMeasurer.ObjectParameter.AREA
)); while (measurer.nextBoundary()
) { measurer.checkInterruption
(ac); measurer.updateProgress
(ac); measurer.scanBoundary
(ac); long area = measurer.area()
; // some operations with the found area }
Note: this class works much faster (in several times)
if the scanned matrix is created by SimpleMemoryModel
,
especially if its horizontal dimension dimX()
is divisible by 64
(dimX()
%64==0).
So, if the matrix is not created by SimpleMemoryModel
and is not too large,
we recommend to create its clone by SimpleMemoryModel
,
expanded by x to the nearest integer divisible by 64, and use this class for the clone.
Note: this class can process only 2-dimensional matrices. An attempt to create an instance of this class for a matrix with other number of dimensions leads to IllegalArgumentException.
This class does not use multithreading optimization, unlike
Arrays.copy(ArrayContext, UpdatableArray, Array)
and similar methods.
In other words, all methods of this class are executed in the current thread.
This class is not thread-safe, but is thread-compatible and can be synchronized manually, if multithread access is necessary. Warning! Even if you use in several different threads different instances of this class, created via one of the following methods:
getAllBoundariesScanner(Matrix matrix, Matrix buffer1, Matrix buffer2, ConnectivityType)
,getMainBoundariesScanner(Matrix matrix, Matrix buffer, ConnectivityType)
,
then you either must pass different buffer matrices in different threads, or manually synchronize all called methods. In other case, the content of buffer matrices will be unspecified and behavior of the scanning algorithm will be undefined.
- Author:
- Daniel Alievsky
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic enum
The pixel side.static class
The step of scanning the boundary: moving from one boundary segment to the next boundary segment. -
Method Summary
Modifier and TypeMethodDescriptiondouble
area
(ContourLineType contourLineType) Returns the oriented area inside the contour line, following along the scanned boundary, estimated according the specified contour line type.abstract boolean
Returns true if and only if thecurrent pixel side
lies at the boundary of thescanned matrix
.abstract boolean
Returns true if and only if the current position (x()
,y()
,side()
) is identical to the position, set by last call ofnextBoundary()
orgoTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.final void
checkInterruption
(ArrayContext context) Calls context.checkInterruption()
or does nothing if context==null.abstract ConnectivityType
Returns the connectivity kind, used by this object.abstract boolean
abstract long
Returns the index of the current pixel in theunderlying array
of the currently scanned matrix.abstract long
Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding todiagonal steps
.final long
dimX()
final long
dimY()
static void
fillHoles
(Matrix<? extends UpdatableBitArray> result, Matrix<? extends BitArray> source, ConnectivityType connectivityType) Makes completion of the source binary matrix and returns it in the result matrix.static Matrix<UpdatableBitArray>
fillHoles
(MemoryModel memoryModel, Matrix<? extends BitArray> source, ConnectivityType connectivityType) Makes completion of the source binary matrix and returns it in the newly created matrix.abstract boolean
get()
Returns the value of the current element of the currently scanned matrix.static Boundary2DScanner
getAllBoundariesScanner
(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer1, Matrix<? extends UpdatablePFixedArray> buffer2, ConnectivityType connectivityType) Creates an instance of this class, allowing to sequentially trace all segments of all boundaries at the matrix (internal and external).static Boundary2DScanner
getMainBoundariesScanner
(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer, ConnectivityType connectivityType) Creates an instance of this class, allowing to trace all segments of main boundaries at the matrix and to build completions of all objects.static Boundary2DScanner
getSingleBoundaryScanner
(Matrix<? extends BitArray> matrix, ConnectivityType connectivityType) Creates an instance of the simplest kind of this class, allowing to trace all segments of a single boundary (internal or external).abstract void
goTo
(long x, long y, Boundary2DScanner.Side side) Sets the current position in the matrix to the specified coordinates and pixel side.final void
goToSamePosition
(Boundary2DScanner scanner) Sets the current position in the matrix to the same as in the specified scanner.abstract boolean
Returns true if and only if this scanner is an all boundaries scanner.abstract boolean
Returns true if and only if this instance was positioned to some coordinates in the matrix.final boolean
Returns true if and only if .side()
==Side.X_PLUS
abstract boolean
Returns true if and only if this scanner is a main boundaries scanner.abstract boolean
Returns true if and only if this scanner is already positioned (isInitialized()
returns true) and, in addition,next()
orscanBoundary(ArrayContext)
methods were called at least once.abstract boolean
Returns true if and only if this scanner is a single boundary scanner.abstract Boundary2DScanner.Step
lastStep()
Returns information about the movement of the current position, performed by the last call ofnext()
method.matrix()
Returns the reference to the currently scanned matrix.abstract long
abstract void
next()
Move the current position to the next segment of the currently scanned object boundary.abstract boolean
Finds the next vertical segment, belonging to some object boundary, after the current position, and sets the current position to the found one.abstract long
Returns the oriented area inside the boundary, traversed bynext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method.double
perimeter
(ContourLineType contourLineType) Returns the total length of the contour, following along the scanned boundary: perimeter of the measured object, "drawn" at the bit matrix, estimated according the specified contour line type.abstract void
Resets the counters, returned bystepCount()
andorientedArea()
method, and all other counters, that are possibly increased by inheritors of this class.abstract long
Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding torotation steps
.final long
Equivalent ofscanBoundary(null)
.final long
scanBoundary
(ArrayContext context) Scans the current boundary.abstract Boundary2DScanner.Side
side()
Returns the current pixel side (or throws IllegalStateException if this scanner was notpositioned yet
).abstract long
Returns the total number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method.final long
Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding tostraight steps
.toString()
Returns a brief string description of this object.final void
updateProgress
(ArrayContext context) Calls context.updateProgress(event)
with an event, created by the following operator:new ArrayContext.Event(boolean.class, , or does nothing if context==null.currentIndexInArray()
,matrix()
.size()
)abstract long
x()
Returns the current x-coordinate (or throws IllegalStateException if the scanner was notpositioned yet
).abstract long
y()
Returns the current y-coordinate (or throws IllegalStateException if this scanner was notpositioned yet
).
-
Method Details
-
getSingleBoundaryScanner
public static Boundary2DScanner getSingleBoundaryScanner(Matrix<? extends BitArray> matrix, ConnectivityType connectivityType) Creates an instance of the simplest kind of this class, allowing to trace all segments of a single boundary (internal or external).In the created instance:
nextBoundary()
method finds (after the current position) the nearest vertical segment, belonging to some object boundary, sets the current position to the found one and does nothing else.
next()
method switches to the next segment in the current object boundary and does nothing else.
This instance does not save anywhere the fact of tracing the boundary. So, it is not convenient for scanning all boundaries of some kind in the matrix:
nextBoundary()
method will find the same boundary many times, at least 2 times for every horizontal line intersecting the boundary.- Parameters:
matrix
- the matrix that will be scanned by the created instance.connectivityType
- the connectivity kind used by the created instance.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if one of arguments is null.IllegalArgumentException
- if matrix.dimCount()
is not 2.
-
getAllBoundariesScanner
public static Boundary2DScanner getAllBoundariesScanner(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer1, Matrix<? extends UpdatablePFixedArray> buffer2, ConnectivityType connectivityType) Creates an instance of this class, allowing to sequentially trace all segments of all boundaries at the matrix (internal and external).The scanner, created by this method, works with two additional matrices buffer1 and buffer2, that are used for marking already visited boundary segments. These matrices can have any fixed-point element type (but usually it is boolean) and must have the same dimensions as the main matrix. These matrices should be zero-initialized before using the created instance (in other case, some boundaries are possible to be skipped). One of these matrices is always current. In the state 1, the current buffer matrix is buffer1; in the state 2, the current buffer matrix is buffer2. The state 1 is default: it is chosen after creating the scanner.
While scanning boundaries, inside the
next()
method, this scanner writes "brackets" in the current buffer matrix. It means that:- when the
current pixel side
isX_MINUS
, the element with coordinatesx()
,y()
in the current buffer matrix is set to 1 ("opening bracket"), - when the
current pixel side
isX_PLUS
, the element with coordinatesx()
+1,y()
in the current buffer matrix is set to 1 ("closing bracket"), or nothing occurs ifx()
+1>=matrix.dimX()
, - nothing occurs if the
current pixel side
isY_MINUS
orY_PLUS
.
This behavior is the same as in main boundaries scanner created by
getMainBoundariesScanner
method.The
nextBoundary()
method in this scanner finds (after the current position) the nearest vertical segment, belonging to some object boundary, which was not visited yet bynext()
method, and sets the current position to the found one. "Not visited" means that no "brackets" are set for that position neither in buffer1 nor in buffer2 matrix. (There is the only exception from this simple rule: this method never stops at the right side of a last pixel in the horizontal line. If the last element in the horizontal line is 1, the corresponding boundary — its right side — is always skipped, andnextBoundary()
method searches for the next unit element in next lines. The only case when it can be important is callingnextBoundary()
after direct positioning bygoTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method.)If the new found position corresponds to a left pixel side (
side()
isSide.X_MINUS
), this method changes the current state to state 1. It means an external boundary, if the scanning the matrix was started outside any boundaries, in particular, ifgoTo
method was never called.If the new found position corresponds to a right pixel side (
side()
isSide.X_PLUS
), this method changes the current state to state 2 It means an internal boundary, if the scanning the matrix was started outside any boundaries, in particular, ifgoTo
method was never called.While searching the next non-visited boundary,
nextBoundary()
method counts "brackets" in 1st and 2nd buffer matrices and corrects the currentnesting level
.It is possible to specify the same matrix as both buffer1 and buffer2 arguments. In this case, all will work normally excepting the
nesting level
, which will be calculated incorrectly.This instance is convenient for scanning all boundaries in the matrix. To do this, it's possible to use the following loop:
Boundary2DScanner
scanner =getAllBoundariesScanner
(m, um1, um2, connectivityType); while (scanner.nextBoundary()
) { scanner.scanBoundary
(ac); // or some more useful actions }- Parameters:
matrix
- the matrix that will be scanned by the created instance.buffer1
- the 1st buffer matrix for writing "brackets" (usually indicates external boundaries).buffer2
- the 2nd buffer matrix for writing "brackets" (usually indicates internal boundaries). To save memory, you may pass here the same matrix as buffer1 and buffer2 arguments, but in this case thenestingLevel()
method will work incorrectly.connectivityType
- the connectivity kind used by the created instance.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if one of arguments is null.IllegalArgumentException
- if matrix.dimCount()
is not 2.SizeMismatchException
- if the passed matrices have different dimensions.
- when the
-
getMainBoundariesScanner
public static Boundary2DScanner getMainBoundariesScanner(Matrix<? extends BitArray> matrix, Matrix<? extends UpdatablePFixedArray> buffer, ConnectivityType connectivityType) Creates an instance of this class, allowing to trace all segments of main boundaries at the matrix and to build completions of all objects.The scanner, created by this method, works with the additional matrix buffer, where completions of all objects are stored as a result of the scanning. This matrix can have any fixed-point element type (but usually it is boolean) and must have the same dimensions as the main matrix. This matrix should be zero-initialized before using the created instance (in other case, some boundaries are possible to be skipped).
While scanning boundaries, inside the
next()
method, this scanner writes "brackets" in the buffer matrix. It means that:- when the
current pixel side
isX_MINUS
, the element with coordinatesx()
,y()
in the buffer matrix is set to 1 ("opening bracket"), - when the
current pixel side
isX_PLUS
, the element with coordinatesx()
+1,y()
in the buffer matrix is set to 1 ("closing bracket"), or nothing occurs ifx()
+1>=matrix.dimX()
, - nothing occurs if the
current pixel side
isY_MINUS
orY_PLUS
.
This behavior is the same as in all boundaries scanner created by
getAllBoundariesScanner
method.The
nextBoundary()
method in this scanner is more complicated. If the element of the buffer matrix at the current position is zero, it just finds the nearest vertical segment, belonging to some object boundary, after the current position (alike in the simplest scanner returned bygetSingleBoundaryScanner
method). In other case we suppose that we are at the "open bracket" (the beginning of a series of unit elements), andnextBoundary()
method does the following:- finds the next unit element #p in the buffer matrix in the same horizontal line ("close bracket", written while previous scanning the boundary of the current object);
- fills all elements in the buffer matrix from the current position (inclusive) until the found #p position at this line (exclusive) by 1;
- clears the element #p in the buffer matrix to 0;
- and finds the nearest vertical segment in the main matrix, belonging to some object boundary, after all elements filled at step 2.
If the next unit element was not found in the current line at step 1, all buffer elements until the end of the horizontal line are filled by 1 — the p index is supposed to be
matrix()
.dimX()
— and the step 3 is skipped.In fact,
nextBoundary()
method skips all interior of previously scanned boundaries and fills this interior by 1 in the buffer matrix. As a result, the buffer matrix will contain completions of all objects after finishing scanning the matrix.This instance is convenient for scanning main boundaries in the matrix and, as a side effect, for calculating completions of all objects. To do this, you may call
fillHoles(Matrix, Matrix, ConnectivityType)
method or use the equivalent loop:Boundary2DScanner
scanner =getMainBoundariesScanner
(m, um, connectivityType); while (scanner.nextBoundary()
) { scanner.scanBoundary
(); // or some more useful actions } // now um contains the completions of all objects drawn in m // (if um was initially zero-filled)- Parameters:
matrix
- the matrix that will be scanned by the created instance.buffer
- the buffer matrix for writing "brackets" and filling holes.connectivityType
- the connectivity kind used by the created instance.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if one of arguments is null.IllegalArgumentException
- if matrix.dimCount()
is not 2.SizeMismatchException
- if the passed matrices have different dimensions.
- when the
-
fillHoles
public static Matrix<UpdatableBitArray> fillHoles(MemoryModel memoryModel, Matrix<? extends BitArray> source, ConnectivityType connectivityType) Makes completion of the source binary matrix and returns it in the newly created matrix. Equivalent to the following code:Matrix
result = memoryModel. newBitMatrix
(source.dimensions());fillHoles
(result, source, connectivityType);- Parameters:
memoryModel
- the memory model, used for creating the result matrix.source
- the source bit matrix.connectivityType
- the connectivity kind used while building completion.- Throws:
NullPointerException
- if one of argument is null.IllegalArgumentException
- if matrix.dimCount()
is not 2.
-
fillHoles
public static void fillHoles(Matrix<? extends UpdatableBitArray> result, Matrix<? extends BitArray> source, ConnectivityType connectivityType) Makes completion of the source binary matrix and returns it in the result matrix. Compared to the source matrix, all the "holes" ("pores") in the resulting matrix are filled.This method is equivalent to the following code:
Boundary2DScanner
scanner =getMainBoundariesScanner
(source, result, connectivityType);Matrices.clear
(result); // initial zero-filling while (scanner.nextBoundary()
) { scanner.scanBoundary
(); // or some more useful actions }- Parameters:
result
- the completion: the bit matrix with filled holes.source
- the source bit matrix.connectivityType
- the connectivity kind used while building completion.- Throws:
NullPointerException
- if one of argument is null.IllegalArgumentException
- if matrix.dimCount()
is not 2.SizeMismatchException
- if the passed matrices have different dimensions.
-
isSingleBoundaryScanner
public abstract boolean isSingleBoundaryScanner()Returns true if and only if this scanner is a single boundary scanner. More precisely, it is true if and only if:- this instance was created by
getSingleBoundaryScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.matrices.scanning.ConnectivityType)
method - or it is
Boundary2DWrapper
and this method of itsparent scanner
returns true.
- Returns:
- whether this scanner is a a single boundary scanner.
- this instance was created by
-
isAllBoundariesScanner
public abstract boolean isAllBoundariesScanner()Returns true if and only if this scanner is an all boundaries scanner. More precisely, it is true if and only if:- this instance was created by
getAllBoundariesScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.matrices.scanning.ConnectivityType)
method - or it is
Boundary2DWrapper
and this method of itsparent scanner
returns true.
- Returns:
- whether this scanner is a an all boundaries scanner.
- this instance was created by
-
isMainBoundariesScanner
public abstract boolean isMainBoundariesScanner()Returns true if and only if this scanner is a main boundaries scanner. More precisely, it is true if and only if:- this instance was created by
getMainBoundariesScanner(net.algart.arrays.Matrix<? extends net.algart.arrays.BitArray>, net.algart.arrays.Matrix<? extends net.algart.arrays.UpdatablePFixedArray>, net.algart.matrices.scanning.ConnectivityType)
method - or it is
Boundary2DWrapper
and this method of itsparent scanner
returns true.
- Returns:
- whether this scanner is a a main boundaries scanner.
- this instance was created by
-
matrix
Returns the reference to the currently scanned matrix. If this instance was created bygetSingleBoundaryScanner
,getAllBoundariesScanner
orgetMainBoundariesScanner
, the first argument of those methods is returned. If this instance isBoundary2DWrapper
, the result ofmatrix()
method of the parent scanner is returned.- Returns:
- the reference to the currently scanned matrix.
-
dimX
public final long dimX()- Returns:
- x-dimension of the currently scanner matrix.
-
dimY
public final long dimY()- Returns:
- y-dimension of the currently scanner matrix.
-
connectivityType
Returns the connectivity kind, used by this object. It is specified while creating this instance.- Returns:
- the connectivity kind, used by this object.
-
isInitialized
public abstract boolean isInitialized()Returns true if and only if this instance was positioned to some coordinates in the matrix. More precisely, returns false if this instance was newly created and none fromnextBoundary()
,goTo
,goToSamePosition
methods were called yet, or true in all other cases. If this instance isBoundary2DWrapper
, the result of this method for the parent scanner is returned. If this object is not positioned, most of methods, processing pixels in the current position, throw IllegalStateException.- Returns:
- true if and only if this instance was already positioned by
nextBoundary
orgoTo
method.
-
isMovedAlongBoundary
public abstract boolean isMovedAlongBoundary()Returns true if and only if this scanner is already positioned (isInitialized()
returns true) and, in addition,next()
orscanBoundary(ArrayContext)
methods were called at least once.This information can be useful before calling
lastStep()
method (for example, for debugging goals): that method throws IllegalStateException if and only if this method returns false.- Returns:
- true if and only
next()
orscanBoundary(ArrayContext)
methods were successfully called after creating this instance.
-
x
public abstract long x()Returns the current x-coordinate (or throws IllegalStateException if the scanner was notpositioned yet
).- Returns:
- the current x-coordinate.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
y
public abstract long y()Returns the current y-coordinate (or throws IllegalStateException if this scanner was notpositioned yet
).- Returns:
- the current y-coordinate.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
side
Returns the current pixel side (or throws IllegalStateException if this scanner was notpositioned yet
).- Returns:
- the current pixel side.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
atMatrixBoundary
public abstract boolean atMatrixBoundary()Returns true if and only if thecurrent pixel side
lies at the boundary of thescanned matrix
. In other words, returns true if:side()
==Side.X_MINUS
andx()
==0,- or
side()
==Side.Y_MINUS
andy()
==0, - or
side()
==Side.X_PLUS
andx()
==dimX()
-1, - or
side()
==Side.Y_PLUS
andy()
==dimY()
-1.
Note: if this scanner was not
positioned yet
, this method does not throw an exception and simply returns false.- Returns:
- whether the current segment of the boundary is a part of the boundary of the whole scanned matrix.
-
nestingLevel
public abstract long nestingLevel()Returns the current nesting level of object boundaries: the number of boundaries (external or internal), inside which the current pixel side — the segment with the length 1.0, described byx()
,y()
,side()
— is located. (Here we suppose, that if the current pixel side lies at some boundary, then it lies inside this boundary.)Just after creating an instance of this class the nesting level is 0. After the first call of
nextBoundary()
it becomes 1. After finding the first internal boundary (if it exists) bynextBoundary()
the nesting level becomes 2. After each intersection of a boundary while searching for the next boundary the nesting level is increased by 1 or decreased by 1. So, odd values of the nesting level correspond to external boundaries and even values correspond to internal boundaries, excepting the case of a newly created instance (the only case when it is 0).Please note: the nesting level is supported only
- if this scanner was created via
getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method; - if the buffer1 and buffer2 argument of that method are different, independently allocated matrices;
- and if
goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method was never called; - note: if this instance is
Boundary2DWrapper
, the result of this method for the parent scanner is returned.
If this scanner was created via
getSingleBoundaryScanner(Matrix, ConnectivityType)
orgetMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
, the returned nesting level is always 0. In all other cases, the result of this method is not specified.- Returns:
- the current nesting level of object boundaries.
- if this scanner was created via
-
currentIndexInArray
public abstract long currentIndexInArray()Returns the index of the current pixel in theunderlying array
of the currently scanned matrix. This method is almost equivalent to , with the only difference that it works even if this scanner was noty()
*matrix()
.dimX()
+x()
positioned yet
: in the last case it returns 0.- Returns:
- the index of the current pixel in the underlying array of the scanned matrix.
-
goTo
Sets the current position in the matrix to the specified coordinates and pixel side.Usually this method is not necessary for scanners, created by
getAllBoundariesScanner
andgetMainBoundariesScanner
methods: it is enough to usenextBoundary()
andnext()
(orscanBoundary(ArrayContext)
) methods to visit all object boundaries at the matrix. But this method may be helpful if you need to scan a single boundary (for example, that was found by another scanner).- Parameters:
x
- new current x-coordinate.y
- new current y-coordinate.side
- new current pixel side.- Throws:
NullPointerException
- if side argument is null.IndexOutOfBoundsException
- if x<0, y<0, x>=matrix()
.dimX()
or y>=matrix()
.dimY()
.
-
goToSamePosition
Sets the current position in the matrix to the same as in the specified scanner. Equivalent to the following call:goTo
(scanner.x()
, scanner.y()
, scanner.side()
).- Parameters:
scanner
- some other scanner.- Throws:
NullPointerException
- if scanner argument is null.IllegalStateException
- if the specified scanner was notpositioned yet
.IndexOutOfBoundsException
- in the same situations asgoTo
method (impossible if the currently scanned matrices of this and passed scanners have identical dimensions).
-
resetCounters
public abstract void resetCounters()Resets the counters, returned bystepCount()
andorientedArea()
method, and all other counters, that are possibly increased by inheritors of this class. This method is automatically called at the end ofnextBoundary()
andgoTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
methods. -
get
public abstract boolean get()Returns the value of the current element of the currently scanned matrix. This method is equivalent to , but works little faster. This method works even if this scanner was notmatrix()
.array()
.getBit
(currentIndexInArray()
)positioned yet
; in this case, it returns the value of (0,0) matrix element (i.e. ).matrix()
.array()
.getBit(0)
- Returns:
- the value of the current element of the currently scanned matrix.
-
nextBoundary
public abstract boolean nextBoundary()Finds the next vertical segment, belonging to some object boundary, after the current position, and sets the current position to the found one.More precisely, it finds some "next" position after the current position, in the natural order, where the side is
X_MINUS
orX_PLUS
and one from two matrix elements on the left and on the right from the specified segment (pixel side) is 1, but another from these two elements is 0 or lies outside the matrix. If this scanner was notpositioned yet
, this method finds the first such position.The precise sense of the "next" term above depends on the kind of the boundary scanner.
- If this scanner is created by
getSingleBoundaryScanner(Matrix, ConnectivityType)
method, it is just the nearest possible position (in the natural order). - If this scanner is created by
getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method, it is the nearest boundary segment that was not visited yet bynext()
method. - If this scanner is created by
getMainBoundariesScanner(Matrix, Matrix, ConnectivityType)
method, it is the nearest boundary segment that was not visited yet bynext()
method and that does not lie inside some already scanned boundary.
In addition to searching for the next position, this method may do something else: see comments to methods
getSingleBoundaryScanner
,getAllBoundariesScanner
,getMainBoundariesScanner
.This method returns true if it can find the necessary "next" position, or false if there is no required position, i.e. if the matrix scanning is finished. In the second case, the current position is not changed.
Note that if this scanner was not
positioned yet
, it becomes positioned if this method returns true, but stays not positioned if it returns false.- Returns:
- true if this method has successfully found new boundary.
- If this scanner is created by
-
next
public abstract void next()Move the current position to the next segment of the currently scanned object boundary. External boundaries are scanned in clockwise order, internal boundaries in anticlockwise order (if we suppose that the x axis is directed rightwards and the y axis is directed downwards).If the current position does not correspond to an object boundary, the position will be changed to some unknown position near the current one (precise behavior is not specified).
In addition to switching to the next position, this method can do something else: see comments to methods
getSingleBoundaryScanner
,getAllBoundariesScanner
,getMainBoundariesScanner
, and comments to classesBoundary2DSimpleMeasurer
,Boundary2DProjectionMeasurer
.- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
lastStep
Returns information about the movement of the current position, performed by the last call ofnext()
method.If that method was never called (in particular, as a part of
scanBoundary(ArrayContext)
), this method throws IllegalStateException. You can check this situation with help ofisMovedAlongBoundary()
method.- Returns:
- the step of scanning boundary, performed by the call of
next()
method. - Throws:
IllegalStateException
- ifnext()
(orscanBoundary(ArrayContext)
) method was never called for this instance.- See Also:
-
coordinatesChanged
public abstract boolean coordinatesChanged()Returns true if the last call ofnext()
method has changedx()
ory()
coordinate. Returns false if the last call ofnext()
method has changed only thecurrent pixel side
.Equivalent to !
lastStep()
.isSamePixel()
, but works little faster.- Returns:
- whether the last call of
next()
method has changed current pixel coordinates. - Throws:
IllegalStateException
- ifnext()
(orscanBoundary(ArrayContext)
) method was never called for this instance.
-
boundaryFinished
public abstract boolean boundaryFinished()Returns true if and only if the current position (x()
,y()
,side()
) is identical to the position, set by last call ofnextBoundary()
orgoTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
method. Usually it means that the current boundary has been successfully scanned.- Returns:
- true if the current boundary scanning is finished.
-
stepCount
public abstract long stepCount()Returns the total number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method.The result of this method is based on internal counters, incremented by 1 in
next()
method and cleared to 0 while object creation and while every call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
methods.Note that we always have:
stepCount()
=straightStepCount()
+diagonalStepCount()
+rotationStepCount()
.- Returns:
- number of calls of
next()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
.
-
straightStepCount
public final long straightStepCount()Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding tostraight steps
.The result of this method is based on internal counters, incremented by 1 in
next()
method and cleared to 0 while object creation and while every call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
methods.- Returns:
- number of straight steps since the last call of
nextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
.
-
diagonalStepCount
public abstract long diagonalStepCount()Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding todiagonal steps
.The result of this method is based on internal counters, incremented by 1 in
next()
method and cleared to 0 while object creation and while every call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
methods.- Returns:
- number of straight steps since the last call of
nextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
.
-
rotationStepCount
public abstract long rotationStepCount()Returns the number of calls ofnext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method, corresponding torotation steps
.The result of this method is based on internal counters, incremented by 1 in
next()
method and cleared to 0 while object creation and while every call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
methods.- Returns:
- number of straight steps since the last call of
nextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
.
-
orientedArea
public abstract long orientedArea()Returns the oriented area inside the boundary, traversed bynext()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method.The oriented area is the current value of an internal counter, which is reset to 0 while object creation and while every call of
nextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
methods, and which is incremented while eachnext()
method in the following manner:switch (
side()
{ case X_MINUS: orientedArea -=x()
- 1; break; case X_PLUS: orientedArea +=x()
; break; }In other words, the absolute value of oriented area is the number of pixels inside the traversed boundary, and its sign is positive when it is an object (external boundary) or negative when it is a pore inside an object (internal boundary). It is the total number of pixels of the completion of the current measured object (with minus sign if it is an internal boundary, i.e. when it is the number of pixels in the "hole").
- Returns:
- the oriented area inside the boundary, traversed by
next()
method since the last call ofnextBoundary()
,goTo(long, long, net.algart.matrices.scanning.Boundary2DScanner.Side)
orresetCounters()
method
-
area
Returns the oriented area inside the contour line, following along the scanned boundary, estimated according the specified contour line type. "Oriented" means that the result is equal to the area of the figure inside this contour, if the scanned boundary is an external one, or the same value with minus sign if it is an internal one.In particular, if contourLineType==
ContourLineType.STRICT_BOUNDARY
, this method just returns the result oforientedArea()
. In the case contourLineType==ContourLineType.PIXEL_CENTERS_POLYLINE
, the measured area can be 0.0 — for example, for 1-pixel objects (isolated pixels) or for "thin" 1-pixel "lines".- Returns:
- the oriented area inside the scanned contour.
-
perimeter
Returns the total length of the contour, following along the scanned boundary: perimeter of the measured object, "drawn" at the bit matrix, estimated according the specified contour line type.If contourLineType==
ContourLineType.STRICT_BOUNDARY
, this method just returns the result ofstepCount()
.In the case contourLineType==
ContourLineType.PIXEL_CENTERS_POLYLINE
, this method returnsstraightStepCount()
+Step.DIAGONAL_LENGTH
*diagonalStepCount()
.In the case contourLineType==
ContourLineType.SEGMENT_CENTERS_POLYLINE
, this method returnsstraightStepCount()
+Step.HALF_DIAGONAL_LENGTH
* (diagonalStepCount()
+rotationStepCount()
).- Returns:
- the length of the contour line, following along the scanned boundary.
-
isInternalBoundary
public final boolean isInternalBoundary()Returns true if and only if . Usually it means that the current position corresponds to an internal boundary: see comments toside()
==Side.X_PLUS
getAllBoundariesScanner(Matrix, Matrix, Matrix, ConnectivityType)
method.- Returns:
.side()
==Side.X_PLUS
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
scanBoundary
public final long scanBoundary()Equivalent ofscanBoundary(null)
.- Returns:
- the length of scanned boundary (the number of visited pixel sides, not the number of visited pixels!)
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
scanBoundary
Scans the current boundary. This method performs the following simple loop:do { next(); } while (!boundaryFinished());
and returns
stepCount()
(the number of performed iterations, i.e. the length of the scanned boundary). In addition, this method calls context.checkInterruption()
method from time to time (if context!=null) to allow interruption of scanning very long boundaries. No other methods of the context are called.Note: the number of boundary segments, returned of this method, can theoretically be incorrect if the length of the boundary is greater than Long.MAX_VALUE. It is a very exotic case, that can be practically realized only on a virtual matrix, containing almost 263 bits, with special structure. In this case, this method will work during more than 1010 seconds (> 300 years) on a very quick computer that can perform one iteration per 1 ns.
- Parameters:
context
- the context of execution; may be null, then it will be ignored.- Returns:
- the length of scanned boundary (the number of visited pixel sides, not the number of visited pixels!)
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
updateProgress
Calls context.updateProgress(event)
with an event, created by the following operator:new ArrayContext.Event(boolean.class, , or does nothing if context==null.currentIndexInArray()
,matrix()
.size()
)The method can be useful while sequentially scanning the matrix via a usual loop of
nextBoundary()
andscanBoundary(ArrayContext)
calls.- Parameters:
context
- the context of execution; may be null, then it will be ignored.
-
checkInterruption
Calls context.checkInterruption()
or does nothing if context==null.The method can be useful while sequentially scanning the matrix via a usual loop of
nextBoundary()
andscanBoundary(ArrayContext)
calls.- Parameters:
context
- the context of execution; may be null, then it will be ignored.
-
toString
Returns a brief string description of this object.The result of this method may depend on implementation.
-