Class SkeletonScanner
- All Implemented Interfaces:
ArrayProcessor
Scanner of skeletons (bit matrices, generated by skeletonization algorithms), allowing
to scan a skeleton, find and traverse all its structural elements like nodes and branches.
This class works on the base of results of classifying skeleton pixels, performed by
SkeletonPixelClassifier
class, and requires an object of that class for creating an instance.
The nonoriented graph, formed by the skeleton
The base goal of this class is building and scanning the skeleton nonoriented graph,
formed by nodes and branches of the given skeleton (bit matrix).
Below is the formal definition of this graph for some n-dimensional skeleton bit matrix
(or, briefly, skeleton) and some skeleton pixel classifier: an instance of
SkeletonPixelClassifier
class, used by this class
(it is returned by pixelClassifier()
method).
Let's define a pixel with integer coordinates (x, y, z,...)
as a set of points of the n-dimensional space with such coordinates
(x', y', z',...) that
coordinates
(x, y, z,...) corresponds to a pixel (x, y, z,...)
in the n-dimensional Euclidean space.
The skeleton nonoriented graph is a set of n-dimensional points-nodes, connected by polylines-edges. Namely:
- The nodes of the graph are geometrical centers of all pixels of the skeleton, classified as
nodes
,free branch ends
andisolated pixels
by the current skeleton pixel classifier. Such types of nodes have correspondingly ≥3, 1 and 0 incident edges.
Warning: nodes of the graph andnode pixel type
in the skeleton matrix are different concepts! Nodes of the graph are points of the n-dimensional Euclidean space, namely centers of pixels (centers of 1x1 squares in 2-dimensional case), while node pixel type describes matrix elements, corresponding to pixels in the space (1x1 squares in 2-dimensional case). And nodes of the graphs are formed not only by node pixel type, but also byfree branch ends
andisolated pixels
.
- The edges of the graph are polylines
E0E1...Em, m≥1,
connecting some pairs of graph nodes E0 and Em.
Every such edge consists of a sequences of segments Ei Ei+1
with length 1, √2, ... or √n, connecting geometrical centers of 2 pixels
Ei and Ei+1,
corresponding to 2 neighbouring unit matrix elements
(in straight-and-diagonal connectivity terms, see below).
Both ends E0 and Em of each edge are always nodes of the graph.
The sequence of skeleton pixels, corresponding to pointsE1, E2, .., .Em−1 , is also called a skeleton branch. In a case m=1, we also say that the nodes of the graph E0 and Em are connected with a degenerated 0-pixel skeleton branch (or, briefly, a degenerated branch).
- If an edge E0E1...Em
consist of more than 1 segment (m≥2), then all points
E1, E2, ..., Em−1
are geometrical centers of the pixels of the skeleton, classified as
usual branch pixels
orattachable branch ends
by the current skeleton pixel classifier. Only the first and the last among points E1 and Em−1 can correspond to attachable branch ends (but also can be centers of usual branch pixels); all other points Ek, 2≤k≤m−2, always correspond to usual branch pixels.
Every skeleton pixel, classified as a usual branch pixel or an attachable branch end by the current skeleton pixel classifier, is always an element of some skeleton branch, i.e. its center is a point Ek with 1≤k≤m−1 for some graph edge. There is the only exception from this rule, described below in the paragraph 7.
- If m≥3 and the point E1 corresponds to a pixel, classified as
an
attachable branch end
, then E0 pixel is detected as the attached branch node A and E2 pixel is detected as an element of attaching branch B for this branch end E1 by the current skeleton pixel classifier — seecomments to SkeletonPixelClassifier
, section "Pixel types", group 5. In other words, the main classifying methodSkeletonPixelClassifier.asPixelTypes
returns for pixel E1 an index of its neighbour E0, when it is called in the modeNEIGHBOUR_INDEX_OF_ATTACHED_NODE
, or an index of its neighbour E2, when it is called in the modeNEIGHBOUR_INDEX_OF_ATTACHING_BRANCH
.
Analogously, if m≥3 and the point Em−1 corresponds to a pixel, classified as anattachable branch end
, then Em pixel is detected as the attached branch node A and Em−2 pixel is detected as an element of attaching branch B for this branch end Em−1 by the current skeleton pixel classifier.
- If m=2 and the only "internal" point E1 corresponds to a pixel, classified as
an
attachable branch end
, then, for this branch end, either E0 pixel is detected as the attached branch node A and E2 pixel is detected as an element of attaching branch B, or E2 pixel is detected as the attached branch node A and E0 pixel is detected as an element of attaching branch B by the current skeleton pixel classifier — seecomments to SkeletonPixelClassifier
, section "Pixel types", group 5.
- In addition to the situations 4 and 5, this graph contains edges without "internal" points (m=1),
corresponding to degenerated 0-pixel branches.
Such edges connect:
- any pairs of neighbouring graph nodes, where both nodes are the centers of pixels, classified as
free branch ends
(it is a very simple case of a short 2-pixel branch); - any pairs of neighbouring graph nodes, where one node is the center of a pixel, classified as
a
node
, and another is the center of a pixel, classified as afree branch end
; - any pairs of neighbouring graph nodes, where both nodes are the centers of pixels, classified as
nodes
, when the connecting them with a degenerated branch is not prohibited by method (and, so, when each from these 2 nodes appears in the list of neighbour indexes, returned bySkeletonPixelClassifier.markNeighbouringNodesNotConnectedViaDegeneratedBranches(int[])
adjacentBranches()
method for the second node).
- any pairs of neighbouring graph nodes, where both nodes are the centers of pixels, classified as
- The skeleton can also contain specific kind of branches, called cyclic branches,
consisting of
usual branch pixels
only and containing no nodes; such branches do not correspond to any elements of the graph and are recognized separately by this class.
- We can conclude from statements 1, 2 and 3, that each connected component of the skeleton, excepting cyclic branches, corresponds to a connected component of the graph, and vice versa.
All said above is correct only if the processed skeleton matrix does not contain
"illegal
" unit elements. If it contains them,
the skeleton nonoriented graph can be partially incorrect.
It is easy to note, that this graph does not contain nodes with 2 incident edges and has no self-loops (though we could interpret the case 7 in this manner).
This class contains methods, allowing to scan this graph, i.e. to find all branches, incident with
some node of the graph, i.e.
node
or
free branch end
pixel type,
and to scan any branch from one its end (node of the graph) to another.
Thus, you can use this class to vectorize a skeleton by transforming this graph into some
geometrical vector model, consisting of points (nodes) and curves (lines connecting nodes).
The main methods, which you need to use for full scanning the graph, are the following:
nextNodeOrBranch()
— you should call this method to start scanning the next part of the skeleton;firstStep(int, boolean)
— starts scanning a branch from anode
(but not afree branch end
);firstStepFromBranch(boolean)
— starts scanning a branch from a branch pixel (includingfree branch end
);nextStep()
— continues scanning the current branch.
See comments to nextNodeOrBranch()
about possible strategies of scanning, such as recursive
depth-first search. See also comments to scanBranch(int, boolean, boolean)
and
scanBranchFromBranch(boolean, boolean)
methods to understand, how to fully scan every branch.
And see below a full example of breadth-first traversal algorithm.
Skeleton matrix, pixel classifier, current position
This class always processes some fixed bit matrix, which is called the scanned skeleton
and can be retrieved by skeleton()
method, and uses some fixed
skeleton pixel classifier
, which has the same number of dimensions
and can be retrieved by pixelClassifier()
method.
Inside the scanned skeleton, this class always supports some current position:
it is just some set of coordinates, which can be set by goTo(long...)
or
retrieved by currentCoordinates()
.
After creation or calling reset()
method, this object is considered to be not
positioned
, that is the current position is considered to be not set yet and
most methods throw IllegalStateException.
Remembering and lightweight skeleton scanners
In addition to storing the current position, this class may support storing information about visiting some pixels (probably in a form of an internal bit matrix, allocated while instantiation of this object). Instances of this class, that support storing this information, are called remembering skeleton scanners; instances, that do not support this, are called lightweight skeleton scanners.
Lightweight skeleton scanners do not occupy a lot of memory and can be created quickly,
and their functionality is enough for scanning a concrete skeleton branch or for classifying
all skeleton pixels by their types
(asPixelTypes(SkeletonPixelClassifier.AttachmentInformation)
method).
But they do not allow to scan the nonoriented graph, formed by the skeleton, without additional
efforts for storing visited nodes and branches in some external data structures.
Remembering skeleton scanners is what you should use in most cases.
They allow to correctly build a nonoriented graph from nodes and branches, described above,
while a single pass through the skeleton, for example, by a simple loop or by a breadth-first search,
as shown in the example of usage below.
The main feature of a remembering scanner is that it allocates additional memory
(probably a bit matrix with the same sizes as the scanned skeleton), where it can store the fact of visiting
some pixels by visit()
and visitPreviousBranchPixel()
methods,
and this information is used by some other methods (see the following list of differences in behaviour
of the methods).
You can find out, is a skeleton scanner lightweight or remembering, by isRemembering()
method.
Besides this, the following methods work differently in lightweight and remembering skeleton scanners:
- in lightweight scanners,
visit()
andvisitPreviousBranchPixel()
do nothing;
in remembering scanners, these methods mark the corresponding pixels as "visited" andreset()
method clears this information (makes all pixels "unvisited"); - in lightweight scanners,
pixelVisitRemembered()
andneighbourVisitRemembered(int)
always return false;
in remembering scanners, these methods return true if the corresponding pixels are marked as "visited" by previous calls ofvisit()
orvisitPreviousBranchPixel()
; - in lightweight scanners, onlyToUnvisited argument of
firstStep(int, boolean)
,firstStepFromBranch(boolean)
,scanBranch(int, boolean, boolean)
,scanBranchFromBranch(boolean, boolean)
methods, as well as withVisiting argument ofscanBranch(int, boolean, boolean)
,scanBranchFromBranch(boolean, boolean)
methods do not matter (as if they would be false);
in remembering scanners, these arguments affect the behaviour (see comments to those methods).
Note, that in remembering scanners the only ways to mark some pixels as "visited" are explicit direct calls
of the corresponding methods visit()
and visitPreviousBranchPixel()
.
You should call these methods manually: this class does not try to mark pixels as "visited" indirectly,
inside other methods, scanning the skeleton.
The only exception from this is scanBranch
/ scanBranchFromBranch
methods, which calls visitPreviousBranchPixel()
when their withVisiting argument
is true.
Connectivity model (straight-and-diagonal) and "neighbour" term
Note, that this class, as well as SkeletonPixelClassifier
, supposes
the straight-and-diagonal connectivity kind: see
ConnectivityType.STRAIGHT_AND_DIAGONAL
. It means, that all skeletons
are supposed to be connected in terms of this connectivity: every connected component of the skeleton matrix
is a "carcass" or "skeleton" of some connected component of the original matrix, for which this skeleton
was built.
So, the term "neighbour" of some pixel (matrix element) in this class
and in SkeletonScanner
always means another pixel (matrix element), so that
max (|ik−jk|)=1
where
Example of usage
Below is a complete example of code, using a remembering skeleton scanner ss for breadth-first traversal of the nonoriented graph, formed by the skeleton.
while (ss.nextNodeOrBranch()
) { ss.checkInterruption()
; ss.updateProgress()
; long saveBaseIndex = ss.currentIndexInArray()
; if (ss.isUsualBranch()
) { // should check cyclic loops here ss.scanBranchFromBranch
(true, false); boolean cyclicBranch = ss.isUsualBranch(); // in particular, not TYPE_ILLEGAL if (cyclicBranch) { assert ss.currentIndexInArray() == saveBaseIndex; // should return to the same position if (ss.firstStepFromBranch
(true)) { // can be false if this pixel or its neighbour was visited do { // some processing the cyclic branch segment // from ss.previousCoordinates()
to ss.currentCoordinates()
... ss.visitPreviousBranchPixel()
; } while (ss.nextStep()
); } continue; } // else we shall now start from the nearest node, but after all return to saveBaseIndex } if (ss.isNodeOrFreeBranchEnd()
// necessary check: it is also possible an attached or illegal pixel && !ss.pixelVisitRemembered()
) // for correct processing degenerated 0-pixel branches { Queuequeue = new LinkedList (); ss. visit()
; queue.add(ss.currentIndexInArray()
); while (!queue.isEmpty()) { long saveIndex = queue.poll(); ss.goToIndexInArray
(saveIndex); if (ss.isIllegal()
) { continue; // why not? } assert ss.isNodeOrFreeBranchEnd() : ss; int[] adjacentBranches = ss.isNode()
? ss.adjacentBranches()
: new int[]{-1}; for (int nb : adjacentBranches) { ss.goToIndexInArray
(saveIndex); int dnb = nb == -1 ? ss.firstStepFromBranchNeighbourIndex
(false) : nb; boolean degeneratedBranch = ss.isNeighbourNodeOrFreeBranchEnd
(dnb); // Special case: we cannot mark 0-pixel degenerated branches between 2 neighbouring nodes // as "visited", because they contain no internal pixels, and we can store visiting // information in the remembering scanner for pixels only. // However, we still need to process degenerated branches, and only 1 time for each branch. // Let's scan such a branch if and only if the neighbour: // 1) is not visited yet or // 2) is inside the queue. // Then every degenerated branch PQ will be processed strictly 1 time. // Proof. // Note that pixels are visited at the moment when they are added to the queue. // Let's suppose that P appears the first in this loop (as the pixel at "saveIndex"). // At this moment, Q is either not visited, or added to the queue, but not removed // from it yet (because P appears here before it). So, we'll really process PQ branch. // On the other hand, when Q will appear in this loop (as the pixel at "saveIndex"), // P will be already removed from the queue, so we'll not process PQ branch twice. // This completes the proof. if (degeneratedBranch && ( !ss.neighbourVisitRemembered
(dnb) || queue.contains(ss.neighbourIndexInArray
(dnb)))) { // some processing the degenerated branch // from ss.currentCoordinates() to ss.neighbourCoordinates(nb)... } if (!(nb == -1 ? ss.firstStepFromBranch
(true) : ss.firstStep
(nb, true))) { continue; } if (!degeneratedBranch) { do { // some processing the branch segment // from ss.previousCoordinates() to ss.currentCoordinates()... ss.visitPreviousBranchPixel()
; } while (ss.nextStep()
); } if (!ss.pixelVisitRemembered()
) { ss.visit()
; queue.add(ss.currentIndexInArray()
); } } } } ss.goToIndexInArray
(saveBaseIndex); }
Of course, you can use another order of graph traversal, for example, depth-first. Note that some applications do not need depth-first or breadth-first order at all: it can be enough to find and detect nodes and edges in any order. In this case, you can just remove all operations with the queue.
Also note: you can process degenerated 0-pixel branches in a common way, just by assigning
degeneratedBranch=false instead of calling isNeighbourNodeOrFreeBranchEnd
method in the example above.
In this case, breadth-first traversal algorithm will form slightly incorrect graph, namely, some degenerated
branches will not be processed, and it can lead to appearing graph nodes with 2 incident edges
(with low probability). However, the resulting graph will still be good enough, with connected components
corresponding to connected components of the skeleton, maybe even better than the full correct graph
with all degenerated branches in a role of edges.
Creating instances of this class
This class is designed for a case of any number of dimensions, though, of course, the most popular case is 2-dimensional. You can create an instance of this class by the following basic methods:
getRememberingInstance(ArrayContext, Matrix, SkeletonPixelClassifier)
,getLightweightInstance(ArrayContext, Matrix, SkeletonPixelClassifier)
and also by the following methods, designed for using 2-dimensional pixel classifier
BasicSkeletonPixelClassifier2D
:
getRememberingOctupleThinningInstance2D(ArrayContext, Matrix)
,getLightweightOctupleThinningInstance2D(ArrayContext, Matrix)
,getRememberingQuadruple3x5ThinningInstance2D(ArrayContext, Matrix)
,getLightweightQuadruple3x5ThinningInstance2D(ArrayContext, Matrix)
,getRememberingStrongQuadruple3x5ThinningInstance2D(ArrayContext, Matrix)
,getLightweightStrongQuadruple3x5ThinningInstance2D(ArrayContext, Matrix)
.
Pseudo-cyclic continuation
This class supposes that the processed matrix is 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)
Matrix.ContinuationMode.ZERO_CONSTANT
.
Multithread compatibility
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 Summary
Modifier and TypeMethodDescriptionint[]
On the assumption that thecurrent pixel
is anode
, returns indexes of all its neighbours, which are the starting pixels of branches, incident with this node.int
adjacentBranches
(int[] result) More efficient version ofadjacentBranches()
method, which stores the results in the passed Java array instead of creating new Java array.Matrix<? extends PIntegerArray>
asPixelTypes
(SkeletonPixelClassifier.AttachmentInformation attachmentInformation) Equivalent topixelClassifier()
.asPixelTypes
(skeleton()
, attachmentInformation), but probably works faster.void
context()
Returns the current context used by this instance for some operations.long[]
Returns the current coordinates (or throws IllegalStateException if the scanner was notpositioned yet
).long
Reduced and more efficient version ofcurrentCoordinates()
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix.int
Returns the type of the current pixel of the skeleton matrix or, if it is an attachable branch end, returns the index of its neighbour, which is a node, which is one of the ends of the branch.int
Returns the type of the current pixel of the skeleton matrix or, if it is an attachable branch end, returns the index of its neighbour, which lies at the branch, to which this pixel should be attached.boolean
Returns the value of the element of the skeleton matrix at thecurrent coordinates
.int
dimCount()
Equivalent toskeleton()
.dimCount()
.boolean
firstStep
(int neighbourIndex, boolean onlyToUnvisited) On the assumption that thecurrent pixel
is anode or isolated pixel
, checks whether we have a skeleton branch, originating at this node and going towards its neighbour with the index neighbourIndex, and, if so, moves the current position to this neighbour and returns true, if not, does nothing and returns false.boolean
firstStepFromBranch
(boolean onlyToUnvisited) On the assumption that thecurrent pixel
issome branch pixel
, moves the current position to a neighbour along this skeleton branch and returns true.int
firstStepFromBranchNeighbourIndex
(boolean onlyToUnvisited) Returns the index of the neighbour, to whichfirstStepFromBranch(boolean onlyToUnvisited)
moves when called with the same onlyToUnvisited argument.Creates new instance of this class with the identical behaviour, excepting that the returned object is always lightweight skeleton scanner (not remembering visits of pixels).Creates new instance of this class with the identical behaviour, excepting that the returned object is always remembering skeleton scanner (remembering visits of pixels).static SkeletonScanner
getLightweightInstance
(ArrayContext context, Matrix<? extends BitArray> skeleton, SkeletonPixelClassifier pixelClassifier) Creates new remembering skeleton scanner, which will process the given skeleton matrix on the base of the given pixel classifier.static SkeletonScanner
getLightweightOctupleThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byOctupleThinningSkeleton2D
algorithm.static SkeletonScanner
getLightweightQuadruple3x5ThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byQuadruple3x5ThinningSkeleton2D
algorithm.static SkeletonScanner
getLightweightStrongQuadruple3x5ThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byStrongQuadruple3x5ThinningSkeleton2D
algorithm.static SkeletonScanner
getRememberingInstance
(ArrayContext context, Matrix<? extends BitArray> skeleton, SkeletonPixelClassifier pixelClassifier) Creates new remembering skeleton scanner, which will process the given skeleton matrix on the base of the given pixel classifier.static SkeletonScanner
getRememberingOctupleThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byOctupleThinningSkeleton2D
algorithm.static SkeletonScanner
getRememberingQuadruple3x5ThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byQuadruple3x5ThinningSkeleton2D
algorithm.static SkeletonScanner
getRememberingStrongQuadruple3x5ThinningInstance2D
(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byStrongQuadruple3x5ThinningSkeleton2D
algorithm.void
goTo
(long... newCurrentCoordinates) Sets the current position in the skeleton matrix to the specified coordinates.void
goToIndexInArray
(long newIndexInArray) Reduced and more efficient version ofgoTo(long...)
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix.void
goToNeighbour
(int neighbourIndex) Moves the current position in the skeleton matrix to the given neighbour of the current element.boolean
Returns true if thecurrent element
of the skeleton is anattachable branch end
, i.e. a unit pixel having 3 or more unit neighbours, which this class considers to be not a node, but an ending pixel of some branch.boolean
isBranch()
Returns true if thecurrent element
of the skeleton is a branch element: usual (whereisUsualBranch()
returns true), free branch end (whereisFreeBranchEnd()
returns true) or attachable branch end (whereisAttachableBranchEnd()
returns true).boolean
Returns true if thecurrent element
of the skeleton is afree branch end
, i.e. a unit pixel having exactly 1 unit neighbour.boolean
Returns true if thecurrent element
of the skeleton is "illtegal
", i.e. a center of an impossible configuration for a correct result of the given skeletonization algorithm.boolean
Returns true if and only if this instance was positioned to some coordinates in the skeleton matrix.boolean
isNeighbourNodeOrFreeBranchEnd
(int neighbourIndex) Returns true if the neighbour with the given index of thecurrent element
of the skeleton is anode
or afree branch end
.boolean
isNode()
Returns true if thecurrent element
of the skeleton is anode
, i.e. a unit element where 3 or more thin connected 1-pixel branches meet or, as a degenerated case, anisolated pixel
: a unit element having no unit neighbours.boolean
Returns true if thecurrent element
of the skeleton is a node (isNode()
returns true) or a free branch end (isFreeBranchEnd()
returns true).boolean
Returns true if this scanner is remembering or false if it is lightweight.boolean
Returns true if thecurrent element
of the skeleton is ausual branch pixel
, i.e. a unit pixel having exactly 2 unit neighbours.long[]
neighbourCoordinates
(int neighbourIndex) Returns the coordinates of the element of the skeleton matrix, which is a neighbour with the given index of thecurrent element
.long
neighbourIndexInArray
(int neighbourIndex) Reduced and more efficient version ofneighbourCoordinates(int)
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix.long
neighbourOffsetInArray
(int neighbourIndex) Reduced and more efficient version ofneighbourOffset(int)
method of thepixel classifier
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix.int
neighbourTypeOrAttachedNode
(int neighbourIndex) Returns the type of the pixel of the skeleton matrix, which is a neighbour with the given index of thecurrent element
, or, if it is an attachable branch end, returns the index of a neighbour of this neighbour, which is a node, which is one of the ends of the branch.int
neighbourTypeOrAttachingBranch
(int neighbourIndex) Returns the type of the pixel of the skeleton matrix, which is a neighbour with the given index of thecurrent element
, or, if it is an attachable branch end, returns the index of a neighbour of this neighbour, which lies at the branch, to which this neighbour should be attached.boolean
neighbourValue
(int neighbourIndex) Returns the value of the element of the skeleton matrix, which is a neighbour with the given index of thecurrent element
.boolean
neighbourVisitRemembered
(int neighbourIndex) Returns true if this scanner isremembering
and the neighbour of thecurrent element
with the given index was already visited byvisit()
orvisitPreviousBranchPixel()
method.boolean
Finds the next unit element in the skeleton matrix (in natural order of elements) after thecurrent position
, the type of which isnode
,isolated
orsome branch pixel
, and moves the current position to this element.Enhanced version ofnextNodeOrBranch()
, which returns the type of the successfully found element in the result or return null if the required element is not found.boolean
nextStep()
Continues movement along the skeleton branch, started byfirstStep(int, boolean)
orfirstStepFromBranch(boolean)
method, and returns true, if the end of the current branch is not reached yet, or does nothing and returns false if we have reached the end of the branch (usually anode
orfree branch end
).int
Equivalent topixelClassifier()
.numberOfNeighbours()
.Returns a reference to the pixel classifier, used by this object.boolean
Returns true if this scanner isremembering
and thecurrent element
was already visited byvisit()
orvisitPreviousBranchPixel()
method.int
Returns an index of the neighbour, towards which the current position was moved by the previous change of the current position viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
method, or -1 if the previous change of the current position was performed by some other method likegoTo(long...)
ornextNodeOrBranch()
.long[]
Returns the coordinates of the neighbour of the current element, which was current before the last change of the current position, if this change was performed viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
method, or throws IllegalStateException if the previous change of the current position was performed by some other method likegoTo(long...)
ornextNodeOrBranch()
.long
Reduced and more efficient version ofpreviousCoordinates()
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix.void
reset()
Clears the state of this scanner: resets the current position tonot positioned
state and, inremembering
scanners, resets the state of all pixels tounvisited
.void
scanBranch
(int neighbourIndex, boolean onlyToUnvisited, boolean withVisiting) On the assumption that thecurrent pixel
is anode or isolated pixel
, completely scans the branch, originating at this node and going towards its neighbour with the given index.void
scanBranchFromBranch
(boolean onlyToUnvisited, boolean withVisiting) On the assumption that thecurrent pixel
issome branch pixel
, scans the part of this branch towards one of the sides of the current pixel.skeleton()
Returns a reference to the skeleton, scanned by this object.toString()
Returns a brief string description of this object.void
Callscontext()
.updateProgress(event)
with an event, created by the following operator:new ArrayContext.Event(boolean.class, , or does nothing ifcurrentIndexInArray()
,skeleton()
.size()
)context()
==null.void
visit()
Inremembering
scanners, marks thecurrent element
of the skeleton matrix as "visited".void
Inremembering
scanners, marks theprevious visited element
of the skeleton matrix as "visited".
-
Method Details
-
getRememberingInstance
public static SkeletonScanner getRememberingInstance(ArrayContext context, Matrix<? extends BitArray> skeleton, SkeletonPixelClassifier pixelClassifier) Creates new remembering skeleton scanner, which will process the given skeleton matrix on the base of the given pixel classifier. See thecomments to this class
about remembering and lightweight skeleton scanners.The passed matrix is supposed to be a final result of some skeletonization algorithm, for example, provided by this package. If it is not so, the passed pixel classifier scanner will probably consider many unit pixels of this matrix as "
illegal
", so the branches and nodes found by this class will not form a correct skeletal graph. The same situation is also possible if the passed pixel classifier does not match the algorithm, used for skeletonization.- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.pixelClassifier
- thepixel classifier
, which will be used by this scanner for detecting types of all pixels.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix or pixelClassifier argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=pixelClassifier.dimCount()
.
-
getRememberingOctupleThinningInstance2D
public static SkeletonScanner getRememberingOctupleThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byOctupleThinningSkeleton2D
algorithm. Equivalent to
getRememberingInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getOctupleThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
getRememberingQuadruple3x5ThinningInstance2D
public static SkeletonScanner getRememberingQuadruple3x5ThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byQuadruple3x5ThinningSkeleton2D
algorithm. Equivalent to
getRememberingInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getQuadruple3x5ThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
getRememberingStrongQuadruple3x5ThinningInstance2D
public static SkeletonScanner getRememberingStrongQuadruple3x5ThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byStrongQuadruple3x5ThinningSkeleton2D
algorithm. Equivalent to
getRememberingInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getStrongQuadruple3x5ThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
getLightweightInstance
public static SkeletonScanner getLightweightInstance(ArrayContext context, Matrix<? extends BitArray> skeleton, SkeletonPixelClassifier pixelClassifier) Creates new remembering skeleton scanner, which will process the given skeleton matrix on the base of the given pixel classifier. See thecomments to this class
about remembering and lightweight skeleton scanners.The passed matrix is supposed to be a final result of some skeletonization algorithm, for example, provided by this package. If it is not so, the passed pixel classifier scanner will probably consider many unit pixels of this matrix as "
illegal
", so the branches and nodes found by this class will not form a correct skeletal graph. The same situation is also possible if the passed pixel classifier does not match the algorithm, used for skeletonization.- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.pixelClassifier
- thepixel classifier
, which will be used by this scanner for detecting types of all pixels.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix or pixelClassifier argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=pixelClassifier.dimCount()
.
-
getLightweightOctupleThinningInstance2D
public static SkeletonScanner getLightweightOctupleThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byOctupleThinningSkeleton2D
algorithm. Equivalent to
getLightweightInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getOctupleThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
getLightweightQuadruple3x5ThinningInstance2D
public static SkeletonScanner getLightweightQuadruple3x5ThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byQuadruple3x5ThinningSkeleton2D
algorithm. Equivalent to
getLightweightInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getQuadruple3x5ThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
getLightweightStrongQuadruple3x5ThinningInstance2D
public static SkeletonScanner getLightweightStrongQuadruple3x5ThinningInstance2D(ArrayContext context, Matrix<? extends BitArray> skeleton) Creates new remembering skeleton scanner, which will process the given skeleton matrix, supposed to be the final result of skeletonization byStrongQuadruple3x5ThinningSkeleton2D
algorithm. Equivalent to
getLightweightInstance
(context, skeleton,BasicSkeletonPixelClassifier2D.getStrongQuadruple3x5ThinningInstance()
).- Parameters:
context
- thecontext
that will be used by this object; may be null, then it will be ignored.skeleton
- theskeleton
: a bit matrix that should be processed by this scanner.- Returns:
- new instance of this class.
- Throws:
NullPointerException
- if matrix argument is null.IllegalArgumentException
- if skeleton.dimCount()
!=2.
-
context
Returns the current context used by this instance for some operations. This context is specified while creating an instance of this class. It is used, for example, inscanBranch(int, boolean, boolean)
andscanBranchFromBranch(boolean, boolean)
methods.- Specified by:
context
in interfaceArrayProcessor
- Returns:
- the current context used by this instance; may be null.
-
getCompatibleRememberingInstance
Creates new instance of this class with the identical behaviour, excepting that the returned object is always remembering skeleton scanner (remembering visits of pixels). See thecomments to this class
about remembering and lightweight skeleton scanners.The returned scanner is always newly created instance (in particular, not
positioned
), even if this instance is remembering. So, this method usually leads to allocating necessary amount of memory.- Returns:
- the remembering version of this scanner.
-
getCompatibleLightweightInstance
Creates new instance of this class with the identical behaviour, excepting that the returned object is always lightweight skeleton scanner (not remembering visits of pixels). See thecomments to this class
about remembering and lightweight skeleton scanners.The returned scanner is always newly created instance (in particular, not
positioned
), even if this instance is lightweight.- Returns:
- the lightweight version of this scanner.
-
skeleton
Returns a reference to the skeleton, scanned by this object. It is usually specified while creating an instance of this object.- Returns:
- a reference to the skeleton, scanned by this object.
-
pixelClassifier
Returns a reference to the pixel classifier, used by this object. It is usually specified while creating an instance of this object.- Returns:
- a reference to the pixel classifier, used by this object.
-
dimCount
public int dimCount()- Returns:
- the number of dimensions of the matrices, which can be processed by this object.
-
numberOfNeighbours
public int numberOfNeighbours()Equivalent topixelClassifier()
.numberOfNeighbours()
.- Returns:
- the number of neighbours of each element of a skeleton matrix.
-
neighbourOffsetInArray
public long neighbourOffsetInArray(int neighbourIndex) Reduced and more efficient version ofneighbourOffset(int)
method of thepixel classifier
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix. This method is equivalent to , but usually works much faster (in particular, does not allocate any arrays).skeleton()
.pseudoCyclicIndex
(pixelClassifier()
.neighbourOffset
(neighbourIndex))- Parameters:
neighbourIndex
- an index if the neighbour of some central element of the matrix.- Returns:
- non-negative increment of the index in the built-in AlgART array of the skeleton matrix, corresponding to the shift from the central element to this neighbour.
-
asPixelTypes
public Matrix<? extends PIntegerArray> asPixelTypes(SkeletonPixelClassifier.AttachmentInformation attachmentInformation) Equivalent topixelClassifier()
.asPixelTypes
(skeleton()
, attachmentInformation), but probably works faster.- Parameters:
attachmentInformation
- what should this method return for attachable pixels.- Returns:
- the matrix of integer codes with the same sizes as the
scanned skeleton matrix
, describing the types of all skeleton pixels. - Throws:
NullPointerException
- if attachmentInformation is null.
-
isInitialized
public boolean isInitialized()Returns true if and only if this instance was positioned to some coordinates in the skeleton matrix. More precisely, returns false if this instance was newly created and none fromnextNodeOrBranch()
,nextNodeOrBranchPixelType()
,goTo(long...)
,goToIndexInArray(long)
methods were called yet, or true in all other cases. 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
nextNodeOrBranch()
/nextNodeOrBranchPixelType()
orgoTo(long...)
/goToIndexInArray(long)
methods.
-
currentCoordinates
public long[] currentCoordinates()Returns the current coordinates (or throws IllegalStateException if the scanner was notpositioned yet
).The returned array is always a newly allocated Java array. Its length is always equal to
dimCount()
. The returned coordinates are always in ranges0 ≤ result[k] <
where result[k] is the element #k in the returned array.skeleton()
.dim
(k),- Returns:
- the current coordinates in the skeleton matrix.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
currentIndexInArray
public long currentIndexInArray()Reduced and more efficient version ofcurrentCoordinates()
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix. This method is equivalent to , but usually works much faster (in particular, does not allocate any arrays). It is very possible that the implementation really stores the current index, returned by this method, and does not store an array of current coordinates: so, this method just returns an internal field, butskeleton()
.index
(currentCoordinates()
)currentCoordinates()
calculates results on the base on this value.The result of this method is always in range 0..
skeleton()
.size()
-1.- Returns:
- the current index in the built-in AlgART array of the skeleton matrix.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
currentPixelValue
public boolean currentPixelValue()Returns the value of the element of the skeleton matrix at thecurrent coordinates
. Equivalent to .skeleton()()
.array()
.getBit
(currentIndexInArray()
)- Returns:
- the value of the current element of the skeleton matrix.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
currentPixelTypeOrAttachingBranch
public int currentPixelTypeOrAttachingBranch()Returns the type of the current pixel of the skeleton matrix or, if it is an attachable branch end, returns the index of its neighbour, which lies at the branch, to which this pixel should be attached. Equivalent tom. , wherearray()
.getInt
(currentIndexInArray()
)m =
but probably works faster. See comments toasPixelTypes
(SkeletonPixelClassifier.AttachmentInformation.NEIGHBOUR_INDEX_OF_ATTACHING_BRANCH
)asPixelTypes
method for more details.- Returns:
- the type of the current pixel of the skeleton matrix or (for attachable branch end) the direction towards the branch, to which this pixel should be attached.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
currentPixelTypeOrAttachedNode
public int currentPixelTypeOrAttachedNode()Returns the type of the current pixel of the skeleton matrix or, if it is an attachable branch end, returns the index of its neighbour, which is a node, which is one of the ends of the branch. Equivalent tom. , wherearray()
.getInt
(currentIndexInArray()
)m =
but probably works faster. See comments toasPixelTypes
(SkeletonPixelClassifier.AttachmentInformation.NEIGHBOUR_INDEX_OF_ATTACHED_NODE
)asPixelTypes
method for more details.- Returns:
- the type of the current pixel of the skeleton matrix or (for attachable branch end) the direction towards the node, which is one of the branch ends.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
neighbourCoordinates
public long[] neighbourCoordinates(int neighbourIndex) Returns the coordinates of the element of the skeleton matrix, which is a neighbour with the given index of thecurrent element
. Equivalent to , whereskeleton()
.coordinates
(index, null)index = (
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of thecurrentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.The returned array is always a newly allocated Java array. Its length is always equal to
dimCount()
. The returned coordinates are always in ranges0 ≤ result[k] <
where result[k] is the element #k in the returned array.skeleton()
.dim
(k),- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- the coordinates of the given neighbour of the current element of the skeleton matrix.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
neighbourIndexInArray
public long neighbourIndexInArray(int neighbourIndex) Reduced and more efficient version ofneighbourCoordinates(int)
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix. Equivalent to(
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of thecurrentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.The result of this method is always in range 0..
skeleton()
.size()
-1.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- the index in the built-in AlgART array of the given neighbour of the current element of the skeleton matrix.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
neighbourValue
public boolean neighbourValue(int neighbourIndex) Returns the value of the element of the skeleton matrix, which is a neighbour with the given index of thecurrent element
. Equivalent to , whereskeleton()
.array()
.getBit
(index)index = (
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of thecurrentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- the value of the given neighbour of the current element of the skeleton matrix.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.
-
neighbourTypeOrAttachingBranch
public int neighbourTypeOrAttachingBranch(int neighbourIndex) Returns the type of the pixel of the skeleton matrix, which is a neighbour with the given index of thecurrent element
, or, if it is an attachable branch end, returns the index of a neighbour of this neighbour, which lies at the branch, to which this neighbour should be attached. Equivalent tom. , wherearray()
.getInt
(index)m =
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of theasPixelTypes
(SkeletonPixelClassifier.AttachmentInformation.NEIGHBOUR_INDEX_OF_ATTACHING_BRANCH
) index = (currentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- the value of the given neighbour of the current element of the skeleton matrix.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
neighbourTypeOrAttachedNode
public int neighbourTypeOrAttachedNode(int neighbourIndex) Returns the type of the pixel of the skeleton matrix, which is a neighbour with the given index of thecurrent element
, or, if it is an attachable branch end, returns the index of a neighbour of this neighbour, which is a node, which is one of the ends of the branch. Equivalent tom. , wherearray()
.getInt
(index)m =
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of theasPixelTypes
(SkeletonPixelClassifier.AttachmentInformation.NEIGHBOUR_INDEX_OF_ATTACHED_NODE
) index = (currentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- the value of the given neighbour of the current element of the skeleton matrix.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.- See Also:
-
isNode
public boolean isNode()Returns true if thecurrent element
of the skeleton is anode
, i.e. a unit element where 3 or more thin connected 1-pixel branches meet or, as a degenerated case, anisolated pixel
: a unit element having no unit neighbours. Equivalent both toSkeletonPixelClassifier.isNodePixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isNodePixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is a node, including the degenerated case of an isolated pixel. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isUsualBranch
public boolean isUsualBranch()Returns true if thecurrent element
of the skeleton is ausual branch pixel
, i.e. a unit pixel having exactly 2 unit neighbours. Equivalent both toSkeletonPixelClassifier.isUsualBranchPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isUsualBranchPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is a usual (non-ending) branch pixel. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isFreeBranchEnd
public boolean isFreeBranchEnd()Returns true if thecurrent element
of the skeleton is afree branch end
, i.e. a unit pixel having exactly 1 unit neighbour. Equivalent both toSkeletonPixelClassifier.isFreeBranchEndPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isFreeBranchEndPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is a free branch end. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isAttachableBranchEnd
public boolean isAttachableBranchEnd()Returns true if thecurrent element
of the skeleton is anattachable branch end
, i.e. a unit pixel having 3 or more unit neighbours, which this class considers to be not a node, but an ending pixel of some branch. Equivalent both toSkeletonPixelClassifier.isAttachableBranchEndPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isAttachableBranchEndPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is an attachable branch end. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isNodeOrFreeBranchEnd
public boolean isNodeOrFreeBranchEnd()Returns true if thecurrent element
of the skeleton is a node (isNode()
returns true) or a free branch end (isFreeBranchEnd()
returns true). Equivalent both toSkeletonPixelClassifier.isNodeOrFreeBranchEndPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isNodeOrFreeBranchEndPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is a node or a free branch end. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isBranch
public boolean isBranch()Returns true if thecurrent element
of the skeleton is a branch element: usual (whereisUsualBranch()
returns true), free branch end (whereisFreeBranchEnd()
returns true) or attachable branch end (whereisAttachableBranchEnd()
returns true). Equivalent both toSkeletonPixelClassifier.isBranchPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isBranchPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is some element of a branch. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isIllegal
public boolean isIllegal()Returns true if thecurrent element
of the skeleton is "illtegal
", i.e. a center of an impossible configuration for a correct result of the given skeletonization algorithm. Equivalent both toSkeletonPixelClassifier.isIllegalPixelType
(currentPixelTypeOrAttachingBranch()
) and toSkeletonPixelClassifier.isIllegalPixelType
(currentPixelTypeOrAttachedNode()
).- Returns:
- true if the
current element
of the skeleton is a part of pixel configuration which is incorrect for the given type of skeleton. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
isNeighbourNodeOrFreeBranchEnd
public boolean isNeighbourNodeOrFreeBranchEnd(int neighbourIndex) Returns true if the neighbour with the given index of thecurrent element
of the skeleton is anode
or afree branch end
. Equivalent both toSkeletonPixelClassifier.isNodeOrFreeBranchEndPixelType
(neighbourTypeOrAttachingBranch(neighbourIndex)
) and toSkeletonPixelClassifier.isNodeOrFreeBranchEndPixelType
(neighbourTypeOrAttachedNode(neighbourIndex)
).This method is helpful when
isNodeOrFreeBranchEnd()
method returns true, i.e. the current position is a node or a free branch end (and corresponds to a node in the nonoriented graph, formed by the skeleton), and we need to check, is it connected via a degenerated 0-pixel branch with a possible neighbouring node or free branch end (also corresponding to a node in the nonoriented graph). It is necessary in algorithms which process degenerated 0-pixel branches in some special way, like an algorithm, given in thecomments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- true if the given neighbour of the current element of the skeleton matrix is a node or a free branch end.
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.
-
goTo
public void goTo(long... newCurrentCoordinates) Sets the current position in the skeleton matrix to the specified coordinates.In simple applications, you do not need this method: it is enough to implement a loop of calls of
nextNodeOrBranch()
method.- Parameters:
newCurrentCoordinates
- new current coordinates: thecurrentCoordinates()
method will return an identical array after this call.- Throws:
NullPointerException
- if newCurrentCoordinates argument is null.IllegalArgumentException
- if the length of newCurrentCoordinates array is not equal todimCount()
.IndexOutOfBoundsException
- if one of new coordinates newCurrentCoordinates[k] is out of range 0..skeleton()
.dim
(k)-1.- See Also:
-
goToIndexInArray
public void goToIndexInArray(long newIndexInArray) Reduced and more efficient version ofgoTo(long...)
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix. This method is equivalent to , but usually works much faster (in particular, does not allocate any arrays).goto
(skeleton()
.coordinates
(newIndexInArray, null))- Parameters:
newIndexInArray
- new current index in the built-in AlgART array of the skeleton matrix.- Throws:
IndexOutOfBoundsException
- if newIndexInArray is out of range 0..skeleton()
.size()
-1.- See Also:
-
goToNeighbour
public void goToNeighbour(int neighbourIndex) Moves the current position in the skeleton matrix to the given neighbour of the current element. The neighbour index is specified in terms of method. This method is equivalent topixelClassifier()
.neighbourOffset(int)
, wheregoToIndexInArray(index)
index = (
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of thecurrentIndexInArray()
+neighbourOffsetInArray
(neighbourIndex)) %skeleton()
.size()
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.
-
nextNodeOrBranch
public boolean nextNodeOrBranch()Finds the next unit element in the skeleton matrix (in natural order of elements) after thecurrent position
, the type of which isnode
,isolated
orsome branch pixel
, and moves the current position to this element. If this scanner was notpositioned yet
, finds the first such element. Returns true if this method has successfully found the required element, 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.More precisely, this method finds the minimal k, so that
k ≥ ( andisInitialized()
? 0 :currentIndexInArray()
+1) is not equal toasPixelTypes(...)
.array()
.getInt
(k)TYPE_ZERO
orTYPE_ILLEGAL
(the argument ofasPixelTypes
is not important here).
If this index k exists, this method performs
and returns true, in other case doesn't change the state of this object and returns false.goToIndexInArray
(k)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.After successful call of this method, you can be sure that the current position corresponds:
- either to a
node
or, maybe,isolated pixel
(as a degenerated case of the node), - or to a skeleton branch (including possible case of
free branch end
).
In the first case, i.e. if
isNode()
, you can find all branches, originating at this node, byadjacentBranches()
method, which returns indexes of all corresponding neighbours. Then you can scan all these branches in a loop, starting the scanning of each branch by
firstStep(int neighbourIndex, boolean onlyToUnvisited)
method, where neighbourIndex is an element of the array — result ofadjacentBranches()
. If necessary, you can scan each branch until its end by a loop ofnextStep()
calls, like in method, and, if the end will be ascanBranch(int, boolean, boolean)
node
again, for example, recursively process this node in the same manner (that means deapth-first graph traversal).Note, that the recursion should be used only if you remember all visited nodes (to avoid infinite recursion), for example, by using a
remembering
scanner with the argument onlyToUnvisited=true. Also note, that even remembering scanner does not allow to remember a fact of visiting degenerated 0-pixel branches: you should keep this in mind and process degenerated branches, if necessary, by some other mechanism.In the second case, i.e. if
isBranch()
, you should start scanning the found branch by
firstStepFromBranch(boolean onlyToUnvisited)
method. But here it is important to distinguish two situations:free branch end
and all other variants (usual branch elements and attachable branch ends). The first situation is similar to the first case (a node): it is a node of the skeleton nonoriented graph (see thecomments to this class
), having only one incident edge (branch). In the second situation, you can try to move to somenode
/free branch end
(one of 2 ends of this branch), for example, byscanBranchFromBranch
method with withVisiting=false argument, but you should remember, that it can be also a cyclic branch (the case 7 in thecomments to this class
). This case can be identified via the current pixel type afterscanBranchFromBranch
call: it will beusual branch pixel
only in a case of a cyclic branch.Generally speaking, we do not recommend using Java recursion for graph traversal, because the internal JVM stack can be not enough for processing complex skeletons. Another approach is using breadth-first algorithm, based on some form of queue.
A full example of implementation of the breadth-first algorithm, with correct processing degenerated 0-pixel branches and cyclic branches, is given in the
comments to this class
.- Returns:
- true if this method has successfully found new node or branch pixel.
- See Also:
-
nextNodeOrBranchPixelType
Enhanced version ofnextNodeOrBranch()
, which returns the type of the successfully found element in the result or return null if the required element is not found. More precisely, it is equivalent tonextNodeOrBranch()
? Integer.valueOf(currentPixelTypeOrAttachedNode()
) : null- Returns:
- the type of the found pixel (or, for attachable branch end, the index of its neighbouring node, which is one of the ends of the branch), or null if this method does not found the required element.
-
adjacentBranches
On the assumption that thecurrent pixel
is anode
, returns indexes of all its neighbours, which are the starting pixels of branches, incident with this node. In particular, if some of neighbours of this node are also nodes, this method detects and returns in the result the indexes of such from them, which are connected with this node by degenerated branches (consisting of 0 pixels). If the current pixel is not a node (or isolated pixel), this method throws IllegalStateException.More precisely, the neighbour index k is an element of the returned array, if and only if:
- the
firstStep
(k,false) call, performed at this position, would be successful (would return true and successfully move the position to that neighbour); - and, in a case when this neighbour is a
node
, this neighbour is not marked (set to Integer.MIN_VALUE) by method, called for an array of types of all neighbours of the current node.pixelClassifier()
.markNeighbouringNodesNotConnectedViaDegeneratedBranches
The returned array is always a newly allocated Java array. Its length is always not greater than
numberOfNeighbours()
; it can be also empty, if the current element is anisolated pixel
. The returned indexes specify neighbours in terms of method. If you need maximal performance, you can eliminate memory allocation in a scanning loop by usingpixelClassifier()
.neighbourOffset(int)
adjacentBranches(int[])
method.- Returns:
- the list of indexes of neighbours of the current pixel (node), towards which this class supposes existence of a branch, originating from this node.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isNode()
.
- the
-
adjacentBranches
More efficient version ofadjacentBranches()
method, which stores the results in the passed Java array instead of creating new Java array. This method is equivalent to calling that method and copying its result into the beginning of result Java array, but does not allocate any arrays. It is a better solution if we need to calculate adjacent branches in a long loop, because allows to avoid allocating a lot of short arrays.The length of the passed array must be greater than or equal to
numberOfNeighbours()
.- Parameters:
result
- Java array for storing the results.- Returns:
- the number of found neighbours (which are the starting pixels of branches, incident with this node): after calling this method, you should use this number of first elements of the result array.
- Throws:
NullPointerException
- if result argument is null.IllegalArgumentException
- if result.length<numberOfNeighbours()
.IllegalStateException
- if this scanner was notpositioned yet
or if !isNode()
.
-
firstStep
On the assumption that thecurrent pixel
is anode or isolated pixel
, checks whether we have a skeleton branch, originating at this node and going towards its neighbour with the index neighbourIndex, and, if so, moves the current position to this neighbour and returns true, if not, does nothing and returns false. The neighbour index is specified in terms of method. If the current pixel is not a node (or isolated pixel), this method throws IllegalStateException. The movement along the branch, started by this method, can be continued by a loop ofpixelClassifier()
.neighbourOffset(int)
nextStep()
calls: see an example in comments toscanBranch
method.Warning: unlike
adjacentBranches()
and in violation of the definition of the nonoriented graph, formed by the skeleton, this method works as if every neighbouring node (when such nodes exist) is connected with this one via a degenerated 0-pixel branch. So, you should use it together withadjacentBranches()
.More precisely, this method checks the type of the given neighbour:
neighbourTypeOrAttachingBranch(neighbourIndex)
.- If it is a
node
, this method always moves the current position to that node and returns true (it can lead to extra degenerated branches, but you can useadjacentBranches()
method to avoid this); - If the type of the given neighbour is a
usual branch element
or afree branch end
, this method moves the current position to this neighbour and returns true. - If the given neighbour is
attachable branch end
, this method checks its attached node A (returned by and the element of attaching branch B (returned byneighbourTypeOrAttachedNode(neighbourIndex)
) — see the description of group 5 of pixel types in theneighbourTypeOrAttachingBranch(neighbourIndex)
)comments to SkeletonPixelClassifier
. If one of pixels A or B is the current node, this method moves the current position to this neighbour and returns true, in other case if does nothing and returns false. - In all other situations (the given neighbour is zero or
"
illegal
" unit element), this method does nothing and returns false.
The rules, listed above, are used as described if the argument onlyToUnvisited is false. If it is true and if this scanner is
remembering
, this method also checks, whether the given neighbour was already visited, i.e. checks the result of call. If that call returns true, this method does nothing and returns false, in other case it works as described above.neighbourVisitRemembered(neighbourIndex)
Note, that even if this scanner is
remembering
, this method does not store information about visiting pixels. If you want, you should do this manually byvisitPreviousBranchPixel()
method.Note, that we allow a situation when the neighbouring elements are out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of the
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
onlyToUnvisited
- whether this method should go only to neighbours, which were never be visited before (this argument affects only if this scanner isremembering
).- Returns:
- true if the current position has been successfully moved to the neighbour.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isNode()
.IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.- See Also:
- If it is a
-
firstStepFromBranch
On the assumption that thecurrent pixel
issome branch pixel
, moves the current position to a neighbour along this skeleton branch and returns true. If the current pixel is not a branch pixel, this method throws IllegalStateException. In a case of success (this method successfully changes the position and returns true), the previous current position is internally stored: it will be used innextStep()
method to finish scanning a cyclic branch. The movement along the branch, started by this method, can be continued by a loop ofnextStep()
calls: see an example in comments toscanBranchFromBranch
method.More precisely:
- if the current pixel is a
free branch end
, this method moves the current position to its only unit neighbour Q; - if the current pixel is a
usual branch element
, this method moves the current position to some of 2 its unit neighbours Q1 and Q2; - if the current pixel is a
attachable branch end
, this method moves the current position to some of 2 its neighbours Q1 and Q2, indexes of which are returned bycurrentPixelTypeOrAttachingBranch()
andcurrentPixelTypeOrAttachedNode()
methods.
The rules, listed above, are used as described if the argument onlyToUnvisited is false. If it is true and if this scanner is
remembering
, this method also checks, whether the neighbours were already visited, i.e. checks the result of call for the one (case 1) or for both (cases 2 and 3) neighbours. If that call returns false for the only neighbour Q (case 1) or for some of two neighbours Q1 and Q2 (cases 2 and 3), this method moves to that neighbour and returns true. In other case it does nothing and returns false.neighbourVisitRemembered(...)
Note, that even if this scanner is
remembering
, this method does not store information about visiting pixels. If you want, you should do this manually byvisitPreviousBranchPixel()
method.Note, that it is undocumented, which of two neighbours Q1 and Q2 is selected in cases 2 and 3 (if one of them is not disabled because onlyToUnvisited=true and it was already visited).
Note, that we allow a situation when the neighbouring elements are out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of the
comments to this class
.This method is implemented in the following way:
int nextNeighbourIndex =
firstStepFromBranchNeighbourIndex
(onlyToUnvisited); if (nextNeighbourIndex == -1) { return false; } this.startIndexInArray =currentIndexInArray()
; // - an internal field; it will be used innextStep()
to finish scanning a cyclic branchgoToNeighbour
(nextNeighbourIndex); this.previousBranchStepDirection = nextNeighbourIndex; // - an internal field, returned bypreviousBranchStepDirection()
method return true;- Parameters:
onlyToUnvisited
- whether this method should go only to neighbours, which were never be visited before (this argument affects only if this scanner isremembering
).- Returns:
- true if the current position has been successfully moved to the neighbour.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isBranch()
.- See Also:
- if the current pixel is a
-
firstStepFromBranchNeighbourIndex
Returns the index of the neighbour, to whichfirstStepFromBranch(boolean onlyToUnvisited)
moves when called with the same onlyToUnvisited argument. If that method returns false and does not move anywhere, this method returns -1. If that method throws IllegalStateException, this method also throws this exception. UnlikefirstStepFromBranch(boolean)
, this method does not change anything in the internal state of the object.See comments to
firstStepFromBranch(boolean)
method for more details.- Parameters:
onlyToUnvisited
- whether this method should check only neighbours, which were never be visited before (this argument affects only if this scanner isremembering
).- Returns:
- the index of the neighbour true, to which
firstStepFromBranch(boolean)
will move if it will be called. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isBranch()
.
-
nextStep
Continues movement along the skeleton branch, started byfirstStep(int, boolean)
orfirstStepFromBranch(boolean)
method, and returns true, if the end of the current branch is not reached yet, or does nothing and returns false if we have reached the end of the branch (usually anode
orfree branch end
).This method may be called only if the previous change of the current position was performed by
firstStep(int, boolean)
,firstStepFromBranch(boolean)
or this method. In other case (for example, if the last change of the current position was performed bygoTo(long...)
ornextNodeOrBranch()
), this method throws IllegalStateException.More precisely:
- if the current position is equal to the position, stored in the beginning of the last
firstStepFromBranch(boolean)
call, this method does nothing and returns false (it means that we've finished scanning of this cyclic branch and returned to the original position); - if the current pixel is a
free branch end
, this method does nothing and returns false (it means that we've reached the free end of this branch); - if the current pixel is a
usual branch element
, this method moves the current position to that from its 2 unit neighbours Q1 and Q2, which was not current before the previous change of the current position viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
or this method, and returns true; - if the current pixel is an
attachable branch end
, this method finds 2 its neighbours Q1 and Q2, indexes of which are returned bycurrentPixelTypeOrAttachingBranch()
andcurrentPixelTypeOrAttachedNode()
methods, and moves the current position to that from Q1 and Q2, which was not current before the previous change of the current position viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
or this method, and returns true; - if the current pixel is a
node
or an "illegal
" pixel, this method does nothing and returns false (it means that we've reached the node at the end of this branch or we cannot continue scanning because this pixel cannot belong to a correct skeleton); - if the current element is
zero
, this method throws IllegalStateException.
Note, that this method does not use information about possible previous visits of the pixels, probably remembered if this scanner is
remembering
.Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of the
comments to this class
.- Returns:
- true if the current position has been successfully moved to the neighbour.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
, or if the previous change of the current position was performed not by this method and not byfirstStep(int, boolean)
orfirstStepFromBranch(boolean)
.
- if the current position is equal to the position, stored in the beginning of the last
-
scanBranch
public void scanBranch(int neighbourIndex, boolean onlyToUnvisited, boolean withVisiting) throws IllegalStateException On the assumption that thecurrent pixel
is anode or isolated pixel
, completely scans the branch, originating at this node and going towards its neighbour with the given index. If onlyToUnvisited argument is true, this method does not try to scan a branch, the first pixel of which was already visited. If withVisiting argument is true, this method marks all visited and left pixels (including the starting node, but excluding the finish pixel) byvisitPreviousBranchPixel()
method. Both arguments onlyToUnvisited and withVisiting have no effect if this scanner is notremembering
.More precisely, this method is equivalent to the following code:
if (
with the only addition that this method also callsfirstStep
(neighbourIndex, onlyToUnvisited)) { do { if (withVisiting) {visitPreviousBranchPixel()
; } } while (nextStep()
); }context()
.checkInterruption()
method from time to time (if ) to allow interruption of scanning very long branches. No other methods of the context are called.context()
!=null- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
onlyToUnvisited
- whether this method should go only to neighbours, which were never be visited before (this argument affects only if this scanner isremembering
).withVisiting
- whether this method should callvisitPreviousBranchPixel()
after each step (this argument affects only if this scanner isremembering
).- Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isNode()
.IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.
-
scanBranchFromBranch
public void scanBranchFromBranch(boolean onlyToUnvisited, boolean withVisiting) throws IllegalStateException On the assumption that thecurrent pixel
issome branch pixel
, scans the part of this branch towards one of the sides of the current pixel. If the current pixel is afree branch end
or if this branch is cyclic (consists ofusual branch pixels
only), this method completely scans whole this branch. If onlyToUnvisited argument is true, this method does not try to scan a branch, if the first scanned pixel was already visited. If withVisiting argument is true, this method marks all visited and left pixels (including the starting pixel, but excluding the finish one) byvisitPreviousBranchPixel()
method. Both arguments onlyToUnvisited and withVisiting have no effect if this scanner is notremembering
.More precisely, this method is equivalent to the following code:
if (
with the only addition that this method also callsfirstStepFromBranch
(onlyToUnvisited)) { do { if (withVisiting) {visitPreviousBranchPixel()
; } } while (nextStep()
); }context()
.checkInterruption()
method from time to time (if ) to allow interruption of scanning very long branches. No other methods of the context are called.context()
!=null- Parameters:
onlyToUnvisited
- whether this method should go only to neighbours, which were never be visited before (this argument affects only if this scanner isremembering
).withVisiting
- whether this method should callvisitPreviousBranchPixel()
after each step (this argument affects only if this scanner isremembering
).- Throws:
IllegalStateException
- if this scanner was notpositioned yet
or if !isBranch()
.
-
previousBranchStepDirection
public int previousBranchStepDirection()Returns an index of the neighbour, towards which the current position was moved by the previous change of the current position viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
method, or -1 if the previous change of the current position was performed by some other method likegoTo(long...)
ornextNodeOrBranch()
. This neighbour index is specified in terms of method. So, if direction is the result of this method and it is not -1, thenpixelClassifier()
.neighbourOffset(int)
is an index of the neighbour of the current element, which was current before the last movement.pixelClassifier()
.reverseNeighbourIndex(direction)
- Returns:
- the direction of the last movement of the current position along a branch,
performed by
firstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
method, or -1 if the last movement was performed by another method. - Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
previousCoordinates
public long[] previousCoordinates()Returns the coordinates of the neighbour of the current element, which was current before the last change of the current position, if this change was performed viafirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
method, or throws IllegalStateException if the previous change of the current position was performed by some other method likegoTo(long...)
ornextNodeOrBranch()
.The returned array is always a newly allocated Java array. Its length is always equal to
dimCount()
. The returned coordinates are always in ranges0 ≤ result[k] <
where result[k] is the element #k in the returned array.skeleton()
.dim
(k),- Returns:
- the coordinates of the previous current pixel.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
, or if the previous change of the current position was performed not by not byfirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
.- See Also:
-
previousIndexInArray
public long previousIndexInArray()Reduced and more efficient version ofpreviousCoordinates()
, designed for indexing elements of thebuilt-in AlgART array
of the skeleton matrix. This method is equivalent to , but usually works much faster (in particular, does not allocate any arrays).skeleton()
.index
(previousCoordinates()
)The result of this method is always in range 0..
skeleton()
.size()
-1.- Returns:
- the previous current index in the built-in AlgART array of the skeleton matrix.
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
, or if the previous change of the current position was performed not by not byfirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
.
-
isRemembering
public boolean isRemembering()Returns true if this scanner is remembering or false if it is lightweight. See thecomments to this class
about remembering and lightweight skeleton scanners.- Returns:
- whether this class is remembering.
-
pixelVisitRemembered
public boolean pixelVisitRemembered()Returns true if this scanner isremembering
and thecurrent element
was already visited byvisit()
orvisitPreviousBranchPixel()
method.If this scanner is lightweight, this method always returns false.
- Returns:
- whether the current pixel is marked as "visited".
- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
neighbourVisitRemembered
public boolean neighbourVisitRemembered(int neighbourIndex) Returns true if this scanner isremembering
and the neighbour of thecurrent element
with the given index was already visited byvisit()
orvisitPreviousBranchPixel()
method. The result will be the same as if we would callgoToNeighbour
(neighbourIndex) and then callpixelVisitRemembered()
, but this method does not change the current position.If this scanner is lightweight, this method always returns false.
Note, that we allow a situation when the neighbouring element is out of ranges of the matrix coordinates. This situation is processed according to the model of infinite pseudo-cyclical continuation — see the end of the
comments to this class
.- Parameters:
neighbourIndex
- the index of the neighbour, in terms of method.pixelClassifier()
.neighbourOffset(int)
- Returns:
- whether the given neighbour of the current pixel is marked as "visited".
- Throws:
IndexOutOfBoundsException
- if neighbourIndex is out of range 0..numberOfNeighbours()
-1.IllegalStateException
- if this scanner was notpositioned yet
.
-
visit
public void visit()Inremembering
scanners, marks thecurrent element
of the skeleton matrix as "visited". In lightweight scanners, this method does nothing.Note that the only way to "unmark" the visited element (i.e. to change its state back to "unvisited") is calling
reset()
method.- Throws:
IllegalStateException
- if this scanner was notpositioned yet
.
-
visitPreviousBranchPixel
public void visitPreviousBranchPixel()Inremembering
scanners, marks theprevious visited element
of the skeleton matrix as "visited". In lightweight scanners, this method does nothing. The "previous visited element" means the neighbour of the current element, coordinates of which are returned bypreviousCoordinates()
method.Note that the only way to "unmark" the visited element (i.e. to change its state back to "unvisited") is calling
reset()
method.This methods is useful in loops of scanning skeleton branches: see examples of such loops in comments to
scanBranch(int, boolean, boolean)
andscanBranchFromBranch(boolean, boolean)
methods.- Throws:
IllegalStateException
- if this scanner was notpositioned yet
, or if the previous change of the current position was performed not by not byfirstStep(int, boolean)
,firstStepFromBranch(boolean)
ornextStep()
.
-
reset
public void reset()Clears the state of this scanner: resets the current position tonot positioned
state and, inremembering
scanners, resets the state of all pixels tounvisited
. -
updateProgress
public void updateProgress()Callscontext()
.updateProgress(event)
with an event, created by the following operator:new ArrayContext.Event(boolean.class, , or does nothing ifcurrentIndexInArray()
,skeleton()
.size()
)context()
==null.The method can be useful while sequentially scanning the skeleton via a usual loop of
nextNodeOrBranch()
calls. -
checkInterruption
public void checkInterruption()Callscontext()
.checkInterruption()
or does nothing ifcontext()
==null.The method can be useful while sequentially scanning the skeleton via a usual loop of
nextNodeOrBranch()
calls. -
toString
Returns a brief string description of this object.The result of this method may depend on implementation and usually contains a short description of the current state of the scanner.
-