The 2.0 API of Micro-Manager is a substantial departure from previous
versions. With it, we want to provide access to many aspects of the
program that were previously hidden in internal classes. For example, it
is now much more straightforward to access acquisition image data,
control image display windows, read and modify acquisition metadata, and
save data to disk. Additionally, we have rearranged the
org.micromanager
package. org.micromanager.api
has been removed;
instead, everything that is not part of the API has been moved into
various “internal” packages (like org.micromanager.internal
,
org.micromanager.display.internal
, etc.).
The Javadocs should prove useful to anyone wanting to familiarize themselves with the API. This page provides some more focused and descriptive guidance, with reference to the Javadocs where appropriate.
Acquisition data: the data API
The org.micromanager.data package contains all of the API for reading and manipulating acquisition data. In particular, there are several new classes that you should familiarize yourself with if you want to read, modify, or create acquisition data or metadata.
As a general note, many of the classes (in particular, Image, Coords, Metadata, and SummaryMetadata) below are immutable. This means that a given instance of the class cannot be modified – it will have methods to read its state, but no methods to modify them. If you want a modified version of a class, instead you make a copy that is different in the relevant ways. Immutability provides many advantages for ensuring coherency of data in complex workflows. However, it can take some getting used to.
The DataProvider and Datastore classes
org.micromanager.data.DataProvider
is a new class that holds the image data and summary metadata of an
acquisition. Whereas data can only be read from a DataProvider
(i.e. you can not write data to a DataProvider), data can be added to a
org.micromanager.data.Datastore
(which otherwise has the same functionality as a DataProvider).
Every acquisition has an associated Datastore, and you can
create your own Datastores to perform custom acquisitions (see the
section on the
DataManager,
below). A RewriteableDatastore
lets you replace already present images with new ones, whereas a Datastore will not let you do that.
The primary functions you will likely be interested in are:
- putImage and
getImage, which allow you to add and retrieve Images from the
Datastore (see the
Image
class, below). - save, which saves the contents of the Datastore to disk. There’s also a version of the method that prompts the user for a directory to save to.
- registerForEvents, which allows you to be notified whenever the data in the Datastore (i.e. the Images or SummaryMetadata) are modified. The Datastore has a com.google.common.eventbus.EventBus that signals events; appropriate methods with the com.google.common.eventbus.Subscribe annotation will be called when the event occurs. For example:
import com.google.common.eventbus.Subscribe;
import org.micromanager.data.Datastore;
import org.micromanager.data.Image;
import org.micromanager.data.NewImageEvent;
import org.micromanager.Studio;
public class MyClass {
public MyClass(Studio studio) {
Datastore myDatastore = studio.data().createRAMDatastore();
myDatastore.registerForEvents(this);
}
@Subscribe
public void onNewImage(NewImageEvent event) {
Image newImage = event.getImage();
// Examine the newly-added image...
}
}
The following events can appear through the DataProvider eventbus:
- org.micromanager.data.DataProviderHasNewImageEvent
- org.micromanager.data.DataProviderHasNewNameEvent
- org.micromanager.data.DataProviderHasNewSummaryMetadataEvent
- org.micromanager.data.DataStoreFrozenEvent
- org.micromanager.data.ImageDeletedEvent
- org.micromanager.data.ImageOverwrittenEvent
The Image class
The
Image
class replaces the old TaggedImage
class as our internal
representation of a single 2D, monochromatic image. Where TaggedImage
had a JSONObject
for all metadata and an Object
for the image pixel
data, Image has getter methods for:
- The Metadata of the image (see below)
- The Coords of the image, which represent its location within the Datastore.
- The image data itself. As with TaggedImage, this data is available
as a raw Object (most likely a
byte[]
orshort[]
); however, the Image class can also generate an ImgPlus if you are running in Fiji and want to make use of the ImgLib image manipulation routines.
In addition there are various methods for creating copies of the Image
with different metadata or at different coordinates.
While Image
is intended to completely supplant TaggedImage
, the
latter class is not completely gone; it is still generated by various
MMCore
functions. You can generate an Image
from a TaggedImage
by
calling the
convertTaggedImage method of the DataManager
, below.
Metadata and SummaryMetadata
These classes replace the JSONObject
that 1.4 used to store image
metadata and acquisition summary metadata. The old system did not
enforce any kind of type safety, tended to have redundant and/or
vaguely-named entries, and was slow. The new classes have pre-defined
supported fields that are strongly-typed, and can be rapidly constructed
based on a template metadata object.
Both of these classes (and the Coords and DisplaySettings classes, discussed later) use a “Builder” pattern to create new instances. See Using_Builders for more information.
If you have extra fields that you wish to include in the metadata or
summary metadata, there is a userData
field with the type of
PropertyMap,
which allows for storing semi-arbitrary key-value pairs. You can add
whatever fields you like to this object. Micro-Manager itself (the
“first-party” code) will not pay any attention to values in that object,
but it will preserve them for any third-party code (plugins and scripts)
that does.
Coords
A Coords is a zero-based location in N-dimensional space. It includes an arbitrary list of axes (represented by strings like “time”, “channel”, etc.), and a position along each axis. Every Image has a Coords denoting its position in the Datastore, and every Image’s coordinates must be unique. If you attempt to add an Image to a Datastore whose Coords match those of an image that is already in the Datastore, an error will occur (whereas you can replace existing images with the same Coords in a RewritableDatastore).
You are not restricted to the classic 4 axes of channel, time, z slice, and stage position. You can freely add additional axes and call them whatever you like. The only axis that Micro-Manager treats specially is the channel axis, and then only in the display code. Additionally, you are not required to provide a position along any given axis. If you ask a Coords what its position is for an axis it does not have, it will return 0. Note that this is not a useful way to figure out if a DataProvider contains images for the given axis, since it will also return 0 for the first image along each axis.
However, please note that Micro-Manager’s savefile formats do not support extra axes at this time. Consequently, use of arbitrary axes must be limited to RAM-based Datastores (created with () DataManager.createRAMDatastore). In the future we will add support for saving arbitrary axes to disk.
As with the Metadata
and SummaryMetadat
classes, Coords
are
generated using a builder.
Coords.CoordsBuilder builder = mm.data().coordsBuilder();
// Convenience functions to set the standard axes
builder.time(4); // 5th timepoint
builder.channel(1); // 2nd channel
builder.position("polarization", 2); // Example custom axis
Coords coords = builder.build();
int timepoint = coords.getPositionAt(Coords.TIME); // 4
int polarization = coords.getPositionAt("polarization"); // 2
int zSlice = coords.getPositionAt(Coords.Z) // 0
The DataManager
Because the API only exposes interfaces (i.e. it does not expose concrete implementations of classes), you do not have the ability to directly create new instances of the Datastore, Image, Coords, Metadata, and SummaryMetadata classes. The DataManager provides that functionality for you (via e.g. () coordsBuilder, () metadataBuilder, and () summaryMetadataBuilder), and also implements some useful utility methods (like convertTaggedImage, mentioned earlier, and loadData to load data from disk). The DataManager can be accessed via the () Studio.data() method, and uses of it are shown in examples above.
Display controls: the display API
The display package contains the API for creating, controlling, and manipulating image display windows. It provides the ability to create new display windows, programatically control them (including both adjusting the parameters used to draw the image, and the ability to draw overlays), and listen to certain events they generate when interacted with.
DataViewer
DataViewer is the class that all Micro-Manager image display windows implement. Each DataViewer is associated with a single Datastore, and displays the images in that store. However, there may be multiple DataViewers for a single Datastore. The DataViewer class exposes a large number of methods for selecting which images are displayed and controlling how they are displayed. For example, the setDisplayedImageTo method takes a Coords object (described above) and changes the displayed image to be the one at the specified coordinates.
Most of the properties of the display, like the channel colors, contrast settings, and magnification, are encompassed by the DisplaySettings object (see below). This object can be accessed with () getDisplaySettings and changed with setDisplaySettings.
An important caveat for ImageJ operations: Micro-Manager’s DataViewer is not an ImageJ ij.gui.StackWindow as it was in the 1.x versions. Micro-Manager strives to maintain backwards compatibility by creating a hidden StackWindow that transparently redirects most calls to the DataViewer; this allows ImageJ plugins and tools to still largely operate as expected. However, direct window manipulations (e.g. changing the window size or position), and possibly other operations, could behave unexpectedly. If you need to find a DataViewer (i.e. you do not have a reference to it handy), use the () DisplayManager.getActiveDataViewer method or the () DisplayManager.getAllDataViewers method. If you need to manipulate a DataViewer as a Java window (for example to change its position onscreen), use the () DataViewer.getWindow method.
As with Datastore, each DisplayWindow has a com.google.common.eventbus.EventBus, whose events you can subscribe to using the registerForEvents method. In particular, you should listen to the RequestToCloseEvent if you want to be notified when the user clicks on the close button of the window. DisplayWindows cannot close unless their () forceClosed method is called. If you are not interested in handling this logic, read about DisplayWindow tracking in the DisplayManager section, below.
DisplaySettings
The DisplaySettings class tracks most aspects of how exactly images are displayed in a particular DisplayWindow. As with the Metadata and SummaryMetadata classes discussed earlier, it is constructed with a Builder pattern.
Whenever the DisplayWindow receives new DisplaySettings (e.g. via the setDisplaySettings method), it posts a NewDisplaySettingsEvent on its EventBus. The various controls in the window listen for this event, and automatically update themselves to reflect the new settings. Your own code may also listen for this event if you have controls that need to be updated to reflect the new state of the display.
DisplayManager
The DisplayManager class (accessible via () Studio.displays) performs various tasks that aren’t specific to other classes in the display package. In particular, as with DataManager above, it provides ways to instantiate objects whose constructors are not exposed in the API: createDisplay to create a new DisplayWindow, and () getDisplaySettingsBuilder to generate a new DisplaySettingsBuilder. It also implements two utility methods to find DisplayWindows: () getCurrentWindow to return the topmost DisplayWindow, and () getAllImageWindows to return a list of all DisplayWindows.
Display Management
In addition to the above, DisplayManager also handles management of Datastores. A “managed” Datastore will be disposed of when all of its DisplayWindows go away. Additionally, before the last DisplayWindow is allowed to close, either the Datastore must have saved its data, or the user must have explicitly confirmed closing without saving.
By default, newly-created Datastores from the DataManager’s Datastore-creation methods are not managed; thus, you are responsible for ensuring that they are saved, if so desired, as well as calling the forceClosed method of windows that are no longer needed. If you want to transfer this responsibility to Micro-Manager, call the manage method of DisplayManager. It will automatically find all DisplayWindows associated with the Datastore, and register on their EventBuses. All DisplayWindows except the last are allowed to close when the user clicks the close button. When the last one tries to close, the DisplayManager will ensure that the user has a chance to save their data.
All Datastores that Micro-Manager creates for its own purposes (for example, for storing the results of a multi-dimensional acquisition) are automatically managed.
Other API entities
Album
The Album provides access to a “contextual” Datastore that users can use to create ad-hoc collections of images. This is the store that the “Snap to image” button adds images to. You can access the Album via the () Studio.album method.
CompatibilityInterface
The CompatibilityInterface is where various miscellaneous API methods from 1.4 live. These include methods for running acquisitions, manipulating the data processing pipeline, checking the day/night mode, working with the Stage Position List, etc. Effectively it is a subset of the old ScriptInterface object from 1.4; however, many methods that used to be in it have been moved into more specific entities, or removed altogether due to changes in how 2.0 handles data.
You can access the CompatibilityInterface via the () Studio.compat method.
MMCore
The Micro-Manager Core is unchanged compared to 1.4. You can access it using () Studio.core.
EventManager
The EventManager is used to register for various system-wide events, as listed in the org.micromanager.events package. You can access the EventManager via the () Studio.events method.
SnapLiveManager
SnapLiveManager is used for controlling the “Snap” and “Live” components and is thus important for taking images with the camera(s). It can be accessed via the () Studio.live method.
LogManager
The LogManager is used to log and display messages, which can be useful for indicating when important events occur or when something goes wrong. Accessed via the () Studio.logs method.
UserProfile
The UserProfile stores information specific to the current user of the microscope, such as preferred window positions and remembered form inputs. When the user starts Micro-Manager, they are prompted to select a profile to use; relevant information is then pulled from that profile. You can use the UserProfile to store and retrieve values using a simple key/value system. Accessed via the () Studio.profile method.
ScriptController
ScriptController is a small entity for interacting with the BeanShell scripting system. Access via the () Studio.scripter method.