Edit page History How do I edit this website?

Using the Micro-Manager python libraries

There are two main Python libraries to interface with microscopes through Micro-Manager:

  1. Pycro-manager is a multi-purpose python package for the control of microscope hardware and the acquisition of data. It is capable of performing in demanding, data-intensive applications where large datasets at mutiple GB/s for hours at a time, while also providing a concise yet flexible API that can be adapted to many different modailities of microscopy. It can be used:
    • Alongside the Micro-manager GUI to support additional automation and customization
    • As a standalone library for acquiring data from a Python environment
    • As a backend data acquisition system with Napari as an image viewer, or a custom image viewer
    • With no GUI at all in a server environment (on a single computer or across a network)
    • Pycro-manager contains a translation layer that dynamically translates Java code into Python, which allows for any Micro-Manager APIs/plugins written in Java to be controlled as if they had been writtin in Python
  2. pymmcore provides a Python bindings of the underlying C++ library mmCoreAndDevices that Micro-Manager uses. This option does not require you to launch Micro-Manager, or even to have Java installed. However, it does not come with a GUI. If you want a GUI that integrates with pymmcore you can use the community developed napari-micromanager.
    • pymmcore-plus is a thin wrapper of pymmcore that extends the core object, adding additional methods, docstrings, type hints, as well as better support for integration into a GUI event loop.
    • pymmcore is the successor of MMCorePy which was formerly included in Micro-Manager and supported only Python 2.x.

The instructions below are for an alternative mechanism in which you compile the micro-manager core yourself with python bindings.

Micromanager’s main parts:

  • CMMCore - basic module, written in C++.
  • Device adapters - various libraries that allow support for various hardware. If you want to built one and extend MM devise support, follow this guide.
  • MMCoreJ - java wrapper
  • MMStudio - Micromanager GUI (technically it is ImageJ plugin).

Environment setup

You must install python and numpy. An easy way to do this is through the conda package manager. The easiest way to install conda (or the faster mamba) is by the downloads here.

conda create -n micro -c conda-forge python matplotlib pymmcore
conda activate micro

alternatively you can install using pip:

pip install pymmcore #or pip install pymmcore-plus

Other python distributions

Other python distributions you may find useful in place of conda include:

Useful libraries

  • Scipy - scientific algorithms, multidimensional image processing toolbox.
  • Matplotlib - fastest way to show your image data.
  • Opencv - computer vision and image processing library. Sometimes faster than scipy.
  • Pillow - very basic image processing. Scipy uses it for image loading and writing.
  • Scikit-image - “pythonic” scientific-oriented image processing algorithms collection.
  • IPython - improved interactive python environment

Getting Device Adapters

To use these Python options you minimally need to have installed the Device Adapters. The easiest way to get these is to install Micro-Manager, however you can also install only the Core and Adapters by following the instructions in the mmCoreAndDevices README (this will be the best option for Linux users).

If you are using Pycro-Manager then just mmCoreAndDevices is insufficient, you will need a complete Micro-Manager installation.

Using Python API

The primary API you will interact with is the MMCore object. You can read it’s API documentation here.

Other useful pages to read are:

First steps

Next code snippets aims to be most generic. We use numpy and matplotlib as is from pure python interactive shell, but it’s convenient to use IPython with nice autocompletion capabilities.

Install pymmcore:

pip install pymmcore # or pip install pymmcore-plus

Start python interactive session. Import `pymmcore` and make sure everything is working properly.

import pymmcore
mmc = pymmcore.CMMCore()  # Instance micromanager core
mmc.getVersionInfo() # gives: 'MMCore version 2.3.2'
mmc.getAPIVersionInfo() # gives: 'Device API version 59, Module API version 10'

For this tutorial you may also use pymmcore-plus. To do this replace the first two lines with:

import pymmcore_plus
mmc = pymmcore_plus.CMMCore.instance()  # Instance micromanager core

We just get some basic information about current Micromanager installation. If there an ImportError, check your PYTHONPATH variable. If output is too verbose, run mmc.enableStderrLog(False); mmc.enableDebugLog(False).

Device loading

pymmcore-plus automatically sets the device adapter search paths for you.

There are two approaches to loading devices: loading a .cfg file and manually loading the devices one by one. For this example we will use the Demo Devices distributed with the Device Adapters.

