knop.psfj.BeadImage.java Source code

Java tutorial

Introduction

Here is the source code for knop.psfj.BeadImage.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.measure.Calibration;
import ij.process.Blitter;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import ij.process.ShortProcessor;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Observable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.ImageIcon;

import knop.psfj.heatmap.EasyLUT;
import knop.psfj.locator.BeadLocator;
import knop.psfj.locator.BeadLocator3D;
import knop.psfj.resolution.Counter3D;
import knop.psfj.resolution.DataTricks;
import knop.psfj.resolution.Microscope;
import knop.psfj.utils.MathUtils;
import knop.psfj.utils.VisibleColor;
import knop.psfj.view.Message;
import loci.formats.ChannelSeparator;
import loci.formats.FormatException;
import loci.plugins.util.ImageProcessorReader;
import loci.plugins.util.LociPrefs;

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

/**
 * The Class BeadImage.
 */
public class BeadImage extends Observable {

    /**
     * The file address.
     */
    String fileAddress;

    /**
     * The stack size.
     */
    int stackSize = -1;

    /**
     * The dot location : set of Rectangle of a view pixels wide determining the
     * position of beads.
     */
    ArrayList<Rectangle> beadLocation;

    /**
     * The dot frames : calculated from the the dotLocation, each Rectangle in
     * this list represents the extracted area of a bead.
     */
    BeadFrameList beadFrames = new BeadFrameList();

    /**
     * Image Processor Reader object : used when working from disk.
     */
    private ImageProcessorReader ipr = new ImageProcessorReader();

    /**
     * Rhe original image.
     */
    private ImageStack stack;

    /**
     * Image corresponding to the bead focal plane.
     */
    private ImageProcessor middleImage;

    /**
     * List of stack images : each one is a bead.
     */
    private HashMap<Integer, ImageStack> stackList; // = new HashMap<Integer,
    // ImageStack>();

    /**
     * The not set.
     */
    public static int NOT_SET = -1;

    /**
     * The not found.
     */
    public static int NOT_FOUND = -9;

    /**
     * The threshold level for the background substraction.
     */
    protected int thresholdValue = NOT_SET;

    /**
     * The size of the area extracted for each bead.
     */
    protected int frameSize = NOT_SET;

    /**
     * When auto calculating the automatic frame size will use
     * beadEnlargementFactor * sigma in pixel.
     */
    protected int beadEnlargementFactor = 10;

    /**
     * The lut min.
     */
    protected double minIntentisyOfWholeStack;

    /**
     * The lut max.
     */
    protected double maxIntensityOfWholeStack = NOT_SET;

    /**
     * The segmentation mask.
     */
    protected ImageProcessor segmentationMask;

    /**
     * The preview of the process.
     */
    protected ImageProcessor preview;

    /**
     * The image width.
     */
    protected int imageWidth;

    /**
     * The image height.
     */
    protected int imageHeight;

    /**
     * The preview width.
     */
    protected int previewWidth = 500;

    /**
     * The preview height.
     */
    protected int previewHeight = 500;

    /**
     * The preview x.
     */
    protected int previewX = 0;

    /**
     * The preview y.
     */
    protected int previewY = 0;

    /**
     * The bead focus plane.
     */
    protected int beadFocusPlane = NOT_SET;

    /**
     * The image name.
     */
    protected String imageName;

    /**
     * The image path.
     */
    protected String imageFolder = null;

    /**
     * The min threshold.
     */
    protected int minThreshold = NOT_SET;

    /**
     * The max threshold.
     */
    protected int maxThreshold = NOT_SET;

    /**
     * The file size.
     */
    protected double fileSize;

    /**
     * The progress.
     */
    protected int progress;

    /**
     * The is valid.
     */
    protected boolean isValid = false;

    /**
     * The is opening.
     */
    protected boolean isOpening = false;

    /**
     * The image disk size.
     */
    protected long imageDiskSize = -1;

    /**
     * The raw preview.
     */
    protected ImageProcessor rawPreview = null;

    /**
     * The status.
     */
    protected String status;

    /**
     * The frame number.
     */
    protected int frameNumber;

    protected BeadLocator locator = new BeadLocator3D();

    /**
     * ignored frame number.
     */
    protected int ignoredFrameNumber = 0;

    /**
     * The lut.
     */
    protected EasyLUT lut = new EasyLUT("blue_orange_white");

    /**
     * clean but let stacks in memory.
     */
    public static int CLEAN_BUT_LET_STACKS_IN_MEMORY = 0;

    /**
     * clean all.
     */
    public static int CLEAN_ALL = 1;

    /**
     * clean for deletion.
     */
    public static int CLEAN_FOR_DELETION = 2;

    /**
     * The clean frames.
     */
    public static int CLEAN_FRAMES = 3;

    /**
     * The counter3d.
     */
    Counter3D counter3d;

    /**
     * The microscope.
     */
    protected Microscope microscope;

    public static final String MSG_PARAMETER_CHANGED = "parameter changed";

    public static final String MSG_THRESHOLD_CHANGED = "threshold changed";
    public static final String MSG_FOCUS_CHANGED = "focus changed";
    public static final String MSG_FRAMESIZE_CHANGED = "bead enlargement changed";

    public static final String MSG_PREVIEW_UPDATED = "preview updated";
    public static final String MSG_IMAGE_NOT_OKAY = "image not okay";
    public static final String MSG_IMAGE_OKAY = "image okay";

    public static final String MSG_NEW_CHANNEL_DETECTED = "other channel detected";

    /**
     * Instantiates a new bead image.
     */
    public BeadImage() {

    }

    /**
     * The Constant TOP_LEFT_CORNER.
     */
    public final static int TOP_LEFT_CORNER = 0;

    /**
     * The Constant TOP_RIGHT_CORNER.
     */
    public final static int TOP_RIGHT_CORNER = 1;

    /**
     * The Constant BOTTOM_LEFT_CORNER.
     */
    public final static int BOTTOM_LEFT_CORNER = 2;

    /**
     * The Constant BOTTOM_RIGHT_CORNER.
     */
    public final static int BOTTOM_RIGHT_CORNER = 3;

    /**
     * The Constant CENTER.
     */
    public final static int CENTER = 4;

    /**
     * Instantiates a new bead image from the file with the specified address.
     *
     * @param s the address of the file
     */
    public BeadImage(String s) {
        super();
        setFileAddress(s);

    }

    /**
     * Instantiates a new bead image with an image stack address.
     *
     * @param s the ImageStack
     */
    public BeadImage(ImageStack s) {
        super();
        setStack(s);

    }

    /**
     * Sets the stack image.
     *
     * @param s the new stack
     */
    public void setStack(ImageStack s) {
        stack = s;

        setImageHeight(s.getHeight());
        setImageWidth(s.getWidth());

    }

    /**
     * Checks if the image is in memory or if we work from the disk.
     *
     * @return true, if is in memory
     */
    public boolean isInMemory() {
        return (stack != null);
    }

    /**
     * Checks if is the object is opening the image.
     *
     * @return true, if is opening
     */
    public boolean isOpening() {
        return isOpening;
    }

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

        BeadImage startImage = new BeadImage("/Users/cyril/Downloads/ApoTIRF_60x_dual_channel/ch2_ApoTIRF_60x.tif");

