package fr.unistra.pelican.algorithms.segmentation.qfz.gray; import java.util.ArrayList; import fr.unistra.pelican.Algorithm; import fr.unistra.pelican.AlgorithmException; import fr.unistra.pelican.Image; import fr.unistra.pelican.IntegerImage; import fr.unistra.pelican.algorithms.conversion.AverageChannels; import fr.unistra.pelican.algorithms.conversion.RGBToGray; import fr.unistra.pelican.util.Point4D; /** * Gray level connected component analysis * * - alpha is the local range limit: neighbor pixel p and q are alpha-Connected <=> |f(p)-f(q)|<=alpha * - pixels p q are alpha connected if their exists a set of pixels p_i forming a path from p to q and each p_i p_i+1 are alpha connected * - omega is the global rang: maximum range beetwen two pixel of a alpha,omega connected component is omega * * Contrary to Soille's there is no results unicity. * * Work in byte precision => give alpha and omega as byte values * * Deal with X-Y-Z-T dim * * @author Jonathan Weber * */ public class GrayAlphaOmegaConnectivityCC extends Algorithm { /** * Image to process */ public Image inputImage; /** * alpha value for local range */ public int alpha; /** * optional omega value for global range */ public int omega; /** * Connectivity used to determine the flat zones */ public Point4D[] neighbourhood; /** * Flat Zones labels */ public IntegerImage outputImage; private static final int INITVALUE = -2; private static final int INQUEUE = -1; private ArrayList<Point4D> neighboursToExpand; private int xDim; private int yDim; private int zDim; private int tDim; private int currentLabel; private int minValueOfCC; private int maxValueOfCC; /** * Constructor */ public GrayAlphaOmegaConnectivityCC() { super(); super.inputs = "inputImage,alpha,omega,neighbourhood"; super.outputs = "outputImage"; } @Override public void launch() throws AlgorithmException { xDim = inputImage.getXDim(); yDim = inputImage.getYDim(); zDim = inputImage.getZDim(); tDim = inputImage.getTDim(); neighboursToExpand = new ArrayList<Point4D>(); outputImage= new IntegerImage(xDim,yDim,zDim,tDim,1); outputImage.fill(INITVALUE); // Transforming image in grey level if not if(inputImage.getBDim()==3) { inputImage = RGBToGray.exec(inputImage); } else if(inputImage.getBDim()!=1) { inputImage = AverageChannels.exec(inputImage); } currentLabel=-1; for(int t=0;t<tDim;t++) for(int z=0;z<zDim;z++) for(int y=0;y<yDim;y++) for(int x=0;x<xDim;x++) { if(outputImage.getPixelXYZTInt(x, y, z, t)==INITVALUE) { outputImage.setPixelXYZTInt(x, y, z, t, ++currentLabel); minValueOfCC = inputImage.getPixelXYZTByte(x, y, z, t); maxValueOfCC = inputImage.getPixelXYZTByte(x, y, z, t); addUnlabelledNeighboursRespectToKValueToQueue(x, y, z, t); while(neighboursToExpand.size()!=0) { expandCurrentLabelTo(neighboursToExpand.get(0)); neighboursToExpand.remove(0); } } } } private void expandCurrentLabelTo(Point4D pixel) { boolean isExpandPossible=true; int pixelValue = inputImage.getPixelXYZTByte(pixel.x, pixel.y, pixel.z, pixel.t); if(pixelValue>maxValueOfCC) { if(pixelValue-minValueOfCC>omega) { isExpandPossible=false; } else { maxValueOfCC=pixelValue; } } else if(pixelValue<minValueOfCC) { if(maxValueOfCC-pixelValue>omega) { isExpandPossible=false; } else { minValueOfCC=pixelValue; } } if(isExpandPossible) { outputImage.setPixelXYZTInt(pixel.x, pixel.y, pixel.z, pixel.t, currentLabel); addUnlabelledNeighboursRespectToKValueToQueue(pixel.x, pixel.y, pixel.z, pixel.t); } else { outputImage.setPixelXYZTInt(pixel.x, pixel.y, pixel.z, pixel.t, INITVALUE); } } private void addUnlabelledNeighboursRespectToKValueToQueue(int x, int y, int z, int t) { int pixelValue = inputImage.getPixelXYZTByte(x, y, z, t); for(int i=0;i<neighbourhood.length;i++) { int locX = x + neighbourhood[i].x; int locY = y + neighbourhood[i].y; int locZ = z + neighbourhood[i].z; int locT = t + neighbourhood[i].t; if(locX>=0&&locY>=0&&locZ>=0&&locT>=0&&locX<xDim&&locY<yDim&&locZ<zDim&&locT<tDim) { if(outputImage.getPixelXYZTInt(locX, locY, locZ, locT)==INITVALUE) { if(Math.abs(pixelValue-inputImage.getPixelXYZTByte(locX, locY, locZ, locT))<=alpha) { neighboursToExpand.add(new Point4D(locX, locY, locZ, locT)); outputImage.setPixelXYZTInt(locX, locY, locZ, locT,INQUEUE); } } } } } public static IntegerImage exec(Image inputImage, int alpha, int omega, Point4D[] neighbourhood) { return (IntegerImage)new GrayAlphaOmegaConnectivityCC().process(inputImage,alpha,omega,neighbourhood); } }