Micro-manager diSPIM Plugin

There is a Micro-manager plugin to control the diSPIM. It is part of the standard Micro-Manager build under Plugins → Device Control → ASIdiSPIM.

Details about the plugin operation can be found in the manual section of this website in the Micro-manager diSPIM Plugin User Guide. Please see that manual for information about using the plugin. There is also a brief overview is on its Micro-manager wiki page.

Currently the diSPIM plugin is supported in 1.4.x only. In 2023 there have been efforts to create a new plugin in Micro-Manager 2.0 called LightSheetManager that will operate the diSPIM as well as other types of light sheet microscopes. Contributions to the effort of making this new plugin are appreciated. As of August 2023 this is in alpha testing by the first few bleeding edge adopters, and we expect that by end of 2023 it will be ready for more adopters and a full replacement in 2024.

You can download the nightly builds of Micro-manager 1.4 for Windows here; the date is encoded in the file name and the latest date is at the top of the page. Make sure to get the appropriate 32bit or 64bit depending on your computer.

Changelog

For convenience the change log at https://micro-manager.org/wiki/ASIdiSPIM_Plugin#Change_Log is listed below. Bug fixes are not listed. The feature was fully implemented as of the nightly build listed.

  • 20190306: added overview acquisition feature to show 2D slice of large sample
  • 20180928: added path-specific presets
  • 20180222: added XYZ grid creator, useful for large tiled datasets
  • 20171031: added reflective imaging option
  • 20170217: can use light sheet mode on supported cameras
  • 20170208: new camera tab, allows setting ROI numerically or with buttons
  • 20170112: allow remote access via Java RMI for automated microscopy
  • 20161222: plugin can deskew stage scan data
  • 20160701: oSPIM support (need to build JAR with flag set)
  • 20151218: added API calls to control acquisition programmatically
  • 20151125: added ImageJ toolset for common image manipulation tasks
  • 20151124: ability to run test acquisition with optionally exported raw file
  • 20151013: added API calls to change acquisition settings programmatically
  • 20150925: significantly expanded autofocus options
  • 20150819: buttons to raise/lower SPIM head and sync upper and lower Z axes
  • (1.4.22 release)
  • 20150811: autofocus during timelapse
  • 20150807: rapid-fire (hardware-timed) acquisition
  • (1.4.21 release)
  • 20150428: autofocus to define calibration (not yet during timelapse)
  • 20150415: added basic stage scanning (contact ASI for firmware if needed)
  • 20150225: added hardware switching for multi-channel using Tiger's PLC
  • 20150113: added multi-position support and software-based multi-channel support
  • (1.4.20 release)
  • 20141219: added ability to easily do offset-only calibration adjustment
  • 20141215: improved slice timing calculations including speeding up optimized easy timing slice period by 0.25 ms
  • 20141209: added colored status indicators for “quick glance” position information (helpful esp. to avoid manual alignment when not centered)
  • 20141207: added ability to export data for Fiji Multiview plugin (as of April 2015 the Fiji plugin can directly import data without requiring this step)
  • (1.4.19 release)
  • first included with 1.4.16 release Jan 2014

You can view all changes to the diSPIM plugin code by searching the Micro-Manager timeline for "ASIdiSPIM". You can also browse the plugin source code.

Troubleshooting

Problem Possible Reason Possible Fix
Stage scanning cameras not getting triggered Hardware/firmware out of date Make sure you have Rev F XY card or later, make sure micro-mirror card has jumper on positions 11/12 of the backplane jumper bank (SV6 for Rev E and later card revs, SV2 for pre-E)
Dropped frames during acquisition insufficient sequence buffer Increase sequence buffer size in MM options, e.g. 1000 MB. Setting larger than needed can introduce unnecessary lags so don't go overboard.
Dropped frames during acquisition trying to access acquisition file during acquisition Break up multi-timepoint acquisitions into per-timepoint files if you need to access earlier data before experiment end
Acquisition fails (“Camera did not send the first image within a reasonable time“) Camera trigger cables aren't properly connected Ensure that the camera trigger cables between the Tiger controller and camera are connected to the correct camera.
Error on running plugin “vector index out of range” Plugin bug in 1.4.22 release Use recent nightly build (or one-time initialize of “Autofocus Channel” dropdown in middle of Autofocus tab)
Error “Missed some images during acquisition” Camera triggers sent too fast Bug in plugin calculations of readout time. Try enabling “Minimize slice period”, noting the slice period, and then disabling it and setting a longer slice period. See information in the manual in section “Easy Timing Mode”.

