package gdsc.utils; /*----------------------------------------------------------------------------- * GDSC Plugins for ImageJ * * Copyright (C) 2011 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * This program 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 2 of the License, or * (at your option) any later version. *---------------------------------------------------------------------------*/ import ij.IJ; import ij.ImagePlus; import ij.gui.Roi; import ij.plugin.filter.PlugInFilter; import ij.process.FloatProcessor; import ij.process.ImageProcessor; import java.awt.Rectangle; import java.lang.reflect.Method; import gdsc.UsageTracker; /** * Fits a circular 2D Gaussian. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class GaussianFit implements PlugInFilter { private static boolean fittingEnabled; private static String errorMessage; private static Throwable exception; // Try and perform a Gaussian fit static { fittingEnabled = false; errorMessage = ""; exception = null; try { // Get a class in this package to find the package class loader GaussianPlugin pluginClass = new GaussianPlugin(); Class c = Class.forName("gdsc.smlm.ij.plugins.GaussianFit", true, pluginClass.getClass().getClassLoader()); // ... it exists on the classpath final int size = 99; final float mu = size / 2; final float s0 = 3; final float N = 10; // Create a 2D Gaussian curve final float[] data = new float[size * size]; for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) data[y * size + x] = (float) (N * (Math.exp(-0.5 * ((mu - x) * (mu - x) + 0.5 * (mu - y) * (mu - y)) / (s0 * s0)))); // Try a fit. double[] fit = null; //gdsc.smlm.ij.plugins.GaussianFit gf = new gdsc.smlm.ij.plugins.GaussianFit(); //fit = gf.fit(data, size, size); // Use reflection to allow building without the SMLM plugins on the classpath final Method m = c.getDeclaredMethod("fit", new Class[] { float[].class, int.class, int.class }); fit = (double[]) m.invoke(c.newInstance(), data, size, size); if (fit != null) { fittingEnabled = true; } } catch (ExceptionInInitializerError e) { exception = e; errorMessage = "Failed to initialize class: " + e.getMessage(); } catch (LinkageError e) { exception = e; errorMessage = "Failed to link class: " + e.getMessage(); } catch (ClassNotFoundException ex) { exception = ex; errorMessage = "Failed to find class: " + ex.getMessage(); //StringWriter sw = new StringWriter(); //PrintWriter pw = new PrintWriter(sw); //ex.printStackTrace(pw); //IJ.log(sw.toString()); } catch (Exception ex) { exception = ex; errorMessage = ex.getMessage(); } if (!fittingEnabled) { System.out.println(errorMessage); } } private void init(float[] data, int width, int height) { if (data == null || data.length != width * height || width < 1 || height < 1) throw new IllegalArgumentException("Data must be same length as width * height"); } /** * @param data * @param width * @param height * @return The fitted Gaussian parameters (Background, Amplitude, x0, x1, s) */ public double[] fit(float[] data, int width, int height) { init(data, width, height); //gdsc.smlm.ij.plugins.GaussianFit gf = new gdsc.smlm.ij.plugins.GaussianFit(); //return gf.fit(data, width, height); try { // Use reflection to allow building without the SMLM plugins on the classpath Class c = Class.forName("gdsc.smlm.ij.plugins.GaussianFit", true, this.getClass().getClassLoader()); Method m = c.getDeclaredMethod("fit", new Class[] { float[].class, int.class, int.class }); return (double[]) m.invoke(c.newInstance(), data, width, height); } catch (Exception e) { return null; } } /** * @return true if fitting is possible */ public boolean isFittingEnabled() { return fittingEnabled; } /** * @return the errorMessage if fitting is not possible */ public String getErrorMessage() { return errorMessage; } /** * @return the exception if fitting is not possible */ public Throwable getException() { return exception; } /* * (non-Javadoc) * * @see ij.plugin.filter.PlugInFilter#setup(java.lang.String, ij.ImagePlus) */ public int setup(String arg, ImagePlus imp) { UsageTracker.recordPlugin(this.getClass(), arg); Roi roi = imp.getRoi(); if (roi == null || !roi.isArea()) { IJ.error("Require a region ROI"); return DONE; } return DOES_ALL | NO_CHANGES; } /* * (non-Javadoc) * * @see ij.plugin.filter.PlugInFilter#run(ij.process.ImageProcessor) */ public void run(ImageProcessor ip) { FloatProcessor fp = ip.toFloat(0, null); Rectangle bounds = fp.getRoi(); fp = (FloatProcessor) fp.crop(); double[] params = fit((float[]) fp.getPixels(), fp.getWidth(), fp.getHeight()); if (params != null) IJ.log(String.format("f(x,y) = %g + %g exp -( ( (x-%g)^2 / (2 * %g^2) + (y-%g)^2 ) / (2 * %g^2) )", params[0], params[1], bounds.x + params[2], params[4], bounds.y + params[3], params[5])); } }