Move through this tutorial using the arrow keys.
Code is in grey color.
Try to write the requested lines or scripts yourself, before looking at one of the many possible solutions. Use the web resources to find the appropriate functions. If you can not easily figure things out, do not hesitate to look at the answer, and use the example given to make your code work.
The Micro-Manager object (usually named "mm”, but sometimes called “studio”) is organized in various “managers”, such as the “AcquisitionManager”, and “SnapLiveManager”, that contain functions executing most of the things users normally do with Micro-Manager. Let’s start with the SnapLiveManager that you can obtain using “mm.live()”.
Use the REPL to Snap and Display an image in a single line of code:
images = mm.live().snap(true);
Note that this call returns a list of type “Image”. Image is a datastructure that holds the actual pixel data, but also metadata such as width and height, bytes per pixel, microscope metadata, and “Coords”. Coords specify the position in a multi-dimensional acquisition where the image belongs. Coords need to be created with a “builder”. You can obtain a builder using:
import org.micromanager.data.Coordinates;
cb = Coordinates.builder();
You do need to set all axes of the Coordinates builder, or strange bugs will slip in. Build a Coords with the channel set to position 2, time to 1, z, and position to 0:
coord = cb.c(2).t(1).z(0).p(0).build();
Datastore objects can be thought of as collections of images. They can store images in RAM or on disk (in multiple formats). The DataManager can create Datastores, for instance:
store = mm.data().createRAMDatastore();
To open a viewer displaying the data in your store, use:
viewer = mm.displays().createDisplay(store);
Now write a script that snaps two images without displaying them, inserts them into a datastore, and displays the datastore.
One way to do this:
// import needed to get the Coords builder import org.micromanager.data.Coordinates; store = mm.data().createRAMDatastore(); display = mm.displays().createDisplay(store); // create the Coords builder, and initialize all axes to zero cb = Coordinates.builder(); cb.c(0).t(0).p(0).z(0); for (c = 0; c < 2; c++) { // snap an image but do not display images = mm.live().snap(false); img = images.get(0); // copy image with Coords specified by our builder cImg = img.copyAtCoords(cb.c(c).build()); // and place it in the store store.putImage(cImg); }
User feedback
The LogManager (get it using: mm.logs()) interacts with the logging system of Micro-Manager. Logs can be found in the "CoreLogs" directory within the Micro-Manager application directory (for instance: C:\Program Files\Micro-Manager\Corelogs). The LogManager can also be used to show messages to the user. Use the REPL to display a message in the GUI:
mm.logs().showMessage("Hello");
Use the REPL to log an error, and check the logs to see if it is there:
mm.logs().logError("This is not really an error");
Use the ScriptManager to print a message to the REPL:
mm.scripter().message("Insert interesting message here");
Position List
The PositionListManager gives access to the Stage Position List Dialog content. A PositionList contains instances of MultiStagePosition. You can use the static function MultiStagePosition.goToPosition to move the stage(s) to a desired postion. Add a few positions to the Stage Position List in the GUI (use the Stage Control to move to different positions), then write a script to move the stage to the second position in the list:
pl = mm.positions().getPositionList(); msp = pl.getPosition(1); msp.goToPosition(msp, mmc);
Using image analysis (ImageJ
Pixels can be accessed through the Image object, and used in any image processing/analysis code that is on the classpath. ImageJ is one such library that is readily available (others are imglib2 and boofCV). Convenience functions to convert between Micro-Manager and ImageJ data structures are provided in ImageJConverter, accessible through mm.data().ij().
Write a short script that snaps an image without displaying it, gets an ImageJ ImageProcessor from that image, uses the Imagej function "findEdges()" to find edges, converts it back into a Micro-Manager Image, and displays it using mm.live():
images = mm.live().snap(false); img = images.get(0); // convert image into an ImageJ ImageProcessor ip = mm.data().ij().createProcessor(img); ip.findEdges(); // convert ImageProcessor back into a MM Image and display newImg = mm.data().ij().createImage(ip, img.getCoords(), null); mm.live().displayImage(newImg);
Dealing with image sequences
Often, streaming the camera is the fastest way to acquire images. Combined with hardware synchronization, very fast multi-D acquisitions are possible. Some special coding strategies are needed to acquire images from an image sequence. Write a script that starts a sequence of 10 images, and puts them as a time series into a data store:
cb = mm.data().getCoordsBuilder().z(0).c(0).p(0).t(0); store = mm.data().createRAMDatastore(); display = mm.displays().createDisplay(store); mmc.startSequenceAcquisition(10, 0, true); int curFrame = 0; while (mmc.getRemainingImageCount() > 0 || mmc.isSequenceRunning(mmc.getCameraDevice())) { if (mmc.getRemainingImageCount() > 0) { tagged = mmc.popNextTaggedImage(); // Convert to an Image at the desired time point image = mm.data().convertTaggedImage(tagged, cb.time(curFrame).build(), null); store.putImage(image); curFrame++; } else { mmc.sleep(5); } }
Dealing with image sequences(2)
Now run a sequence of 10 images, and put the odd numbered images into channel 0, and the even numbered ones in channel 1 (resulting in 5 time points total):
cb = mm.data().getCoordsBuilder().z(0).c(0).p(0).t(0); store = mm.data().createRAMDatastore(); display = mm.displays().createDisplay(store); mmc.startSequenceAcquisition(10, 0, true); int curFrame = 0; while (mmc.getRemainingImageCount() > 0 || mmc.isSequenceRunning(mmc.getCameraDevice())) { if (mmc.getRemainingImageCount() > 0) { tagged = mmc.popNextTaggedImage(); // Convert to an Image at the desired channel. image = mm.data().convertTaggedImage(tagged, cb.t(curFrame / 2).c(curFrame % 2).build(), null); store.putImage(image); curFrame++; } else { mmc.sleep(5); } }
Acquisitions
The Micro-Manager acquisition engine translates instructions such as those provided through the MDA window into instructions to the hardware, and puts the resulting images into a datastore. You get access to the Acquisition Engine through the AcquisitionManager. Write a one liner to run an acquisition with the current settings in the MDA dialog:
mm.acquisitions().runAcquistion();
// or
mm.acquisitions().runAcquisitionNonBlocking();
Acquisition Settings are contained in the SequenceSettings class. Although these can be changed directly, the preferred (and in the future, only) way to do so is through a "Builder", which you can obtain with:
import org.micromanager.acquisition.SequenceSettings; sequenceSettings = mm.acquisitions().getAcquisitionSettings(); sb = new SequenceSettings.Builder(sequenceSettings);
Acquisitions
Write a short script that updates the comments in the MDA dialog:
import org.micromanager.acquisition.SequenceSettings; sequenceSettings = mm.acquisitions().getAcquisitionSettings(); sb = new SequenceSettings.Builder(sequenceSettings); sb.comment("Where is the Red Necked Phalarope?"); mm.acquisitions().setAcquisitionSettings(sb.build());
A very powerful feature is the addition of "Runnables" to an acquisition. Using the function "attachRunnable()", you can add a Runnable, and specify when (i.e. before which specific image(s) in the acquisition the Runnable should run. Runnables are a concept from the Java world, specifying a piece of code that will "run" when it is called. In Beanshell, you can create a Runnable with the following code:
runnable = new Runnable() { public void run() { // do stuff here } }
Acquisitions
Write a script that attaches a Runnable to the acquisition. Imagine a non-motorized microscope, and you want to prompt the user to put a new filter cube in place whenever a new channel is imaged:
mm.acquisitions().clearRunnables(); runnable = new Runnable() { int count = 0; public void run() { settings = mm.acquisitions().getAcquisitionSettings(); channels = settings.channels; channel = channels.get(count); mm.logs().showMessage("Move filters for channel " + channel.config + " in place"); ++count; if (count >= channels.size()) { count = 0; } } }; mm.acquisitions().attachRunnable(-1, 0, -1, 0, runnable);