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.ByteImage;
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.FIFOQueue;
import fr.unistra.pelican.util.Point4D;
import fr.unistra.pelican.util.qfz.GrayLogicalPredicate;
/**
* This class computes the Quasi-Flat Zones by logical predicates connectivity
*
* @author Jonathan Weber
*
*/
public class GrayLogicalPredicateConnectivity extends Algorithm {
/**
* Image to be processed
*/
public ByteImage inputImage;
/**
* Neighbourhoud under consideration
*/
public Point4D[] neighbourhood;
/**
* ArrayList of logical predicates
*/
public ArrayList<GrayLogicalPredicate> predicates;
/**
* alpha value for alpha-connectivity
*/
public int alpha=-1;
/**
* Quasi-Flat Zones obtained by Logical Predicates Connectivity
*/
public IntegerImage QFZ;
private static final int UNLABELLED=-1;
private int XDim;
private int YDim;
private int ZDim;
private int TDim;
private int currentX;
private int currentY;
private int currentZ;
private int currentT;
private int alphac;
private int currentLabel=0;
private int alphaMax;
private int alphaMin;
private FIFOQueue<Point4D> fifoQ;
private ArrayList<Point4D> currentZQP;
private ArrayList<GrayLogicalPredicate> localPredicates;
private ArrayList<GrayLogicalPredicate> globalPredicates;
private boolean finalAlpha=false;
public GrayLogicalPredicateConnectivity()
{
super.inputs = "inputImage,predicates,neighbourhood";
super.options ="alpha";
super.outputs="QFZ";
}
@Override
public void launch() throws AlgorithmException
{
//Convert inputImage in grey image if not
if(inputImage.getBDim()==3)
{
inputImage = (ByteImage)RGBToGray.exec(inputImage);
} else if(inputImage.getBDim()!=1)
{
inputImage = (ByteImage)AverageChannels.exec(inputImage);
}
XDim = inputImage.getXDim();
YDim = inputImage.getYDim();
ZDim = inputImage.getZDim();
TDim = inputImage.getTDim();
if(alpha<0)
{
alpha=255;
}
localPredicates= new ArrayList<GrayLogicalPredicate>();
globalPredicates= new ArrayList<GrayLogicalPredicate>();
for(int i=0;i<predicates.size();i++)
{
if(predicates.get(i).getType()==GrayLogicalPredicate.LOCALPREDICATE)
{
localPredicates.add(predicates.get(i));
}
else
{
globalPredicates.add(predicates.get(i));
}
}
fifoQ = new FIFOQueue<Point4D>();
QFZ = inputImage.newIntegerImage();
QFZ.fill(UNLABELLED);
for(currentT=0;currentT<TDim;currentT++)
for(currentZ=0;currentZ<ZDim;currentZ++)
for(currentY=0;currentY<YDim;currentY++)
for(currentX=0;currentX<XDim;currentX++)
{
if(QFZ.getPixelXYZTInt(currentX,currentY,currentZ,currentT)==UNLABELLED)
{
currentLabel++;
resetAlpha();
currentZQP = new ArrayList<Point4D>();
for(int i=0;i<predicates.size();i++)
predicates.get(i).resetData();
addPixelToCurrentZQP(currentX,currentY,currentZ,currentT);
addUnlabelledAlphaNeighboursToFIFO(currentX,currentY,currentZ,currentT);
}
boolean zQPValide=false;
do{
while(fifoQ.size()!=0)
{
Point4D currentPixel = fifoQ.pop();
addPixelToCurrentZQP(currentPixel.x, currentPixel.y, currentPixel.z, currentPixel.t);
if(checkLocalPredicates())
{
addUnlabelledAlphaNeighboursToFIFO(currentPixel.x,currentPixel.y,currentPixel.z,currentPixel.t);
}
else
{
predicateViolationUpdate();
resetCurrentZQP();
}
}
if(checkGlobalPredicates())
{
predicateValidationUpdate();
if(finalAlpha)
zQPValide=true;
else
resetCurrentZQP();
} else
{
predicateViolationUpdate();
resetCurrentZQP();
}
}while(!zQPValide);
}
}
private final void addUnlabelledAlphaNeighboursToFIFO(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;
Point4D currentNeighbour = new Point4D(locX,locY,locZ,locT);
if(!QFZ.isOutOfBoundsXYZT(locX,locY,locZ,locT))
{
if(Math.abs(pixelValue-inputImage.getPixelXYZTByte(locX, locY, locZ, locT))<=alphac
&&QFZ.getPixelXYZTInt(locX, locY, locZ, locT)!=currentLabel
&&!fifoQ.contains(currentNeighbour))
{
if(QFZ.getPixelXYZTInt(locX, locY, locZ, locT)==UNLABELLED)
{
fifoQ.add(new Point4D(locX,locY,locZ,locT));
}
else
{
predicateViolationUpdate();
resetCurrentZQP();
break;
}
}
}
}
}
private void predicateViolationUpdate()
{
alphaMax=alphac;
alphac=(alphaMax+alphaMin)/2;
}
private void predicateValidationUpdate()
{
if(alphac+1==alphaMax)
{
finalAlpha=true;
}
else
{
alphaMin=alphac;
alphac=(alphaMax+alphaMin)/2;
}
}
private void resetAlpha()
{
alphac=alpha;
finalAlpha=false;
alphaMax=alpha+1;
alphaMin=0;
}
private final void addPixelToCurrentZQP(int tX,int tY,int tZ,int tT)
{
QFZ.setPixelXYZTInt(tX, tY, tZ, tT, currentLabel);
currentZQP.add(new Point4D(tX,tY,tZ,tT));
for(int i=0;i<predicates.size();i++)
{
predicates.get(i).updatePredicateData(inputImage,QFZ,alpha,alphac,tX, tY, tZ, tT,currentLabel, neighbourhood);
}
/*System.out.print("c ZQP : ");
for(int i=0;i<currentZQP.size();i++)
{
Point4D p = currentZQP.get(i);
System.out.print("("+p.x+","+p.y+") ");
}
System.out.println("=> "+checkLocalPredicates());*/
}
private final boolean checkLocalPredicates()
{
for(int i=0;i<localPredicates.size();i++)
{
if(!localPredicates.get(i).check(alphac))
return false;
}
return true;
}
private final boolean checkGlobalPredicates()
{
for(int i=0;i<globalPredicates.size();i++)
{
if(!globalPredicates.get(i).check(alphac))
return false;
}
return true;
}
private final void resetCurrentZQP()
{
while(currentZQP.size()!=0)
{
Point4D tmp = currentZQP.remove(0);
QFZ.setPixelXYZTInt(tmp.x,tmp.y,tmp.z,tmp.t,UNLABELLED);
}
for(int i=0;i<predicates.size();i++)
{
predicates.get(i).resetData();
}
addPixelToCurrentZQP(currentX,currentY,currentZ,currentT);
fifoQ = new FIFOQueue<Point4D>();
addUnlabelledAlphaNeighboursToFIFO(currentX,currentY,currentZ,currentT);
}
public final static IntegerImage exec (Image inputImage, ArrayList<GrayLogicalPredicate> predicates, Point4D[] neighbourhood )
{
return (IntegerImage) new GrayLogicalPredicateConnectivity().process(inputImage,predicates,neighbourhood);
}
public final static IntegerImage exec (Image inputImage, ArrayList<GrayLogicalPredicate> predicates, Point4D[] neighbourhood, int alpha )
{
return (IntegerImage) new GrayLogicalPredicateConnectivity().process(inputImage,predicates,neighbourhood, alpha);
}
}