For both of these examples you will need to point pymmcore to the folder with the device adapters. You can do this with the setDeviceAdapterSearchPaths function. The standard folder is the micro-manager folder which is installed into these folders by default:

  • Linux: /usr/local/lib/micro-manager
  • Mac: /Applications/Micro-Manager
  • Windows: C:/Program Files/Micro-Manager

System Configuration The recommended way to load devices is to use a Micro-Manager config file. For example to load the Demo Config on windows you would run:

mmc.loadSystemConfiguration("C:/Program Files/Micro-Manager/MMConfig_demo.cfg")

and then see the loaded devices with:

mmc.getLoadedDevices()

Property discovery

Every device has properties settings that let you control the device more precisely. Default values should be fine, but if you need something sophisticated, this example help you figure out how to explore it.

Snapping single image

Images returned as numpy array by calls to an instance of the pythonized Micro-Manager CMMCore class. The array dtype depends on property named PixelType (see below).

Grayscale

   >>> mmc.snapImage()
   >>> img = mmc.getImage()  # img - it's just numpy array
   >>> img
   array([[12, 12, 13, ..., 11, 12, 12],
          [12, 12, 13, ..., 11, 12, 12],
          [12, 13, 13, ..., 12, 12, 12],
          ...,
          [22, 22, 22, ..., 22, 22, 22],
          [22, 22, 22, ..., 22, 22, 22],
          [22, 22, 22, ..., 22, 22, 22]], dtype=uint8)

DemoCamera snaps grayscale 8-bit image, by default. It presented as two-dimensional numpy array. Let’s show image data with matplotlib.

   >>> import matplotlib.pyplot as plt
   >>> plt.imshow(img, cmap='gray')
   >>> plt.show()  # And window will appear

Color

Of course, color image is more suitable for optical microscopy purposes. So take one, if your camera support it:

   >>> mmc.setProperty('Camera', 'PixelType', '32bitRGB')  # Change pixel type
   >>> rgb32 = mmc.getImage()
   >>> rgb32
   array([[1250067, 1250067, 1315860, ..., 1250067, 1250067, 1250067],
          [1250067, 1315603, 1315860, ..., 1250067, 1250067, 1250067],
          [1250067, 1315859, 1315860, ..., 1250067, 1250067, 1250067],
          ...,
          [1246483, 1246483, 1246483, ..., 1181204, 1246740, 1246484],
          [1246483, 1246483, 1246483, ..., 1246740, 1246740, 1246483],
          [1246483, 1246483, 1312019, ..., 1246740, 1246740, 1246483]], dtype=uint32)

Interesting output isn’t it? We expect something like 3-dimensional RGB array, but get bunch of 32-bit uints in 2-D shape.

Numpy array

Now we should look at RGB32 pixel data structure. Every pixel has 32-bit depth and contain 4 values for blue, green, red and blank channel. Blank channel is more technical peculiarity, than necessity.

low memory address    ---->      high memory address
| pixel | pixel | pixel | pixel | pixel | pixel |...
|-------|-------|-------|-------|-------|-------|...
|B|G|R|A|B|G|R|A|B|G|R|A|B|G|R|A|B|G|R|A|B|G|R|A|...

http://avisynth.nl/index.php/RGB32

Let’s numpy handle that.

>>> import numpy as np
>>> rgb32.shape
(512, 512)
>>> rgb = rgb32.view(dtype=np.uint8).reshape(
        rgb32.shape[0], rgb32.shape[1], 4)[...,2::-1]
>>> rgb.shape
(512, 512, 3)
>>> rgb.dtype
dtype('uint8')

It is a fastest way to get pixel data as RGB array without copying. There is no conversion - just creating new view to same data. Now you can process image with scipy or scikits-image. Note, that opencv uses BGR order (replace slice to [..., :3] for that).

Continuous acquisition

’'’Don’t run this code directly.’’’ It’s a partial sample.

mmc.startContinuousSequenceAcquisition(1)
while True:
    if mmc.getRemainingImageCount() > 0:
        frame = mmc.getLastImage()
        # or frame = mmc.popNextImage()

Code examples

A longer example script, MMCoreWrapDemo.py, is available in the Micro-Manager root directory.

Also check out micromanager-samples repo (live video acquisition, property discovery etc).

Further reading

Written by Eugene Dvoretsky – Radioxoma 09:19, 14 June 2014 (PDT)