        startImage.workFromMemory();

        startImage.autoFocus();
        startImage.autoThreshold();
        startImage.setFrameSize(20);
        BeadFrameProcessor processor = new BeadFrameProcessorAsync(startImage.getBeadFrameList());
        //startImage.buildStackList();
        processor.process();
        processor.filter();
        System.out.println("signal to noise ration : " + startImage.getSignalToNoiseRatio());

    }

    /**
     * Sign of a double.
     *
     * @param d the double
     * @return -1 for negative doubles, 1 for positive ones
     */
    public int signOf(double d) {
        if (d > 0) {
            return 1;
        }
        if (d < 0) {
            return -1;
        }
        return 0;

    }

    /**
     * Wait until the image is opened.
     */
    public void waitForOpening() {
        while (isOpening()) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
        }
    }

    /**
     * Sets the image name.
     *
     * @param name the new image name
     */
    public void setImageName(String name) {
        Pattern p = Pattern.compile("(.*)[.]\\w$");
        Matcher m = p.matcher(name);

        if (!m.matches()) {
            imageName = name;
        } else {
            imageName = m.group(1);
        }

        imageName = name;
    }

    /**
     * Sets the image path.
     *
     * @param path the new image path
     */
    public void setImageFolder(String path) {

        if (path.endsWith("/") == false) {
            path = path + "/";
        }

        imageFolder = path;
    }

    /**
     * Gets the image name.
     *
     * @return the image name
     */
    public String getImageName() {

        return imageName;
    }

    /**
     * Gets the image name without extension.
     *
     * @return the image name without extension
     */
    public String getImageNameWithoutExtension() {

        return getImageName().replace(".tif", "");
    }

    /**
     * Gets the image path.
     *
     * @return the image path
     */
    public String getImageFolder() {
        return imageFolder;
    }

    /**
     * Gets the image intensity sum.
     *
     * @param ip the ip
     * @return the image intensity sum
     */
    public double getImageIntensitySum(ImageProcessor ip) {

        double sum = 0;
        for (int x = 0; x != ip.getWidth(); x++) {
            for (int y = 0; y != ip.getHeight(); y++) {
                sum += (ip.getPixel(x, y));
            }
        }

        return sum;

    }

    /**
     * Gets the file address.
     *
     * @return the file address
     */
    public String getFileAddress() {
        return fileAddress;
    }

    /**
     * Gets the fiel of view area.
     *
     * @return the fiel of view area
     */
    public double getFielOfViewArea() {
        return getCalibration().pixelWidth * getImageWidth() * getImageHeight() * getCalibration().pixelHeight;
    }

    /**
     * Gets the field of view width.
     *
     * @return the field of view width
     */
    public double getFieldOfViewWidth() {
        return getCalibration().pixelWidth * getImageWidth();
    }

    /**
     * Gets the field of view height.
     *
     * @return the field of view height
     */
    public double getFieldOfViewHeight() {
        return getCalibration().pixelHeight * getImageHeight();
    }

    /**
     * Gets the bead density.
     *
     * @return the bead density
     */
    public double getBeadDensity() {

        return getBeadLocation().size() / getFielOfViewArea();

    }

    /**
     * Gets the background intensity.
     *
     * @return the background intensity
     */
    public double getBackgroundIntensity() {
        ImageStatistics stats = getPlane(0).getStatistics();
        return stats.mean + stats.stdDev;
    }

    /**
     * Read width and height from disk.
     */
    public synchronized void readWidthAndHeightFromDisk() {
        ImageProcessorReader ipr = new ImageProcessorReader(new ChannelSeparator(LociPrefs.makeImageReader()));

        try {
            ipr.setId(fileAddress);

        } catch (FormatException e) {

            e.printStackTrace();
            try {
                ipr.close();
            } catch (IOException e1) {

                e1.printStackTrace();
            }
            isValid = false;
            return;

        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        int width = ipr.getSizeX();
        int height = ipr.getSizeY();
        int numChannel = ipr.getSizeC();
        int bitsPerPixel = ipr.getBitsPerPixel();

        if (numChannel > 2) {
            notifyObservers("error", "Only monochannel image can be added.", null, null);
            isValid = false;
            return;
        }

        if (bitsPerPixel > 16) {
            isValid = false;
            setInvalidityReason("Only 8-bits and 16-bits tif images are supported.");
            setProgress(0, MSG_IMAGE_OKAY);

            notifyObservers(new Message(this, "error", "Only 8-bits and 16-bits tif images are supported."));
            try {
                ipr.close();
            } catch (IOException e) {

                e.printStackTrace();
            }
            return;
        }

        setImageWidth(width);
        setImageHeight(height);

    }

    /**
     * load the stack into memory.
     */
    public synchronized void workFromMemory() {
        // if the fileAddress is not equal to null, this means that
        // the image has not been previously load into memory
        if (fileAddress != null && stack == null) {
            System.out.println("Loading image in memory...");
            setProgress(0, "Loading image in memory...");

            try {
                // BF.openImagePlus(path)

                // stack = BF.openImagePlus(fileAddress)[0].getImageStack();
                // stack = new
                // Opener().openImage(fileAddress).getImageStack();//
                // IJ.openImage(fileAddress).getStack();
                ImageProcessorReader ipr = new ImageProcessorReader(
                        new ChannelSeparator(LociPrefs.makeImageReader()));
                DescriptiveStatistics standardDeviations = new DescriptiveStatistics();

                ipr.setId(fileAddress);

                int width = ipr.getSizeX();
                int height = ipr.getSizeY();
                int num = ipr.getImageCount();
                int numChannel = ipr.getSizeC();
                int bitsPerPixel = ipr.getBitsPerPixel();

                double stdDev;
                double min;
                double max;

                // if a second channel in the image is detected
                boolean isSecondChannel = false;
                BeadImage secondChannel = null;
                ImageStack secondStack = null;

                if (numChannel == 2) {

                    secondChannel = new BeadImage();
                    secondChannel.setFileAddress(fileAddress);
                    secondChannel.setImageName(secondChannel.getImageName() + "_channel_2");
                    secondStack = new ImageStack(width, height);
                    isSecondChannel = true;
                    secondChannel.setStack(secondStack);

                    notifyObservers(MSG_NEW_CHANNEL_DETECTED, "Two channels detected", null, secondChannel);

                }

                stack = new ImageStack(width, height);

                for (int i = 0; i != num; i++) {
                    setProgress(i, num);
                    setStatus("Loading slice " + (i + 1) + "/" + num + "...");

                    ImageProcessor ip = ipr.openProcessors(i)[0];

                    if (isSecondChannel) {
                        i++;
                        ImageProcessor ip2 = ipr.openProcessors(i)[0];
                        secondStack.addSlice(ip2);
                        secondChannel.setStatus("Loading slice " + (i + 1) + "/" + num + "...");
                        secondChannel.setProgress(i, num);
                    }

                    min = ip.getMin();
                    max = ip.getMax();

                    if (min < minIntentisyOfWholeStack) {
                        minIntentisyOfWholeStack = min;
                    }
                    if (max > maxIntensityOfWholeStack) {
                        maxIntensityOfWholeStack = max;
                    }

                    stdDev = ip.getStatistics().stdDev;

                    if (standardDeviations.getMax() < stdDev) {
                        beadFocusPlane = i;
                    }
                    standardDeviations.addValue(stdDev);
                    stack.addSlice(ip);
                    updateView(ip);
                }
                if (isSecondChannel) {
                    autoFocus();
                    secondChannel.autoFocus();
                    secondChannel.setProgress(100);
                }
                try {
                    ipr.close();
                } catch (Exception e) {
                    System.err.println("Error when closing the image reader.");
                }
                setChanged();
                notifyObservers(new Message(this, MSG_IMAGE_OKAY));
                setStatus("Done.");
                setProgress(100);

                setImageHeight(stack.getHeight());
                setImageWidth(stack.getWidth());

                isValid = true;
                new ImagePlus("", stack).resetDisplayRange();
                // openImage();

            } catch (NullPointerException e) {
                notifyError("Image not valid.");
                e.printStackTrace();
                setProgress(0, "image not okay");
                return;
            } catch (FormatException e) {
                setProgress(0, "This image format is not supported.");
                notifyError("This image format is not supported");
                e.printStackTrace();
            } catch (IOException e) {

                notifyError("File not accessible");
                setProgress(0, "Image not reachable.");
                e.printStackTrace();
            }

        }
    }

    /**
     * Sets the file address and load it if the parameter "load" is true.
     *
     * @param fileAddress the file address
     * @param load load the stack if true
     */
    public void setFileAddress(String fileAddress, boolean load) {

        setFileAddress(fileAddress);
        if (load) {
            workFromMemory();
        }

    }

    /**
     * Sets the file address.
     *
     * @param fileAddress the new file address
     */
    public void setFileAddress(String fileAddress) {

        this.fileAddress = fileAddress;

        File file = new File(fileAddress);

        setImageFolder(file.getParent());
        setImageName(file.getName());

        // make sure to store the image size in memory for further use
        getImageDiskSize();

    }

    /**
     * Notify the observer of an error.
     *
     * @param error the error message
     */
    public void notifyError(String error) {
        setChanged();
        notifyObservers(new Message(this, "error", error));
    }

    /**
     * Gets the raw focus point, without launching the auto calculation
     * algorithm if the focus point is not set.
     *
     * @return the raw focus point
     */
    public int getRawFocusPoint() {
        return beadFocusPlane;
    }

    /**
     * Gets the stack size.
     *
     * @return the stack size
     */
    public int getStackSize() {
        if (stack != null) {
            return stack.getSize();
        } else {
            return 0;
        }
    }

    /**
     * Gets the individual bead stack with the specified id.
     *
     * @param id the bead id
     * @return the image stack of the bead
     */
    public ImageStack getStack(int id) {
        if (stackList == null) {
            buildStackList();
        }

        if (id >= stackList.size()) {
            System.err.println("Stack " + id + " doesn't exist.");
            return null;
        }

        return stackList.get(id);

    }

    /**
     * Builds the stack list : retrieve every bead from the original image stack
     * and store them into a ImageStack array.
     *
     * A list of bead stack is created and each bead stack is filled while
     * loading (and browsing) the stack.
     */
    public void buildStackList() {

        if (stackList != null) {
            return;
        }

        stackList = new HashMap<Integer, ImageStack>();

        setStatus("Retreiving stacks...");

        stackList.clear();

        long start = System.currentTimeMillis();

        // creating an hashmap for storing the different planes
        final HashMap<Integer, ImageProcessor> planes = new HashMap<Integer, ImageProcessor>();

        // an thread that load the images from the disk is created
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i != getStackSize(); i++) {
                    planes.put(i, getPlane(i));
                }
            }
        }.start();

        // while the loading thread is on...
        BeadFrameList frames = getBeadFrameList();

        System.out.println(frames.size() + " beads to retrieve !");
        // for each plane of the stack
        for (int i = 0; i != getStackSize(); i++) {

            // we wait for each plane to loaded
            while (planes.containsKey(i) == false) {
                try {
                    Thread.currentThread();
                    Thread.sleep(10);
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
                ;
            }

            // the plane is loaded !! Let's start doing some processing
            // (in the mean time, the next thread starts to be loaded
            // System.out.println("Start processing " + i + " ...");
            // getting the plane
            ImageProcessor sp = planes.get(i);

            // for each bead frame,
            for (BeadFrame frame : frames) {

                final int stackNumber = i;

                // copy the part of the frame from the plane image
                ImageProcessor imagePart = copyRoi(sp, frame.getBoundariesAsRectangle());

                // if the current plane is the first, a new image stack
                // corresponding to the bead is created
                if (stackNumber == 0) {
                    stackList.put(frame.getId(), new ImageStack(imagePart.getWidth(), imagePart.getHeight()));
                }

                // adding the slice to the bead stack
                stackList.get(frame.getId()).addSlice(imagePart);

            }

            // System.out.println("Stop processing " + i + "");
            setProgress(i, getStackSize());

            // freeing some memory
            planes.put(i, null);
            planes.put(i, sp.createProcessor(1, 1));
            sp = null;

        }

        for (Integer id : stackList.keySet()) {
            frames.getFromId(id).setSubstack(new ImagePlus("", stackList.get(id)));
        }

        planes.clear();
        stackList.clear();
        stackList = null;

        System.gc();
        long end = System.currentTimeMillis();

        setStatus("Stacks retreived.");
        setProgress(100);

        System.out.println("Stacks retreived in  " + (end - start) + " ms.");

    }

    /**
     * Gets the plane.
     *
     * @param id the plane id
     * @return the plane image processor
     */
    public ImageProcessor getPlane(int id) {

        // System.out.println("Querying plan " + id + "...");
        /*
           * if (id == -1) { findBeadFocusPlaneWithHeuristic(); }
           */
        try {
            if (stack != null) {
                //System.out.println("Getting plane " + id + "...");
                ImageProcessor plane = stack.getProcessor(id + 1);
                // plane.setColorModel(lut.getColorModel());
                //plane.setLut(lut.getLUT(lutMin, lutMax * 0.8));
                return plane;
            } else {
                try {

                    ImageProcessor plane = ipr.openProcessors(id)[0];

                    plane.setColorModel(lut.getColorModel());

                    return plane;
                } catch (FormatException e) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }
            }
        } catch (Exception e) {
            // setProgress(0);
            // setStatus("Can't open image.");

        }

        System.err.println("Couldn't get stack " + id + " !");
        return null;
    }

    /**
     * Gets the stack.
     *
     * @return the stack
     */
    public ImageStack getStack() {
        return stack;
    }

    /**
     * Gets the image corresponding to the focal plane.
     *
     * @return the focal plane image
     */
    public ImageProcessor getMiddleImage() {
        if (middleImage == null) {

            if (getFocusPlane() == -1) {
                return null;
            }

            middleImage = getPlane(getFocusPlane());

            ImageStatistics stats = middleImage.getStatistics();
            minThreshold = DataTricks.round(stats.min) - 1;
            maxThreshold = DataTricks.round(stats.max) + 1;

        }
        return middleImage;
    }

    /**
     * Show.
     *
     * @param ip the ip
     * @return the image plus
     */
    public ImagePlus show(ImageProcessor ip) {
        ImagePlus imp = new ImagePlus("PatroloJ", ip);
        imp.show();
        return imp;
    }

    public void setLocator(BeadLocator locator) {
        this.locator = locator;
        beadLocation = null;
        beadFrames = null;
        preview = null;
    }

    public BeadLocator getLocator() {
        locator.setBeadImage(this);
        return locator;
    }

    /**
     * Gets the bead location. This threshold the focus plane image and detect
     * all objects using a Connected Component algorithm. It retuns a list of
     * Rectangle object representing each detected object (beads usually).
     *
     * @return the bead location
     */
    public synchronized ArrayList<Rectangle> getBeadLocation() {
        if (beadLocation == null) {
            beadLocation = getLocator().getBeadLocation();
            System.out.println("beadLocation : " + beadLocation);
        }
        return beadLocation;
    }

    /**
     * Auto bid enlargement.
     *
     * @return the int
     */
    public int autoFrameSize() {
        return autoBeadEnlargement(getMicroscope());
    }

    /**
     * Auto bead enlargement.
     *
     * @param m the m
     * @return the int
     */
    protected int autoBeadEnlargement(Microscope m) {

        int autoFrameSize;// = MathUtils.round(
        //   1.0 * beadEnlargementFactor * m.getXYTheoreticalResolution() / 2 / Math.sqrt(2 * Math.log(2)) / getMicroscope().getCalibration().pixelWidth
        //);

        autoFrameSize = MathUtils
                .round(m.getXYTheoreticalResolution() * beadEnlargementFactor / m.getCalibration().pixelWidth);

        setFrameSize(autoFrameSize);

        setChanged();
        notifyObservers(new Message(this, MSG_FRAMESIZE_CHANGED));
        System.out.println("auto calculated frame size : " + frameSize);
        return frameSize;
    }

    /**
     * Gets the enlarged frame.
     *
     * @param r the r
     * @return the enlarged frame
     */
    public Rectangle getEnlargedFrame(Rectangle r) {
        Rectangle rn = new Rectangle();

        int f = frameSize;

        if (frameSize * frameSize >= imageWidth * imageHeight * 0.8) {
            rn.setLocation(0, 0);
            rn.setSize(imageWidth, imageHeight);
            return rn;
        }

        int x = MathUtils.round(r.getX() + r.getWidth() / 2 - f / 2);

        int y = MathUtils.round(r.getY() + r.getHeight() / 2 - f / 2);

        x -= 1;
        y -= 1;

        int w = f;
        int h = f;
        rn.setLocation(x, y);
        rn.setSize(w, h);

        return rn;
    }

    /**
     * Sets the wave length.
     *
     * @param wavelength the new wave length
     */
    public void setWaveLength(double wavelength) {
        getMicroscope().setWaveLength(wavelength);
    }

    /**
     * Sets the wave length in nano meters.
     *
     * @param wavelength the new wave length in nano meters
     */
    public void setWaveLengthInNanoMeters(String wavelength) {
        try {
            getMicroscope().setWaveLength(Double.parseDouble(wavelength) / 1000);
        } catch (Exception e) {
            System.err.println("Couldn't set wavelength properly");
        }

    }

    /**
     * Gets the wave length in nano meters.
     *
     * @return the wave length in nano meters
     */
    public String getWaveLengthInNanoMeters() {
        if (getMicroscope() != null) {
            return "" + MathUtils.round(getMicroscope().getWaveLength() * 1000);
        } else {
            return "";
        }
    }

    /**
     * Gets the enlarged frame.
     *
     * @param r the r
     * @param f the f
     * @return the enlarged frame
     */
    public static Rectangle getEnlargedFrame(Rectangle r, int f) {
        Rectangle rn = new Rectangle();
        rn.setLocation(round(r.getCenterX() - f / 2), round(r.getCenterY() - f / 2));
        rn.setSize(f, f);

        return rn;
    }

    /**
     * Gets the BeadFrame list (areas extracted for the PSF analysis). This
     * function uses the result of the getBeadLocation, and create a frame
     * around each object. When two frames are overlapping too much, only the
     * frame containing the brightest bead is kept.
     *
     * @return the BeadFrame list object (areas extracted for the PSF analysis)
     */
    public synchronized BeadFrameList getBeadFrameList() {
        if (beadFrames == null) {
            beadFrames = getLocator().getBeadFrameList();
        }
        return beadFrames;
    }

    /**
     * Gets the frame number.
     *
     * @return the frame number
     */
    public int getFrameNumber() {

        return frameNumber;
    }

    /**
     * Sets the frame number.
     *
     * @param frameNumber the new frame number
     */
    public void setFrameNumber(int frameNumber) {
        this.frameNumber = frameNumber;
    }

    /**
     * Gets the ignored frame number.
     *
     * @return the ignored frame number
     */
    public int getIgnoredFrameNumber() {
        return ignoredFrameNumber;
    }

    /**
     * Gets the bead max intensity.
     *
     * @param r the r
     * @return the bead max intensity
     */
    public double getBeadMaxIntensity(Rectangle r) {

        ImageProcessor middleImage = getMiddleImage();
        middleImage.setRoi((Rectangle) r.clone());
        double max = middleImage.getStatistics().max;
        middleImage.resetRoi();
        return max;
    }

    /**
     * Copy roi.
     *
     * @param r the r
     * @return the image processor
     */
    public ImageProcessor copyRoi(Rectangle r) {
        return copyRoi(getMiddleImage(), r);
    }

    /**
     * Copy roi into a new image.
     *
     * @param ip the source image
     * @param r the rectangle represanting the ROI
     * @return a ShortProcessor containing the ROI.
     */
    public ImageProcessor copyRoi(ImageProcessor ip, Rectangle r) {

        // System.out.println("Copying a roi of "+r.toString());
        ImageProcessor result = ip.createProcessor(r.width, r.height);

        for (int x = r.x; x != r.x + r.width; x++) {
            for (int y = r.y; y != r.y + r.height; y++) {
                result.setColor(ip.getPixel(x, y));
                result.drawPixel(x - r.x, y - r.y);
            }
        }

        return result;
    }

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

        return getMicroscope().getCalibration();
    }

    /**
     * Sets the calibration.
     *
     * @param cal the new calibration
     */
    public void setCalibration(Calibration cal) {
        getMicroscope().setCalibration(cal);
    }

    /**
     * Gets the segmentation mask from a specified image.
     *
     * @param ip the input image
     * @return the segmentation mask
     */
    private ImageProcessor getSegmentationMask(ImageProcessor ip) {
        return getSegmentationMask(ip, getThresholdValue());
    }

    /**
     * Gets the segmentation mask. This mask is used for bead detection.
     *
     * @param ip the ip
     * @param threshold the threshold
     * @return the segmentation mask
     */
    private ImageProcessor getSegmentationMask(ImageProcessor ip, int threshold) {
        System.out.println("Getting segmentation mask with threshold : " + threshold);
        ImageProcessor mask = ip.duplicate();

        mask.threshold(threshold);
        mask = mask.convertToByte(false);

        mask.invert();
        int[] h = mask.getHistogram();
        for (int i = 0; i != h.length; i++) {
            if (h[i] > 0) {
                System.out.println("i : " + i);
            }
        }
        System.out.println("mask : +" + mask);
        return mask;
    }

    /**
     * Gets the segmented image.
     *
     * @return the segmented image
     */
    public ImageProcessor getSegmentedImage() {
        if (segmentationMask == null) {
            System.out.println("Recalculating mask...");
            segmentationMask = getSegmentationMask(getMiddleImage());
        }
        return segmentationMask;
    }

    /**
     * Returns an image showing the focus plane image with rectangles
     * reprensenting the detected bead and their respective area.
     *
     * @return the bead preview
     */
    public synchronized ImageProcessor getBeadPreview() {

        return getBeadPreview(3);

    }

    /**
     * Gets the bead preview.
     *
     * @param trials the trials
     * @return the bead preview
     */
    public ImageProcessor getBeadPreview(int trials) {
        if (trials == 0) {
            return null;
        }
        if (preview == null) {
            try {
                System.out.println("generating new preview");

                preview = getMiddleImage();

                if (preview == null) {
                    return new ShortProcessor(previewWidth, previewHeight);
                }

                preview = preview.convertToRGB();

                for (BeadFrame frame : getBeadFrameList()) {
                    Rectangle2D r = frame.getBoundaries();

                    if (frame.isValid() == true) {
                        preview.setColor(Color.green);
                    } else {
                        preview.setColor(Color.yellow);
                    }

                    if (frame.isValid() == false) {
                        r = getEnlargedFrame((Rectangle) r, MathUtils.round(r.getWidth() / 2));
                    }

                    preview.drawRect(round(r.getX()), round(r.getY()), round(r.getWidth()), round(r.getHeight()));
                    if (frame.getId() == null) {
                        continue;
                    }

                }

                status = "Done.";
                progress = 100;

                notifyObservers(MSG_PREVIEW_UPDATED, "Done.", 100, null);
            } catch (Exception e) {
                trials--;
                getBeadPreview(trials);
            }
        }
        return preview;

    }

    /**
     * Gets the bead preview in a asynchronous way.
     *
     * @return nothing : you should wait for the signal
     */
    public void getBeadPreviewAsync() {
        new Thread() {
            @Override
            public void run() {

                if (thresholdValue == -1) {
                    autoThreshold();
                }
                if (frameSize == -1) {
                    autoBeadEnlargement(new Microscope());
                }

                try {
                    System.out.println("Getting bead previous async");

                    getBeadPreview();
                    System.out.println("Finished...");

                } catch (ConcurrentModificationException e) {
                    System.out.println("oups...");
                }

            }
        }.start();
    }

    /**
     * Gets the auto threshold.
     *
     * @param sigma the sigma
     * @return the auto threshold
     */
    private double getAutoThreshold(double sigma) {
        // retrieve the focal plane
        ImageProcessor backgroundPlane = getPlane(0);

        // if their's a problem with the focal plane, we return -1;
        if (backgroundPlane == null) {
            return -1;
        }

        // gettting focal plane statistics
        setProgress(50, "Calculating threshold...");
        backgroundPlane.resetRoi();
        ImageStatistics stats = backgroundPlane.getStatistics();

        // retrieving the mean of the first image to serve as background mean
        double bgMean = stats.mean;
        double bgStd = stats.stdDev;

        return bgMean + (bgStd * sigma);
    }

    /**
     * Calculate automatically the threshold value for bead detection.
     *
     * @return the calculated threshold
     */
    public int autoThreshold() {

        double threshold = getAutoThreshold(10);

        System.out.println("Auto detected threshold : " + threshold);
        setThresholdValue(MathUtils.round(threshold));

        setProgress(100, "Done.");

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

        return getThresholdValue();
    }

    /**
     * Calculate the minimum and maximum values for the threshold setting.
     *
     * @return nothing : they can be accessed via the attributes minThreshold
     * and maxThreshold.
     */
    private void updateMinAndMax() {

        if (getFocusPlane() == -1) {

            minThreshold = 0;
            maxThreshold = 100;
        }

        if (minThreshold == -1 || maxThreshold == -1) {
            ImageStatistics stats = getMiddleImage().getStatistics();
            minThreshold = DataTricks.round(stats.min) - 1;
            maxThreshold = DataTricks.round(stats.max) + 1;
        }
        setChanged();
        notifyObservers(new Message(this, "min and max changed"));
    }

    /**
     * Gets the threshold minimum value (depends on the focal plane).
     *
     * @return the threshold min
     */
    public int getThresholdMin() {

        return minThreshold;
    }

    /**
     * Gets the threshold maximum value of the whole stack
     *
     * @return the threshold max
     */
    public int getThresholdMax() {

        return maxThreshold;
    }

    /**
     * Gets the threshold value used for bead detection.
     *
     * @return the threshold level
     */
    public int getThresholdValue() {

        return thresholdValue;
    }

    /**
     * Sets the threshold value for.
     *
     * @param newThreshold the new threshold value
     */
    public void setThresholdValue(int newThreshold) {

        System.out.println("** setting threshold to " + newThreshold);

        this.thresholdValue = newThreshold;

        // the segmentation mask is reset
        segmentationMask = null;

        // the bead location is reset
        if (beadLocation != null) {
            beadLocation.clear();
        }

        beadLocation = null;

        beadFrames = null;
        preview = null;
        stackList = null;
        System.gc();

    }

    /**
     * Gets the size of the bead area used for analysis.
     *
     * @return the bead area size
     */
    public int getFrameSize() {

        return frameSize;
    }

    /**
     * Gets the bead enlargement factor.
     *
     * @return the bead enlargement factor
     */
    public int getBeadEnlargementFactor() {
        return beadEnlargementFactor;
    }

    /**
     * Sets the bead enlargement factor.
     *
     * @param beadEnlargementFactor the new bead enlargement factor
     */
    protected void setBeadEnlargementFactor(int beadEnlargementFactor) {
        this.beadEnlargementFactor = beadEnlargementFactor;
    }

    /**
     * Sets the bid enlargement.
     *
     * @param beadEnlargement the new bid enlargement
     */
    public void setFrameSize(int beadEnlargement) {

        if (beadEnlargement == 0 || beadEnlargement == this.frameSize) {
            return;
        }

        System.out.println("** setting bead frame to " + beadEnlargement);

        this.frameSize = beadEnlargement;
        if (beadFrames != null) {
            beadFrames.clear();
        }
        beadFrames = null;
        preview = null;
        stackList = null;
        System.gc();

    }

    /**
     * Find exact focus point.
     */
    public void findExactFocusPoint() {

        ImageProcessor ip = getPlane(0);

        int best = 0;
        // a new standard deviation
        double newStdDev = ip.getStatistics().stdDev;

        // the best among the best
        double bestStdDev = newStdDev;

        // for each plane
        for (int i = 1; i != getStackSize(); i++) {
            setProgress(i, getStackSize());

            // get the plane
            ImageProcessor plane = getPlane(i);

            // tell the views that we are looking
            // at this particular plane
            updateView(plane);

            // the standard deviation of this plane is ...
            newStdDev = plane.getStatistics().stdDev;

            // if it's the best, then congratulation
            if (newStdDev > bestStdDev) {
                bestStdDev = newStdDev;

                // id of the best standard deviation
                best = i;
                /*
                 * System.out
                 * .println("A new best standard deviation\nhas been found : " +
                 * newStdDev);
                 */
                // telling the view what we are doing
                setProgress(100 * i / getStackSize(), "Searching focus...");
            }

        }

        // setting the focus plane to the best
        setFocusPlane(best);

        setProgress(0, "Done.");

    }

    /**
     * Sets the focus plane.
     *
     * @param focusPlane the new focus point
     */
    public void setFocusPlane(int focusPlane) {

        if (focusPlane >= 0 && focusPlane < getStackSize() && focusPlane != beadFocusPlane) {
            System.out.println("** setting focus to " + focusPlane);
            beadFocusPlane = focusPlane;
            middleImage = null;
            segmentationMask = null;
            // beadLocation = null;
            preview = null;
            if (getLocator().isFocusPlaneDependent()) {
                beadLocation = null;
                beadFrames = null;
            }
            updateMinAndMax();
        }
    }

    /**
     * Auto focus.
     */
    public void autoFocus() {

        if (isProcessing()) {
            return;
        }

        lockForProcessing();

        try {

            // if (isInMemory()) {
            findExactFocusPoint();
            // } else {
            // findBeadFocusPlaneWithHeuristic();
            // }
        } catch (Exception e) {
            e.printStackTrace();
        }
        getThresholdMax();
        setProcessingFinished();
        notifyObservers("focus changed", "Done.", 100, null);
    }

    /**
     * Gets the focus plane.
     *
     * @return the focus plane (from 0 to stackSize -1)
     */
    public int getFocusPlane() {

        return beadFocusPlane;
    }

    /**
     * Find bead focus plane using an heuristic.
     */
    public void findBeadFocusPlaneWithHeuristic() {

        setStatus("Searching for focus point");

        int mean = Math.round(new Float(DataTricks.mean(getPlane(0))));
        mean = mean * 2;
        int plane = getStackSize() / 3;
        double delta_mean = 0;
        int direction = 1;
        int step = 5;
        int initStep = step;

        while (true) {

            ImageProcessor plane1 = getPlane(plane);
            ImageProcessor plane2 = getPlane(plane + step);

            double mean1 = DataTricks.mean(plane1, mean);
            double mean2 = DataTricks.mean(plane2, mean);

            // System.out.println("mean1 : " + mean1);
            // System.out.println("mean2 : " + mean2);
            delta_mean = (mean2 - mean1) / 5;

            if (direction != signOf(delta_mean)) {
                direction = direction * (-1);
                step = step - 1;

                setProgress(initStep - step, initStep);

            }
            updateView(plane1);
            plane = plane + step * direction;
            if (plane < 0) {

            }

            if (step == 0) {
                System.out.println("it breaks at " + plane + " !!!!");

                if (mean1 > mean2) {
                    setFocusPlane(plane);
                } else {
                    setFocusPlane(beadFocusPlane);
                }

                break;
            }
            setProgress(100 * (initStep - step) / initStep, "Searching focus (heuristic) ...");
            plane1 = null;
            plane2 = null;

        }

        setStatus("Done.");
        setProgress(0);

        return;
    }

    /**
     * Gets the bead frame.
     *
     * @param i the i
     * @return the bead frame
     */
    public BeadFrame getBeadFrame(int i) {
        return getBeadFrameList().get(i);
    }

    /**
     * Rounds a double to an int.
     *
     * @param a the a
     * @return the int
     */
    public static int round(double a) {
        return Math.round(new Float(a));
    }

    /**
     * Gets the image height.
     *
     * @return the image height
     */
    public int getImageHeight() {
        return imageHeight;
    }

    /**
     * Sets the image height.
     *
     * @param imageHeight the new image height
     */
    public void setImageHeight(int imageHeight) {
        this.imageHeight = imageHeight;
    }

    /**
     * Gets the image width.
     *
     * @return the image width
     */
    public int getImageWidth() {
        return imageWidth;
    }

    /**
     * Sets the image width.
     *
     * @param imageWidth the new image width
     */
    public void setImageWidth(int imageWidth) {
        this.imageWidth = imageWidth;
    }

    /**
     * Creates the rectangle.
     *
     * @param w the width
     * @param h the height
     * @param x the x coordinate
     * @param y the y coordinate
     * @return the rectangle
     */
    public Rectangle createRectangle(int w, int h, int x, int y) {
        Rectangle r = new Rectangle(new Point(x, y), new Dimension(w, h));
        return r;

    }

    /**
     * Update view (notify the observers.
     *
     * @param ip the image processing that the view is suggested to display
     * @param name the name of the message sent to the observers
     */
    public void updateView(ImageProcessor ip, String name) {
        rawPreview = ip;
        Message message = new Message(this, name, ip);
        setChanged();
        notifyObservers(message);
    }

    /**
     * Update the view (or suggest the observers to display a ImageProcessor
     * object).
     *
     * @param ip the ip
     */
    public void updateView(ImageProcessor ip) {
        updateView(ip.duplicate(), "beadimage changing raw preview");
    }

    // **********************************
    // Pattern Observer related functions
    // **********************************
    /**
     * Notify model change.
     */
    public void notifyModelChange() {
        setChanged();
        notifyObservers(this);
    }

    /**
     * Notify observers.
     *
     * @param name the name of the message
     * @param strData the string data inside the message (e.g : "Doing
     * something...")
     * @param intData the int data inside the message (e.g : 10 %)
     * @param data the data
     */
    public void notifyObservers(String name, String strData, Integer intData, Object data) {

        setChanged();
        notifyObservers(new Message(this, name, strData, intData, data));
    }

    /**
     * Gets the raw preview (doesn't calculate the preview like it usually do).
     *
     * @return the raw preview
     */
    public ImageProcessor getRawPreview() {
        return preview;
    }

    /**
     * Sets the progress of an ongoing process.
     *
     * @param progress the progress
     * @param status the status
     */
    public void setProgress(final int progress, final String status) {
        this.progress = progress;
        this.status = status;

        sendMessage();

    }

    /**
     * Sets the progress.
     *
     * @param progress the new progress
     */
    public void setProgress(int progress) {
        if (progress == this.progress) {
            return;
        }
        this.progress = progress;
        sendMessage();
    }

    /**
     * Sets the progress.
     *
     * @param progress the progress
     * @param total the total
     */
    public void setProgress(int progress, int total) {
        setProgress(100 * progress / total);
    }

    /**
     * Sets the status.
     *
     * @param status the new status
     */
    public void setStatus(String status) {

        this.status = status;
        if (this.status.equals(status)) {
            return;
        }
        sendMessage();

    }

    /**
     * Send message saying that the processing status changed.
     */
    public void sendMessage() {
        try {
            notifyObservers("processing status changed", status, progress, null);
        } catch (Exception e) {
            System.err.println("Error while notifying observers.");
            e.printStackTrace();
        }
    }

    /**
     * Gets the progress.
     *
     * @return the progress
     */
    public int getProgress() {
        return progress;
    }

    /**
     * Gets the status.
     *
     * @return the status
     */
    public String getStatus() {
        return status;
    }

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

    /**
     * Gets the size of the image in the disk.
     *
     * @return the image disk size
     */
    public long getImageDiskSize() {

        if (imageDiskSize == -1) {
            if (getFileAddress() != null) {
                File file = new File(getFileAddress());
                imageDiskSize = file.length() / 1000 / 1000;
            }
        }
        return imageDiskSize;

    }

    /**
     * Sets the image disk size.
     *
     * @param imageDiskSize the new image disk size
     */
    public void setImageDiskSize(long imageDiskSize) {
        this.imageDiskSize = imageDiskSize;
    }

    /**
     * Ask for deletion.
     */
    public void askForDeletion() {
        isValid = false;
        setChanged();
        notifyObservers(new Message(this, MSG_IMAGE_NOT_OKAY));
    }

    /**
     * Auto focus async.
     */
    public void autoFocusAsync() {
        new Thread() {
            @Override
            public void run() {
                autoFocus();
            }
        }.start();

    }

    /**
     * Clean memory.
     */
    public void cleanMemory() {
        cleanMemory(CLEAN_ALL);
    }

    /**
     * Clean memory.
     *
     * @param level the level
     */
    public void cleanMemory(int level) {
        preview = null;
        rawPreview = null;
        middleImage = null;

        if (level == CLEAN_FRAMES) {
            if (beadFrames != null) {
                beadFrames = null;
            }
        }

        if (level == CLEAN_ALL || level == CLEAN_FOR_DELETION) {

            beadFrames = null;
            beadLocation = null;
            stackList = null;

            preview = null;
            segmentationMask = null;

        }

        if (level == CLEAN_FOR_DELETION) {
            stack = null;
            try {
                if (ipr != null) {
                    ipr.close();
                }
            } catch (Exception e) {

                e.printStackTrace();
            }
            ipr = null;

            stackList = null;
            beadLocation = null;
            beadFrames = null;

        }

        System.gc();

    }

    /**
     * Update bead counts.
     */
    public void updateBeadCounts() {

        if (beadFrames != null) {
            frameNumber = beadFrames.size();
            for (BeadFrame frame : beadFrames) {
                if (frame.isIgnored()) {
                    //ignoredFrameCount++;
                } else if (frame.isValid()) {

                }
            }
        }

    }

    /**
     * Gets the kepts beads count.
     *
     * @return the kepts beads count
     */
    public int getValidBeadCount() {
        return getBeadFrameList().getValidBeadFrameCount();
    }

    /**
     * Gets the paired bead count.
     *
     * @return the paired bead count
     */
    public int getPairedBeadCount() {
        return getBeadFrameList().getValidBeadFrames().getWithAlterEgo().size();
    }

    /**
     * Gets the total bead count.
     *
     * @return the total bead count
     */
    public int getTotalBeadCount() {
        return getBeadFrameList().size();
    }

    /**
     * The is processing.
     */
    boolean isProcessing;

    /**
     * Checks if is processing.
     *
     * @return true, if is processing
     */
    public boolean isProcessing() {
        // System.out.println("isProcessing : " + isProcessing);
        return isProcessing;
    }

    /**
     * Lock for processing.
     */
    public synchronized void lockForProcessing() {
        // System.out.println("Locking for processing");
        isProcessing = true;
    }

    /**
     * Sets the processing finished.
     */
    public synchronized void setProcessingFinished() {
        // System.out.println("Process finished");
        isProcessing = false;
    }

    /**
     * Gets the microscope.
     *
     * @return the microscope
     */
    public Microscope getMicroscope() {

        if (microscope == null) {
            microscope = Microscope.loadMicroscopeFromImage(fileAddress);
        }

        return microscope;
    }

    /**
     * Sets the microscope.
     *
     * @param microscope the new microscope
     */
    public void setMicroscope(Microscope microscope) {
        this.microscope = microscope;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {

        String result = "******\n[Bead Image] " + stack;
        result += "\nfile : " + getImageName();
        result += "\nBead found : " + getBeadLocation().size();
        result += "\nFrame number : " + getBeadFrameList().size();
        result += "\nThreshold : " + getThresholdValue();
        result += "\nFrame size : " + getFrameSize();
        result += "\nFocus : " + getFocusPlane();
        result += "\n************";
        return result;
    }

    /**
     * Gets the counter.
     *
     * @return the counter
     */
    public Counter3D getCounter() {
        return counter3d;
    }

    /**
     * Reset preview.
     */
    public void resetPreview() {
        middleImage = null;
        preview = null;
        System.out.println("reseting bead preview");
        System.gc();
    }

    /**
     * Gets the map.
     *
     * @return the map
     */
    public ImageProcessor getMap() {

        ImageProcessor ip = getMiddleImage().convertToRGB();

        for (BeadFrame frame : getBeadFrameList()) {

            frame.draw(ip, 10);

        }

        return ip;

    }

    /**
     * Gets the beads color.
     *
     * @return the beads color
     */
    public Color getBeadsColor() {
        if (getMicroscope() == null) {
            return Color.black;
        }
        return VisibleColor.wavelengthToColor(getMicroscope().getWaveLength() * 1000);
    }

    /**
     * Gets the icon.
     *
     * @param size the size
     * @return the icon
     */
    public ImageIcon getIcon(int size) {
        ImageProcessor i = new ColorProcessor(size, size);
        i.setColor(getBeadsColor());
        i.fill();
        return new ImageIcon(i.getBufferedImage());

    }

    /**
     * Gets the zone.
     *
     * @param x the x
     * @param y the y
     * @param factor the factor
     * @return the zone
     */
    public Rectangle getZone(int x, int y, int factor) {

        int beadNumber = getBeadFrameList().getWithAlterEgo().size();
        int w = MathUtils.round(Math.sqrt(1.0 * getImageWidth() * getImageHeight() / beadNumber * 8));

        // int w = getImageWidth() / factor;
        int h = w; // getImageHeight() / factor;

        int x0 = x;
        int y0 = y;

        x = x0 * getImageWidth() / 2;
        y = y0 * getImageHeight() / 2;

        x = x - w / 2 * (x0 + 1);
        y = y - h / 2 * (y0 + 1);

        x = x + getImageWidth() / 2;
        y = y + getImageHeight() / 2;

        System.out.println(new Rectangle(x, y, w, h).toString() + String.format(" for %d,%d", x0, y0));

        return new Rectangle(x, y, w, h);

    }

    /**
     * Gets the corner name.
     *
     * @param x the x
     * @param y the y
     * @return the corner name
     */
    private String getCornerName(int x, int y) {

        String[] xString = { "LEFT", "CENTER", "RIGHT" };
        String[] yString = { "TOP", "CENTER", "BOTTOM" };

        return yString[y + 1] + " - " + xString[x + 1];

    }

    /**
     * Gets the montage.
     *
     * @return the montage
     */
    public ImageProcessor getMontage() {

        int beadPerCorner = 4;

        int beadNumber = getBeadFrameList().getWithAlterEgo().size();

        if (beadNumber < 4 * 9 * 2) {
            beadPerCorner = 1;
        }

        if (beadNumber < 18) {
            return getBeadFrameList().getValidBeadFrames().getBeadMontage();
        }

        int enlargementFactor = 3;

        int cornerWidth = MathUtils.round(Math.sqrt(beadPerCorner)) * getBeadFrame(0).getWidth()
                * enlargementFactor;
        int cornerHeight = MathUtils.round(Math.sqrt(beadPerCorner)) * getBeadFrame(0).getHeight()
                * enlargementFactor;
        int mw = cornerWidth * 3;
        int mh = cornerHeight * 3;

        ColorProcessor finalMontage = new ColorProcessor(mw, mh);

        for (int x = -1; x != 2; x++) {
            for (int y = -1; y != 2; y++) {

                int xf = (x + 1) * cornerWidth;
                int yf = (y + 1) * cornerHeight;
                ImageProcessor beadMontage = getBeadFrameList().getWithAlterEgo()
                        .getSampleFromCorner(getZone(x, y, 4), beadPerCorner).getBeadMontage();
                beadMontage = beadMontage.resize(beadMontage.getWidth() * enlargementFactor,
                        beadMontage.getHeight() * enlargementFactor, true);
                finalMontage.copyBits(beadMontage, xf, yf, Blitter.COPY);

                finalMontage.setColor(Color.white.darker().darker());
                finalMontage.setFont(new Font(java.awt.Font.SANS_SERIF, java.awt.Font.CENTER_BASELINE, 10));
                finalMontage.drawString(getCornerName(x, y), xf + 5, yf + 16);

                finalMontage.setColor(Color.yellow.darker());
                finalMontage.drawRect((x + 1) * cornerWidth, (y + 1) * cornerHeight, cornerWidth, cornerHeight);

            }
        }

        return finalMontage;

    }

    /**
     * Gets the distance threshold.
     *
     * @return the distance threshold
     */
    public double getDistanceThreshold() {
        return getMicroscope().getXYTheoreticalResolution() * 2;
    }

    /**
     * The bg mean.
     */
    double bgMean = -1;

    /**
     * The bg std dev.
     */
    double bgStdDev = -1;

    /**
     * The bead max intensity.
     */
    double beadMaxIntensity = -1;

    /**
     * The bead max standard deviation.
     */
    double beadMaxStandardDeviation = -1;

    /**
     * The bead mean b paramter.
     */
    double beadMeanBParamter = -1;

    /**
     * The bead mean b parameter std dev.
     */
    double beadMeanBParameterStdDev = -1;

    /**
     * The offset.
     */
    double offset = 0;

    /**
     * The invalidity reason.
     */
    private String invalidityReason;

    /**
     * Gets the signal mean.
     *
     * @return the signal mean
     */
    public double getSignalMean() {
        return beadMeanBParamter;
    }

    /**
     * Gets the signal standard deviation.
     *
     * @return the signal standard deviation
     */
    public double getSignalStandardDeviation() {
        calculateSignalAndNoise();
        return beadMeanBParameterStdDev;
    }

    /**
     * Gets the signal to noise ratio.
     *
     * @return the signal to noise ratio
     */
    public double getSignalToNoiseRatio() {
        calculateSignalAndNoise();

        return (getSignalMean() - getBackgroundMean()) / getBackgroundStandardDeviation();

    }

    /**
     * Gets the background mean.
     *
     * @return the background mean
     */
    public double getBackgroundMean() {
        calculateSignalAndNoise();
        return bgMean;
    }

    /**
     * Gets the background standard deviation.
     *
     * @return the background standard deviation
     */
    public double getBackgroundStandardDeviation() {
        calculateSignalAndNoise();
        return bgStdDev;
    }

    /**
     * Calculate signal and noise.
     */
    public void calculateSignalAndNoise() {
        if (bgMean == -1) {

            // ImageProcessor beadMask = getSegmentedImage().duplicate();
            // bgMask.invert();
            // middleImage.setMask(beadMask);
            DescriptiveStatistics stats = new DescriptiveStatistics();
            DescriptiveStatistics aStats = new DescriptiveStatistics();
            DescriptiveStatistics bStats = new DescriptiveStatistics();
            for (BeadFrame bead : getBeadFrameList().getOnlyValidBeads()) {
                stats.addValue(bead.getMaximumIntensity());
                aStats.addValue(bead.getFittingParameter(0, 0) - offset);
                bStats.addValue(bead.getFittingParameter(0, 1) - offset);

            }
            beadMaxIntensity = stats.getMean();
            beadMaxStandardDeviation = stats.getStandardDeviation();
            bgMean = aStats.getMean();
            bgStdDev = aStats.getStandardDeviation();
            beadMeanBParamter = bStats.getMean();
            beadMeanBParameterStdDev = bStats.getStandardDeviation();

            /*
             * beadMask.invert(); beadMask.dilate(); beadMask.dilate();
             * beadMask.dilate(); beadMask.dilate();
                
             for (double i = 0; i != 5; i += 0.5) {
             ImageProcessor middleImage = getMiddleImage().duplicate();
             double threshold = getAutoThreshold(i);
             double realMean = middleImage.getStatistics().mean;
             double realStdDev = middleImage.getStatistics().stdDev;
                
             ImageProcessor bgMask = getSegmentationMask(middleImage,
             MathUtils.round(threshold));
             middleImage.setMask(bgMask);
                
                
             dataset.addValue("i", i);
             dataset.addValue("threshold",threshold);
             dataset.addValue("bgMean", value);
             dataset.addValue("bgStandard",gbS)
                
             bgMean = middleImage.getStatistics().mean;
             bgStdDev = middleImage.getStatistics().stdDev;
             System.out
             .println(String
             .format(
             "Signal to noise : \nMean : %.0f,\nBg. Mean : %.0f,\nDev : %.0f",
             beadMeanIntensity, bgMean, bgStdDev));
             new ImagePlus("bgMask", bgMask).show();
             }
             /*
             * middleImage.setMask(beadMask); bgMean =
             * middleImage.getStatistics().mean; bgStdDev =
             * middleImage.getStatistics().stdDev;
                
             */
            System.out.println(
                    String.format("** Old Method **\nMean : %.0f,\nBg. Mean : %.0f,\nDev : %.0f\nBMean : %.0f\n",
                            beadMaxIntensity, bgMean, bgStdDev, beadMeanBParamter));
        }
    }

    /**
     * Gets the deleted beads count.
     *
     * @return the deleted beads count
     */
    public int getDeletedBeadsCount() {
        return getTotalBeadCount() - getValidBeadCount();
    }

    /**
     * Gets the invalidy reason.
     *
     * @return the invalidy reason
     */
    public String getInvalidyReason() {
        if (this.invalidityReason != null) {
            return this.invalidityReason;
        }
        return null;
    }

    /**
     * Sets the invalidity reason.
     *
     * @param reason the new invalidity reason
     */
    public void setInvalidityReason(String reason) {
        if (invalidityReason == null) {
            invalidityReason = reason;
        }
    }

    public int getMaxaximumIntensityOfTheWholeStack() {
        // TODO Auto-generated method stub

        if (maxIntensityOfWholeStack == NOT_SET) {

            DescriptiveStatistics stats = new DescriptiveStatistics();
            for (int i = 0; i != stack.getSize(); i++) {

                stats.addValue(stack.getProcessor(i + 1).getStatistics().max);

            }
            maxIntensityOfWholeStack = stats.getMax();

        }

        return MathUtils.round(maxIntensityOfWholeStack);
    }

    public void setIgnoredFrameNumber(int size) {
        ignoredFrameNumber = size;
    }

}