net.algart.matrices.skeletons.BasicSkeletonPixelClassifier2D Maven / Gradle / Ivy
Show all versions of algart Show documentation
/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.algart.matrices.skeletons;
import net.algart.arrays.JArrays;
import java.util.Objects;
/**
* Ready classifier of pixel of 2-dimensional {@link ThinningSkeleton thinning skeletons}.
*
*
There are only 3 instances of this class, returned by the methods:
*
*
* - {@link #getOctupleThinningInstance()},
* - {@link #getQuadruple3x5ThinningInstance()},
* - {@link #getStrongQuadruple3x5ThinningInstance()}.
*
*
* These classifiers are designed for processing bit matrices, which are the final results of
* most popular 2-dimensional skeletonization algorithms: {@link OctupleThinningSkeleton2D},
* {@link Quadruple3x5ThinningSkeleton2D} and {@link StrongQuadruple3x5ThinningSkeleton2D}.
* These algorithms have different "strength", so, using "stronger" classifier for "weaker" skeleton
* (for example, using {@link #getStrongQuadruple3x5ThinningInstance()} for the resulting skeleton
* of {@link OctupleThinningSkeleton2D} algorithm) can lead to recognition of some skeleton pixels as
* "{@link #TYPE_ILLEGAL illegal}".
*
* This class is immutable and thread-safe:
* there are no ways to modify settings of the created instance.
*
* @author Daniel Alievsky
*/
public class BasicSkeletonPixelClassifier2D extends ApertureBasedSkeletonPixelClassifier {
/*Repeat() XM_YM\s+=\s0 ==> YM = 1,,XP_YM = 2,,XP = 3,,XP_YP = 4,,YP = 5,,XM_YP = 6,,XM = 7 */
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XM_YM = 0;
/*Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! */
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_YM = 1;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XP_YM = 2;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XP = 3;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XP_YP = 4;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_YP = 5;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XM_YP = 6;
/**
* {@value}: an index of the neighbour #{@value} of any element in terms of
* {@link #neighbourOffset(int) neighbourOffset(int)} method.
* It is one of possible non-negative values, returned by {@link #asPixelTypes asPixelTypes} method.
* You can understand the sense of the name of this constant from the comments to these methods.
*/
public static final int NEIGHBOUR_INDEX_XM = 7;
/*Repeat.AutoGeneratedEnd*/
/**
* {@value}: minimal non-negative value, that can be returned by {@link #asPixelTypes asPixelTypes} method
* in this class.
*/
public static final int NEIGHBOUR_INDEX_MIN = 0;
/**
* {@value}: maximal non-negative value, that can be returned by {@link #asPixelTypes asPixelTypes} method
* in this class.
*/
public static final int NEIGHBOUR_INDEX_MAX = 7;
private static final long[][] SHIFTS_A = {
{-1, -1}, // A1
{0, -1}, // A2
{1, -1}, // A3
{1, 0}, // A4
{1, 1}, // A5
{0, 1}, // A6
{-1, 1}, // A7
{-1, 0}, // A8
};
private static final int[] OCTUPLE_THINNING_SKELETON_CLASSIFICATION_MAP = {
// This constant contains of triplets:
// the bits of 3x3 neighbourhood,
// the negative pixel type or the index of the branch, to which this pixel should be attached,
// the negative pixel type or the index of the node, from which this branch is originated.
// This constant is applicable for OctupleThinningSkeleton2D and, with removing some configurations,
// for Quadruple3x5ThinningSkeleton2D and StrongQuadruple3x5ThinningSkeleton2D.
// For OctupleThinningSkeleton2D, 148 3x3 bit configurations in the skeleton are totally possible;
// we'll give all them, usually with 5x5 neighbourhood, but without configurations,
// which can be produced by rotation of the coordinate system.
//
// In bit configurations below:
// "*" means the unit element (1),
// "." means the zero element (0),
// "?" (in 5x5 neighbourhood) means that this bit can be 0 in some configurations and 1 in others;
// "O" means "?" for OctupleThinningSkeleton2D and "." for [Strong]Quadruple3x5ThinningSkeleton2D.
//
// Below "improbable" and "questionable" configurations are those bit sets 5x5, where the test
// net.algart.matrices.skeletons.demo.AnalyseSkeletonConfigurations cannot guarantee that they are impossible;
// all configurations besides possible, improbable and questionable are surely impossible.
// We shall give all possible, improbable and questionable configurations of 5x5 neighbourhood
// for OctupleThinningSkeleton2D/Quadruple3x5ThinningSkeleton2D and, if StrongQuadruple3x5ThinningSkeleton2D
// has other configurations, give them separately (if StrongQuadruple3x5ThinningSkeleton2D has the same
// possibilities as Quadruple3x5ThinningSkeleton2D, we don't comment this).
// Note that "improbable" configurations have no relation to OctupleThinningSkeleton2D, but just to
// Quadruple3x5ThinningSkeleton2D/StrongQuadruple3x5ThinningSkeleton2D.
// (1/148) 0=0x0 (3x3, 1 unit bits):
// . . .
// . * .
// . . .
0, TYPE_ISOLATED, TYPE_ISOLATED,
// (2/148) 1=0x1 (3x3, 2 unit bits):
// * . .
// . * .
// . . .
1, TYPE_FREE_BRANCH_END, TYPE_FREE_BRANCH_END,
// (6/148) 2=0x2 (3x3, 2 unit bits):
// . * .
// . * .
// . . .
2, TYPE_FREE_BRANCH_END, TYPE_FREE_BRANCH_END,
// (10/148) 5=0x5 (3x3, 3 unit bits):
// * . *
// . * .
// . . .
5, TYPE_USUAL_BRANCH, TYPE_USUAL_BRANCH,
// (14/148) 9=0x9 (3x3, 3 unit bits):
// * . .
// . * *
// . . .
9, TYPE_USUAL_BRANCH, TYPE_USUAL_BRANCH,
// (18/148) 17=0x11 (3x3, 3 unit bits):
// * . .
// . * .
// . . *
17, TYPE_USUAL_BRANCH, TYPE_USUAL_BRANCH,
// (20/148) 18=0x12 (3x3, 3 unit bits):
// . * .
// . * .
// . . *
18, TYPE_USUAL_BRANCH, TYPE_USUAL_BRANCH,
// (24/148) 34=0x22 (3x3, 3 unit bits):
// . * .
// . * .
// . * .
34, TYPE_USUAL_BRANCH, TYPE_USUAL_BRANCH,
// (26/148) 13=0xD (3x3, 4 unit bits):
// * . *
// . * *
// . . .
// 13=0xD (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * .
// ? . . . *
// ? ? ? ? ?
// 13=0xD (3x3, 4 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * . * ?
// ? . * * .
// ? . . . *
// ? ? ? ? ?
13, NEIGHBOUR_INDEX_XM_YM, NEIGHBOUR_INDEX_XP,
// (30/148) 19=0x13 (3x3, 4 unit bits):
// * * .
// . * .
// . . *
// 19=0x13 (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . . * ?
// ? ? ? ? ?
// 19=0x13 (3x3, 4 unit bits) inside the following series of questionable 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . . * ?
// ? ? ? ? ?
19, NEIGHBOUR_INDEX_XP_YP, NEIGHBOUR_INDEX_YM,
// (34/148) 21=0x15 (3x3, 4 unit bits):
// * . *
// . * .
// . . *
21, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (38/148) 22=0x16 (3x3, 4 unit bits):
// . * *
// . * .
// . . *
// 22=0x16 (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * ?
// ? . * . O
// ? . . * ?
// ? ? ? ? ?
// 22=0x16 (3x3, 4 unit bits) inside the following series of questionable 5x5 configurations
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * ?
// ? . * . O
// ? . . * ?
// ? ? ? ? ?
22, NEIGHBOUR_INDEX_XP_YP, NEIGHBOUR_INDEX_YM,
// (42/148) 25=0x19 (3x3, 4 unit bits):
// * . .
// . * *
// . . *
// 25=0x19 (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . . *
// ? . * * .
// ? . . * ?
// ? ? ? ? ?
// 25=0x19 (3x3, 4 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . . *
// ? . * * .
// ? . . * ?
// ? ? ? ? ?
25, NEIGHBOUR_INDEX_XM_YM, NEIGHBOUR_INDEX_XP,
// (46/148) 35=0x23 (3x3, 4 unit bits):
// * * .
// . * .
// . * .
// 35=0x23 (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
// 35=0x23 (3x3, 4 unit bits) inside the following series of questionable 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
35, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM,
// (50/148) 37=0x25 (3x3, 4 unit bits):
// * . *
// . * .
// . * .
37, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (54/148) 38=0x26 (3x3, 4 unit bits):
// . * *
// . * .
// . * .
// 38=0x26 (3x3, 4 unit bits) is possible inside the following series of 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
// 38=0x26 (3x3, 4 unit bits) inside the following series of questionable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
38, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM,
// (58/148) 41=0x29 (3x3, 4 unit bits):
// * . .
// . * *
// . * .
41, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (62/148) 23=0x17 (3x3, 5 unit bits):
// * * *
// . * .
// . . *
// 23=0x17 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . O
// ? . . * ?
// ? ? ? ? ?
// 23=0x17 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . O
// ? . . * ?
// ? ? ? ? ?
23, NEIGHBOUR_INDEX_XP_YP, NEIGHBOUR_INDEX_YM,
// (66/148) 29=0x1D (3x3, 5 unit bits):
// * . *
// . * *
// . . *
// 29=0x1D (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * *
// ? . . * ?
// ? ? ? ? ?
// 29=0x1D (3x3, 5 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * . * ?
// ? . * * *
// ? . . * ?
// ? ? ? ? ?
// 29=0x1D (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * *
// ? . . * ?
// ? ? ? ? ?
29, NEIGHBOUR_INDEX_XM_YM, NEIGHBOUR_INDEX_XP,
// (70/148) 39=0x27 (3x3, 5 unit bits):
// * * *
// . * .
// . * .
// 39=0x27 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
// 39=0x27 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? . * . ?
// ? ? ? ? ?
39, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM,
// (74/148) 45=0x2D (3x3, 5 unit bits):
// * . *
// . * *
// . * .
// 45=0x2D (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * .
// ? . * . *
// ? ? ? ? ?
// 45=0x2D (3x3, 5 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * . * .
// ? . * * .
// ? . * . *
// ? ? ? ? ?
// 45=0x2D (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * .
// ? . * . *
// ? ? ? ? ?
45, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // together with symmetric configuration
// (78/148) 51=0x33 (3x3, 5 unit bits):
// * * .
// . * .
// . * *
// 51=0x33 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . * * ?
// ? * . ? ?
// 51=0x33 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? . * ?
// ? * * . ?
// ? . * . ?
// ? . * * ?
// ? * . ? ?
51, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (80/148) 53=0x35 (3x3, 5 unit bits):
// * . *
// . * .
// . * *
// 53=0x35 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// 53=0x35 (3x3, 5 unit bits) inside the following series of improbable 5x5 configurations
// ? ? ? ? ?
// ? * . * ?
// ? . * . ?
// ? . * * ?
// ? * . ? ?
// 53=0x35 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
53, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (84/148) 54=0x36 (3x3, 5 unit bits):
// . * *
// . * .
// . * *
// 54=0x36 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// 54=0x36 (3x3, 5 unit bits) inside the following series of improbable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . *
// ? . * * .
// ? * . ? .
// 54=0x36 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
54, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (88/148) 57=0x39 (3x3, 5 unit bits):
// * . .
// . * *
// . * *
// 57=0x39 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . . *
// ? . * * .
// ? . * * ?
// ? * . ? ?
// 57=0x39 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// * ? ? ? ? ? ? ? ? ?
// ? * . . * ? * . . *
// ? . * * . for OctupleThinningSkeleton2D, ? . * * . for [Strong]Quadruple3x5ThinningSkeleton2D
// ? . * * ? ? . * * ?
// ? * . ? ? ? * . ? ?
57, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (92/148) 85=0x55 (3x3, 5 unit bits):
// * . *
// . * .
// * . *
85, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (93/148) 86=0x56 (3x3, 5 unit bits):
// . * *
// . * .
// * . *
// 86=0x56 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? * . * ?
// ? ? ? ? ?
// 86=0x56 (3x3, 5 unit bits) inside the following series of improbable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? * . * ?
// ? ? * * ?
// 86=0x56 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? * . * ?
// ? ? ? ? ?
86, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (97/148) 90=0x5A (3x3, 5 unit bits):
// . * .
// . * *
// * . *
// 90=0x5A (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * . *
// ? . * * .
// ? * . * O
// ? ? O ? ?
// 90=0x5A (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * . *
// ? . * * .
// ? * . * O
// ? ? O ? ?
90, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // together with symmetric configuration
// (101/148) 102=0x66 (3x3, 5 unit bits):
// . * *
// . * .
// * * .
// 102=0x66 (3x3, 5 unit bits) is possible inside the following series of 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? * * . ?
// ? ? . * ?
// 102=0x66 (3x3, 5 unit bits) inside the following series of questionable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * . ?
// ? * * . ?
// ? ? . * ?
102, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (103/148) 170=0xAA (3x3, 5 unit bits):
// . * .
// * * *
// . * .
170, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (104/148) 55=0x37 (3x3, 6 unit bits):
// * * *
// . * .
// . * *
// 55=0x37 (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// 55=0x37 (3x3, 6 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . *
// ? . * * .
// ? * . ? .
// 55=0x37 (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? . * * ?
// ? * . O ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
55, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (108/148) 61=0x3D (3x3, 6 unit bits):
// * . *
// . * *
// . * *
// In a case of OctupleThinningSkeleton2D/Quadruple3x5ThinningSkeleton2D:
// 61=0x3D (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * *
// ? . * * ?
// ? * . ? ?
// 61=0x3D (3x3, 6 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * . * .
// ? . * * *
// ? . * * ?
// ? * . ? ?
// 61=0x3D (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? . * * *
// ? . * * ?
// ? * . ? ?
// In a case of StrongQuadruple3x5ThinningSkeleton2D:
// 61=0x3D (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? ? ? ? ?
// ? * . * .
// ? . * * *
// ? . * * .
// ? * . . *
// 61=0x3D (3x3, 6 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * . * .
// ? . * * *
// ? . * * .
// ? * . . *
// 61=0x3D (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? ? ? ? ?
// ? * . * .
// ? . * * *
// ? . * * .
// ? * . . *
61, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (112/148) 87=0x57 (3x3, 6 unit bits):
// * * *
// . * .
// * . *
// 87=0x57 (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * . * ?
// ? ? ? ? ?
// 87=0x57 (3x3, 6 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? * . * ?
// ? ? ? ? ?
// 87=0x57 (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * . * ?
// ? ? ? ? ?
87, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// (116/148) 91=0x5B (3x3, 6 unit bits):
// * * .
// . * *
// * . *
// (91=0x5B is possible in OctupleThinningSkeleton2D only)
// 91=0x5B (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . *
// ? . * * .
// ? * . * ?
// ? ? ? ? ?
// 91=0x5B (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? ? . * ?
// ? * * . *
// ? . * * .
// ? * . * ?
// ? ? ? ? ?
91, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
// don't attach to the left down one to avoid asymmetry while finding the originating node
// (120/148) 94=0x5E (3x3, 6 unit bits):
// . * *
// . * *
// * . *
// In a case of OctupleThinningSkeleton2D/Quadruple3x5ThinningSkeleton2D:
// 94=0x5E (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * * *
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? ? O ? ?
// 94=0x5E (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? * . ? ?
// ? . * * ?
// ? . * * *
// ? * . * O ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? ? O ? ?
// In a case of StrongQuadruple3x5ThinningSkeleton2D:
// 94=0x5E (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? * . . *
// ? . * * .
// ? . * * *
// ? * . * .
// ? ? . ? ?
// 94=0x5E (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? * . . *
// ? . * * .
// ? . * * *
// ? * . * .
// ? ? . ? ?
94, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (124/148) 103=0x67 (3x3, 6 unit bits):
// * * *
// . * .
// * * .
// 103=0x67 (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * * . ?
// ? ? . * ?
// 103=0x67 (3x3, 6 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// . * * * ?
// * . * . ?
// ? * * . ?
// ? ? . * ?
// 103=0x67 (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * * . ?
// ? ? . * ?
103, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (128/148) 171=0xAB (3x3, 6 unit bits):
// * * .
// * * *
// . * .
// 171=0xAB (3x3, 6 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . ?
// . * * * ?
// * . * . ?
// ? O ? ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// 171=0xAB (3x3, 6 unit bits) inside the following series of questionable 5x5 configurations
// ? ? . * ?
// ? * * . ?
// . * * * ?
// * . * . ?
// ? O ? ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
171, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (132/148) 95=0x5F (3x3, 7 unit bits):
// * * *
// . * *
// * . *
// In a case of OctupleThinningSkeleton2D/Quadruple3x5ThinningSkeleton2D:
// 95=0x5F (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// ? O * O * ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * O
// ? . * * *
// ? * . * O
// ? ? . ? ?
// 95=0x5F (3x3, 7 unit bits) inside the following series of improbable 5x5 configurations
// ? . * ? *
// ? * * * ?
// * . * * *
// ? * . * .
// ? ? . ? ?
// 95=0x5F (3x3, 7 unit bits) inside the following series of questionable 5x5 configurations
// ? O * O * ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * O
// ? . * * *
// ? * . * O
// ? ? . ? ?
// In a case of StrongQuadruple3x5ThinningSkeleton2D:
// 95=0x5F (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// (no differences)
// 95=0x5F (3x3, 7 unit bits) inside the following series of improbable 5x5 configurations
// ? . * . *
// ? * * * .
// * . * * *
// ? * . * .
// ? ? . ? ?
// 95=0x5F (3x3, 7 unit bits) inside the following series of questionable 5x5 configurations
// ? . * . *
// ? * * * .
// ? . * * *
// ? * . * .
// ? ? . ? ?
95, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (136/148) 119=0x77 (3x3, 7 unit bits):
// * * *
// . * .
// * * *
// 119=0x77 (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * * * ?
// ? ? * O ?
// 119=0x77 (3x3, 7 unit bits) inside the following series of improbable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// ? . * . ?
// ? * * * ?
// ? ? * ? ?
// 119=0x77 (3x3, 7 unit bits) inside the following series of questionable 5x5 configurations
// ? O * ? ? ("O" means "?" for OctupleThinningSkeleton2D, "." for others)
// ? * * * ?
// ? . * . ?
// ? * * * ?
// ? ? * O ?
119, NEIGHBOUR_INDEX_YP, NEIGHBOUR_INDEX_YM, // 1-pixel branch
// (138/148) 175=0xAF (3x3, 7 unit bits):
// * * *
// * * *
// . * .
// (175=0xAF is possible in OctupleThinningSkeleton2D and Quadruple3x5ThinningSkeleton2D only)
// In a case of OctupleThinningSkeleton2D:
// 175=0xAF (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// . * * * .
// * . * . *
// ? ? ? ? ?
// 175=0xAF (3x3, 7 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// . * * * .
// * . * . *
// ? ? ? ? ?
// In a case of Quadruple3x5ThinningSkeleton2D/StrongQuadruple3x5ThinningSkeleton2D:
// 175=0xAF (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// * . * . *
// . * * * .
// . * * * .
// * . * . *
// ? . * . ?
// 175=0xAF (3x3, 7 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// . * * * .
// * . * . *
// ? . * ? ?
175, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (142/148) 187=0xBB (3x3, 7 unit bits):
// * * .
// * * *
// . * *
// (187=0xBB is possible in OctupleThinningSkeleton2D only)
// 187=0xBB (3x3, 7 unit bits) is possible inside the following series of 5x5 configurations
// ? ? . * ?
// ? * * . *
// . * * * .
// * . * * ?
// ? * . ? ?
187, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (144/148) 191=0xBF (3x3, 8 unit bits):
// * * *
// * * *
// . * *
// (191=0xBF is possible in OctupleThinningSkeleton2D only)
// 191=0xBF (3x3, 8 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// . * * * *
// * . * * ?
// ? * . ? ?
// 191=0xBF (3x3, 8 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// . * * * *
// * . * * ?
// ? * . ? ?
191, TYPE_USUAL_NODE, TYPE_USUAL_NODE, // always consider 2x2, 2x3, 3x2 and 3x3 unit areas as nodes
// (148/148) 255=0xFF (3x3, 9 unit bits):
// * * *
// * * *
// * * *
// (255=0xFF is possible in OctupleThinningSkeleton2D and Quadruple3x5ThinningSkeleton2D only)
// In a case of OctupleThinningSkeleton2D:
// 255=0xFF (3x3, 9 unit bits) is possible inside the following series of 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// * * * * *
// ? * * * ?
// ? ? * ? ?
// 255=0xFF (3x3, 9 unit bits) inside the following series of questionable 5x5 configurations
// ? ? * ? ?
// ? * * * ?
// * * * * *
// ? * * * ?
// ? ? * ? ?
// In a case of Quadruple3x5ThinningSkeleton2D/StrongQuadruple3x5ThinningSkeleton2D:
// 255=0xFF (3x3, 9 unit bits) is possible inside the following series of 5x5 configurations
// * . * . *
// . * * * .
// * * * * *
// . * * * .
// * . * . *
255, TYPE_USUAL_NODE, TYPE_USUAL_NODE,
};
private static final BasicSkeletonPixelClassifier2D
BASIC_OCTUPLE_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D = new BasicSkeletonPixelClassifier2D(
OCTUPLE_THINNING_SKELETON_CLASSIFICATION_MAP),
BASIC_QUADRUPLE_3X5_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D = new BasicSkeletonPixelClassifier2D(
OCTUPLE_THINNING_SKELETON_CLASSIFICATION_MAP, 91, 187, 191),
BASIC_STRONG_QUADRUPLE_3X5_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D = new BasicSkeletonPixelClassifier2D(
OCTUPLE_THINNING_SKELETON_CLASSIFICATION_MAP, 91, 187, 191, 175, 255);
private final int[] classificationTableWithAttachingBranches = new int[256];
private final int[] classificationTableWithAttachedNodes = new int[256];
private BasicSkeletonPixelClassifier2D(int[] classificationMap, int... additionalIllegalConfigurations) {
super(2, SHIFTS_A);
Objects.requireNonNull(classificationMap, "Null classificationMap argument");
if (classificationMap.length % 3 != 0) {
throw new IllegalArgumentException("Length of classificationMap does not divide by 3");
}
JArrays.fill(this.classificationTableWithAttachingBranches, TYPE_ILLEGAL);
JArrays.fill(this.classificationTableWithAttachedNodes, TYPE_ILLEGAL);
for (int k = 0; k < classificationMap.length; k += 3) {
int bitsA = classificationMap[k];
if (bitsA < 0 || bitsA > 255) {
throw new IllegalArgumentException("First element of a triplet "
+ "in classificationMap (bit configuration code) is out of 0..255 range");
}
int pixelTypeWithAttachingBranch = classificationMap[k + 1];
if (pixelTypeWithAttachingBranch > NEIGHBOUR_INDEX_MAX) {
throw new IllegalArgumentException("Second element of a triplet "
+ "in classificationMap (pixel type or direction of attaching branch end) "
+ "is greater than " + NEIGHBOUR_INDEX_MAX);
}
int pixelTypeWithAttachedNode = classificationMap[k + 2];
if (pixelTypeWithAttachedNode > NEIGHBOUR_INDEX_MAX) {
throw new IllegalArgumentException("Third element of a triplet "
+ "in classificationMap (pixel type or direction of attached node) "
+ "is greater than " + NEIGHBOUR_INDEX_MAX);
}
if ((pixelTypeWithAttachingBranch < 0 || pixelTypeWithAttachedNode < 0)
&& pixelTypeWithAttachedNode != pixelTypeWithAttachingBranch) {
throw new IllegalArgumentException("Negative second and third elements of a triplet "
+ "in classificationMap (pixel type or direction of attached node) are not equal: "
+ pixelTypeWithAttachingBranch + " and " + pixelTypeWithAttachedNode);
}
this.classificationTableWithAttachingBranches[bitsA] = pixelTypeWithAttachingBranch;
this.classificationTableWithAttachedNodes[bitsA] = pixelTypeWithAttachedNode;
for (int directionIndex = 1; directionIndex <= 3; directionIndex++) {
bitsA = rotated90BitsA(bitsA);
pixelTypeWithAttachingBranch = rotated90Direction(pixelTypeWithAttachingBranch);
pixelTypeWithAttachedNode = rotated90Direction(pixelTypeWithAttachedNode);
this.classificationTableWithAttachingBranches[bitsA] = pixelTypeWithAttachingBranch;
this.classificationTableWithAttachedNodes[bitsA] = pixelTypeWithAttachedNode;
}
}
for (int bitsA : additionalIllegalConfigurations) {
this.classificationTableWithAttachingBranches[bitsA] = TYPE_ILLEGAL;
this.classificationTableWithAttachedNodes[bitsA] = TYPE_ILLEGAL;
for (int directionIndex = 1; directionIndex <= 3; directionIndex++) {
bitsA = rotated90BitsA(bitsA);
this.classificationTableWithAttachingBranches[bitsA] = TYPE_ILLEGAL;
this.classificationTableWithAttachedNodes[bitsA] = TYPE_ILLEGAL;
}
}
}
/*Repeat() OctupleThinning ==> Quadruple3x5Thinning,,StrongQuadruple3x5Thinning;;
OCTUPLE_THINNING ==> QUADRUPLE_3X5_THINNING,,STRONG_QUADRUPLE_3X5_THINNING
*/
/**
* Returns the instance of this class, intended for processing skeletons, which are
* the final result of skeletonization by {@link OctupleThinningSkeleton2D} algorithm.
*
* @return the classifier of pixels of {@link OctupleThinningSkeleton2D} skeletons.
*/
public static BasicSkeletonPixelClassifier2D getOctupleThinningInstance() {
return BASIC_OCTUPLE_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D;
}
/*Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! */
/**
* Returns the instance of this class, intended for processing skeletons, which are
* the final result of skeletonization by {@link Quadruple3x5ThinningSkeleton2D} algorithm.
*
* @return the classifier of pixels of {@link Quadruple3x5ThinningSkeleton2D} skeletons.
*/
public static BasicSkeletonPixelClassifier2D getQuadruple3x5ThinningInstance() {
return BASIC_QUADRUPLE_3X5_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D;
}
/**
* Returns the instance of this class, intended for processing skeletons, which are
* the final result of skeletonization by {@link StrongQuadruple3x5ThinningSkeleton2D} algorithm.
*
* @return the classifier of pixels of {@link StrongQuadruple3x5ThinningSkeleton2D} skeletons.
*/
public static BasicSkeletonPixelClassifier2D getStrongQuadruple3x5ThinningInstance() {
return BASIC_STRONG_QUADRUPLE_3X5_THINNING_SKELETON_PIXEL_CLASSIFIER_2_D;
}
/*Repeat.AutoGeneratedEnd*/
@Override
protected int pixelTypeOrAttachingBranch(int apertureBits) {
return classificationTableWithAttachingBranches[apertureBits];
}
@Override
protected int pixelTypeOrAttachedNode(int apertureBits) {
return classificationTableWithAttachedNodes[apertureBits];
}
// bit index:
// A A A 0 1 2
// A * A 7 * 3
// A A A 6 5 4
private static int rotated90BitsA(int apertureBits) {
assert 0 <= apertureBits && apertureBits < 256;
int bit0 = apertureBits & 1;
int bit1 = (apertureBits >> 1) & 1;
int bit2 = (apertureBits >> 2) & 1;
int bit3 = (apertureBits >> 3) & 1;
int bit4 = (apertureBits >> 4) & 1;
int bit5 = (apertureBits >> 5) & 1;
int bit6 = (apertureBits >> 6) & 1;
int bit7 = (apertureBits >> 7) & 1;
return bit6 |
(bit7 << 1) |
(bit0 << 2) |
(bit1 << 3) |
(bit2 << 4) |
(bit3 << 5) |
(bit4 << 6) |
(bit5 << 7);
}
// pixelType:
// A A A 0 1 2
// A * A 7 * 3
// A A A 6 5 4
private static int rotated90Direction(int pixelType) {
if (pixelType < 0) {
return pixelType;
}
return (pixelType + 2) & 7;
}
}