Bug reports

The first step is to check and see if your problem has already been fixed by using a recent nightly build of Micro-Manager (Windows and Mac). If your firmware is very old it wouldn't hurt to update it; instructions and firmware files are on this wiki filed under hardware > controller > firmware.

If the bug still happens, is helpful to generate a problem report using “Help” → “Report Problem…” in the main Micro-Manager window. It will prompt you to reproduce the problem, for example if you are having a problem during diSPIM acquisition then run an acquisition and verify that the bug is manifest. After clicking “Done” do not submit the report the usual way, instead click “View Report”, save the text as a file, and then email that file directly to the plugin developers.

If you cannot reproduce the problem in order to generate a bug report, then an alternative is to enable debug logging in “Tools” → “Options…” and then when the problem happens send the developers the core log file being written to at the time (find the logs in the Core Logs directory located inside the Micro-manager install directory, e.g. C:/Program Files/Micro-Manager-1.4/Core Logs and then log files named by the launch time), also noting the time that the error occurred. Because the problem report file contains extra information, if it is possible to replicate the error on demand then it is better to use the problem report method.

You can see recent changes to the plugin code by searching the Micro-manager code timeline for ASIdiSPIM. Sometimes changes to the ASI Tiger device adapter also affect the plugin operation ( Micro-manager code timeline for ASITiger).

Launching the plugin on startup

