/** * */ package fr.unistra.pelican.util.connectivityTrees; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Stack; import fr.unistra.pelican.Image; import fr.unistra.pelican.util.Point3D; import fr.unistra.pelican.util.connectivityTrees.attributes.AttributePointList; import fr.unistra.pelican.util.connectivityTrees.attributes.ComponentAttribute; import fr.unistra.pelican.util.connectivityTrees.attributes.UnsupportedDataTypeException; import fr.unistra.pelican.util.connectivityTrees.connectivity.Connectivity3D; /** * A connected component tree. A tree is composed as a set of component nodes. * A component node is accessed through its canonical element, i.e. a special point associated to it. * The union find technique is used to link each point to the canonical element representing the connected node it belongs to. * * The class provides several utilities to manipulate the tree (access to nodes, iterate over nodes, delete nodes, add attributes) * * Designed for 3D (xyz) images * * @author Benjamin Perret * */ public class ComponentTree <T> { /** * Dimensions of underlying image */ private int xdim,ydim,zdim; /** * root node */ public ComponentNode<T> root; private Comparator<ComponentNode<T>> comparator; /** * Base connectivity */ private Connectivity3D connectivity; /** * Set of all node managed using union find helper class */ public UnionFindHelper nodeSet; /** * Set of all node accessed through xyz coordinate */ public ComponentNode<T> [][][] nodes; /** * Underlying image */ public Image image; /** * @param root * @param nodeSet */ public ComponentTree(ComponentNode<T> root, UnionFindHelper nodeSet, ComponentNode<T> [][][] nodes) { super(); this.root = root; this.nodeSet = nodeSet; this.nodes=nodes; } /** * Find canonical node associated to pixel (x,y) * @param x * @param y * @return */ public ComponentNode<T> findNodeAt(int x,int y) { Point3D p = find(x,y); return nodes[p.z][p.y][p.x]; } /** * Find canonical node associated to pixel (x,y,z) * @param x * @param y * @param z * @return */ public ComponentNode<T> findNodeAt(int x,int y,int z) { Point3D p = find(x,y,z); return nodes[p.z][p.y][p.x]; } /** * Find canonical node associated to pixel (x,y)given point * @param p * @return */ public ComponentNode<T> findNodeAt(Point3D p) { p=find(p); return nodes[p.z][p.y][p.x]; } /** * get root node * @return */ public ComponentNode<T> getRoot() { return root; } /** * Find location of canonical node associated to pixel (x,y) * @param x * @param y * @return */ public Point3D find(int x, int y) { return nodeSet.find(x,y); } /** * Find location of canonical node associated to pixel (x,y,z) * @param x * @param y * @param z * @return */ public Point3D find(int x, int y, int z) { return nodeSet.find(x,y,z); } /** * Find location of canonical node associated to point p * @param p * @return */ public Point3D find(Point3D p) { return nodeSet.find(p); } /** * Add an attribute to all nodes of the tree * @param a * @throws UnsupportedDataTypeException */ public void addAttribute(ComponentAttribute a) throws UnsupportedDataTypeException { //System.out.println("adding attribute " +a ); a.computeAttribute(this); } /** * Removean attribute to all nodes of the tree * @param a * @throws UnsupportedDataTypeException */ public <E> void removeAttribute(Class<? extends ComponentAttribute<E>> clazz) { for (ComponentNode<T> c : iterateFromRootToLeaf()) { c.remove(clazz); } } /** * Compute number of nodes in the tree (nodes + leaves) * @return */ public int countNodes() { int nb=0; Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); nb++; for(ComponentNode<T> child:c.getChildren()) { s.push(child); } } return nb; } /** * Count number of leaves * @return */ public int countLeaf() { int nb=0; Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); if(c.numberOfChildren()==0) nb++; else { for (ComponentNode<T> child : c.getChildren()) { s.push(child); } } } return nb; } /** * Delete all nodes having flag value equal to given value * @param value */ public void deleteOldNodeWithFlag(int value) { Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); int i=0; while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); boolean flag = true; while (flag) { // world is collapsing now! Sure it can be more efficient but it seems to work ! flag = false; int size=c.numberOfChildren(); for(int j=0;j<size;j++) { ComponentNode<T> child = c.getChild(j); if (child.flag == value) { deleteNode(child); size=c.numberOfChildren(); j--; if((i++)%100==0) compressPathFinding(); } } /*for (ComponentNode<T> child : c.getChildrenSafe()) { if (child.flag == value) { deleteNode(child); flag = true; i++; // System.out.println("father " + c.location + " delete // " + child.location); } }*/ } for (ComponentNode<T> child : c.getChildren()) { s.push(child); } // System.out.print(root); } } /** * Reset flag value of all nodes to given value * @param value */ public void resetFlag(int value) { Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); c.flag=value; for(ComponentNode<T> child:c.getChildren()) { s.push(child); } } } public void resetFlag2(int value) { Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); c.flag2=value; for(ComponentNode<T> child:c.getChildren()) { s.push(child); } } } public void resetFlag3(int value) { Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(root); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); c.flag3=value; for(ComponentNode<T> child:c.getChildren()) { s.push(child); } } } /** * link to node together (make them connected) once the tree is already build * @param root * @param child */ private void lateLinkage(ComponentNode<T> root,ComponentNode<T> child) { nodeSet.linkNoRankCheck(root.locator, child.locator); /*if(!p.equals(root.location)) // to improve path length root and child have been swapped, we must impact this change on the tree structure. { ComponentNode<T> tmp = nodes[root.location.z][root.location.y][root.location.x]; nodes[root.location.z][root.location.y][root.location.x]=nodes[child.location.z][child.location.y][child.location.x]; nodes[child.location.z][child.location.y][child.location.x]=tmp; }*/ } /** * A big hack to assign arbitrary a point to given node! Use it with carefully! * @param node * @param p */ public void givePointToNode(ComponentNode<T> node, Point3D p) { //ComponentNode<T> nt=findNodeAt(p); /*if(nt.location.equals(p)) { System.out.println("changinf node location"); List<Point3D> pts=nt.getAttributeValue(AttributePointList.class); for(int i=pts.size()-1;i>=0;i--) { Point3D np=pts.get(i); if(!np.equals(p)) { System.out.println("new loc: " + np); nodeSet.changePointLink(p, np); nt.location=np; break; } } }*/ nodeSet.changePointLink(p, node.locator); //if(findNodeAt(p)!=node) // System.out.println("grrr " ); } /** * Delete given node and all its children * @param n */ public void deleteNodeAndChildren(ComponentNode<T> n){ if(n!=root) { ComponentNode<T> parent=n.getParent(); if(parent == null) { throw new RuntimeException("node at " + n.location + " has no parent but is not root! tree structure is corrupted!"); } parent.removeChild(n); lateLinkage(parent, n); //nodeSet.linkNoRankCheck( parent.location,n.location); Stack<ComponentNode<T>> s = new Stack<ComponentNode<T>>(); s.push(n); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); for(ComponentNode<T> child:c.getChildren()) { lateLinkage(parent, child); //nodeSet.linkNoRankCheck(parent.location, child.location); //parent.location=nodeSet.link(parent.location, n.location); child.setParent(null); s.push(child); } c.clearChildren(); } } } /** * Call this method if you have stack overflow troubles. * The union find method may lead to long path between a location and the canonical associated to it. */ public void compressPathFinding(){ nodeSet.compressPathFinding(); } /** * Test if given point is owned by given connected component * @param p * @param c * @return */ public boolean isMember(Point3D p, ComponentNode<T> c) { ComponentNode<T> mycc=findNodeAt(p); boolean res=(mycc==c); while(mycc!=null && !res) { mycc=mycc.parent; res=(mycc==c); } return res; } /** * Delete given node, its children are added to the parent of given node. * @param n */ public void deleteNode(ComponentNode<T> n){ if(n!=root) { //nodeSet.drop(); ComponentNode<T> parent=n.getParent(); if(parent.location == null) { //throw new RuntimeException("node at " + n.location + " has no parent but is not root! tree structure is corrupted!"); return; } parent.removeChild(n); lateLinkage(parent, n); //nodeSet.linkNoRankCheck(parent.location, n.location); /*for(ComponentNode<T> child:n.getChildren()) { child.setParent(parent); }*/ parent.addAllChildren(n.getChildren()); n.clearChildren(); //n.parent=null; //System.out.println("after proessing"); //nodeSet.drop(); } } /** * get x dimension of underlying image * @return */ public int getXdim() { return xdim; } public void setXdim(int xdim) { this.xdim = xdim; } public int getYdim() { return ydim; } public void setYdim(int ydim) { this.ydim = ydim; } public int getZdim() { return zdim; } public void setZdim(int zdim) { this.zdim = zdim; } /** * Provides an iterator starting from leaves to given node * @return */ public Iterable<ComponentNode<T>> iterateFromLeafToNode(ComponentNode<T> n){ return new SuffixIterator(n); } /** * Provides an iterator starting from leaves to root * @return */ public Iterable<ComponentNode<T>> iterateFromLeafToRoot(){ return new SuffixIterator(root); } /** * Provides an iterator starting from root to leaves * @return */ public Iterable<ComponentNode<T>> iterateFromRootToLeaf(){ return new RootToLeafIterator(); } /** * A suffix iterator over the tree * @author Benjamin Perret * */ private class SuffixIterator implements Iterator<ComponentNode<T>>, Iterable<ComponentNode<T>>{ Stack<ComponentNode<T>> s; public SuffixIterator(ComponentNode<T> ori){ s = new Stack<ComponentNode<T>>(); s.push(ori); while(!s.isEmpty()) { ComponentNode<T> c = s.pop(); c.iteratorFlag=0; for(ComponentNode<T> child:c.getChildren()) { s.push(child); } } s.push(ori); ori.iteratorFlag=0; } @Override public boolean hasNext() { return !s.isEmpty(); } @Override public ComponentNode<T> next() { ComponentNode<T> n=s.peek(); while(n.iteratorFlag!=1) { n.iteratorFlag=1; for(ComponentNode<T> child:n.getChildren()) { s.push(child); } n=s.peek(); } s.pop(); return n; } @Override public void remove() { // TODO Auto-generated method stub } @Override public Iterator<ComponentNode<T>> iterator() { return this; } } /** * A root to leaves iterator * @author Ben * */ private class RootToLeafIterator implements Iterator<ComponentNode<T>>, Iterable<ComponentNode<T>>{ Stack<ComponentNode<T>> s; public RootToLeafIterator(){ s = new Stack<ComponentNode<T>>(); s.push(root); } @Override public boolean hasNext() { return !s.isEmpty(); } @Override public ComponentNode<T> next() { ComponentNode<T> n=s.pop(); for(ComponentNode<T> child:n.getChildren()) { s.push(child); } return n; } @Override public void remove() { // TODO Auto-generated method stub } @Override public Iterator<ComponentNode<T>> iterator() { return this; } } /** * Print tree as a String on System.out */ public void debugDropNodeMap() { nodeSet.drop(); } /** * @return */ public Connectivity3D getConnectivity() { return connectivity; } public void setConnectivity(Connectivity3D connectivity) { this.connectivity = connectivity; } /** * @return the comparator */ public Comparator<ComponentNode<T>> getComparator() { return comparator; } /** * @param comparator the comparator to set */ public void setComparator(Comparator<ComponentNode<T>> comparator) { this.comparator = comparator; } }