From 2143dc8d33946fa25c945f8d54e8988da7822254 Mon Sep 17 00:00:00 2001 From: Marcel Stefko Date: Tue, 30 May 2017 17:05:51 +0200 Subject: [PATCH] Refactored FIJI plugin organization and added Javadocs --- src/beanshell/ConsoleFrame.java | 21 +- src/commandline/CommandLineInterface.java | 32 +- src/ijplugin/App.java | 406 ++++++++------- src/ijplugin/Console.java | 45 ++ src/ijplugin/{Main_Frame.form => GUI.form} | 0 src/ijplugin/{Main_Frame.java => GUI.java} | 50 +- src/ijplugin/InitSettingsFrame.java | 52 +- src/plugins.config | 4 +- src/simulator/ImageGenerator.java | 138 ++--- src/simulator/Simulator.java | 493 +++++++++--------- src/simulator/generators/realtime/Device.java | 450 ++++++++-------- .../generators/realtime/Fluorophore.java | 104 ++-- .../realtime/FluorophoreGenerator.java | 265 +++++----- .../realtime/FluorophoreProperties.java | 49 +- src/simulator/generators/realtime/RNG.java | 112 ++-- .../generators/realtime/STORMsim.java | 382 +++++++------- 16 files changed, 1379 insertions(+), 1224 deletions(-) create mode 100644 src/ijplugin/Console.java rename src/ijplugin/{Main_Frame.form => GUI.form} (100%) rename src/ijplugin/{Main_Frame.java => GUI.java} (97%) diff --git a/src/beanshell/ConsoleFrame.java b/src/beanshell/ConsoleFrame.java index 121adaf..592917e 100644 --- a/src/beanshell/ConsoleFrame.java +++ b/src/beanshell/ConsoleFrame.java @@ -51,6 +51,10 @@ public ConsoleFrame() { interpreter.setShowResults(true); } + /** + * + * @return BeanShell interpreter associated with this ConsoleFrame + */ public Interpreter getInterpreter() { return interpreter; } @@ -109,23 +113,6 @@ private void console_panelComponentResized(java.awt.event.ComponentEvent evt) {/ this.console.setSize(this.console_panel.getWidth(), this.console_panel.getHeight()); }//GEN-LAST:event_console_panelComponentResized - /** - * Accessed from FIJI. Initializes console and prints welcome text. - * @param args the command line arguments - */ - public static void main(String args[]) { - final ConsoleFrame console = new ConsoleFrame(); - java.awt.EventQueue.invokeLater(new Runnable() { - public void run() { - - console.setVisible(true); - } - }); - CommandLineInterface.printWelcomeText(console.getInterpreter().getOut()); - new Thread(console.getInterpreter()).start(); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel console_panel; // End of variables declaration//GEN-END:variables diff --git a/src/commandline/CommandLineInterface.java b/src/commandline/CommandLineInterface.java index cc26d9d..77d5b86 100644 --- a/src/commandline/CommandLineInterface.java +++ b/src/commandline/CommandLineInterface.java @@ -40,23 +40,32 @@ import org.apache.commons.cli.ParseException; /** - * - * @author stefko + * Main class of the project, launches the BeanShell script interface. + * @author Marcel Stefko */ public final class CommandLineInterface { private static final Options options = constructOptions(); private static final URL urlToWelcomeText = CommandLineInterface.class.getResource("/beanshell/welcome_text.txt"); + /** + * + * @return all understood options for ALICA execution + */ public static Options constructOptions() { final Options options = new Options(); options.addOption("i", "interpreter", false, "run BeanShell interpreter inside current terminal window"); - options.addOption("s", "script", true, "execute BeanShell script"); + options.addOption("s", "script", true, "execute BeanShell script (can be combined with -i)"); options.addOption("h", "help", false, "show this help"); return options; } + /** + * Shows help, launches the interpreter and executes scripts according to input args. + * @param args input arguments + */ public static void main(String args[]) { + // parse input arguments CommandLineParser parser = new DefaultParser(); CommandLine line = null; try { @@ -66,14 +75,20 @@ public static void main(String args[]) { System.err.println("Use -help for usage."); System.exit(1); } + + // decide how do we make the interpreter available based on options Interpreter interpreter = null; + // show help and exit if (line.hasOption("help")) { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("java -jar ", options, true); System.exit(0); + // launch interpreter inside current terminal } else if (line.hasOption("interpreter")) { + // assign in, out and err streams to the interpreter interpreter = new Interpreter(new InputStreamReader(System.in), System.out, System.err, true); interpreter.setShowResults(true); + // if a script was given, execute it before giving access to user if (line.hasOption("script")) { try { interpreter.source(line.getOptionValue("script")); @@ -83,7 +98,9 @@ public static void main(String args[]) { Logger.getLogger(ConsoleFrame.class.getName()).log(Level.SEVERE, "EvalError while executing shell script.", ex); } } + // give access to user new Thread( interpreter ).start(); + // only execute script and exit } else if (line.hasOption("script")) { interpreter = new Interpreter(); try { @@ -96,22 +113,30 @@ public static void main(String args[]) { Logger.getLogger(ConsoleFrame.class.getName()).log(Level.SEVERE, "EvalError while executing shell script.", ex); System.exit(1); } + // if System.console() returns null, it means we were launched by + // double-clicking the .jar, so launch own ConsoleFrame } else if (System.console() == null) { ConsoleFrame cframe = new ConsoleFrame(); cframe.setVisible(true); System.setOut(cframe.getInterpreter().getOut()); System.setErr(cframe.getInterpreter().getErr()); new Thread(cframe.getInterpreter()).start(); + // otherwise, show help } else { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("java -jar ", options, true); System.exit(0); } + if (interpreter != null) { printWelcomeText(interpreter.getOut()); } } + /** + * Reads the welcome_text file and prints it to a PrintStream. + * @param out stream to print to + */ public static void printWelcomeText(PrintStream out) { try { InputStream inputStream = urlToWelcomeText.openStream(); @@ -121,7 +146,6 @@ public static void printWelcomeText(PrintStream out) { while ((length = inputStream.read(buffer)) != -1) { result.write(buffer, 0, length); } - // StandardCharsets.UTF_8.name() > JDK 7 String message = result.toString("UTF-8"); out.print(message+"\n"); } catch (IOException ex) { diff --git a/src/ijplugin/App.java b/src/ijplugin/App.java index 12cf2c5..e9bce66 100644 --- a/src/ijplugin/App.java +++ b/src/ijplugin/App.java @@ -17,203 +17,209 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package ijplugin; - -import simulator.Simulator; -import simulator.ImageGenerator; -import ch.epfl.leb.alica.Analyzer; -import ch.epfl.leb.alica.Controller; -import ij.ImagePlus; -import ij.gui.GenericDialog; -import ij.gui.Plot; -import ij.process.ImageProcessor; -import java.awt.Color; -import java.awt.Font; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Backend for the FIJI plugin GUI - * @author Marcel Stefko - */ -public class App extends Simulator { - private final ImagePlus imp; - private final Plot plot; - private Worker worker; - - /** - * Initialize simulation, generate 2 images for correct - * stack display, then display the stack - * and plot of controller state. - */ - public App() { - super(); - generator.getNextImage(); - generator.getNextImage(); - imp = new ImagePlus("Sim window", generator.getStack()); - imp.show(); - plot = new Plot("Controller history", "Frame id.", "Value"); - plot.show(); - - } - - public App(Analyzer analyzer, - ImageGenerator generator, Controller controller) { - super(analyzer, generator, controller); - controller.setSetpoint(0.0); - generator.getNextImage(); - generator.getNextImage(); - imp = new ImagePlus("Sim window", generator.getStack()); - imp.show(); - plot = new Plot("Controller history", "Frame id.", "Value"); - plot.show(); - } - - /** - * Start continuously generating new images until stopped. - */ - public void startSimulating() { - worker = new Worker(this, generator, controller, analyzer, imp); - worker.start(); - } - - /** - * Stop generating new images. - */ - public void stopSimulating() { - worker.stop = true; - try { - worker.join(); - } catch (InterruptedException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } - } - - /** - * Set new setpoint for the controller - * @param value new setpoint value - */ - public void setSetpoint(double value) { - controller.setSetpoint(value); - } - - /** - * Return the controller plot handle - * @return plot with controller history - */ - public Plot getPlot() { - return plot; - } - - - -} - -class Worker extends Thread { - public boolean stop; - private final App app; - private final ImageGenerator generator; - private final Controller controller; - private final Analyzer analyzer; - private final ImagePlus imp; - - public Worker(App app, ImageGenerator generator, Controller controller, Analyzer active_analyzer, ImagePlus imp) { - this.app = app; - this.generator = generator; - this.controller = controller; - this.analyzer = active_analyzer; - this.imp = imp; - stop = false; - } - - @Override - public void run() { - ImageProcessor ip; - while (!stop) { - app.incrementCounter(); - ip = generator.getNextImage(); - long time_start, time_end; - time_start = System.nanoTime(); - analyzer.processImage(ip.getPixelsCopy(), ip.getWidth(), ip.getHeight(), generator.getPixelSizeUm(), time_start); - time_end = System.nanoTime(); - System.out.format("%s: Image analyzed in %d microseconds.\n", analyzer.getName(), (time_end - time_start)/1000); - - if (app.getImageCount() % 20 == 0) { - //System.out.println(image_count); - controller.nextValue(analyzer.getBatchOutput()); - } - generator.setControlSignal(controller.getCurrentOutput()); - - imp.setSlice(imp.getNSlices()); - imp.updateAndRepaintWindow(); - try { - sleep(20); - } catch (InterruptedException ex) { - Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex); - } - if (app.getImageCount()%10==0) { - updatePlot(); - } - } - } - - public void updatePlot() { - Plot plot = app.getPlot(); - - int count = app.getImageCount()-1; - double[] x = new double[count]; - double[] real = new double[count]; - double[] laser = new double[count]; - double[] spot = new double[count]; - double[] set_point = new double[count]; - for (int i=1; i<=count; i++) { - x[i-1] = (double) i; - real[i-1] = generator.getTrueSignal(i); - /* - laser[i-1] = controller.getOutputHistory(i); - spot[i-1] = controller.getAnalyzer().getErrorSignal(i); - set_point[i-1] = controller.getSetpointHistory(i); - */ - } - plot.setColor(Color.black); - plot.addPoints(x, real, Plot.LINE); - plot.setFont(new Font("Helvetica", Font.PLAIN, 14)); - plot.addLabel(0.02,0.1,"True signal"); - plot.setColor(Color.red); - plot.addPoints(x, spot, Plot.LINE); - plot.addLabel(0.02,0.2,"Measured signal"); - plot.setColor(Color.blue); - plot.addPoints(x, set_point, Plot.LINE); - plot.addLabel(0.02,0.3,"Setpoint"); - plot.setColor(Color.orange); - plot.addPoints(x, laser, Plot.LINE); - plot.addLabel(0.02,0.4,"Laser power"); - plot.setLimits(getMin(x), getMax(x), getMin(real), getMax(real)); // hack to get a correct rescale - plot.draw(); - - } - - private double getMin(double[] arr) { - if (arr.length == 0) { - return Double.NaN; - } - double min = arr[0]; - for (double d: arr) { - min = min>d ? d : min; - } - return min; - } - - private double getMax(double[] arr) { - if (arr.length == 0) { - return Double.NaN; - } - double max = arr[0]; - for (double d: arr) { - max = maxd ? d : min; + } + return min; + } + + private double getMax(double[] arr) { + if (arr.length == 0) { + return Double.NaN; + } + double max = arr[0]; + for (double d: arr) { + max = max. + */ +package ijplugin; + +import beanshell.ConsoleFrame; +import commandline.CommandLineInterface; + +/** + * Launched by FIJI + * @author stefko + */ +public class Console { + + /** + * Accessed from FIJI. Initializes console and prints welcome text. + */ + public Console() { + final ConsoleFrame console = new ConsoleFrame(); + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + + console.setVisible(true); + } + }); + CommandLineInterface.printWelcomeText(console.getInterpreter().getOut()); + new Thread(console.getInterpreter()).start(); + } +} diff --git a/src/ijplugin/Main_Frame.form b/src/ijplugin/GUI.form similarity index 100% rename from src/ijplugin/Main_Frame.form rename to src/ijplugin/GUI.form diff --git a/src/ijplugin/Main_Frame.java b/src/ijplugin/GUI.java similarity index 97% rename from src/ijplugin/Main_Frame.java rename to src/ijplugin/GUI.java index 653c079..2d52b28 100644 --- a/src/ijplugin/Main_Frame.java +++ b/src/ijplugin/GUI.java @@ -1,22 +1,22 @@ -/* - * Copyright (C) 2017 Laboratory of Experimental Biophysics - * Ecole Polytechnique Federale de Lausanne - * - * Author: Marcel Stefko - * - * 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ +/* + * Copyright (C) 2017 Laboratory of Experimental Biophysics + * Ecole Polytechnique Federale de Lausanne + * + * Author: Marcel Stefko + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ package ijplugin; import simulator.generators.realtime.STORMsim; @@ -33,13 +33,13 @@ * Main FIJI plugin frame. * @author Marcel Stefko */ -public class Main_Frame extends PlugInFrame { +public class GUI extends PlugInFrame { App app; /** * Creates new form MainFrame * @param title title of the window */ - public Main_Frame(String title) { + public GUI(String title) { super(title); initComponents(); } @@ -47,7 +47,7 @@ public Main_Frame(String title) { /** * Initialize the new frame */ - public Main_Frame() { + public GUI() { super("STORMsim"); setSize(200, 200); initComponents(); @@ -67,6 +67,10 @@ public void run(String arg) { } + /** + * Set the App which this GUI should control + * @param app + */ public void setApp(App app) { this.app = app; } @@ -222,7 +226,7 @@ private void saveCsvButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIR public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { - new Main_Frame("main").setVisible(true); + new GUI("main").setVisible(true); } }); } diff --git a/src/ijplugin/InitSettingsFrame.java b/src/ijplugin/InitSettingsFrame.java index 9db2ec8..681a584 100644 --- a/src/ijplugin/InitSettingsFrame.java +++ b/src/ijplugin/InitSettingsFrame.java @@ -1,22 +1,22 @@ -/* - * Copyright (C) 2017 Laboratory of Experimental Biophysics - * Ecole Polytechnique Federale de Lausanne - * - * Author: Marcel Stefko - * - * 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ +/* + * Copyright (C) 2017 Laboratory of Experimental Biophysics + * Ecole Polytechnique Federale de Lausanne + * + * Author: Marcel Stefko + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ package ijplugin; import simulator.generators.realtime.Camera; @@ -46,18 +46,24 @@ import javax.swing.filechooser.FileNameExtensionFilter; /** - * - * @author stefko + * Outdated frame for basic setup of simulation + * @author Marcel Stefko */ public class InitSettingsFrame extends java.awt.Dialog { File emitterCsvFile; File backgroundTifFile; - Main_Frame main; + GUI main; private final AnalyzerFactory analyzer_factory; private final ControllerFactory controller_factory; - public InitSettingsFrame(java.awt.Frame parent, boolean modal, Main_Frame main) { + /** + * Assemble the frame and display it + * @param parent + * @param modal should the window be persistent + * @param main GUI to notify + */ + public InitSettingsFrame(java.awt.Frame parent, boolean modal, GUI main) { super(parent, modal); this.main = main; this.analyzer_factory = new AnalyzerFactory(); diff --git a/src/plugins.config b/src/plugins.config index 4951343..2ee0d5a 100644 --- a/src/plugins.config +++ b/src/plugins.config @@ -5,5 +5,5 @@ # If something like ("") is appended to the class name, the setup() method # will get that as arg parameter; otherwise arg is simply the empty string. -Plugins>SASS, "GUI", ijplugin.Main_Frame -Plugins>SASS, "Console", beanshell.ConsoleFrame \ No newline at end of file +Plugins>SASS, "GUI", ijplugin.GUI +Plugins>SASS, "Console", ijplugin.Console \ No newline at end of file diff --git a/src/simulator/ImageGenerator.java b/src/simulator/ImageGenerator.java index ccb1fcc..0ea267d 100644 --- a/src/simulator/ImageGenerator.java +++ b/src/simulator/ImageGenerator.java @@ -17,70 +17,74 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator; - -import ij.ImageStack; -import ij.process.ImageProcessor; -import java.io.File; -import java.util.HashMap; - -/** - * Interface through which AlgorithmTester generates new images to be - * analyzed by EvaluationAlgorithms. - * @author Marcel Stefko - */ -public interface ImageGenerator { - - /** - * Generates a new image and adds it to the internal stack. - * @return newly generated image - */ - public ImageProcessor getNextImage(); - - /** - * Sets control signal of the generator (e.g. laser power). This should be - * used by the controller. - * @param value new value of the control signal - */ - public void setControlSignal(double value); - - /** - * Returns currently set control signal of the generator (e.g. laser power - * settings). - * @return control signal value - */ - public double getControlSignal(); - - /** - * Returns the actual value of signal (if applicable) for given image. - * @param image_no 1-based image number in history - * @return value of signal (e.g. no. of active emitters) - */ - public double getTrueSignal(int image_no); - - /** - * Sets custom parameters of the generator. - * @param map map of custom parameters - */ - public void setCustomParameters(HashMap map); - - /** - * Returns custom parameters of the generator. - * @return map of custom parameters - */ - public HashMap getCustomParameters(); - - /** - * Saves .tif stack to selected file. - * @param selectedFile file to save to - */ - public void saveStack(File selectedFile); - - /** - * Returns internal stack with all generated images. - * @return internal stack - */ - public ImageStack getStack(); - - public double getPixelSizeUm(); -} +package simulator; + +import ij.ImageStack; +import ij.process.ImageProcessor; +import java.io.File; +import java.util.HashMap; + +/** + * Interface through which AlgorithmTester generates new images to be + * analyzed by EvaluationAlgorithms. + * @author Marcel Stefko + */ +public interface ImageGenerator { + + /** + * Generates a new image and adds it to the internal stack. + * @return newly generated image + */ + public ImageProcessor getNextImage(); + + /** + * Sets control signal of the generator (e.g. laser power). This should be + * used by the controller. + * @param value new value of the control signal + */ + public void setControlSignal(double value); + + /** + * Returns currently set control signal of the generator (e.g. laser power + * settings). + * @return control signal value + */ + public double getControlSignal(); + + /** + * Returns the actual value of signal (if applicable) for given image. + * @param image_no 1-based image number in history + * @return value of signal (e.g. no. of active emitters) + */ + public double getTrueSignal(int image_no); + + /** + * Sets custom parameters of the generator. + * @param map map of custom parameters + */ + public void setCustomParameters(HashMap map); + + /** + * Returns custom parameters of the generator. + * @return map of custom parameters + */ + public HashMap getCustomParameters(); + + /** + * Saves .tif stack to selected file. + * @param selectedFile file to save to + */ + public void saveStack(File selectedFile); + + /** + * Returns internal stack with all generated images. + * @return internal stack + */ + public ImageStack getStack(); + + /** + * + * @return length of one pixel side in micrometers + */ + public double getPixelSizeUm(); +} diff --git a/src/simulator/Simulator.java b/src/simulator/Simulator.java index fad7f55..df05ba1 100644 --- a/src/simulator/Simulator.java +++ b/src/simulator/Simulator.java @@ -17,239 +17,260 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator; - -import ch.epfl.leb.alica.Analyzer; -import ch.epfl.leb.alica.Controller; -import simulator.generators.realtime.STORMsim; -import ch.epfl.leb.alica.analyzers.autolase.AutoLase; -import ch.epfl.leb.alica.analyzers.quickpalm.QuickPalm; -import ch.epfl.leb.alica.analyzers.spotcounter.SpotCounter; -import ch.epfl.leb.alica.controllers.manual.ManualController; -import ij.ImageStack; -import ij.process.ImageProcessor; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileNameExtensionFilter; -import org.json.JSONException; -import org.json.JSONObject; -import simulator.generators.realtime.RNG; - -/** - * Carries out the actual simulation. - * @author Marcel Stefko - */ -public class Simulator { - - - protected final Analyzer analyzer; - - /** - * Generator which is the source of images to be analyzed. - */ - protected final ImageGenerator generator; - - /** - * Takes the output of a single analyzer, processes it, and outputs a - * signal to the generator, for feedback loop control. - */ - protected final Controller controller; - - /** - * Number of already-generated images. - */ - protected int image_count; - - protected HashMap history = new HashMap(); - - /** - * Initializes all analyzers, the generator and controller. - */ - public Simulator() { - // Real time generator - generator = new STORMsim(null); - analyzer = new SpotCounter(100, 5, true); - // Set up controller - //controller = new SimpleController(); - controller = new ManualController(30, 1); - controller.setSetpoint(1.0); - } - - public Simulator(Analyzer analyzer, - ImageGenerator generator, Controller controller) { - this.analyzer = analyzer; - this.generator = generator; - this.controller = controller; - } - - /** - * Main function which executes the testing procedure. - * - * @param args the command line arguments - */ - public static void main(String[] args) { - RNG.setSeed(1); - Simulator tester = new Simulator(); - tester.execute(1000,100,"C:\\Users\\stefko\\Documents\\stormsim_log.csv","C:\\Users\\stefko\\Documents\\stormsim_tif.tif"); - System.exit(0); - } - - /** - * Define the testing procedure in this method. - */ - public ImageStack execute() { - JFileChooser fc = new JFileChooser(); - int returnVal; - - //* - // File chooser dialog for saving output csv - fc.setDialogType(JFileChooser.SAVE_DIALOG); - //set a default filename - fc.setSelectedFile(new File("tester_output.csv")); - //Set an extension filter - fc.setFileFilter(new FileNameExtensionFilter("CSV file","csv")); - returnVal = fc.showSaveDialog(null); - if (returnVal != JFileChooser.APPROVE_OPTION) { - return null; - } - File csv_output = fc.getSelectedFile(); - - ImageProcessor ip; - for (image_count = 0; image_count < 25; image_count++) { - ip = generator.getNextImage(); - analyzer.processImage(ip.getPixelsCopy(),ip.getWidth(), ip.getHeight(), 0.100, 10); - //System.out.println(image_count); - if (image_count % 10 == 0) { - controller.nextValue(analyzer.getBatchOutput()); - generator.setControlSignal(controller.getCurrentOutput()); - } - } - - fc.setFileFilter(new FileNameExtensionFilter("TIF file","tif")); - fc.setSelectedFile(new File("gen_stack.tif")); - fc.setDialogType(JFileChooser.SAVE_DIALOG); - returnVal = fc.showSaveDialog(null); - if (returnVal != JFileChooser.APPROVE_OPTION) { - return null; - } - generator.saveStack(fc.getSelectedFile()); - - - //*/ - - // Save analysis results to a csv file. - saveToCsv(csv_output); - return generator.getStack(); - } - - public ImageStack execute(int no_of_images, int controller_refresh_rate, String csv_save_path, String tiff_save_path) { - if (no_of_images < 1 || controller_refresh_rate < 1) { - throw new IllegalArgumentException("Wrong simulation parameters!"); - } - - ImageProcessor ip; - for (image_count = 1; image_count <= no_of_images; image_count++) { - JSONObject history_entry = new JSONObject(); - - ip = generator.getNextImage(); - analyzer.processImage(ip.getPixelsCopy(),ip.getWidth(), ip.getHeight(), 0.100, 10); - //System.out.println(image_count); - if (image_count % controller_refresh_rate == 0) { - double analyzer_batch_output = analyzer.getBatchOutput(); - controller.nextValue(analyzer_batch_output); - generator.setControlSignal(controller.getCurrentOutput()); - } - try { - history_entry.put("true-signal",generator.getTrueSignal(image_count)); - history_entry.put("analyzer-output", analyzer.getIntermittentOutput()); - history_entry.put("controller-output", controller.getCurrentOutput()); - history_entry.put("controller-setpoint", controller.getSetpoint()); - } catch (JSONException ex) { - Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error in storing JSON values.", ex); - } - - history.put(image_count, history_entry); - } - image_count -= 1; // to accurately represent image count - - if (csv_save_path != null) { - File csv_file = new File(csv_save_path); - saveToCsv(csv_file); - } - - if (tiff_save_path != null) { - File tiff_file = new File(tiff_save_path); - generator.saveStack(tiff_file); - } - - return generator.getStack(); - - } - /** - * Saves the data for generator, analyzer and controller for each frame into a .csv file - * @param file destination csv file - */ - public void saveToCsv(File file) {// Open the file for writing - PrintWriter writer; - try { - writer = new PrintWriter(file.getAbsolutePath()); - } catch (FileNotFoundException ex) { - Logger.getLogger(Simulator.class.getName()).log(Level.SEVERE, null, ex); - return; - } - - HashMap output_map; - // Print header: Column description - writer.println("#Columns:"); - String column_names = "frame-id,true-signal,analyzer-output,controller-output,controller-setpoint"; - writer.println(column_names); - - // Print data - one line for each frame - for (int i=1; i<=image_count; i++) { - JSONObject e = history.get(i); - String s; - try { - s = String.format("%d,%8.4e,%8.4e,%8.4e,%8.4e", - i,e.get("true-signal"),e.get("analyzer-output"),e.get("controller-output"),e.get("controller-setpoint")); - } catch (JSONException ex) { - s = String.format("%d",i); - Logger.getLogger(Simulator.class.getName()).log(Level.FINER, null, ex); - } catch (NullPointerException ex) { - s = String.format("%d",i); - Logger.getLogger(Simulator.class.getName()).log(Level.FINER, null, ex); - } - writer.println(s); - } - - writer.close(); - - - } - - public void saveStack(File tiff_file) { - generator.saveStack(tiff_file); - } - - /** - * Increments image counter in case an image was generated outside of - * this class. - */ - public void incrementCounter() { - image_count++; - } - - /** - * Returns the number of generated images since simulation start. - * @return number of generated images - */ - public int getImageCount() { - return image_count; - } -} +package simulator; + +import ch.epfl.leb.alica.Analyzer; +import ch.epfl.leb.alica.Controller; +import simulator.generators.realtime.STORMsim; +import ch.epfl.leb.alica.analyzers.autolase.AutoLase; +import ch.epfl.leb.alica.analyzers.quickpalm.QuickPalm; +import ch.epfl.leb.alica.analyzers.spotcounter.SpotCounter; +import ch.epfl.leb.alica.controllers.manual.ManualController; +import ij.ImageStack; +import ij.process.ImageProcessor; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.json.JSONException; +import org.json.JSONObject; +import simulator.generators.realtime.RNG; + +/** + * Carries out the actual simulation. + * @author Marcel Stefko + */ +public class Simulator { + + /** + * Analyzer which analyzes generated images + */ + protected final Analyzer analyzer; + + /** + * Generator which is the source of images to be analyzed. + */ + protected final ImageGenerator generator; + + /** + * Takes the output of a single analyzer, processes it, and outputs a + * signal to the generator, for feedback loop control. + */ + protected final Controller controller; + + /** + * Number of already-generated images. + */ + protected int image_count; + + protected HashMap history = new HashMap(); + + /** + * Initializes all analyzers, the generator and controller. + */ + public Simulator() { + // Real time generator + generator = new STORMsim(null); + analyzer = new SpotCounter(100, 5, true); + // Set up controller + //controller = new SimpleController(); + controller = new ManualController(30, 1); + controller.setSetpoint(1.0); + } + + /** + * Initialize simulator from components + * @param analyzer + * @param generator + * @param controller + */ + public Simulator(Analyzer analyzer, + ImageGenerator generator, Controller controller) { + this.analyzer = analyzer; + this.generator = generator; + this.controller = controller; + } + + /** + * Main function which executes the testing procedure. + * + * @param args the command line arguments + */ + public static void main(String[] args) { + RNG.setSeed(1); + Simulator tester = new Simulator(); + tester.execute(1000,100,"C:\\Users\\stefko\\Documents\\stormsim_log.csv","C:\\Users\\stefko\\Documents\\stormsim_tif.tif"); + System.exit(0); + } + + /** + * Define the testing procedure in this method. + * @return generated ImageStack + */ + public ImageStack execute() { + JFileChooser fc = new JFileChooser(); + int returnVal; + + //* + // File chooser dialog for saving output csv + fc.setDialogType(JFileChooser.SAVE_DIALOG); + //set a default filename + fc.setSelectedFile(new File("tester_output.csv")); + //Set an extension filter + fc.setFileFilter(new FileNameExtensionFilter("CSV file","csv")); + returnVal = fc.showSaveDialog(null); + if (returnVal != JFileChooser.APPROVE_OPTION) { + return null; + } + File csv_output = fc.getSelectedFile(); + + ImageProcessor ip; + for (image_count = 0; image_count < 25; image_count++) { + ip = generator.getNextImage(); + analyzer.processImage(ip.getPixelsCopy(),ip.getWidth(), ip.getHeight(), 0.100, 10); + //System.out.println(image_count); + if (image_count % 10 == 0) { + controller.nextValue(analyzer.getBatchOutput()); + generator.setControlSignal(controller.getCurrentOutput()); + } + } + + fc.setFileFilter(new FileNameExtensionFilter("TIF file","tif")); + fc.setSelectedFile(new File("gen_stack.tif")); + fc.setDialogType(JFileChooser.SAVE_DIALOG); + returnVal = fc.showSaveDialog(null); + if (returnVal != JFileChooser.APPROVE_OPTION) { + return null; + } + generator.saveStack(fc.getSelectedFile()); + + + //*/ + + // Save analysis results to a csv file. + saveToCsv(csv_output); + return generator.getStack(); + } + + /** + * An example simulation + * @param no_of_images + * @param controller_refresh_rate + * @param csv_save_path + * @param tiff_save_path + * @return + */ + public ImageStack execute(int no_of_images, int controller_refresh_rate, String csv_save_path, String tiff_save_path) { + if (no_of_images < 1 || controller_refresh_rate < 1) { + throw new IllegalArgumentException("Wrong simulation parameters!"); + } + + ImageProcessor ip; + for (image_count = 1; image_count <= no_of_images; image_count++) { + JSONObject history_entry = new JSONObject(); + + ip = generator.getNextImage(); + analyzer.processImage(ip.getPixelsCopy(),ip.getWidth(), ip.getHeight(), 0.100, 10); + //System.out.println(image_count); + if (image_count % controller_refresh_rate == 0) { + double analyzer_batch_output = analyzer.getBatchOutput(); + controller.nextValue(analyzer_batch_output); + generator.setControlSignal(controller.getCurrentOutput()); + } + try { + history_entry.put("true-signal",generator.getTrueSignal(image_count)); + history_entry.put("analyzer-output", analyzer.getIntermittentOutput()); + history_entry.put("controller-output", controller.getCurrentOutput()); + history_entry.put("controller-setpoint", controller.getSetpoint()); + } catch (JSONException ex) { + Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error in storing JSON values.", ex); + } + + history.put(image_count, history_entry); + } + image_count -= 1; // to accurately represent image count + + if (csv_save_path != null) { + File csv_file = new File(csv_save_path); + saveToCsv(csv_file); + } + + if (tiff_save_path != null) { + File tiff_file = new File(tiff_save_path); + generator.saveStack(tiff_file); + } + + return generator.getStack(); + + } + /** + * Saves the data for generator, analyzer and controller for each frame into a .csv file + * @param file destination csv file + */ + public void saveToCsv(File file) {// Open the file for writing + PrintWriter writer; + try { + writer = new PrintWriter(file.getAbsolutePath()); + } catch (FileNotFoundException ex) { + Logger.getLogger(Simulator.class.getName()).log(Level.SEVERE, null, ex); + return; + } + + HashMap output_map; + // Print header: Column description + writer.println("#Columns:"); + String column_names = "frame-id,true-signal,analyzer-output,controller-output,controller-setpoint"; + writer.println(column_names); + + // Print data - one line for each frame + for (int i=1; i<=image_count; i++) { + JSONObject e = history.get(i); + String s; + try { + s = String.format("%d,%8.4e,%8.4e,%8.4e,%8.4e", + i,e.get("true-signal"),e.get("analyzer-output"),e.get("controller-output"),e.get("controller-setpoint")); + } catch (JSONException ex) { + s = String.format("%d",i); + Logger.getLogger(Simulator.class.getName()).log(Level.FINER, null, ex); + } catch (NullPointerException ex) { + s = String.format("%d",i); + Logger.getLogger(Simulator.class.getName()).log(Level.FINER, null, ex); + } + writer.println(s); + } + + writer.close(); + + + } + + /** + * Save current ImageStack to TIFF file + * @param tiff_file file to save to + */ + public void saveStack(File tiff_file) { + generator.saveStack(tiff_file); + } + + /** + * Increments image counter in case an image was generated outside of + * this class. + */ + public void incrementCounter() { + image_count++; + } + + /** + * Returns the number of generated images since simulation start. + * @return number of generated images + */ + public int getImageCount() { + return image_count; + } +} diff --git a/src/simulator/generators/realtime/Device.java b/src/simulator/generators/realtime/Device.java index 6558795..b5035ab 100644 --- a/src/simulator/generators/realtime/Device.java +++ b/src/simulator/generators/realtime/Device.java @@ -17,224 +17,232 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -import simulator.generators.realtime.fluorophores.SimpleFluorophore; -import simulator.generators.realtime.fluorophores.SimpleProperties; -import simulator.generators.realtime.obstructors.GoldBeads; -import cern.jet.random.Gamma; -import cern.jet.random.Normal; -import ij.process.FloatProcessor; -import ij.process.ShortProcessor; -import static java.lang.Math.sqrt; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Random; -import cern.jet.random.Poisson; -import cern.jet.random.engine.MersenneTwister; - -/** - * Encapsulator class which contains all device objects (camera, laser...) - * @author Marcel Stefko - */ -public class Device { - private final Camera camera; - private final FluorophoreProperties fluo; - private final ArrayList fluorophores; - private final Laser laser; - - private final ArrayList obstructors; - - private final Poisson poisson = RNG.getPoissonGenerator(); - private final Random random = RNG.getUniformGenerator(); - private final Gamma gamma = RNG.getGammaGenerator(); - private final Normal gaussian = RNG.getGaussianGenerator(); - - /** - * Initialize device with default parameters. - */ - public Device() { - camera = new Camera(400, //res_x - 400, //res_y - 100, //acq_speed, - 1.6, //readout_noise, - 0.06, //dark_current, - 0.8, //quantum_efficiency, - 6, //gain, - 6.45 * 1e-6, //pixel_size, - 1.3, //NA, - 600 * 1e-9, //wavelength, - 100, //magnification, - 8 * 1e-9); //radius) - - fluo = new SimpleProperties(2500, //signal_per_frame, - 50, //background_per_frame, - 8, //base_Ton_frames, - 30, //base_Toff_frames, - 300); //base_Tbl_frames) - - laser = new Laser(0.1, //start_power, - 5.0, //max_power, - 0.1); //min_power) - - fluorophores = FluorophoreGenerator.generateFluorophoresRandom( - 1600, //n_fluos, - camera, - fluo); - - obstructors = new ArrayList(); - - for (Fluorophore e: fluorophores) { - e.recalculate_lifetimes(laser.getPower()); - } - } - - /** - * Initializes the device with given parameters. - * @param cam camera properties - * @param fluo fluorophore properties - * @param laser laser settings - * @param emitters list of fluorophores - * @param obstructors list of obstructors - */ - public Device(Camera cam, FluorophoreProperties fluo, Laser laser, ArrayListemitters, - ArrayList obstructors) { - camera = cam; - this.fluo = fluo; - this.laser = laser; - this.fluorophores = emitters; - - if (obstructors!=null) - this.obstructors = obstructors; - else - this.obstructors = new ArrayList(); - - for (Fluorophore e: emitters) { - e.recalculate_lifetimes(laser.getPower()); - } - } - - /** - * Return the camera resolution - * @return [res_x, res_y] int array - */ - public int[] getResolution() { - int[] result = new int[2]; - result[0] = camera.res_x; result[1] = camera.res_y; - return result; - } - - public double getFOVsize_um() { - return (camera.pixel_size*1e6/camera.magnification)*(camera.pixel_size*1e6/camera.magnification)*camera.res_x*camera.res_y; - } - - public double getPixelSizeUm() { - return camera.pixel_size*1e6/camera.magnification; - } - - /** - * Modifies the laser power to desired value. - * @param laser_power new laser power [W] - */ - public void setLaserPower(double laser_power) { - laser.setPower(laser_power); - for (Fluorophore e: fluorophores) { - e.recalculate_lifetimes(laser.getPower()); - } - } - - /** - * Return current power of the laser. - * @return laser power - */ - public double getLaserPower() { - return laser.getPower(); - } - - /** - * Returns the number of currently active emitters. - * @return number of shining emitters - */ - public double getOnEmitterCount() { - int count = 0; - for (Fluorophore e: fluorophores) { - if (e.isOn()) { - count++; - } - } - return count; - } - - /** - * Generates a new frame based on the current device state, and moves - * device state forward. - * First the obstructions are drawn on the frame, then the fluorophores, - * and afterwards noise is added. - * @return simulated frame - */ - public ShortProcessor simulateFrame() { - float[][] pixels = new float[camera.res_x][camera.res_y]; - for (int row = 0; row < pixels.length; row++) - Arrays.fill(pixels[row], 0.0f); - - // Add obstructions - if (obstructors != null) { - for (Obstructor o: obstructors) { - o.applyTo(pixels); - } - } - // Add emitters - for (Fluorophore f: fluorophores) { - f.applyTo(pixels); - } - - // Add noise - addNoises(pixels); - - // Convert to short array - FloatProcessor fp = new FloatProcessor(pixels); - return fp.convertToShortProcessor(false); - } - - /** - * Adds Poisson noise to the image. - * @param image input image - */ - private void addPoissonNoise(float[][] image) { - for (int x = 0; x < image.length; x++) { - for (int y = 0; y < image[0].length; y++) { - image[x][y] = (float) poisson.nextInt(image[x][y]); - } - } - } - - /** - * Adds Poisson, readout, thermal and quantum gain noise to the image. - * @param image image to be noised up - */ - private void addNoises(float[][] image) { - // add background - for (int row=0; row < image.length; row++) { - for (int col=0; col < image[row].length; col++) { - image[row][col] += fluo.background; - } - } - - // Poisson noise - addPoissonNoise(image); - - // Other noises - for (int row=0; row < image.length; row++) { - for (int col=0; col < image[row].length; col++) { - image[row][col] += camera.readout_noise*gaussian.nextDouble() + - camera.thermal_noise*gaussian.nextDouble() + - gamma.nextDouble(image[row][col]+0.1f,camera.quantum_gain); - } - } - } - -} - - - - +package simulator.generators.realtime; + +import simulator.generators.realtime.fluorophores.SimpleFluorophore; +import simulator.generators.realtime.fluorophores.SimpleProperties; +import simulator.generators.realtime.obstructors.GoldBeads; +import cern.jet.random.Gamma; +import cern.jet.random.Normal; +import ij.process.FloatProcessor; +import ij.process.ShortProcessor; +import static java.lang.Math.sqrt; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Random; +import cern.jet.random.Poisson; +import cern.jet.random.engine.MersenneTwister; + +/** + * Encapsulator class which contains all device objects (camera, laser...) + * @author Marcel Stefko + */ +public class Device { + private final Camera camera; + private final FluorophoreProperties fluo; + private final ArrayList fluorophores; + private final Laser laser; + + private final ArrayList obstructors; + + private final Poisson poisson = RNG.getPoissonGenerator(); + private final Random random = RNG.getUniformGenerator(); + private final Gamma gamma = RNG.getGammaGenerator(); + private final Normal gaussian = RNG.getGaussianGenerator(); + + /** + * Initialize device with default parameters. + */ + public Device() { + camera = new Camera(400, //res_x + 400, //res_y + 100, //acq_speed, + 1.6, //readout_noise, + 0.06, //dark_current, + 0.8, //quantum_efficiency, + 6, //gain, + 6.45 * 1e-6, //pixel_size, + 1.3, //NA, + 600 * 1e-9, //wavelength, + 100, //magnification, + 8 * 1e-9); //radius) + + fluo = new SimpleProperties(2500, //signal_per_frame, + 50, //background_per_frame, + 8, //base_Ton_frames, + 30, //base_Toff_frames, + 300); //base_Tbl_frames) + + laser = new Laser(0.1, //start_power, + 5.0, //max_power, + 0.1); //min_power) + + fluorophores = FluorophoreGenerator.generateFluorophoresRandom( + 1600, //n_fluos, + camera, + fluo); + + obstructors = new ArrayList(); + + for (Fluorophore e: fluorophores) { + e.recalculate_lifetimes(laser.getPower()); + } + } + + /** + * Initializes the device with given parameters. + * @param cam camera properties + * @param fluo fluorophore properties + * @param laser laser settings + * @param emitters list of fluorophores + * @param obstructors list of obstructors + */ + public Device(Camera cam, FluorophoreProperties fluo, Laser laser, ArrayListemitters, + ArrayList obstructors) { + camera = cam; + this.fluo = fluo; + this.laser = laser; + this.fluorophores = emitters; + + if (obstructors!=null) + this.obstructors = obstructors; + else + this.obstructors = new ArrayList(); + + for (Fluorophore e: emitters) { + e.recalculate_lifetimes(laser.getPower()); + } + } + + /** + * Return the camera resolution + * @return [res_x, res_y] int array + */ + public int[] getResolution() { + int[] result = new int[2]; + result[0] = camera.res_x; result[1] = camera.res_y; + return result; + } + + /** + * + * @return size of current FOV in square micrometers + */ + public double getFOVsize_um() { + return (camera.pixel_size*1e6/camera.magnification)*(camera.pixel_size*1e6/camera.magnification)*camera.res_x*camera.res_y; + } + + /** + * + * @return length of one pixel side in micrometers + */ + public double getPixelSizeUm() { + return camera.pixel_size*1e6/camera.magnification; + } + + /** + * Modifies the laser power to desired value. + * @param laser_power new laser power [W] + */ + public void setLaserPower(double laser_power) { + laser.setPower(laser_power); + for (Fluorophore e: fluorophores) { + e.recalculate_lifetimes(laser.getPower()); + } + } + + /** + * Return current power of the laser. + * @return laser power + */ + public double getLaserPower() { + return laser.getPower(); + } + + /** + * Returns the number of currently active emitters. + * @return number of shining emitters + */ + public double getOnEmitterCount() { + int count = 0; + for (Fluorophore e: fluorophores) { + if (e.isOn()) { + count++; + } + } + return count; + } + + /** + * Generates a new frame based on the current device state, and moves + * device state forward. + * First the obstructions are drawn on the frame, then the fluorophores, + * and afterwards noise is added. + * @return simulated frame + */ + public ShortProcessor simulateFrame() { + float[][] pixels = new float[camera.res_x][camera.res_y]; + for (int row = 0; row < pixels.length; row++) + Arrays.fill(pixels[row], 0.0f); + + // Add obstructions + if (obstructors != null) { + for (Obstructor o: obstructors) { + o.applyTo(pixels); + } + } + // Add emitters + for (Fluorophore f: fluorophores) { + f.applyTo(pixels); + } + + // Add noise + addNoises(pixels); + + // Convert to short array + FloatProcessor fp = new FloatProcessor(pixels); + return fp.convertToShortProcessor(false); + } + + /** + * Adds Poisson noise to the image. + * @param image input image + */ + private void addPoissonNoise(float[][] image) { + for (int x = 0; x < image.length; x++) { + for (int y = 0; y < image[0].length; y++) { + image[x][y] = (float) poisson.nextInt(image[x][y]); + } + } + } + + /** + * Adds Poisson, readout, thermal and quantum gain noise to the image. + * @param image image to be noised up + */ + private void addNoises(float[][] image) { + // add background + for (int row=0; row < image.length; row++) { + for (int col=0; col < image[row].length; col++) { + image[row][col] += fluo.background; + } + } + + // Poisson noise + addPoissonNoise(image); + + // Other noises + for (int row=0; row < image.length; row++) { + for (int col=0; col < image[row].length; col++) { + image[row][col] += camera.readout_noise*gaussian.nextDouble() + + camera.thermal_noise*gaussian.nextDouble() + + gamma.nextDouble(image[row][col]+0.1f,camera.quantum_gain); + } + } + } + +} + + + + diff --git a/src/simulator/generators/realtime/Fluorophore.java b/src/simulator/generators/realtime/Fluorophore.java index 3eb082a..a8970e7 100644 --- a/src/simulator/generators/realtime/Fluorophore.java +++ b/src/simulator/generators/realtime/Fluorophore.java @@ -17,47 +17,63 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -import java.util.Random; - -/** - * - * @author stefko - */ -public abstract class Fluorophore extends Emitter { - protected double current_laser_power; - protected final Random random; - - public Fluorophore(Camera camera, double x, double y) { - super(camera, x, y); - this.current_laser_power = 0.0; - this.random = RNG.getUniformGenerator(); - } - - protected double nextExponential(double mean) { - if (java.lang.Double.isInfinite(mean)) - return java.lang.Double.POSITIVE_INFINITY; - else - return Math.log(1 - random.nextDouble()) * (-mean); - } - - /** - * Returns the current state of the emitter (on or off), but does not - * inform if this emitter is also bleached! - * @return true-emitter is on, false-emitter is off - */ - public abstract boolean isOn(); - - /** - * Informs if this emitter switched into the irreversible bleached state. - * @return boolean, true if emitter is bleached - */ - public abstract boolean isBleached(); - - /** - * Recalculates the lifetimes of this emitter based on current laser power. - * @param laser_power current laser power - */ - public abstract void recalculate_lifetimes(double laser_power); -} +package simulator.generators.realtime; + +import java.util.Random; + +/** + * A general fluorescent molecule which emits light. + * @author Marcel Stefko + */ +public abstract class Fluorophore extends Emitter { + + /** + * Laser power value for which the currently stored lifetime values are calculated. + */ + protected double current_laser_power; + + private final Random random; + + /** + * Initialize fluorophore and calculate its pattern on camera + * @param camera Camera used for calculating diffraction pattern + * @param x x-position in pixels + * @param y y-position in pixels + */ + public Fluorophore(Camera camera, double x, double y) { + super(camera, x, y); + this.current_laser_power = 0.0; + this.random = RNG.getUniformGenerator(); + } + + /** + * Sample an random number from an exponential distribution + * @param mean mean of the distribution + * @return random number from this distribution + */ + protected final double nextExponential(double mean) { + if (java.lang.Double.isInfinite(mean)) + return java.lang.Double.POSITIVE_INFINITY; + else + return Math.log(1 - random.nextDouble()) * (-mean); + } + + /** + * Returns the current state of the emitter (on or off), but does not + * inform if this emitter is also bleached! + * @return true-emitter is on, false-emitter is off + */ + public abstract boolean isOn(); + + /** + * Informs if this emitter switched into the irreversible bleached state. + * @return boolean, true if emitter is bleached + */ + public abstract boolean isBleached(); + + /** + * Recalculates the lifetimes of this emitter based on current laser power. + * @param laser_power current laser power + */ + public abstract void recalculate_lifetimes(double laser_power); +} diff --git a/src/simulator/generators/realtime/FluorophoreGenerator.java b/src/simulator/generators/realtime/FluorophoreGenerator.java index 9509853..be5a6ba 100644 --- a/src/simulator/generators/realtime/FluorophoreGenerator.java +++ b/src/simulator/generators/realtime/FluorophoreGenerator.java @@ -17,132 +17,139 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Random;import javax.swing.JFileChooser; -import javax.swing.filechooser.FileNameExtensionFilter; - -/** - * Randomly populates the field of view with fluorophores. - * @author Marcel Stefko - */ -public class FluorophoreGenerator { - - /** - * Randomly populate the field of view with fluorophores. - * @param n_fluos number of emitters to be generated - * @param cam camera properties - * @param fluo fluorophore properties - * @return - */ - public static ArrayList generateFluorophoresRandom(int n_fluos, Camera cam, FluorophoreProperties fluo) { - Random rnd = RNG.getUniformGenerator(); - ArrayList result = new ArrayList(); - double x; double y; - for (int i=0; i generateFluorophoresGrid(int spacing, Camera cam, FluorophoreProperties fluo) { - int limit_x = cam.res_x; - int limit_y = cam.res_y; - ArrayList result = new ArrayList(); - for (int i=spacing; i parseFluorophoresFromCsv(File file, Camera camera, FluorophoreProperties fluo, boolean rescale) throws FileNotFoundException, IOException { - if (file==null) { - file = getFileFromDialog(); - } - - // load all fluorophores - ArrayList result = new ArrayList(); - double x; double y; - BufferedReader br; - String line; - String splitBy = ","; - br = new BufferedReader(new FileReader(file)); - // read all lines - while ((line = br.readLine()) != null) { - // skip comments - if (line.startsWith("#")) { - continue; - } - - // read 2 doubles from beginning of line - String[] entries = line.split(splitBy); - x = Double.parseDouble(entries[0]); - y = Double.parseDouble(entries[1]); - // ignore ones with negative positions - if (x>=0.0 && y>=0.0) - result.add(fluo.createFluorophore(camera, x, y)); - } - - // rescale positions to fit into frame - if (rescale) { - ArrayList result_rescaled = new ArrayList(); - double max_x_coord = 0.0; - for (Fluorophore f: result) { - if (f.x>max_x_coord) - max_x_coord = f.x; - } - double factor = camera.res_x/max_x_coord; - for (Fluorophore f: result) { - result_rescaled.add(fluo.createFluorophore(camera, f.x*factor, f.y*factor)); - } - return result_rescaled; - // or crop fluorophores outside of frame - } else { - ArrayList result_cropped = new ArrayList(); - for (Fluorophore f: result) { - if (f.x < camera.res_x && f.y < camera.res_y) { - result_cropped.add(f); - } - } - return result_cropped; - } - } - - private static File getFileFromDialog() { - JFileChooser fc = new JFileChooser(); - int returnVal; - fc.setDialogType(JFileChooser.OPEN_DIALOG); - //set a default filename - fc.setSelectedFile(new File("emitters.csv")); - //Set an extension filter - fc.setFileFilter(new FileNameExtensionFilter("CSV file","csv")); - returnVal = fc.showOpenDialog(null); - if (returnVal != JFileChooser.APPROVE_OPTION) { - throw new RuntimeException("You need to select an emitter file!"); - } - return fc.getSelectedFile(); - } -} +package simulator.generators.realtime; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Random;import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; + +/** + * Randomly populates the field of view with fluorophores. + * @author Marcel Stefko + */ +public class FluorophoreGenerator { + + /** + * Randomly populate the field of view with fluorophores. + * @param n_fluos number of emitters to be generated + * @param cam camera properties + * @param fluo fluorophore properties + * @return + */ + public static ArrayList generateFluorophoresRandom(int n_fluos, Camera cam, FluorophoreProperties fluo) { + Random rnd = RNG.getUniformGenerator(); + ArrayList result = new ArrayList(); + double x; double y; + for (int i=0; i generateFluorophoresGrid(int spacing, Camera cam, FluorophoreProperties fluo) { + int limit_x = cam.res_x; + int limit_y = cam.res_y; + ArrayList result = new ArrayList(); + for (int i=spacing; i parseFluorophoresFromCsv(File file, Camera camera, FluorophoreProperties fluo, boolean rescale) throws FileNotFoundException, IOException { + if (file==null) { + file = getFileFromDialog(); + } + + // load all fluorophores + ArrayList result = new ArrayList(); + double x; double y; + BufferedReader br; + String line; + String splitBy = ","; + br = new BufferedReader(new FileReader(file)); + // read all lines + while ((line = br.readLine()) != null) { + // skip comments + if (line.startsWith("#")) { + continue; + } + + // read 2 doubles from beginning of line + String[] entries = line.split(splitBy); + x = Double.parseDouble(entries[0]); + y = Double.parseDouble(entries[1]); + // ignore ones with negative positions + if (x>=0.0 && y>=0.0) + result.add(fluo.createFluorophore(camera, x, y)); + } + + // rescale positions to fit into frame + if (rescale) { + ArrayList result_rescaled = new ArrayList(); + double max_x_coord = 0.0; + for (Fluorophore f: result) { + if (f.x>max_x_coord) + max_x_coord = f.x; + } + double factor = camera.res_x/max_x_coord; + for (Fluorophore f: result) { + result_rescaled.add(fluo.createFluorophore(camera, f.x*factor, f.y*factor)); + } + return result_rescaled; + // or crop fluorophores outside of frame + } else { + ArrayList result_cropped = new ArrayList(); + for (Fluorophore f: result) { + if (f.x < camera.res_x && f.y < camera.res_y) { + result_cropped.add(f); + } + } + return result_cropped; + } + } + + private static File getFileFromDialog() { + JFileChooser fc = new JFileChooser(); + int returnVal; + fc.setDialogType(JFileChooser.OPEN_DIALOG); + //set a default filename + fc.setSelectedFile(new File("emitters.csv")); + //Set an extension filter + fc.setFileFilter(new FileNameExtensionFilter("CSV file","csv")); + returnVal = fc.showOpenDialog(null); + if (returnVal != JFileChooser.APPROVE_OPTION) { + throw new RuntimeException("You need to select an emitter file!"); + } + return fc.getSelectedFile(); + } +} diff --git a/src/simulator/generators/realtime/FluorophoreProperties.java b/src/simulator/generators/realtime/FluorophoreProperties.java index d78fe30..0d78753 100644 --- a/src/simulator/generators/realtime/FluorophoreProperties.java +++ b/src/simulator/generators/realtime/FluorophoreProperties.java @@ -17,24 +17,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -/** - * - * @author stefko - */ -public abstract class FluorophoreProperties { - public final double signal; - public final double background; - - public FluorophoreProperties(double signal, double background) { - if (signal < 0.0 || background < 0.0) { - throw new IllegalArgumentException(); - } - - this.signal = signal; - this.background = background; - } - - public abstract Fluorophore createFluorophore(Camera camera, double x, double y); -} +package simulator.generators.realtime; + +/** + * Each fluorophore has a signal value and background value + * @author Marcel Stefko + */ +public abstract class FluorophoreProperties { + public final double signal; + public final double background; + + public FluorophoreProperties(double signal, double background) { + if (signal < 0.0 || background < 0.0) { + throw new IllegalArgumentException(); + } + + this.signal = signal; + this.background = background; + } + + /** + * Create fluorophore with these given properties + * @param camera Camera to calculate pattern + * @param x x-position in pixels + * @param y y-position in pixels + * @return generated fluorophore + */ + public abstract Fluorophore createFluorophore(Camera camera, double x, double y); +} diff --git a/src/simulator/generators/realtime/RNG.java b/src/simulator/generators/realtime/RNG.java index af02fd1..2d7ea62 100644 --- a/src/simulator/generators/realtime/RNG.java +++ b/src/simulator/generators/realtime/RNG.java @@ -17,51 +17,67 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -import cern.jet.random.Gamma; -import cern.jet.random.Normal; -import cern.jet.random.Poisson; -import cern.jet.random.engine.MersenneTwister; -import java.util.Random; - -/** - * Random number generator for STORMsim. Ensures repeatability. - * @author stefko - */ -public final class RNG { - private static Random uniform = new Random(1); - private static Poisson poisson = new Poisson(1.0, new MersenneTwister(uniform.nextInt())); - private static Gamma gamma = new Gamma(1.0, 5.0, new MersenneTwister(uniform.nextInt())); - private static Normal gaussian = new Normal(0.0, 1.0, new MersenneTwister(uniform.nextInt())); - - private RNG() { } - - /** - * This resets the generators - * @param seed - */ - public static void setSeed(int seed) { - uniform = new Random(seed); - poisson = new Poisson(1.0, new MersenneTwister(uniform.nextInt())); - gamma = new Gamma(1.0, 5.0, new MersenneTwister(uniform.nextInt())); - gaussian = new Normal(0.0, 1.0, new MersenneTwister(uniform.nextInt())); - } - - public static Random getUniformGenerator() { - return uniform; - } - - public static Poisson getPoissonGenerator() { - return poisson; - } - - public static Gamma getGammaGenerator() { - return gamma; - } - - public static Normal getGaussianGenerator() { - return gaussian; - } - -} +package simulator.generators.realtime; + +import cern.jet.random.Gamma; +import cern.jet.random.Normal; +import cern.jet.random.Poisson; +import cern.jet.random.engine.MersenneTwister; +import java.util.Random; + +/** + * Random number generator for STORMsim. Ensures repeatability. + * @author stefko + */ +public final class RNG { + private static Random uniform = new Random(1); + private static Poisson poisson = new Poisson(1.0, new MersenneTwister(uniform.nextInt())); + private static Gamma gamma = new Gamma(1.0, 5.0, new MersenneTwister(uniform.nextInt())); + private static Normal gaussian = new Normal(0.0, 1.0, new MersenneTwister(uniform.nextInt())); + + private RNG() { } + + /** + * This resets the generators + * @param seed + */ + public static void setSeed(int seed) { + uniform = new Random(seed); + poisson = new Poisson(1.0, new MersenneTwister(uniform.nextInt())); + gamma = new Gamma(1.0, 5.0, new MersenneTwister(uniform.nextInt())); + gaussian = new Normal(0.0, 1.0, new MersenneTwister(uniform.nextInt())); + } + + /** + * + * @return uniform RNG + */ + public static Random getUniformGenerator() { + return uniform; + } + + /** + * + * @return Poisson RNG + */ + public static Poisson getPoissonGenerator() { + return poisson; + } + + /** + * + * @return Gamma distribution RNG + */ + public static Gamma getGammaGenerator() { + return gamma; + } + + /** + * + * @return Gaussian distribution RNG + */ + public static Normal getGaussianGenerator() { + return gaussian; + } + +} diff --git a/src/simulator/generators/realtime/STORMsim.java b/src/simulator/generators/realtime/STORMsim.java index 15b5f6f..b872331 100644 --- a/src/simulator/generators/realtime/STORMsim.java +++ b/src/simulator/generators/realtime/STORMsim.java @@ -17,192 +17,196 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package simulator.generators.realtime; - -import simulator.generators.realtime.fluorophores.SimpleFluorophore; -import simulator.generators.realtime.fluorophores.SimpleProperties; -import simulator.generators.AbstractGenerator; -import simulator.generators.realtime.obstructors.ConstantBackground; -import simulator.generators.realtime.obstructors.GoldBeads; -import ij.IJ; -import ij.ImagePlus; -import ij.ImageStack; -import ij.gui.GenericDialog; -import ij.io.FileSaver; -import ij.process.ImageProcessor; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * ImageGenerator wrapper around the RealTimeGenerator implementation. - * @author Marcel Stefko - */ -public class STORMsim extends AbstractGenerator { - private Device device; - - private ArrayList emitter_history; - - /** - * Initialize the generator, either from GUI dialog or use default params. - * @param device - */ - public STORMsim(Device device) { - super(); - - if (device == null) { - RNG.setSeed(1); - initDeviceFromDialog(); - } else { - this.device = device; - } - int[] res = this.device.getResolution(); - stack = new ImageStack(res[0],res[1]); - - emitter_history = new ArrayList(); - emitter_history.add(0.0); - } - - @Override - public double getTrueSignal(int image_no) { - return emitter_history.get(image_no) / device.getFOVsize_um(); - } - - @Override - public ImageProcessor getNextImage() { - // we calculate emitter count first so it corresponds with the beginning - // of the frame rather than end of the frame - emitter_history.add(device.getOnEmitterCount()); - ImageProcessor ip = device.simulateFrame(); - stack.addSlice(ip); - return ip; - } - - @Override - public void setCustomParameters(HashMap map) { - parameters = map; - } - - @Override - public HashMap getCustomParameters() { - parameters.put("real_laser_power", device.getLaserPower()); - return parameters; - } - - @Override - public void setControlSignal(double value) { - parameters.put("target_laser_power", value); - device.setLaserPower(value); - } - - @Override - public double getControlSignal() { - return device.getLaserPower(); - } - - private void initDeviceFromDialog() { - GenericDialog gd = new GenericDialog("Camera initialization"); - gd.addMessage("Camera:"); - gd.addNumericField("Resolution X", 400, 0); - gd.addNumericField("Resolution Y", 400, 0); - gd.addNumericField("Framerate [fps]", 100, 0); - gd.addNumericField("Readout noise [rms]", 1.6, 1); - gd.addNumericField("Dark current [e/px/frame]",0.06,3); - gd.addNumericField("Quantum efficiency", 0.8, 2); - gd.addNumericField("Gain", 6, 1); - gd.addNumericField("Pixel size [um]", 6.45, 3); - gd.addNumericField("Numerical aperture", 1.3, 2); - gd.addNumericField("Wavelength [nm]", 600, 0); - gd.addNumericField("Magnification", 100, 0); - gd.addNumericField("Cross-section radius [nm]", 8, 1); - gd.showDialog(); - - Camera camera = new Camera((int)gd.getNextNumber(), //res_x - (int)gd.getNextNumber(), //res_y - (int)gd.getNextNumber(), //acq_speed, - gd.getNextNumber(), //readout_noise, - gd.getNextNumber(), //dark_current, - gd.getNextNumber(), //quantum_efficiency, - gd.getNextNumber(), //gain, - gd.getNextNumber() * 1e-6, //pixel_size, - gd.getNextNumber(), //NA, - gd.getNextNumber() * 1e-9, //wavelength, - gd.getNextNumber(), //magnification, - gd.getNextNumber() * 1e-9); //radius) - - gd = new GenericDialog("Device initialization"); - - gd.addMessage("Fluorophore:"); - gd.addNumericField("Fluo signal", 2500, 0); - gd.addNumericField("Fluo background", 50, 0); - gd.addNumericField("Base Ton", 8, 0); - gd.addNumericField("Base Toff", 30, 0); - gd.addNumericField("Base Tbl", 300, 0); - - gd.addMessage("Laser:"); - gd.addNumericField("Laser start", 0.0, 2); - gd.addNumericField("Laser max", 5.0, 2); - gd.addNumericField("Laser min", 0.0, 2); - - gd.addMessage("Emitter:"); - gd.addNumericField("Emitter no.", 1600, 0); - - - gd.addMessage("Obstructors:"); - gd.addNumericField("Gold bead no.", 10, 0); - gd.addNumericField("Gold bead brightness", 4000, 0); - gd.showDialog(); - - - SimpleProperties fluo = new SimpleProperties(gd.getNextNumber(), //signal_per_frame, - gd.getNextNumber(), //background_per_frame, - gd.getNextNumber(), //base_Ton_frames, - gd.getNextNumber(), //base_Toff_frames, - gd.getNextNumber()); //base_Tbl_frames) - - Laser laser = new Laser(gd.getNextNumber(), //start_power, - gd.getNextNumber(), //max_power, - gd.getNextNumber()); //min_power) - - - ArrayList emitters; - int emitter_no = (int) gd.getNextNumber(); - if (emitter_no == 0) { - try { - emitters = FluorophoreGenerator.parseFluorophoresFromCsv(null, camera, fluo, true); - } catch (IOException ex) { - Logger.getLogger(STORMsim.class.getName()).log(Level.SEVERE, null, ex); - emitters = FluorophoreGenerator.generateFluorophoresRandom( - (int)100, //n_fluos, - camera, - fluo); - } - } else { - emitters = FluorophoreGenerator.generateFluorophoresRandom( - emitter_no, //n_fluos, - camera, - fluo); - } - - ArrayList obstructors = new ArrayList(); - obstructors.add(new GoldBeads((int)gd.getNextNumber(), //beadCount - camera, - (int)gd.getNextNumber())); // bead brightness - try { - obstructors.add(new ConstantBackground(camera)); - } catch (RuntimeException ex) { - Logger.getLogger(STORMsim.class.getName()).log(Level.FINER, null, ex); - } - - - device = new Device(camera, fluo, laser, emitters, obstructors); - } - - @Override - public double getPixelSizeUm() { - return device.getPixelSizeUm(); - } -} +package simulator.generators.realtime; + +import simulator.generators.realtime.fluorophores.SimpleFluorophore; +import simulator.generators.realtime.fluorophores.SimpleProperties; +import simulator.generators.AbstractGenerator; +import simulator.generators.realtime.obstructors.ConstantBackground; +import simulator.generators.realtime.obstructors.GoldBeads; +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.GenericDialog; +import ij.io.FileSaver; +import ij.process.ImageProcessor; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * ImageGenerator wrapper around the RealTimeGenerator implementation. + * @author Marcel Stefko + */ +public class STORMsim extends AbstractGenerator { + private Device device; + + private ArrayList emitter_history; + + /** + * Initialize the generator, either from GUI dialog or use default params. + * @param device + */ + public STORMsim(Device device) { + super(); + + if (device == null) { + RNG.setSeed(1); + initDeviceFromDialog(); + } else { + this.device = device; + } + int[] res = this.device.getResolution(); + stack = new ImageStack(res[0],res[1]); + + emitter_history = new ArrayList(); + emitter_history.add(0.0); + } + + @Override + public double getTrueSignal(int image_no) { + return emitter_history.get(image_no) / device.getFOVsize_um(); + } + + @Override + public ImageProcessor getNextImage() { + // we calculate emitter count first so it corresponds with the beginning + // of the frame rather than end of the frame + emitter_history.add(device.getOnEmitterCount()); + ImageProcessor ip = device.simulateFrame(); + stack.addSlice(ip); + return ip; + } + + @Override + public void setCustomParameters(HashMap map) { + parameters = map; + } + + @Override + public HashMap getCustomParameters() { + parameters.put("real_laser_power", device.getLaserPower()); + return parameters; + } + + @Override + public void setControlSignal(double value) { + parameters.put("target_laser_power", value); + device.setLaserPower(value); + } + + @Override + public double getControlSignal() { + return device.getLaserPower(); + } + + private void initDeviceFromDialog() { + GenericDialog gd = new GenericDialog("Camera initialization"); + gd.addMessage("Camera:"); + gd.addNumericField("Resolution X", 400, 0); + gd.addNumericField("Resolution Y", 400, 0); + gd.addNumericField("Framerate [fps]", 100, 0); + gd.addNumericField("Readout noise [rms]", 1.6, 1); + gd.addNumericField("Dark current [e/px/frame]",0.06,3); + gd.addNumericField("Quantum efficiency", 0.8, 2); + gd.addNumericField("Gain", 6, 1); + gd.addNumericField("Pixel size [um]", 6.45, 3); + gd.addNumericField("Numerical aperture", 1.3, 2); + gd.addNumericField("Wavelength [nm]", 600, 0); + gd.addNumericField("Magnification", 100, 0); + gd.addNumericField("Cross-section radius [nm]", 8, 1); + gd.showDialog(); + + Camera camera = new Camera((int)gd.getNextNumber(), //res_x + (int)gd.getNextNumber(), //res_y + (int)gd.getNextNumber(), //acq_speed, + gd.getNextNumber(), //readout_noise, + gd.getNextNumber(), //dark_current, + gd.getNextNumber(), //quantum_efficiency, + gd.getNextNumber(), //gain, + gd.getNextNumber() * 1e-6, //pixel_size, + gd.getNextNumber(), //NA, + gd.getNextNumber() * 1e-9, //wavelength, + gd.getNextNumber(), //magnification, + gd.getNextNumber() * 1e-9); //radius) + + gd = new GenericDialog("Device initialization"); + + gd.addMessage("Fluorophore:"); + gd.addNumericField("Fluo signal", 2500, 0); + gd.addNumericField("Fluo background", 50, 0); + gd.addNumericField("Base Ton", 8, 0); + gd.addNumericField("Base Toff", 30, 0); + gd.addNumericField("Base Tbl", 300, 0); + + gd.addMessage("Laser:"); + gd.addNumericField("Laser start", 0.0, 2); + gd.addNumericField("Laser max", 5.0, 2); + gd.addNumericField("Laser min", 0.0, 2); + + gd.addMessage("Emitter:"); + gd.addNumericField("Emitter no.", 1600, 0); + + + gd.addMessage("Obstructors:"); + gd.addNumericField("Gold bead no.", 10, 0); + gd.addNumericField("Gold bead brightness", 4000, 0); + gd.showDialog(); + + + SimpleProperties fluo = new SimpleProperties(gd.getNextNumber(), //signal_per_frame, + gd.getNextNumber(), //background_per_frame, + gd.getNextNumber(), //base_Ton_frames, + gd.getNextNumber(), //base_Toff_frames, + gd.getNextNumber()); //base_Tbl_frames) + + Laser laser = new Laser(gd.getNextNumber(), //start_power, + gd.getNextNumber(), //max_power, + gd.getNextNumber()); //min_power) + + + ArrayList emitters; + int emitter_no = (int) gd.getNextNumber(); + if (emitter_no == 0) { + try { + emitters = FluorophoreGenerator.parseFluorophoresFromCsv(null, camera, fluo, true); + } catch (IOException ex) { + Logger.getLogger(STORMsim.class.getName()).log(Level.SEVERE, null, ex); + emitters = FluorophoreGenerator.generateFluorophoresRandom( + (int)100, //n_fluos, + camera, + fluo); + } + } else { + emitters = FluorophoreGenerator.generateFluorophoresRandom( + emitter_no, //n_fluos, + camera, + fluo); + } + + ArrayList obstructors = new ArrayList(); + obstructors.add(new GoldBeads((int)gd.getNextNumber(), //beadCount + camera, + (int)gd.getNextNumber())); // bead brightness + try { + obstructors.add(new ConstantBackground(camera)); + } catch (RuntimeException ex) { + Logger.getLogger(STORMsim.class.getName()).log(Level.FINER, null, ex); + } + + + device = new Device(camera, fluo, laser, emitters, obstructors); + } + + /** + * + * @return length of one pixel side in micrometers + */ + @Override + public double getPixelSizeUm() { + return device.getPixelSizeUm(); + } +}