package fr.unistra.pelican.util.multiscale; import fr.unistra.pelican.BooleanImage; import fr.unistra.pelican.ByteImage; import fr.unistra.pelican.DoubleImage; import fr.unistra.pelican.Image; import fr.unistra.pelican.IntegerImage; import fr.unistra.pelican.PelicanException; import fr.unistra.pelican.algorithms.io.ImageLoader; import fr.unistra.pelican.algorithms.visualisation.Viewer2D; /** * Pyramidal multiscale representation of an image * * @author lefevre * * TODO: generalize the class to consider not only 2x2 average factor * */ public class Pyramid { Image base; Image images[]; int depth; /** * Constructor * @param input base image * @param size pyramid depth * @param computeData pyramid build */ public Pyramid(Image input, int size, boolean computeData) { if (size<1) return; while (input.getXDim()<Math.pow(2,size)) size--; while (input.getYDim()<Math.pow(2,size)) size--; this.depth=size; images=new Image[depth]; base=input.copyImage(true); build(computeData); } /** * Constructor by copy * @param p the pyramid to be copied */ public Pyramid(Pyramid p) { this(p,true); } /** * Constructor by copy * @param p the pyramid to be copied * @param copyData to indicate if data are also copied */ public Pyramid(Pyramid p, boolean copyData) { images=p.images.clone(); depth=p.depth; base=images[0]; if (!copyData) for (int d=0;d<depth;d++) images[d].fill(0); } /** * return the bottom of the pyramid (i.e. the base image) * @return the image at the bottom of the pyramid */ public Image getBottom() { return images[0]; } /** * return the top of the pyramid * @return the image at the top of the pyramid */ public Image getTop() { return images[depth-1]; } /** * get an image at a given scale * * @param d the given scale (should be positive and less or equal to depth) * @return the image at the selected scale */ public Image getScale(int d) { if (d>=0 && d<depth) return images[d]; else return null; } /** * set an image at a given scale * * @param image the input image (should have same dimensions as the pyramid internal image * @param d the selected scale (should be positive and less or equal to depth) */ public void setScale(Image image, int d) { if (d<0 || d>=depth) return; if (Image.haveSameDimensions(image,images[d])==false) return; images[d]=image.copyImage(true); } /** * return the depth of the pyramid * * @return the depth of the pyramid */ public int getDepth() { return depth; } /** * build the pyramid by setting the appropriate values * double values are used during the process * implementation is dedicated to 2x2 multiscale factor and average method * * @param computeData to indicate if data are to be computed or not (initialised to 0) */ public void build(boolean computeData) { int xdim,ydim; int k=1; double val; if (base==null) return; images[0]=base; for (int d=1;d<depth;d++) { // compute the 2-D size of the image k*=2; xdim=base.getXDim()/k; ydim=base.getYDim()/k; // check the nature of the image if (base instanceof BooleanImage) images[d]=new BooleanImage(xdim,ydim,base.getZDim(),base.getTDim(),base.getBDim()); else if (base instanceof ByteImage) images[d]=new ByteImage(xdim,ydim,base.getZDim(),base.getTDim(),base.getBDim()); else if (base instanceof IntegerImage) images[d]=new IntegerImage(xdim,ydim,base.getZDim(),base.getTDim(),base.getBDim()); else if (base instanceof DoubleImage) images[d]=new DoubleImage(xdim,ydim,base.getZDim(),base.getTDim(),base.getBDim()); images[d].fill(0); images[d].copyAttributes(base); if(computeData) // Compute the values at current level for (int b=0;b<base.getBDim();b++) for (int t=0;t<base.getTDim();t++) for (int z=0;z<base.getZDim();z++) for (int x=0;x<images[d].getXDim();x++) for (int y=0;y<images[d].getYDim();y++) { val=images[d-1].getPixelXYZTBDouble(x*2,y*2,z,t,b); val+=images[d-1].getPixelXYZTBDouble(x*2+1,y*2,z,t,b); val+=images[d-1].getPixelXYZTBDouble(x*2,y*2+1,z,t,b); val+=images[d-1].getPixelXYZTBDouble(x*2+1,y*2+1,z,t,b); images[d].setPixelXYZTBDouble(x,y,z,t,b,val/4); } } } /** * * convert a pyramid into an Image * * note : the Z dimension is currently used to display the different scales * * @return the returned image */ public Image convertToImage() { // image creation Image output=base.newInstance(base.getXDim(),base.getYDim(),depth,base.getTDim(),base.getBDim()); // base image computation output.setImage4D(base,0,Image.Z); // other image computation int k=1; for (int d=1;d<depth;d++) { k*=2; output.setImage4D(this.extractImage(d),d,Image.Z); } output.copyAttributes(base); return output; } /** * * extract an image representation of a given scale of the pyramid * * note : size of the image is similar to the original (base) scale * * @param d the selected scale * @return the image represented at the selected scale */ public Image extractImage(int d) { Image output=base.copyImage(false); // image creation int k=(int)Math.pow(2,d); for(int x=0;x<k*images[d].getXDim();x++) for (int y=0;y<k*images[d].getYDim();y++) for(int z=0;z<base.getZDim();z++) for (int t=0;t<base.getTDim();t++) for(int b=0;b<base.getBDim();b++) output.setPixelDouble(x,y,z,t,b,images[d].getPixelDouble(x/k,y/k,z,t,b)); return output; } /** * @param args * @throws PelicanException */ public static void main (String args[]) throws PelicanException { Image image=(Image) new ImageLoader().process("samples/lenna.png"); Pyramid pyramid=new Pyramid(image,12,true); Image output=pyramid.convertToImage(); new Viewer2D().process(output,"multiscale"); } }