io.github.mianalysis.mia.process.skeleton.Skeleton Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mia-algorithms Show documentation
Show all versions of mia-algorithms Show documentation
ModularImageAnalysis (MIA) is an ImageJ plugin which provides a modular framework for assembling image and object analysis workflows. Detected objects can be transformed, filtered, measured and related. Analysis workflows are batch-enabled by default, allowing easy processing of high-content datasets.
package io.github.mianalysis.mia.process.skeleton;
import java.util.ArrayList;
import ij.ImagePlus;
import io.github.mianalysis.mia.object.coordinates.Vertex;
import io.github.mianalysis.mia.object.coordinates.VertexCollection;
/**
* Created by sc13967 on 24/01/2018.
*/
public class Skeleton extends VertexCollection {
/**
*
*/
private static final long serialVersionUID = 3490921805740347855L;
private double longestDistance;
private ArrayList longestPath;
public Skeleton(ImagePlus ipl) {
for (int x=0;x getLongestPath() {
longestDistance = 0;
longestPath = new ArrayList<>();
// Getting the vertices with one neighbour (those at branch ends)
ArrayList endPoints = getEndPoints();
for (Vertex vertex : endPoints) {
ArrayList currentPath = new ArrayList<>();
addDistanceToNextVertex(vertex, 0, currentPath);
}
return longestPath;
}
public ArrayList getEndPoints() {
ArrayList endPoints = new ArrayList<>();
// End points only have one neighbour
for (Vertex vertex:this) {
if (vertex.getNumberOfNeighbours() == 1) endPoints.add(vertex);
}
return endPoints;
}
public ArrayList getBranchPoints() {
ArrayList branchPoints = new ArrayList<>();
// Branch points have 3 or more neighbours
for (Vertex vertex:this) {
if (vertex.getNumberOfNeighbours() >= 3) branchPoints.add(vertex);
}
return branchPoints;
}
public Vertex addBreak() {
// Iterate over each vertex until we find one that creating a break at will cause an end
for (Vertex vertex : this) {
for (Vertex neighbour : vertex.getNeighbours()) {
if (neighbour.getNumberOfNeighbours() == 2) {
remove(vertex);
return vertex;
}
}
}
return null;
}
public int[] getX() {
return stream().mapToInt(Vertex::getX).toArray();
}
public int[] getY() {
return stream().mapToInt(Vertex::getY).toArray();
}
public int[] getZ() {
return stream().mapToInt(Vertex::getZ).toArray();
}
private void assignNeighbours() {
// Assigning neighbours
for (Vertex vertex1:this) {
for (Vertex vertex2:this) {
if (vertex1 == vertex2) continue;
double distance = vertex1.calculateDistanceToPoint(vertex2);
// The longest distance for 26-way connectivity is 1.73
if (distance<1.75) vertex1.addNeighbour(vertex2);
}
}
// There may be junctions with excessive linkages. Identifying these and removing them.
ArrayList linksToRemove = new ArrayList<>();
for (Vertex vertex1:this) {
if (vertex1.getNumberOfNeighbours() > 2) {
// If the current vertex and a neighbour are connected to the same Vertex, keep the links with the
// shortest length
for (Vertex vertex2:vertex1.getNeighbours()) {
for (Vertex vertex3:vertex2.getNeighbours()) {
// Removing the longest link
if (vertex1.getNeighbours().contains(vertex3)) {
double v1v3 = vertex1.calculateDistanceToPoint(vertex3);
double v1v2 = vertex1.calculateDistanceToPoint(vertex2);
double v2v3 = vertex2.calculateDistanceToPoint(vertex3);
if (v1v2 > v1v3 && v1v2 > v2v3) {
linksToRemove.add(new Vertex[]{vertex1,vertex2});
} else if (v1v3 > v1v2 && v1v3 > v2v3) {
linksToRemove.add(new Vertex[]{vertex1, vertex3});
} else if (v2v3 > v1v2 && v2v3 > v1v3) {
linksToRemove.add(new Vertex[]{vertex2, vertex3});
}
}
}
}
}
}
for (Vertex[] link:linksToRemove) {
link[0].removeNeighbour(link[1]);
link[1].removeNeighbour(link[0]);
}
}
private void addDistanceToNextVertex(Vertex currentVertex, double distance, ArrayList currentPath) {
// Making a new path for this point onwards
ArrayList newCurrentPath = new ArrayList<>();
newCurrentPath.addAll(currentPath);
newCurrentPath.add(currentVertex);
for (Vertex neighbourVertex:currentVertex.getNeighbours()) {
// If this Vertex has already been traversed, skip it
if (newCurrentPath.contains(neighbourVertex)) continue;
// Calculating the distance to the new Vertex
distance = distance + currentVertex.calculateDistanceToPoint(neighbourVertex);
addDistanceToNextVertex(neighbourVertex,distance,newCurrentPath);
}
// If we've ended up at an end-point Vertex and the distance travelled is longer than the previously-assigned
// longest distance assign this as the longest path
if (currentVertex.getNumberOfNeighbours() == 1 && distance > longestDistance) {
longestDistance = distance;
longestPath = newCurrentPath;
}
}
@Override
public boolean remove(Object o) {
Vertex vertex = (Vertex) o;
// Removing this as a neighbour of its neighbours
for (Vertex neighbour:vertex.getNeighbours()) {
neighbour.removeNeighbour(vertex);
}
// Removing from the collection
return super.remove(vertex);
}
}