knop.psfj.BeadFrame.java Source code

Java tutorial

Introduction

Here is the source code for knop.psfj.BeadFrame.java

Source

/*
This file is part of PSFj.
    
PSFj is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
PSFj is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with PSFj.  If not, see <http://www.gnu.org/licenses/>. 
    
   Copyright 2013,2014 Cyril MONGIS, Patrick Theer, Michael Knop
       
 */
package knop.psfj;

import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.Line;
import ij.gui.Plot;
import ij.measure.Calibration;
import ij.measure.CurveFitter;
import ij.plugin.Slicer;
import ij.process.Blitter;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;
import ij.process.LUT;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.Observable;

import knop.psfj.resolution.Counter3D;
import knop.psfj.resolution.FindMax;
import knop.psfj.resolution.SideViewGenerator;
import knop.psfj.utils.ImageProcessorUtils;
import knop.psfj.utils.MathUtils;
import knop.psfj.view.Message;

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

import Objects3D.Object3D;

// TODO: Auto-generated Javadoc
/**
 * The Class BeadFrame.
 * 
 * A Bead Frame is a class representing a frame around a bead.
 * 
 */
public class BeadFrame extends Observable {

    /** The bead id. */
    Integer id;

    /** The boundaries. */
    Rectangle2D boundaries;

    /**  The ImagePlus stack containing the bead. */
    ImagePlus ip;

    /**  The BeadImage object from where the BeadFrame has been extracted. */
    BeadImage source;

    /** The stack size. */
    int stackSize;

    /** The calibration object. */
    Calibration calibration;

    /**
     * Boolean indicating is the BeadFrame is valid. The BeadFrame is usually
     * marked as invalid when filtered out because of bad fitting
     */
    boolean isValid = true;

    /** The invalidity reason. */
    String invalidityReason = null;

    /**  The center of the bead in integer. */
    protected int[] center = null;

    /**  The centroid (center of the bead computed by the Counter3D algorithm). */
    protected float[] centroid = null;

    /** Equals sqrt(2*ln(2)), used to calculate FWHM of Gaussians *. */
    public final static double SQRT2LN2 = Math.sqrt(2 * Math.log(2));

    /** Reference to the x dimension *. */
    public static final int X = 0;

    /** Reference to the y dimension *. */
    public static final int Y = 1;

    /** Reference to the z dimension *. */
    public static final int Z = 2;

    /**
     * Stores the x profile, [0=x as a physical distance ,1=raw intensity,
     * 2=fitted data][pixel nb, from 0 to width-1].
     */
    double[][] xProfile = null;

    /** Stores the fitting parameters for the x profile *. */
    double[] xParams = null;

    /** Stores the fitting goodness for the x profile *. */
    double xR2 = Double.NaN;

    /** Stores the fitting parameters for the x profile as a string *. */
    String xParamString = "Fitted on y = a + (b-a)*exp(-(x-c)^2/(2*d^2)";

    /**
     * Stores the y profile, [0=y as a physical distance ,1=raw intensity,
     * 2=fitted data][pixel nb, from 0 to height-1].
     */
    double[][] yProfile = null;

    /** Stores the fitting parameters for the y profile *. */
    double[] yParams = null;

    /** Stores the fitting goodness for the y profile *. */
    double yR2 = Double.NaN;

    /** Stores the fitting parameters for the y profile as a string *. */
    String yParamString = "Fitted on y = a + b*exp(-(x-c)^2/(2*d^2)";

    /**
     * Stores the z profile, [0=z as a physical distance ,1=raw intensity,
     * 2=fitted data][pixel nb, from 0 to nSlices-1].
     */
    double[][] zProfile = null;

    /** Stores the fitting parameters for the z profile *. */
    double[] zParams = null;

    /** Stores the fitting goodness for the z profile *. */
    double zR2 = Double.NaN;

    /** The R2. */
    double[] R2 = new double[3];

    /**  Shift in the X axis with the corresponding bead (alter ego). */
    double deltaX = Double.NaN;

    /**  Shift in the Y axis with the corresponding bead (alter ego). */
    double deltaY = Double.NaN;

    /**  Shift in the Z axis with the corresponding bead. */
    double deltaZ = Double.NaN;

    /**  The side view image (computed after PSF Fitting). */
    protected ImagePlus sideViewImage;

    /** The counter. */
    Counter3D counter;

    /** The max intensity. */
    double maxIntensity;

    /** The is ignored. */
    boolean isIgnored = false;

    /** The object3d. */
    protected Object3D object3d;

    /**  The alter ego (corresponding bead in an other channel). */
    BeadFrame alterEgo;

    public static final String MSG_PSF_FOUND = "psf found";

    /**
     * Stores the calculated resolutions (FWHM) in all the dimensions of the
     * image.
     */
    double[] resol = { 0, 0, 0 };

    /** The asymetry. */
    double asymetry = Double.NaN;

    /** Stores the fitting parameters for the z profile as a string *. */
    String zParamString = "Fitted on y = a + (b-a)*exp(-(x-c)^2/(2*d^2)";

