There are two main Python libraries to interface with microscopes through Micro-Manager:
- 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
- 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.
- pymmcore-plus is a thin wrapper
of
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:
- Enthought’s python distribution (EPD)
- PythonXY totally free.
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:
- Micro-Manager Configuration Guide to help understand how properties work.
- The general Micro-Manager Programming Guide
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
- Image manipulation and processing using Numpy and Scipy by Python Scientific Lecture Notes.
- Scikits-image gallery
- Lectures on scientific computing with Python
- OpenCV-Python Tutorials
Written by Eugene Dvoretsky – Radioxoma 09:19, 14 June 2014 (PDT)