Add the following lines to your MMStartup.bsh script (or create the file if it doesn't exist). The startup script lives in the root MM folder (e.g. C:\Program Files\Micro-Manager-1.4) and is automatically executed when Micro-Manager launches (unless set otherwise in Tools→Options).

import org.micromanager.asidispim.ASIdiSPIM;
import org.micromanager.asidispim.Utils.MyDialogUtils;
 
// the if statement and closing brace can be removed if you want the plugin to always load regardless
if (MyDialogUtils.getConfirmDialogResult("Do you want to load the ASIdiSPIM plugin and initialize?", JOptionPane.YES_NO_OPTION) ) {
  ASIdiSPIM plugin = new ASIdiSPIM();
  plugin.setApp(gui);
}

Scripting the plugin

The Micro-Manager plugin has an API that allows most of its functionality to be accessed via Beanshell scripts run from within Micro-Manager. This allows users to orchestrate complex acquisitions and even use the plugin for adaptive microscopy where automated image analysis guides acquisition. The best way to see what functionality is exposed via the API is to look at the source code of the Java interface which defines the API. The API includes are methods to get and set the most commonly changed settings within the plugin, launch acquisitions, move the microscope, and more. The API can be augmented if needed but every effort is made not to change the documented functionality. Furthermore there is support for Java RMI which allows the plugin API to be access from a completely separate Java VM.

Here is a bare-bones example script:

// required imports
import org.micromanager.asidispim.api.ASIdiSPIMInterface;
import org.micromanager.asidispim.api.ASIdiSPIMImplementation;
 
// get a reference to the plugin; use this to call the plugin's API methods
// API documented in ASIdiSPIMInterface.java in source code 
//   (https://valelab4.ucsf.edu/trac/micromanager/browser/plugins/ASIdiSPIM/src/org/micromanager/asidispim/api/ASIdiSPIMInterface.java)
// note that the plugin must be launched for this to work
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
 
// use the plugin's API to do whatever you like
diSPIM.runAcquisition(); // or other API methods in ASIdiSPIMInterface

Here are some example scripts:

// This script changes channels in the middle of a multi-timepoint acquistion.  It actually runs two separate acquisitions and changes the channel between them.
 
import org.micromanager.asidispim.api.ASIdiSPIMInterface;
import org.micromanager.asidispim.api.ASIdiSPIMImplementation;
 
// change these constants for your use
int timepointIntervalSec = 100;
int numTimepointsFirst = 100;
int numTimepointsSecond = 50;
String channelToRemove = "561";
String channelToAdd_1 = "405";
String channelToAdd_2 = "637";
 
 
// gets a reference to the plugin; use this to call the plugin's API methods
// API documented in ASIdiSPIMInterface.java in source code 
//   (https://valelab4.ucsf.edu/trac/micromanager/browser/plugins/ASIdiSPIM/src/org/micromanager/asidispim/api/ASIdiSPIMInterface.java)
// note that the plugin must be launched
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
 
// script uses existing settings except for the number of timepoints and time point interval which are controlled by the script
// NB: the channels for the first portion of acquisition should be configured correctly
diSPIM.setTimepointInterval(timepointIntervalSec);
diSPIM.setsetTimepointsNumber(numTimepointsFirst);
 
// launch the acquisition with current settings, will block until acquisition is done
diSPIM.runAcquisitionBlocking();
 
// pause script to attempt to simulate the interval between timepoints (really it will take a few seconds longer, could try to compensate if it matters)
Thread.sleep(1000*(timepointIntervalSec));
 
// remove and add the requested channels (here one subtration and 2 additions are hardcoded)
diSPIM.setChannelEnabled(channelToRemove, false);
diSPIM.setChannelEnabled(channelToAdd_1, true);
diSPIM.setChannelEnabled(channelToAdd_2, true);
 
// change the number of timepoints
diSPIM.setsetTimepointsNumber(numTimepointsSecond);
 
// launch the acquisition with current settings, will block until acquisition is done
diSPIM.runAcquisitionBlocking();
// This script runs a multi-timepoint acquisition at multiple positions, collecting all time points at a single position
// before moving to the next position (reversing the time/position order usual in the plugin).
 
import org.micromanager.asidispim.api.ASIdiSPIMInterface;
import org.micromanager.asidispim.api.ASIdiSPIMImplementation;
import org.micromanager.api.PositionList;
import org.micromanager.api.MultiStagePosition;
 
// change these constants for your use
int postMoveDelayMs = 500;  // delay after stage arrives as position before imaging, overrides one set in plugin
 
// gets a reference to the plugin; use this to call the plugin's API methods
// API documented in ASIdiSPIMInterface.java in source code 
//   (https://valelab4.ucsf.edu/trac/micromanager/browser/plugins/ASIdiSPIM/src/org/micromanager/asidispim/api/ASIdiSPIMInterface.java)
// note that the plugin must be launched
// acquisition settings aren't modified in this script, set them in the plugin GUI
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
 
// get MM's position list
// should have positions set there already before launching script
PositionList pl = gui.getPositionList();
int numPositions = pl.getNumberOfPositions();
 
// make sure plugin is set _not_ to use positions
diSPIM.setMultiplePositionsEnabled(false);
 
// base name for file saving
String basePrefix = diSPIM.getSavingNamePrefix();
 
// loop over all positions in position list
for( int i=0; i<numPositions; i++) {
	MultiStagePosition.goToPosition(pl.getPosition(i), mmc);  // move the the next position, this blocks until stage arrives
	Thread.sleep(postMoveDelayMs);   // pause for the requested amount
	diSPIM.setSavingNamePrefix(basePrefix + "_Pos" + i);
	diSPIM.runAcquisitionBlocking()  ;  // launch the acquisition with current settings, will block until done
}
 
// restore prefix
diSPIM.setSavingNamePrefix(basePrefix);
// This script runs a "manual" multiple position acquisitions to save each position
// in a separate acquisition on disk.
 
// required imports
import org.micromanager.asidispim.api.*;
import org.micromanager.api.PositionList;
import org.micromanager.api.MultiStagePosition;
import org.micromanager.asidispim.utils.MyDialogUtils;
import java.text.DecimalFormat;
 
// get reference to plugin (requires plugin to be open)
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
 
// get MM's position list (should have positions set there already before running script)
PositionList pl = gui.getPositionList();
int numPositions = pl.getNumberOfPositions();
 
// make sure plugin is set _not_ to use positions after caching old value
boolean multiXY = diSPIM.getMultiplePositionsEnabled();
diSPIM.setMultiplePositionsEnabled(false);
 
// get value of post-move delay from plugin
double postMoveDelayMs = diSPIM.getMultiplePositionsDelay();
 
// save original name prefix for file saving
String basePrefix = diSPIM.getSavingNamePrefix();
 
// set up formatting with leading 0s
DecimalFormat df = new DecimalFormat("000");
 
// setup for closing finished acquisition windows
// instead of leaving every window open want to close when done
// found that call to closeAcquisitionWindow() can come before writing is finished and cause problems
// so let there be 2 open windows but prevent opening more
String lastAcqName = "";
String lastLastAcqName = "";
 
 
// loop over all positions in position list
try {
	for(int i=1; i<=numPositions; ++i) {
		MultiStagePosition.goToPosition(pl.getPosition(i-1), mmc);  // move the the next position, this blocks until stage arrives
		Thread.sleep(Math.round(postMoveDelayMs));   // pause for the requested amount
		diSPIM.setSavingNamePrefix(basePrefix + "_Pos" + df.format(i));
		try {
			diSPIM.closeAcquisitionWindow(lastLastAcqName);
		} catch (Exception ex) {
			// do nothing
		}
		diSPIM.runAcquisitionBlocking()  ;  // launch the acquisition with current settings, will block until done
		lastLastAcqName = lastAcqName;
		lastAcqName = diSPIM.getLastAcquisitionName();
	}
} catch (Exception ex) {
	MyDialogUtils.showError(ex, "error in manual positions");
} finally {
	// restore prefix and multi-XY value
	diSPIM.setSavingNamePrefix(basePrefix);
	diSPIM.setMultiplePositionsEnabled(multiXY);
}
// This script runs a "manual" multiple position, multiple channel acquisitions
// to save each position in a separate file on disk.
// Positions are the "outer loop", channels are the "inner loop"
// Channels are not necessarily in the order specified in the plugin but should be the same order every time.
// User should put the plugin "Change channel" setting to "Every volume" (currently no API method for that).
 
 
// required imports
import org.micromanager.asidispim.api.*;
import org.micromanager.api.PositionList;
import org.micromanager.api.MultiStagePosition;
import org.micromanager.asidispim.utils.MyDialogUtils;
import java.text.DecimalFormat;
 
// get reference to plugin (requires plugin to be open)
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
 
// get MM's position list (should have positions set there already before running script)
PositionList pl = gui.getPositionList();
int numPositions = pl.getNumberOfPositions();
 
// make sure plugin is set _not_ to use positions after caching old value
boolean multiXY = diSPIM.getMultiplePositionsEnabled();
diSPIM.setMultiplePositionsEnabled(false);
 
// get value of post-move delay from plugin
double postMoveDelayMs = diSPIM.getMultiplePositionsDelay();
 
// save original name prefix for file saving
String basePrefix = diSPIM.getSavingNamePrefix();
 
// set up formatting with leading 0s
DecimalFormat df = new DecimalFormat("000");
 
// setup for closing finished acquisition windows
// instead of leaving every window open want to close when done
// found that call to closeAcquisitionWindow() can come before writing is finished and cause problems
// so let there be 2 open windows but prevent opening more
String lastAcqName = "";
String lastLastAcqName = "";
 
// figure out which channels we are using
boolean channelsEnabled = diSPIM.getChannelsEnabled();
String [] allChannels = diSPIM.getAvailableChannels();
 
try {
	// loop over all positions in position list
	for( int i=1; i<=numPositions; ++i ) {
		MultiStagePosition.goToPosition(pl.getPosition(i-1), mmc);  // move the the next position, this blocks until stage arrives
		Thread.sleep(Math.round(postMoveDelayMs));   // pause for the requested amount
 
		for( s : allChannels) {
			if (!diSPIM.getChannelEnabled(s))
				continue;
			diSPIM.setSavingNamePrefix(basePrefix + "_Ch" + s + "_Pos" + df.format(i));
			try {
				diSPIM.closeAcquisitionWindow(lastLastAcqName);
			} catch (Exception ex) {
				// do nothing
			}
			diSPIM.runAcquisitionBlocking()  ;  // launch the acquisition with current settings, will block until done
			lastLastAcqName = lastAcqName;
			lastAcqName = diSPIM.getLastAcquisitionName();
		}
	}
} catch (Exception ex) {
	MyDialogUtils.showError(ex, "error in manual positions");
} finally {
	// restore prefix and multi-XY value
	diSPIM.setSavingNamePrefix(basePrefix);
	diSPIM.setMultiplePositionsEnabled(multiXY);
	diSPIM.setChannelsEnabled(channelsEnabled);
}

Runnables in the plugin

Support for “runnables” was added to the plugin early July 2019 and extended November 2019. A runnable is a snippet of user-specified code that is executed at a pre-defined sequence during the overall acquisition. Runnables generally should not be used when exact timing matters because their exact execution time will be controlled by Micro-Manager and subject to the whims of the host computer and not done with exactly predictable timing (if you need exact timing, you can use the Tiger controller's flexible Programmable Logic Card (PLC) to implement).

These runnables are normally defined in a Beanshell script executed from Micro-Manager's script window and “attached” to the plugin after the plugin has been invoked. See the below example.

Currently there are six times when runnables can be inserted:

  1. Start of acquisition
  2. End of acquisition
  3. Start of timepoint
  4. End of timepoint
  5. Start of position
  6. End of position

Note that these would be called only once each for an acquisition without timepoints nor positions selected. Recall that timepoints is always the “outer loop” relative to positions using the plugin (but to interchange the order you can write a script that uses positions as the outer loop). The start/end acquisition runnables are only executed once even when the “separate viewer / file” option is enabled in the data saving settings.

Here is an example adding a runnable to each of these possible invocation points. Note the use of global variables in the script so that state can be passed between runnables, as well as local variables within a runnable that cannot be changed by another runnable (e.g. reset at the start of the acquisition).

// required imports
import org.micromanager.asidispim.api.*;
 
// get a reference to the plugin; use this to call the plugin's API methods
// API documented in ASIdiSPIMInterface.java in source code 
//   (https://valelab4.ucsf.edu/trac/micromanager/browser/plugins/ASIdiSPIM/src/org/micromanager/asidispim/api/ASIdiSPIMInterface.java)
// note that the plugin must be launched for this to work
ASIdiSPIMInterface diSPIM = new ASIdiSPIMImplementation();
int runnableTimepointCounter;
int runnablePositionCounter;
 
taskStartAcquisition = new Runnable() {
	public void run() {
		// test code which will raise dialog; should be replaced
		gui.showError("started overall acquisition");
		runnableTimepointCounter = 0;
		runnablePositionCounter = 0;
	}
};
 
taskEndAcquisition = new Runnable() {
	public void run() {
		// test code which will raise dialog; should be replaced
		gui.showError("ended entire acquisition");
	}
};
 
 
taskStartPosition = new Runnable() {
	public void run() {
		runnablePositionCounter++;
		// test code which will raise dialog; should be replaced
		gui.showError("started position " + runnablePositionCounter);
	}
};
 
taskEndPosition = new Runnable() {
	int localCounter = 0;
	public void run() {
		localCounter++;
		// test code which will raise dialog; should be replaced
		gui.showError("ended position, which is call number " + localCounter + " to this runnable");
	}
};
 
taskStartTimepoint = new Runnable() {
	public void run() {
		runnablePositionCounter = 0;
		runnableTimepointCounter++;
		// test code which will raise dialog; should be replaced
		gui.showError("started timepoint " + runnableTimepointCounter );
	}
};
 
taskEndTimepoint = new Runnable() {
	int localCounter = 0;
	public void run() {
		localCounter++;
		// test code which will raise dialog; should be replaced
		gui.showError("ended timepoint, which is call number " + localCounter + " to this runnable");
	}
};
 
// add all run
diSPIM.clearAllRunnables();
diSPIM.attachRunnable(taskStartAcquisition, RunnableType.ACQUISITION_START);
diSPIM.attachRunnable(taskEndAcquisition, RunnableType.ACQUISITION_END);
diSPIM.attachRunnable(taskStartPosition, RunnableType.POSITION_START);
diSPIM.attachRunnable(taskEndPosition, RunnableType.POSITION_END);
diSPIM.attachRunnable(taskStartTimepoint, RunnableType.TIMEPOINT_START);
diSPIM.attachRunnable(taskEndTimepoint, RunnableType.TIMEPOINT_END);