    /**
     * The main method.
     * 
     * @param args
     *           the arguments
     */
    public static void main(String[] args) {

        BeadImageManager manager = new BeadImageManager();

        // manager.add("/home/cyril/test_img/colocalisation/gfp_01_220beads_small.tif");
        // manager.add("/home/cyril/test_img/colocalisation/mc_01_220beads_small.tif");

        manager.add("/home/cyril/test_img/colocalisation/gfp1.tif");
        manager.add("/home/cyril/test_img/colocalisation/mc1.tif");

        manager.setAnalysisType(BeadImageManager.DUAL_CHANNEL);

        manager.autoFrameSize();
        manager.autoFocus(0);
        manager.autoFocus(1);

        manager.processProfiles();

        new ImagePlus("", manager.getBeadImage(0).getMontage().resize(600)).show();

    }

    /**
     * Instantiates a new bead frame.
     * 
     * @param id
     *           the id
     * @param r
     *           the r
     */
    public BeadFrame(int id, Rectangle r) {
        this(r);
        this.id = id;
    }

    /**
     * Instantiates a new bead frame.
     * 
     * @param rectangle
     *           the rectangle
     */
    public BeadFrame(Rectangle rectangle) {
        this.boundaries = rectangle;
    }

    /**
     * Gets the X position of the BeadFrame (not the bead itself).
     * 
     * @return the x
     */
    public int getFrameX() {
        return MathUtils.round(boundaries.getX());
    }

    /**
     * Gets the Y position of the BeadFrame (not the bead itself) .
     * 
     * @return the y
     */
    public int getFrameY() {
        return MathUtils.round(boundaries.getY());
    }

    /**
     * Gets the id.
     * 
     * @return the id
     */
    public Integer getId() {
        return id;
    }

    /**
     * Sets the id.
     * 
     * @param id
     *           the new id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets the boundaries of the BeadFrame in a Rectangle2D form.
     *
     * @return the boundaries
     */
    public Rectangle2D getBoundaries() {
        return boundaries;
    }

    /**
     * Gets the boundaries as Rectangle.
     * 
     * @return the boundaries as rectangle
     */
    public Rectangle getBoundariesAsRectangle() {
        return (Rectangle) boundaries;
    }

    /**
     * Sets the boundaries.
     * 
     * @param boundaries
     *           the new boundaries
     */
    public void setBoundaries(Rectangle boundaries) {
        this.boundaries = boundaries;
    }

    /**
     * Gets the substack.
     * 
     * @return the substack
     */
    public ImagePlus getSubstack() {

        if (ip == null) {

            int width = MathUtils.round(boundaries.getWidth());
            int height = MathUtils.round(boundaries.getHeight());
            int x = getFrameX();
            int y = getFrameY();

            // boundaries.setRect(x, y, width, height);

            ImageStack stack = new ImageStack(width, height);

            for (int i = 0; i != source.getStackSize(); i++) {

                ImageProcessor sourcePlane = source.getPlane(i);
                ImageProcessor targetPlane = ImageProcessorUtils.copyRoi(sourcePlane, boundaries.getBounds());

                stack.addSlice(targetPlane);
            }
            setSubstack(new ImagePlus("", stack));
            // if(getId() == 200) ip.show();;
        }

        return ip;
    }

    /**
     * Sets the substack.
     * 
     * @param substack
     *           the new substack
     */
    public void setSubstack(ImagePlus substack) {
        this.ip = substack;
        stackSize = ip.getStackSize();
        ip.setCalibration(getCalibration());
    }

    /**
     * Gets the distance.
     *
     * @param p1 the p1
     * @param p2 the p2
     * @return the distance
     */
    private double getDistance(float[] p1, float[] p2) {
        double x1 = p1[0]; // getCentroidXInImage();
        double x2 = p2[0]; // bead.getCentroidXInImage();
        double y1 = p1[1]; // getCentroidYInImage();
        double y2 = p2[1]; // bead.getCentroidYInImage();

        if (p1.length > 2 && p2.length > 2) {
            double z1 = p1[2];
            double z2 = p2[2];
            return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2));
        }

        else {

            return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
        }
    }

    /**
     * Gets the distance with an other bead.
     *
     * @param bead           the bead
     * @return the distance
     */
    public double getDistance(BeadFrame bead) {

        if (bead == null)
            return Double.NaN;

        double x1 = getFovX(); // getCentroidXInImage();
        double x2 = bead.getFovX();// bead.getCentroidXInImage();
        double y1 = getFovY();// getCentroidYInImage();
        double y2 = bead.getFovY(); // bead.getCentroidYInImage();

        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));

    }

    /**
     * Gets the distance with alter ego.
     *
     * @return the distance with alter ego
     */
    public double getDistanceWithAlterEgo() {
        if (alterEgo != null) {
            return getDistance(alterEgo);
        }

        else {
            return Double.POSITIVE_INFINITY;
        }
    }

    /**
     * Gets the closest bead from a BeadImageList object.
     * 
     * @param list
     *           the list
     * @return the closest bead
     */
    public BeadFrame getClosestBead(BeadFrameList list) {
        double minDistance = -1;
        double distance;

        BeadFrame found = null;

        for (BeadFrame bead : list) {
            distance = getDistance(bead);

            if (minDistance == -1 || distance < minDistance) {
                minDistance = distance;
                found = bead;
            }
        }

        if (found == null)
            return null;

        System.out.printf("For bead (%d) : %.3f , %.3f\n", getId(), getFovX(), getFovY());
        System.out.printf("The closest bead (%d) has for coordinates : %.3f , %.3f\n", found.getId(),
                found.getFovX(), found.getFovY());

        return found;

    }

    /**
     * Sets the source.
     * 
     * @param beadImage
     *           the new source
     */
    public void setSource(BeadImage beadImage) {

        source = beadImage;
        stackSize = source.getStackSize();
        calibration = new Calibration();
        calibration.pixelWidth = source.getCalibration().pixelWidth;
        calibration.pixelHeight = source.getCalibration().pixelHeight;
        calibration.pixelDepth = source.getCalibration().pixelDepth;

    }

    /**
     * Gets the source.
     * 
     * @return the source
     */
    public BeadImage getSource() {
        return source;
    }

    /**
     * Gets the calibration.
     * 
     * @return the calibration
     */
    public Calibration getCalibration() {
        // return calibration;
        return getSource().getCalibration();
    }

    /**
     * Draw.
     * 
     * @param ip
     *           the ip
     * @param radius
     *           the radius
     */
    public void draw(ImageProcessor ip, int radius) {

        Color c = (isValid() ? Color.white : Color.red);
        draw(ip, radius, c);

    }

    /**
     * Gets the theta.
     *
     * @return the theta
     */
    public double getTheta() {
        return 0.0;
    }

    /**
     * Gets the theta in degrees.
     *
     * @return the theta in degrees
     */
    public double getThetaInDegrees() {
        return getTheta() * 180 / Math.PI;
    }

    /**
     * Draw.
     *
     * @param ip the ip
     * @param radius the radius
     * @param c the c
     */
    public void draw(ImageProcessor ip, int radius, Color c) {
        int x = getWeightedXInImage(); // (getCenterX());
        int y = getWeightedYInImage(); // (getCenterY());

        ip.setColor(c.darker());

        // ip.drawOval(x-(getWidth()/2), y-(getWidth()/2),
        // getWidth(),getWidth());
        ip.drawRect(getFrameX(), getFrameY(), getWidth(), getHeight());
        ip.drawRect(getFrameX() - 1, getFrameY() - 1, getWidth() + 2, getHeight() + 2);
        // ip.setColor(Color.red);
        // ip.drawOval(x-1, y-1, 3, 3);
        ip.setColor(Color.white);
        ip.drawString("" + id, x, y + 16);

    }

    /**
     * Gets the fov x.
     * 
     * @return the fov x
     */
    public double getFovX() {
        return // (getX()+getWeightedXInImage() -
               // getSource().getImageWidth()/2)*getCalibration().pixelWidth;
        (getCentroidXInImage() * getCalibration().pixelWidth) - (getSource().getFieldOfViewWidth() / 2);
    }

    /**
     * Gets the fov y.
     * 
     * @return the fov y
     */
    public double getFovY() {
        return // (getY()+getWeightedYInImage() -
               // getSource().getImageHeight()/2)*getCalibration().pixelHeight;
        -1.0 * ((getCentroidYInImage() * getCalibration().pixelHeight) - (getSource().getFieldOfViewHeight() / 2));// -
    }

    /**
     * Gets the center x.
     * 
     * @return the center x
     */
    public int getFrameCenterX() {
        return MathUtils.round(boundaries.getCenterX());
    }

    /**
     * Gets the center y.
     * 
     * @return the center y
     */
    public int getFrameCenterY() {
        return MathUtils.round(boundaries.getCenterY());
    }

    /**
     * Gets the weighted x in image.
     * 
     * @return the weighted x in image
     */
    public int getWeightedXInImage() {

        if (centroid == null) {
            return getFrameCenterX();
        }

        return getFrameX() + getCentroidXAsInt();
    }

    /**
     * Gets the weighted y in image.
     * 
     * @return the weighted y in image
     */
    public int getWeightedYInImage() {
        if (centroid == null) {
            return getFrameCenterY();
        }
        return getFrameY() + getCentroidYAsInt();
    }

    /**
     * Gets the centroid.
     *
     * @return the centroid
     */
    private float[] getCentroid() {
        if (centroid == null) {
            try {
                findCenter();
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("Couldn't find the center for the bead " + getId());
                centroid = new float[] { -1, -1, -1 };
            }

        }
        return centroid;

    }

    /**
     * Gets the centroid x.
     * 
     * @return the centroid x
     */
    public double getCentroidX() {
        // findCenter();
        return getCentroid()[0];
    }

    /**
     * Gets the centroid y.
     * 
     * @return the centroid y
     */
    public double getCentroidY() {
        // findCenter();
        return getCentroid()[1];
    }

    /**
     * Gets the centroid z.
     * 
     * @return the centroid z
     */
    public double getCentroidZ() {
        // findCenter();
        return getCentroid()[2];
    }

    /**
     * Gets the centroid x in image.
     * 
     * @return the centroid x in image
     */
    public double getCentroidXInImage() {
        return getFrameX() + getCentroidX();
    }

    /**
     * Gets the centroid y in image.
     * 
     * @return the centroid y in image
     */
    public double getCentroidYInImage() {
        return getFrameY() + getCentroidY();
    }

    /**
     * Gets the centroid x as int.
     *
     * @return the centroid x as int
     */
    public int getCentroidXAsInt() {
        return center[0];
    }

    /**
     * Gets the centroid y as int.
     *
     * @return the centroid y as int
     */
    public int getCentroidYAsInt() {
        return center[1];
    }

    /**
     * Gets the centroid z as int.
     *
     * @return the centroid z as int
     */
    public int getCentroidZAsInt() {
        return center[2];
    }

    /**
     * ******************************************************************.
     * 
     * @param coords
     *           the coords
     * @return the intensity
     */
    /*********************** FITTING AREA ********************************/
    /*********************************************************************/

    public int getIntensity(float[] coords) {
        return getIntensity(MathUtils.round(coords[0]), MathUtils.round(coords[1]), MathUtils.round(coords[2]));
    }

    /**
     * Center boundaries around centroid.
     */
    public void centerBoundariesAroundCentroid() {

        int oldCenterX = getFrameCenterX();
        int oldCenterY = getFrameCenterY();
        int newCenterX = getWeightedXInImage();
        int newCenterY = getWeightedYInImage();

        if (oldCenterX == newCenterX && oldCenterY == newCenterY)
            return;

        float xShift = newCenterX - oldCenterX;
        float yShift = newCenterY - oldCenterY;

        centroid[0] = centroid[0] + xShift;
        centroid[1] = centroid[1] + yShift;

        center[0] = MathUtils.round(centroid[0]);
        center[1] = MathUtils.round(centroid[1]);

        int width = MathUtils.round(boundaries.getWidth());
        int height = MathUtils.round(boundaries.getHeight());

        boundaries = new Rectangle(newCenterX - (width / 2), newCenterY - (height / 2), width, height);
    }

    /**
     * Gets the intensity.
     * 
     * @param x
     *           the x
     * @param y
     *           the y
     * @param z
     *           the z
     * @return the intensity
     */
    public int getIntensity(int x, int y, int z) {
        getSubstack().setSlice(z);
        return getSubstack().getProcessor().getPixel(x, y);
    }

    /**
     * Convert int array to float.
     * 
     * @param intArray
     *           the int array
     * @return the float[]
     */
    private float[] convertIntArrayToFloat(int[] intArray) {
        float[] floatArray = new float[intArray.length];
        for (int i = 0; i != floatArray.length; i++) {
            floatArray[i] = intArray[i];
        }
        return floatArray;
    }

    /**
     * Find center.
     */
    public void findCenter() {

        // if the centroid has already been calculated, the routine is aborted
        if (centroid != null)
            return;

        // a counter 3D object is created.
        counter = new Counter3D(getSubstack(), getSource().getThresholdValue(), 1, 10000);
        counter.setSizeFiltering(true);

        float[][] centers = new float[0][0];

        try {
            counter.getObjects();
            // the centers returned by the 3D segmentation are harvested
            centers = counter.getCentreOfMassList();
        } catch (ArrayIndexOutOfBoundsException e) {

        }

        // If no center is found, then the MetroloJ algorithm is used
        if (centers.length == 0) {
            System.out.println("Couldn't find the center using 3D Object Counter for bead " + getId());

            findCenterUsingBrightestPixel();
        }

        // if one or several centers are found
        else {
            // the closest centroid to the center is filtered out
            centroid = filterCentroids(centers);

            // in case the image wouldn't be a stack, the returned centroid would
            // be
            // a 2 value arrays so we transformed it into a 3 values array
            if (centroid.length == 2)
                centroid = new float[] { centroid[0], centroid[1], 0 };

            // converting the centroid to int array for (1D fitting)
            center = new int[centroid.length];

            for (int i = 0; i != centroid.length; i++) {
                // System.out.printf("Rounding centroid from %.3f to %d\n",centroid[i],MathUtils.round(centroid[i]));
                center[i] = MathUtils.round(centroid[i]);

            }
        }

        // System.out.println(String.format("Difference from the center : %d x %d pixels",getBoundariesAsRectangle().width/2-center[0],getBoundariesAsRectangle().height/2-center[1]));

        // the boundaries of the substack are recalculated but not updated (the
        // substack will still be the same)
        // centerBoundariesAroundCentroid();

        // getCenterAsIntArray();

        if (ip.getStack().getSize() == 1) {
            center[2] = 0;
            centroid[2] = 0;
        }

        counter = null;

        // centerBoundariesAroundCentroid();

    }

    /**
     * Find center using brightest pixel.
     */
    public void findCenterUsingBrightestPixel() {
        // finds the brightest pixels of all stacks
        center = new FindMax().getAllCoordinates(getSubstack());

        // in a case of a flat image, the findMax object returns a 2-values array
        // that must be converted to a 3-values array
        if (center.length == 2)
            center = new int[] { center[0], center[1], 0 };

        // since we cannot have a centroid in a subpixel resolution, we convert
        // the int array into a float array
        centroid = convertIntArrayToFloat(center);

        maxIntensity = getSubstack().getStack().getProcessor(center[2] + 1).getPixel(center[0], center[1]);
    }

    /**
     * Filter centroids.
     * 
     * @param centroids
     *           list of centroids found by the 3D Counter Object
     * @return the float[] a 3 items array reprensenting x,y, and z coordinates of the center
     */
    private float[] filterCentroids(float[][] centroids) {

        int indexMax = 0;
        int intensityMax = 0;
        double closest = Double.POSITIVE_INFINITY;
        int indexClosest = 0;

        float[] center = new float[3];

        // getting the boundaries of the center
        if (boundaries == null) {
            boundaries = new Rectangle(0, 0, getSubstack().getWidth(), getSubstack().getHeight());
        }
        center[0] = getWidth() / 2;
        center[1] = getHeight() / 2;
        center[2] = source.getFocusPlane() * (float) ip.getCalibration().pixelDepth;

        if (centroids.length == 1) {
            indexMax = 0;
        } else {
            float[] point;
            int intensity;
            double distanceToCenter;
            for (int i = 0; i != centroids.length; i++) {
                point = centroids[i];
                intensity = getIntensity(point);
                distanceToCenter = getDistance(center, point);
                if (intensity > intensityMax) {
                    indexMax = i;
                    intensityMax = intensity;
                }

                if (distanceToCenter < closest) {
                    closest = distanceToCenter;
                    indexClosest = i;
                }

            }

            object3d = counter.getObject(indexMax);
            maxIntensity = object3d.max;

        }

        return centroids[indexClosest];

    }

    /**
     * Gets the maximum intensity.
     *
     * @return the maximum intensity
     */
    public double getMaximumIntensity() {
        return maxIntensity;
    }

    /**
     * Find psf.
     */
    public void findPSF() {
        if (isValid == false) {
            resol = new double[] { 0.0, 0.0, 0.0 };
            return;
        }
        try {

            findCenter();
            getSubstack().setSlice(getCentroidZAsInt());

            getXprofileAndFit();
            getYprofileAndFit();
            getZprofileAndFit();

            // System.out.println(getId() + " is finished.");
            getSideViewImage();
            ip = null;

            // System.gc();

            setChanged();
            notifyObservers(new Message(this, MSG_PSF_FOUND));

        } catch (OutOfMemoryError e) {
            System.out.println(new Message(this, "error", "There is not enough memory to process the beads !"));
        }

    }

    /**
     * Gets the minimum bead size.
     * 
     * @return the minimum bead size
     */
    public int getMinimumBeadSize() {

        double volume = getSource().getMicroscope().getXYTheoreticalResolution() / 2;

        volume = 4 / 3 * Math.PI * Math.pow(volume, 3);

        return MathUtils.round(volume / Math.pow(getSource().getCalibration().pixelWidth, 3));
    }

    /**
     * Gets the find psf job.
     * 
     * @return the find psf job
     */
    public Runnable getFindPSFJob() {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    findPSF();
                } catch (Exception e) {
                    System.err.println("Error when analysing bead " + getId());
                    e.printStackTrace();
                }
            }

        };
    }

    /**
     * Retrieves data and fills xProfile the x profile through the centre of the
     * bead retrieved from the current ImageProcessor.
     * 
     * @return a double[][] [0=x as a physical distance ,1=raw intensity,
     *         2=fitted data][pixel nb, from 0 to width-1]
     */
    protected void getXprofileAndFit() {
        xProfile = new double[3][ip.getWidth()];
        xProfile[1] = ip.getProcessor().getLine(0, center[1], ip.getWidth() - 1, center[1]);
        fitProfile(xProfile, xParams, X);
    }

    /**
     * Retrieves data and fills yProfile the y profile through the centre of the
     * bead retrieved from the current ImageProcessor.
     * 
     * @return a double[][] [0=y as a physical distance ,1=raw intensity,
     *         2=fitted data][pixel nb, from 0 to width-1]
     */
    protected void getYprofileAndFit() {
        yProfile = new double[3][ip.getHeight()];
        yProfile[1] = ip.getProcessor().getLine(center[0], 0, center[0], ip.getHeight() - 1);
        fitProfile(yProfile, yParams, Y);
    }

    /**
     * Retrieves data and fills zProfile the z profile through the centre of the
     * bead retrieved from the current ImageProcessor.
     * 
     * @return a double[][] [0=z as a physical distance ,1=raw intensity,
     *         2=fitted data][pixel nb, from 0 to width-1]
     */

    protected void getZprofileAndFit() {
        ip.setCalibration(new Calibration());
        ip.setRoi(new Line(0, center[1], ip.getWidth() - 1, center[1]));

        ImagePlus crossX = new Slicer().reslice(ip);

        ip.killRoi();
        ip.setCalibration(getCalibration());

        zProfile = new double[3][ip.getNSlices()];
        zProfile[1] = crossX.getProcessor().getLine(center[0], 0, center[0], crossX.getHeight() - 1);

        fitProfile(zProfile, zParams, Z);

    }

    /**
     * Fit profile.
     * 
     * @param profile
     *           the profile
     * @param params
     *           the params
     * @param dimension
     *           the dimension
     */
    private void fitProfile(double[][] profile, double[] params, int dimension) {
        double max = profile[1][0];
        double pixelSize = 1;
        int resolIndex = 0;
        Calibration cal = getCalibration();

        switch (dimension) {
        case X:
            pixelSize = cal.pixelWidth;
            break;
        case Y:
            pixelSize = cal.pixelHeight;
            resolIndex = 1;
            break;
        case Z:
            pixelSize = cal.pixelDepth;
            resolIndex = 2;
            break;
        }

        params = new double[4];
        params[0] = max;
        params[1] = max;
        params[2] = 0;
        params[3] = 2 * pixelSize;

        for (int i = 0; i < profile[0].length; i++) {
            profile[0][i] = i * pixelSize;
            double currVal = profile[1][i];
            params[0] = Math.min(params[0], currVal);
            if (currVal > max) {
                params[1] = currVal;
                params[2] = profile[0][i];
                max = currVal;
            }
        }

        zParams = params;

        CurveFitter cv = new CurveFitter(profile[0], profile[1]);
        // cv.setOffsetMultiplySlopeParams(0, 1, 2);
        cv.setInitialParameters(params);
        cv.getMinimizer().setMaximumThreads(1);

        // cv.doCustomFit("y = a + b*exp(-(x-c)*(x-c)/(2*d*d))",params,false);
        cv.doFit(CurveFitter.GAUSSIAN);

        params = cv.getParams();

        for (int i = 0; i < profile[0].length; i++)
            profile[2][i] = CurveFitter.f(CurveFitter.GAUSSIAN, params, profile[0][i]);

        String paramString = cv.getResultString();

        params[1] += params[0];

        paramString = paramString.substring(paramString.lastIndexOf("ms") + 2);
        R2[dimension] = cv.getFitGoodness();

        switch (dimension) {
        case X:
            xParamString += paramString;
            xR2 = cv.getFitGoodness();
            xParams = params;
            break;
        case Y:
            yParamString += paramString;
            yR2 = cv.getFitGoodness();
            yParams = params;
            break;
        case Z:
            zParamString += paramString;
            zR2 = cv.getFitGoodness();
            zParams = params;
            break;
        }

        resol[resolIndex] = 2 * SQRT2LN2 * params[3];
    }

    /**
     * Returns a plot object based on the x profile of the current ImagePlus.
     * 
     * @return a plot object
     */
    public Plot getXplot() {

        Plot plot = new Plot("Profile plot along the x axis", "x (" + getCalibration().getUnit() + ")",
                "Intensity (AU)", xProfile[0], xProfile[2]);
        plot.setSize(300, 200);
        plot.setColor(Color.red);
        plot.addPoints(xProfile[0], xProfile[1], Plot.CIRCLE);
        plot.setColor(Color.black);
        plot.addLabel(0.6, 0.13, "Dots: measured\nLine: fitted");
        return plot;
    }

    /**
     * Returns a plot object based on the y profile of the current ImagePlus.
     * 
     * @return a plot object
     */
    public Plot getYplot() {
        Plot plot = new Plot("Profile plot along the y axis", "y (" + getCalibration().getUnit() + ")",
                "Intensity (AU)", yProfile[0], yProfile[2]);
        plot.setSize(300, 200);
        plot.setColor(Color.red);
        plot.addPoints(yProfile[0], yProfile[1], Plot.CIRCLE);
        plot.setColor(Color.black);
        plot.addLabel(0.6, 0.13, "Dots: measured\nLine: fitted");
        return plot;
    }

    /**
     * Returns a plot object based on the z profile of the current ImagePlus.
     * 
     * @return a plot object
     */
    public Plot getZplot() {

        Plot plot = new Plot("Profile plot along the z axis", "z (" + getCalibration().getUnit() + ")",
                "Intensity (AU)", zProfile[0], zProfile[2]);
        plot.setSize(300, 200);
        plot.setColor(Color.red);
        plot.addPoints(zProfile[0], zProfile[1], Plot.CIRCLE);
        plot.setColor(Color.black);
        plot.addLabel(0.6, 0.13, "Dots: measured\nLine: fitted");
        return plot;
    }

    /**
     * Gets the minimum value among X, Y and Z fitting goodness.
     * 
     * @return the fitting goodness
     */
    public double getMinimumFittingGoodness() {

        DescriptiveStatistics stats = new DescriptiveStatistics(R2);
        return stats.getMin();
    }

    /**
     * Gets the fitting goodness.
     * 
     * @param axe
     *           the axe
     * @return the fitting goodness
     */
    public double getFittingGoodness(int axe) {
        return R2[axe];
    }

    /**
     * Gets the fitting parameter mean.
     *
     * @param parameter the parameter
     * @return the fitting parameter mean
     */
    public double getFittingParameterMean(int parameter) {

        DescriptiveStatistics stats = new DescriptiveStatistics();
        for (int a = 0; a != 3; a++) {
            stats.addValue(getFittingParameter(a, parameter));
        }

        return stats.getMean();

    }

    /**
     * Gets the fitting parameter.
     *
     * @param axe the axe
     * @param parameter the parameter
     * @return the fitting parameter
     */
    public double getFittingParameter(int axe, int parameter) {
        try {
            switch (axe) {

            case X:
                return xParams[parameter];
            case Y:
                return yParams[parameter];
            case Z:
                return zParams[parameter];

            }
            return 0.0;
        } catch (NullPointerException e) {
            return 0.0;
        }
    }

    /**
     * Gets the c parameter.
     * 
     * @param axe
     *           the axe
     * @return the c parameter
     */

    public double getCParameter(int axe) {
        try {
            switch (axe) {

            case X:
                return xParams[2];
            case Y:
                return yParams[2];
            case Z:
                return zParams[2];

            }
            return 0.0;
        } catch (NullPointerException e) {
            return 0.0;
        }
    }

    /**
     * Returns the calculated resolutions in all available dimensions, i.e. FWHM
     * after fitting the 2 or 3 profiles
     * 
     * @return the x, y and z (if applicable) resolutions as a double array of
     *         size 2 (or 3).
     */
    public double[] getResolutions() {
        return resol;
    }

    /**
     * Gets the resolution.
     * 
     * @param axe
     *           the axe
     * @return the resolution
     */
    public double getResolution(int axe) {
        return resol[axe];
    }

    /**
     * Gets the stack size.
     * 
     * @return the stack size
     */
    public double getStackSize() {
        return getCalibration().pixelDepth * stackSize;
    }

    /**
     * Discard.
     * 
     * @param reason
     *           the reason
     */
    public void discard(String reason) {
        setValid(false, reason);
        System.out.println("be cause of : " + reason);
    }

    /**
     * Sets the valid.
     * 
     * @param isValid
     *           the is valid
     * @param reason
     *           the reason
     */
    public void setValid(boolean isValid, String reason) {
        if (isValid == false) {
            System.out.println(getId() + " has been discarded !");

            setInvalidityReason(reason);
        }
        this.isValid = isValid;
    }

    /**
     * Checks if is valid.
     * 
     * @return true, if is valid
     */
    public boolean isValid() {
        return isValid;
    }

    /**
     * Gets the correction resolution.
     * 
     * @param axe
     *           the axe
     * @return the correction resolution
     */
    public double getCorrectionResolution(int axe) {
        return getSource().getMicroscope().getCorrectedResolution(getResolution(axe), axe);
    }

    /**
     * Gets the distance from center.
     * 
     * @return the distance from center
     */
    public double getDistanceFromCenter() {
        double centerX = getSource().getFieldOfViewWidth() / 2;
        double centerY = getSource().getFieldOfViewHeight() / 2;

        double x = getFovX();
        double y = getFovY();

        double d = Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2);
        return Math.sqrt(d);
    }

    /**
     * Gets the x params.
     * 
     * @return the x params
     */
    public String getXParams() {
        return xParamString;
    }

    /**
     * Gets the y params.
     * 
     * @return the y params
     */
    public String getYParams() {
        return yParamString;
    }

    /**
     * Gets the z params.
     * 
     * @return the z params
     */
    public String getZParams() {
        return zParamString;
    }

    /**
     * Gets the width.
     * 
     * @return the width
     */
    public int getWidth() {
        // TODO Auto-generated method stub
        return MathUtils.round(boundaries.getWidth());
    }

    /**
     * Gets the real width.
     * 
     * @return the real width
     */
    public double getRealWidth() {
        return getWidth() * getCalibration().pixelWidth;
    }

    /**
     * Gets the real height.
     * 
     * @return the real height
     */
    public double getRealHeight() {
        return getHeight() * getCalibration().pixelHeight;
    }

    /**
     * Gets the height.
     * 
     * @return the height
     */
    public int getHeight() {
        return MathUtils.round(boundaries.getHeight());
    }

    /**
     * Gets the raw fwhm.
     * 
     * @param axe
     *           the axe
     * @return the raw fwhm
     */
    public double getRawFWHM(int axe) {
        return resol[axe];
    }

    /**
     * Gets the shift.
     *
     * @param axe the axe
     * @return the shift
     */
    public double getShift(int axe) {
        if (axe == PSFj.X_AXIS)
            return getDeltaX();
        if (axe == PSFj.Y_AXIS)
            return getDeltaY();
        if (axe == PSFj.Z_AXIS)
            return getDeltaZ();
        return Double.NaN;
    }

    /**
     * Gets the delta x.
     * 
     * @return the delta x
     */
    public double getDeltaX() {
        return deltaX;
    }

    /**
     * Sets the delta x.
     * 
     * @param deltaX
     *           the new delta x
     */
    public void setDeltaX(double deltaX) {
        this.deltaX = deltaX;
    }

    /**
     * Gets the delta y.
     * 
     * @return the delta y
     */
    public double getDeltaY() {
        return deltaY;
    }

    /**
     * Sets the delta y.
     * 
     * @param deltaY
     *           the new delta y
     */
    public void setDeltaY(double deltaY) {
        this.deltaY = deltaY;
    }

    /**
     * Gets the delta z.
     * 
     * @return the delta z
     */
    public double getDeltaZ() {
        return deltaZ;
    }

    /**
     * Sets the delta z.
     * 
     * @param deltaZ
     *           the new delta z
     */
    public void setDeltaZ(double deltaZ) {
        this.deltaZ = deltaZ;
    }

    /**
     * Gets the alter ego.
     * 
     * @return the alter ego
     */
    public BeadFrame getAlterEgo() {
        return alterEgo;
    }

    /**
     * Gets the z profile.
     * 
     * @return the z profile
     */
    public double getZProfile() {
        return getCParameter(BeadFrame.Z);
    }

    /**
     * Reset alter ego.
     */
    public void resetAlterEgo() {
        alterEgo = null;
    }

    /**
     * Sets the alter ego.
     * 
     * @param alterEgo
     *           the new alter ego
     */
    public void setAlterEgo(BeadFrame alterEgo) {

        if (alterEgo == null)
            return;

        // calculating parameters
        deltaX = alterEgo.getCentroidXInImage() - getCentroidXInImage();
        deltaY = alterEgo.getCentroidYInImage() - getCentroidYInImage();

        deltaX = deltaX * getCalibration().pixelWidth;
        deltaY = deltaY * getCalibration().pixelHeight;

        deltaZ = alterEgo.getZProfile() - getZProfile();// getZProfile()-alterEgo.getZProfile();

        this.alterEgo = alterEgo;
    }

    /**
     * Gets the side view image.
     * 
     * @return the side view image
     */
    public ImagePlus getSideViewImage() {
        if (sideViewImage == null) {
            SideViewGenerator svg = new SideViewGenerator();
            sideViewImage = svg.getPanelView(getSubstack(), SideViewGenerator.MAX_METHOD, true, true, 5, false,
                    null, 0);

        }
        return sideViewImage;
    }

    /**
     * Sets the side view image.
     * 
     * @param sideViewImage
     *           the new side view image
     */
    public void setSideViewImage(ImagePlus sideViewImage) {
        this.sideViewImage = sideViewImage;
    }

    /**
     * Gets the distance including z.
     * 
     * @param bead
     *           the bead
     * @return the distance including z
     */
    public double getDistanceIncludingZ(BeadFrame bead) {
        // TODO Auto-generated method stub
        /*
         * double x1 = getFovX(); double x2 = bead.getFovX();
         * 
         * double y1 = getFovY(); double y2 = bead.getFovY();
         * 
         * double z1 = getCentroidZ(); double z2 = bead.getCentroidZ();
         */

        double distance = Math.pow(deltaX, 2) + Math.pow(deltaY, 2) + Math.pow(deltaZ, 2);

        return Math.sqrt(distance);

    }

    /**
     * Gets the invalidity reason.
     * 
     * @return the invalidity reason
     */
    public String getInvalidityReason() {
        return invalidityReason;
    }

    /**
     * Sets the invalidity reason.
     * 
     * @param invalidityReason
     *           the new invalidity reason
     */
    public void setInvalidityReason(String invalidityReason) {

        if (this.invalidityReason == null)
            this.invalidityReason = invalidityReason;
    }

    /**
     * Gets the bead color.
     *
     * @return the bead color
     */
    public Color getBeadColor() {
        return getSource().getBeadsColor();
    }

    /**
     * Gets the overlay with alter ego.
     *
     * @return the overlay with alter ego
     */
    public ImageProcessor getOverlayWithAlterEgo() {
        if (getAlterEgo() != null) {
            ImageProcessor ip1 = getSource().copyRoi(getBoundariesAsRectangle());
            ImageProcessor ip2 = getAlterEgo().getSource().copyRoi(getBoundariesAsRectangle());

            ip1.setLut(LUT.createLutFromColor(getBeadColor()));
            ip2.setLut(LUT.createLutFromColor(getAlterEgo().getBeadColor()));

            ip1 = normalizeImage(ip1).convertToRGB();
            ip2 = getAlterEgo().normalizeImage(ip2).convertToRGB();

            ip1.copyBits(ip2, 0, 0, Blitter.ADD);

            return ip1;
        } else
            return new ColorProcessor(getWidth(), getHeight());
    }

    /**
     * Normalize image.
     *
     * @param ip the ip
     * @return the image processor
     */
    public ImageProcessor normalizeImage(ImageProcessor ip) {
        ip = ip.convertToFloat();
        System.out.println(ip.getStatistics().mean);
        ip.multiply(1.0 / (ip.getStatistics().mean + ip.getStatistics().stdDev));
        ip.add(-1);
        ip.setMinAndMax(0, ip.getMax());
        return ip.convertToByte(true);
    }

    /**
     * Gets the asymetry.
     *
     * @return the asymetry
     */
    public double getAsymetry() {
        return getResolution(0) / getResolution(1);
    }

    /**
     * Gets the slice number.
     *
     * @return the slice number
     */
    public int getSliceNumber() {
        return stackSize;
    }

    /**
     * Checks if is ignored.
     *
     * @return true, if is ignored
     */
    public boolean isIgnored() {
        return isIgnored;
    }

    /**
     * Sets the ignored.
     *
     * @param isIgnored the new ignored
     */
    public void setIgnored(boolean isIgnored) {
        this.isIgnored = isIgnored;
    }
}