ImSwitch

https://joss.theoj.org/papers/10.21105/joss.03394/status.svg

ImSwitch is a software solution in Python that aims at generalizing microscope control by using an architecture based on the model-view-presenter (MVP) design pattern and enabling flexible control of multiple microscope modalities.

The constant development of novel microscopy methods with an increased number of dedicated hardware devices poses significant challenges to software development. ImSwitch is designed to be compatible with many different microscope modalities and customizable to the specific design of individual custom-built microscopes, all while using the same software. We would like to involve the community in further developing ImSwitch in this direction, believing that it is possible to integrate current state-of-the-art solutions into one unified software.

In this documentation page you will find all information you need about the installation, usage and development of ImSwitch, both from the user perspective (GUI description and use cases) as well as for developers (scripting and API modules, and hardware control and JSON config files).

Installation

Option A: Standalone bundles for Windows

Windows users can download ImSwitch in standalone format from the releases page on GitHub. Further information is available there. An existing Python installation is not required.

Option B: Install using pip

ImSwitch is also published on PyPI and can be installed using pip. Python 3.7 or later is required. Additionally, certain components (the image reconstruction module and support for TIS cameras) require the software to be running on Windows, but most of the functionality is available on other operating systems as well.

To install ImSwitch from PyPI, run the following command:

pip install ImSwitch

You will then be able to start ImSwitch with this command:

imswitch

(Developers installing ImSwitch from the source repository should run pip install -r requirements-dev.txt instead, and start it using python -m imswitch)

Changelog

1.2.1

Highlights:

  • Snaps can now be saved to the image viewer (#64)

  • Snaps can now be saved as tiff files (#75)

  • Resolved the issue of not being able to run scans with only one positioner or only one laser

  • Fixed the step up/down buttons not working properly for multi-axis positioners

  • Fixed the api.imcontrol.setDetectorToRecord method not working

A list of all code changes is available on GitHub: https://github.com/kasasxav/ImSwitch/compare/v1.2.0…v1.2.1

1.2.0

Highlights:

  • Saving multi-detector and timelapse recordings in a single file is now supported (#53)

  • Selecting specific detectors to record is now supported (#52)

  • It is now possible to edit values/on-off-state of non-involved lasers during scanning (#51)

  • The image reconstruction module now allows reconstructing all loaded data files (e.g. multi-file timelapses) into a single reconstruction (#34)

  • The documentation has been improved (#61, #63)

  • Fixed the SLM widget causing crashes on macOS (#57)

  • Fixed the module picker being empty in standalone Windows bundles (#55)

A list of all code changes is available on GitHub: https://github.com/kasasxav/ImSwitch/compare/v1.1.0…v1.2.0

1.1.0

Highlights:

  • ImSwitch is now available to install from PyPI, and standalone Windows bundles are also available to download from the releases page on GitHub. (#38)

  • User configuration files are now saved to an appropriate user directory. On Windows, this is the documents directory, and on other operating systems it’s the user’s home directory. (#40)

  • Added a Tools menu item for setting active modules. (#27)

  • Added an image shifting tool to the hardware control module. (#30)

  • Added support for presets to laser widget. (#25)

  • The laser widget is now a vertical list instead of a horizontal one. (#24)

  • Resolved the issue of timelapse recordings sometimes containing too few frames. (#33)

A list of all code changes is available on GitHub: https://github.com/kasasxav/ImSwitch/compare/v1.0.0…v1.1.0

1.0.0

Initial release.

How to contribute

We want to encourage users and developers to give active feedback on their experience and feel free to contribute.

First, we feel very strongly about being inclusive and respectful to other contributors. Please follow the Python Community guidelines if you wish to contribute to our project.

There are different ways you can contribute:

Reporting bugs

If you encounter a bug, you can directly report it in the issues section. Please describe how to reproduce the bug and include as much information as possible that can be helpful for fixing it.

Have you written code that fixes the bug? You can open a new pull request or include your suggested fix in the issue.

User feedback

We would like to hear about your experience when using ImSwitch and suggestions for improvement. You can do that by starting a thread in the discussion section on GitHub.

Suggest a new feature

If you are missing features and want to develop ImSwitch further, you can start a discussion and brainstorm your suggestions. Feel free to open a pull request, but we believe it’s essential to have feedback from the community if you want to add new functionality.

Improving documentation

We would like to include your use-case into the documentation! Feel free to open a discussion thread about what you wish to include or improve.

Adding device support

See this page for information on adding support for new devices. You can start a discussion if you want to get advice and help to get started.

Automated tests

We want to keep including automated tests into the development process, so if you have contributed to the project by fixing a bug or providing new functionality, we encourage you to write code that tests your contribution as well.

ImSwitch’s pytests are located inside the _test/unit/ and _test/ui/ folders under each ImSwitch module’s directory. So if you have added a new widget to the hardware control module and want to write UI tests for it, you should place the UI test files in the /imswitch/imcontrol/_test/ui/ folder.

The test suite will automatically be run on commits and pull requests on GitHub. You can also run it manually by executing the command python -m pytest --pyargs imswitch in the root directory of the repository.

Graphical user interface

_images/gui.png

The ImSwitch GUI is divided in different modules in order to make it intuitive to explore for both users and developers.

_images/gui_skeleton.png

Detector Settings

This section interacts with the different detectors of the system. These can be either Cameras or Point Detectors. The user has access to different parameters like the subarray size (to determine the field-of-view used in a camera), a set of ROIs to use (which can be updated in real time by saving the current ROI), and other properties like the trigger type and exposure time. The parameters shown will depend on the type of detector selected, and additional parameters can be added by the developer in the specific DetectorManagers.

_images/detector-widget.png

Recording and data storage

Either during a scan or free running mode, the recording section will make sure to retrieve all the incoming images from the DetectorManagers selected and save them.

The images can be saved in the disk, in RAM to be shared to the Image Processing module, or both. There are different recording modules depending on the type of recording:

  • Number of frames: the user specifices the number of frames to be saved.

  • Time (s): similar as above but specifying the time instead.

  • Scan once: the recording will stop once the current scan does.

  • Timelapse: there will be sequential recordings spaced by the time that the user inputs. Each image from the scan (or raw frames) will be saved in a different file.

  • 3D Lapse: same as timelaps but moving the positioner in between, alternative way to perform a 3D scan.

  • Run until stop: the recording thread will run until it’s stopped by the user.

The data will be saved in hdf5 together with all user-interactable parameters of ImSwitch (laser power, scan parameters, etc).

Images can additionally be saved after a scan, using the Snap button. This is the use-case for procedurally-updated images while using for example point-detectors as in confocal or STED imaging.

_images/recording-widget.png

Data visualization module

We incorporated Napari for visualizing the real-time images from the detectors. It works with both point detectors and cameras, and Napari offers good support for displaying multiple channels. Additionally and one could add plugins for data analysis or visualization tools. This module enables the use of multiple cameras and point detectors simultaneously. Point detector images are updated on a line-by-line basis during the acquisition.

_images/data-widget.png

Hardware control

In this section the modules for hardware control are implemented. The main components are the laser widget, scanning widget, positioner widget, focus lock widget, and SLM widget. Developers can easily add new modules following the main structure of ImSwitch, for controlling additional hardware or controlling differently current hardware. The hardware control widgets necessary are automatically loaded depending on the user-defined settings in the JSON configuration files.

Laser widget

There are two different ways we normally use the lasers, offline and triggered only by the buttons and sliders in this widget, or trigered by an acquisition card controlled by the scanning widget. In the latter case we press the Digital Modulation button and set the desired powers during the scan.

_images/laser-widget.png

SLM widget

In the SLM widget you can control the phase masks which you use to shape the laser line that is incident on it. The SLM widget is configured to control two simultaneous phase masks applied on a beam, such as for shaping a STED laser beam into an overlayed donut and tophat pattern for 3DSTED, but can readily be reprogrammed to deal with other beam shaping for different methods. Through the widget you can control what type of mask you want to show in each of the two sides (donut, tophat, gaussian, half/quad/hex/split patterns for alignment purposes), the position of the masks, and their respective Zernike-polynomial-based aberration correction parameters for correcting stationary aberrations in the setup (implemented are tip/tilt, defocus, spherical, vertical/horizontal coma, and vertical/oblique astigmatism, additional polynomials can be readily implemented). It also has controls for saving/loading all the parameters to/from a pickled file.

_images/slm-widget.PNG

Focus lock widget

In the focus lock widget you can control a reflection-based focus lock which operates by reflecting a laser beam off the cover slip in total internal reflection and is detected on a camera. Movement of the sample in z corresponds to lateral movement of the laser spot on the camera. The center of the spot is tracked and through a feedback loop (PI controller) commands is sent to the connect z-positioner to move the sample to counter-act the detected movement. The widget has controls for locking/unlocking the sample in the current position, setting the z-position of the connected z-positioner, a setting for handling double reflections from the sample, and settings for the proportional and integral gain of the PI controller.

_images/focuslock-widget.PNG

Positioner widget

For positioner we mean any type of scanning device that we wish to move either during a scan or by using this interface. The widget shows the current position of the positioners used in this interface, and has controls for moving them a set step size. The scripting module will also have access to these functions for automation applications.

_images/positioner-widget.png

Scanning widget

This module is designed for systems that need scanning for acquisition of an image. We have implemented it to be used with a Nidaq card, but it can also be generalized to other DAQs. In the config file the user specifies the analog/digital lines to which the instruments are connected, and the ScanDesigner and SignalDesigner will create the analog/digital signals to send to them for scanning. Specific modalities can implement their own version of the designers, since they are abstract classes.

_images/scanning-widget.png

Alignment tools

The Alignment tools are a set of widgets that we use in the lab for aligning the MoNaLISA microscope. They do not control any hardware but instead perform operations on the images that provide easy feedback on the alignment process. They can be easily hidden or added by listing them in the configuration file. The general idea is that new tools can be implemented for different microscopy modalities and added to the library.

Alignment line

Displays a line with a certain angle on top of the images.

_images/line-widget.PNG

Axial alignment tool

The user selects a ROI and this tool will plot the mean value over time.

_images/axial-widget.PNG

Rotational alignment tool

Similar as before but only over one axis (x or y).

_images/rotation-widget.PNG

uLenses tool

Will display an array of points with a certain periodicity in the image.

_images/ulenses-widget.PNG _images/ulensesview-widget.PNG

FFT tool

Performs the fourier transform of the incoming images in real time.

_images/fft-widget.PNG

Bead rec tool

During a scan, this tool will integrate and reconstruct an image given a beadscan. Each step of the scan represents one pixel.

_images/beadrec-widget.PNG

Use cases

The first time the hardware control module is initialized, it will show a dialog to choose the hardware setup to be loaded. The user can change the setup option during execution in “Tools” -> “Pick hardware setup…” in the hardware control module’s menu bar.

_images/config.png

Parallelized confocal and RESOLFT (MoNaLISA)

Here we explain how we implemented ImSwitch for MoNaLISA. In the article, you will find more information about the setup and how the data is reconstructed.

Configuration file and hardware specifications

For this microscope use case, we created the JSON file example_monalisa.json, located at /imswitch/_data/user_defaults/imcontrol_setups/example_monalisa.json

We chose a National Instruments Data Acquisition (NIDAQ) card for managing the synchronization of the devices.

In the JSON file, two cameras are specified for two-color imaging: Green and Red. Both cameras are Hamamatsu, so they use HamamatsuManager. All the required camera properties are defined there, like the DAQ digital line for external triggering, readout speed, exposure time, field of view, etc.

There are five lasers in this setup, we use acousto-optic modulators (AOM) connected to the DAQ to control some of them, and others need the vendor interface as well, in this case Cobolt. The specific manager is defined for each type, LantzLaserManager or NidaqLaserManager, (see Hardware Control Configuration).

We use a X-Y-Z stage that we control through the DAQ as well, so the axes are defined as positioners using NidaqPositionerManager. The analog lines and conversion factors are specified as well. The modules that will create the signals for the scan are BetaStageScanDesigner for the Stage, and BetaTTLCycleDesigner for the instrument synchronization.

Other config parameters related to scanning, regions of interest (ROI) and a list of widgets to be loaded are added in this file.

Hardware control module

This module is useful to control the hardware and screen the sample using widefield or our other patterns. We have provided a more detailed explanation of the GUI here. To record a super-resolution image the user sets the camera to external-trigger mode and inserts the scan pulse scheme. The scanning module synchronizes the different instruments through the DAQ, and the raw data is displayed in the liveview.

The user can choose to save the raw data either in disk (hdf5) or RAM (or both) using the Recorder widget. So, for example, we program our scan and then click “Scan Once” in REC to start our acquisition. The metadata is also saved in the hdf5 and can be reloaded from the toolbar. It contains all the scanning pulses and hardware parameters related to the experiment.

  • GUI while using two-color widefield:

_images/gui.png

Image processing module for image reconstruction

The raw data can be either manually loaded into the reconstruction module or automatically retreived from the scanning if selected in the Recording widget. The user can further analyze the data using Napari image viewer. In this module we use our custom-designed DLLs for reconstruction, since this is a rather specific type of algorithm for our method. But the idea is that different microscope techniques implement their own modules as well. “Multidata management” stacks all the data incomming from the hardware control module.

  • The Image processing module is illustrated in the following image:

_images/reconstruction.png

Point-scanning confocal and STED

Here we explain how we implemented ImSwitch for a custom-built STED setup in the lab, previously controlled by a combination of closed-source software (image acquisition) and purpose-built software (hardware control). In the article, you will find more information about the setup, what hardware it contains, and the type of image acquisition we want to perform.

Configuration file and hardware specifications

For this microscope use case, we created the JSON file example_sted.json, located at /imswitch/_data/user_defaults/imcontrol_setups/example_sted.json

We chose a National Instruments Data Acquisition (NIDAQ) card for managing the synchronization of the devices and image acquisition.

In the JSON file, two photon-counting point detectors (APD) are specified for two-color imaging: APDGreen and APDRed. These do not need any specific hardware control, but instead are read entirely through the Nidaq. Additionally two cameras are specified: one for widefield, for having an overview of the sample, and one for the focus lock, as described in detail in the cited article. Both cameras are The Imaging Source cameras, so they use TISManager. All the required camera properties are defined, like the camera index in the list of cameras, exposure, gain, brightness, and chip size in pixels.

There are three lasers in this setup, and all three have an associated AOM or AOTF to rapidly control the power, and hence there are six laser devices defined. Two of them controls only fast digital modulation through digital Nidaq lines (561 and 640 lasers); one controls fast digital modulation and analog modulation through digital and analog Nidaq lines (775AOM); one controls the 775 nm laser through RS232 communication and hence has an associated rs232device (775Katana); and the last two controls the power modulation of the multiple channels of the common AOTF for the 561 and 640 nm lasers through RS232 communication with an associated rs232device (561AOTF and 640AOTF). The speicfic manager is defined for each device, NidaqLaserManager, AAAOTFLaserManager, or KatanaLaserManager.

We use galvanometric mirros for the XY-scanning that we control through the DAQ, so the axes are defined as positioners using NidaqPositionerManager. The analog lines of the Nidaq used and conversion factors, for converting µm of the user-input to V for the signal, are specificied as well. Additionally a piezo is used for Z-movement, controlled both through analog signals from the DAQ with a NidaqPositionerManager and through RS232 communication with a PiezoconceptZManager.

The modules that will create the signals for the scan are GalvoScanDesigner for the XY-scanning, and PointScanTTLCycleDesigner for the laser synchronization. The analog scan designer will create smooth scanning signals with linear acquisition regions for good control of the galvanometric mirrors. The TTL designer will create laser modulation signals that can be controlled on a sub-line level with the widget interface, with automatic turn off during the portions of the scan that are not during acquisition.

The Hamamtsu SLM used in the setup is managed through the SLMManager, and is simply controlled by connecting it as a monitor and showing a gray-scale image with the pixel values corresponding to the phase-shift you want to impose. The manager is responsible for building this image based on the user-input from the widget.

The focus lock does not have a separate manager, but instead is associated with one of the TIS cameras and the Z-piezo rs232device. The properties for the focus lock specifies what hardware devices it should associate with, what part of the camera frame should be cropped, and the update frequency (in Hz) of the PI control loop.

The RS232 communication channel protocol parameters necessary for the control of the hardware devices requiring so are also defined in the same file.

Other config parameters and a list of widgets to be loaded are added in this file as well.

Main module

The main, and only, module for this use case is used to control all the hardware, screen the sample with widefield, acquiring the images, and inspecting them with the visualization tools. We have provided a more detailed explanation of the GUI here. To record a confocal image, the user sets the scan parameters that they want for each scan axis (length, pixel size, center position), the pixel dwell time, sets the laser powers they want to use, set the TTL start to 0 and end to 1 (units is lines) for the excitation laser they want to use, and runs the scan. The view of the detectors not in use can be hidden in the visualization tool. The scanning module will build the scanning curves, laser modulation curves, create those tasks in the Nidaq, and start them. The raw data is displayed in the liveview, where the image is updated line-by-line during the acquisition. For recording a STED image the procedure is much the same, with the addition that the use turns on the STED laser in the laser module, and sets the corresponding TTL start and end to the same values, and runs the scan. Before this the SLM has to be configured in order to create a desired depletion pattern, where for using a donut and tophat there are helpful tools in the SLM module to align the mask and the aberration correction that will be specific to each setup.

Previous to any image acquisition, while using either a repeating fast confocal scan or a widefield image, the sample has to be set in focus, and the focus lock can then be used to lock the sample in the focal plane. The focus lock acts independent from the image acquisition and can be continuously turned on for as long as wanted.

The user can choose to save the acquired image to a desired folder and with a desired name by using the Snap button in the recording widget. It will be saved in hdf5 format, and will include all user-defined parameters from the GUI as metadata. Functionality to reload metadata parameters from a previously saved hdf5 file can be found in the toolbar, for easy and precise recreation of a previous experiment. Previously recorded images in tiff format can also be loaded in the visualization module in order to be directly compared with the last recorded image or each other.

  • GUI after having acquired a confocal and a STED image:

_images/sted-confocal-usecase.png

CoolLED control through USB and TTLs using a NIDAQ

_images/coolLED_GUI.png

We got a CoolLED (https://www.coolled.com/) in the lab and decided to try ImSwitch out in a setting where we want to control the 8 lasers of the device, both by doing it manually using the sliders and buttons (using a USB port and RS232 communication protocol), but also being able to design and perform a sequence of TTLs and a X-Y-Z Stage controlled by a National Instruments card. This use case could be combined with the Napari viewer and a camera, or a point scanning system, or any of the other widgets explained in the other Use Cases.

All the lasers are listed in the JSON file example_coolLED.json, located at /imswitch/_data/user_defaults/imcontrol_setups/example_coolLED.json, by specifying:

  • Digital line of each laser in the NIDAQ.

  • Wavelength and range (0 to 100).

  • Channel name (A-H), each corresponding to the laser.

The Positioners define the stage axis with the settings, such as:

  • Analog channel of the NIDAQ.

  • Conversion factors.

  • Min and Max voltages.

  • Axis (X, Y, or Z).

Then, the CoolLEDLaserManager will communicate with the RS232Manager for sending the intensity and on/off commands. The parameters of the RS232Manager are the typical ones of a RS232 connection, such as:

  • Port (Usually COMx).

  • Encoding (ascii).

  • Baudrate (57600).

  • ByteSize (8)

  • Parity (None)

  • Stop bits (1)

The pulses will be directly handled by the National Instruments card and our TTLDesigner.

Scripting

ImSwitch provides a scripting module that can be used to automate tasks in the software. This scripting module allows you to freely write Python code that interacts with ImSwitch.

See the scripting API reference for more information about the available API modules and methods. Aside from the API modules, there are also some global-level functions that you can use, that are documented here.

The API modules may provide signals – events that can be bound to through e.g. the global getWaitForSignal scripting function.

There are a few example scripts that you can check out in the scripting module to see how the scripting functionality works in action.

Module configuration

ImSwitch consists of multiple different modules:

Name

ID

Hardware Control

imcontrol

Image Reconstruction

imreconstruct

Scripting

imscripting

One can select which modules to enable through the “Set active modules…” option in the “Tools” menu of the menu bar. By default, the hardware control and scripting modules are enabled. Modules that are not enabled will not show up in the program.

_images/module-selection.png

HDF5 datafiles

In ImSwitch we use HDF5 files to store the images and metadata containing the experiment’s parameters. The HDF5 files can be opened in for example ImageJ and Matlab with the right extensions. For the metadata, HDFView can display the attributes and datasets of the file.

It is possible to import the experiment parameters in ImSwitch from the File menu (File -> Load parameters from saved HDF5 file…), and the GUI will load and display all the parameters directly in the widgets. When recording multiple detectors simultaneously, one file will be created for each recorded detector.

Datasets

Each image recording is saved in a dataset with dimensions Z × Y × X, where Z is the number of frames while Y and X are the vertical and horizontal axes respectively. Two special parameters are stored in the dataset:

  • detector_name: name of the detector (camera or point-detector) that provided the images.

  • element_size_um: pixel size of the image, this parameter will be automatically read by ImageJ when opening the file.

Object attributes

The rest of the metadata is also stored as HDF5 attributes in the datasets, containing information about the detectors, lasers, recording, and scanning.

Detectors

There is one attribute for each detector property that is listed in the DetectorManager being used. For example, for HamamatsuManager: Binning, model, camera pixel size, readout time, ROI, etc.

The detector’s attributes follow the form:

  • Detector:NameDetector:DetectorProperty

Lasers

The power and whether it was ON/OFF for each laser is stored in the form:

  • Laser:LaserName:Enabled (boolean)

  • Laser:LaserName:Value

Positioners

The value for each positioner is stored to encode in which area the image was taken:

  • Positioner:PostionerName:PostionerAxis:Position

Recording and scanning

The parameters of the recording and scanning are attributes as well. They include all the parameters in the RecordingWidget and ScanWidget, regarding the pulse scheme, the stage positions and step sizes, the type of recording, number of frames, etc. It can vary depending on the setup used, but they generally follow the form shown below:

  • Rec:PropertyName

  • ScanStage:PropertyName

  • ScanTTL:PropertyName

Hardware control configurations

ImSwitch’s hardware control module is designed to be flexible and be usable in a wide variety of microscopy setups. In order to provide this flexibility, hardware configurations are defined in .json files that are loaded when the hardware control module starts.

Hardware configuration files are loaded from the imcontrol_setups directory, which is automatically created inside your user directory for ImSwitch the first time the hardware control module starts. It contains some pre-made configuration files by default. The user directory is located at %USERPROFILE%\Documents\ImSwitch on Windows and ~/ImSwitch on macOS/Linux.

The first time you start the hardware control module, you will be prompted to select a setup file to load. If you want to switch to another hardware configuration later, select “Tools” -> “Pick hardware setup…” in the hardware control module’s menu bar.

How configurations are defined

Hardware configurations are defined in JSON format. Behind the scenes, they are automatically translated to Python class instances when loaded into the software.

A central concept in ImSwitch is that of device managers. Device managers define what kind of device you have, and how ImSwitch communicates with it. For example, if you have a Hamamatsu camera that you would like to control, you would define a detector that uses the HamamatsuManager in the hardware setup file and set its appropriate properties. The list of available managers and their properties can be found here. Each device must have a unique name, which is represented by its object key in the JSON.

Signal designers, which are relevant for users who use the scan functionality, are similar. Microscopy scans can be set up in different ways; in a point-scanning setup, for instance, you might want to set your scan settings to use the PointScanTTLCycleDesigner to generate the appropriate TTL signals. They are documented here.

As a very simple example, a hardware configuration file that allows you to control a single Cobolt 06-01 (non-DPL) laser connected to COM port 11 can look like this:

{
    "lasers": {
        "Cobolt405nm": {
            "managerName": "Cobolt0601LaserManager",
            "managerProperties": {
                "digitalPorts": ["COM11"]
            },
            "valueRangeMin": 0,
            "valueRangeMax": 200,
            "wavelength": 405
        }
    },
    "availableWidgets": [
        "Laser"
    ]
}

Note that the digitalPorts property is specific to Cobolt0601LaserManager.

Configuration file specification

class ViewSetupInfo

This is the object represented by the hardware configuration JSON file. All fields are optional, unless explicitly otherwise specified.

availableWidgets: Union[List[str], bool]

Which widgets to load. The following values are possible to include (case sensitive):

  • Settings (detector settings widget)

  • View (image controls widget)

  • Recording (recording widget)

  • Image (image display widget)

  • FocusLock (focus lock widget; requires focusLock field to be defined)

  • SLM (SLM widget; requires slm field to be defined)

  • Laser (laser control widget)

  • Positioner (positioners widget)

  • Scan (scan widget; requires scan field to be defined)

  • BeadRec (bead reconstruction widget)

  • AlignAverage (axial alignment tool widget)

  • AlignXY (rotation alignment tool widget)

  • AlignmentLine (line alignment tool widget)

  • uLenses (uLenses tool widget; requires Image widget)

  • FFT (FFT tool widget)

  • Console (Python console widget)

You can also set this to true to enable all widgets, or false to disable all widgets.

This field is required.

defaultLaserPresetForScan: Optional[str]

Default laser preset for scanning.

detectors: Dict[str, DetectorInfo]

Detectors in this setup. This is a map from unique detector names to DetectorInfo objects.

focusLock: Optional[FocusLockInfo]

Focus lock settings. Required to be defined to use focus lock functionality.

laserPresets: Dict[str, Dict[str, LaserPresetInfo]]

Laser presets available to select (map preset name -> laser name -> LaserPresetInfo).

lasers: Dict[str, LaserInfo]

Lasers in this setup. This is a map from unique laser names to LaserInfo objects.

nidaq: NidaqInfo

NI-DAQ settings.

positioners: Dict[str, PositionerInfo]

Positioners in this setup. This is a map from unique positioner names to DetectorInfo objects.

pulseStreamer: PulseStreamerInfo

Pulse Streamer settings.

rois: Dict[str, ROIInfo]

Additional ROIs available to select in detector settings.

rs232devices: Dict[str, RS232Info]

RS232 connections in this setup. This is a map from unique RS232 connection names to RS232Info objects. Some detector/laser/positioner managers will require a corresponding RS232 connection to be referenced in their properties.

scan: Optional[ScanInfo]

Scan settings. Required to be defined to use scan functionality.

slm: Optional[SLMInfo]

SLM settings. Required to be defined to use SLM functionality.

Item types that may be included

class DetectorInfo
analogChannel: Optional[Union[str, int]]

Channel for analog communication. null if the device is digital or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/ao{analogChannel}”.

digitalLine: Optional[Union[str, int]]

Line for digital communication. null if the device is analog or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/port0/line{digitalLine}”.

forAcquisition: bool = False

Whether the detector is used for acquisition.

forFocusLock: bool = False

Whether the detector is used for focus lock.

managerName: str

Manager class name.

managerProperties: Dict[str, Any]

Properties to be read by the manager.

class LaserInfo
analogChannel: Optional[Union[str, int]]

Channel for analog communication. null if the device is digital or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/ao{analogChannel}”.

digitalLine: Optional[Union[str, int]]

Line for digital communication. null if the device is analog or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/port0/line{digitalLine}”.

freqRangeInit: Optional[int] = 0

Initial value of frequency modulation. Don’t fill if laser doesn’t support it.

freqRangeMax: Optional[int] = 0

Minimum value of frequency modulation. Don’t fill if laser doesn’t support it.

freqRangeMin: Optional[int] = 0

Minimum value of frequency modulation. Don’t fill if laser doesn’t support it.

managerName: str

Manager class name.

managerProperties: Dict[str, Any]

Properties to be read by the manager.

valueRangeMax: Optional[Union[int, float]]

maximum value of the laser. null if laser doesn’t setting a value.

valueRangeMin: Optional[Union[int, float]]

Minimum value of the laser. null if laser doesn’t setting a value.

valueRangeStep: float = 1.0

The default step size of the value range that the laser can be set to.

wavelength: Union[int, float]

Laser wavelength in nanometres.

class PositionerInfo
analogChannel: Optional[Union[str, int]]

Channel for analog communication. null if the device is digital or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/ao{analogChannel}”.

axes: List[str]

A list of axes (names) that the positioner controls.

digitalLine: Optional[Union[str, int]]

Line for digital communication. null if the device is analog or doesn’t use NI-DAQ. If an integer is specified, it will be translated to “Dev1/port0/line{digitalLine}”.

forPositioning: bool = False

Whether the positioner is used for manual positioning.

forScanning: bool = False

Whether the positioner is used for scanning.

isPositiveDirection: bool = True

Whether the direction of the positioner is positive.

managerName: str

Manager class name.

managerProperties: Dict[str, Any]

Properties to be read by the manager.

class RS232Info
managerName: str

RS232 manager class name.

managerProperties: Dict[str, Any]

Properties to be read by the RS232 manager.

class SLMInfo
angleMount: float

The angle of incidence and reflection of the laser line that is shaped by the SLM, in radians. For adding a blazed grating to create off-axis holography.

correctionPatternsDir: str

Directory of .bmp images provided by Hamamatsu for flatness correction at various wavelengths. A combination will be chosen based on the wavelength.

height: int

Height of SLM, in pixels.

monitorIdx: int

Index of the monitor in the system list of monitors (indexing starts at 0).

pixelSize: float

Pixel size or pixel pitch of the SLM, in millimetres.

wavelength: int

Wavelength of the laser line used with the SLM.

width: int

Width of SLM, in pixels.

class FocusLockInfo
camera: str

Detector name.

frameCroph: int

Height of frame crop.

frameCropw: int

Width of frame crop.

frameCropx: int

Starting X position of frame crop.

frameCropy: int

Starting Y position of frame crop.

positioner: str

Positioner name.

updateFreq: int

Update frequency, in milliseconds.

class ScanInfo
TTLCycleDesigner: str

Name of the TTL cycle designer class to use.

TTLCycleDesignerParams: Dict[str, Any]

Params to be read by the TTL cycle designer.

sampleRate: int

Scan sample rate.

scanDesigner: str

Name of the scan designer class to use.

scanDesignerParams: Dict[str, Any]

Params to be read by the scan designer.

class NidaqInfo
startTrigger: bool = False

Boolean for start triggering for sync.

timerCounterChannel: Optional[Union[int, str]] = None

Output for Counter for timing purposes. If an integer is specified, it will be translated to “Dev1/ctr{timerCounterChannel}”.

class ROIInfo
h: int

Height of ROI, in pixels.

w: int

Width of ROI, in pixels.

x: int

Starting X position of ROI, in pixels.

y: int

Starting Y position of ROI, in pixels.

class LaserPresetInfo
value: float

Laser value.

Available managers

Detector managers

class APDManager

DetectorManager that deals with an avalanche photodiode connected to a counter input on a Nidaq card.

Manager properties:

  • terminal – the physical input terminal on the Nidaq to which the APD is connected

  • ctrInputLine – the counter that the physical input terminal is connected to

class HamamatsuManager

DetectorManager that deals with the Hamamatsu parameters and frame extraction for a Hamamatsu camera.

Manager properties:

  • cameraListIndex – the camera’s index in the Hamamatsu camera list (list indexing starts at 0); set this to an invalid value, e.g. the string “mock” to load a mocker

  • hamamatsu – dictionary of DCAM API properties to pass to the driver

class PhotometricsManager

DetectorManager that deals with frame extraction for a Photometrics camera.

Manager properties:

  • cameraListIndex – the camera’s index in the Photometrics camera list (list indexing starts at 0)

class TISManager

DetectorManager that deals with TheImagingSource cameras and the parameters for frame extraction from them.

Manager properties:

  • cameraListIndex – the camera’s index in the TIS camera list (list indexing starts at 0); set this string to an invalid value, e.g. the string “mock” to load a mocker

  • tis – dictionary of TIS camera properties

Laser managers

class AAAOTFLaserManager

LaserManager for controlling one channel of an AA Opto-Electronic acousto-optic modulator/tunable filter through RS232 communication.

Manager properties:

  • rs232device – name of the defined rs232 communication channel through which the communication should take place

  • channel – index of the channel in the acousto-optic device that should be controlled (indexing starts at 1)

class CoolLEDLaserManager

LaserManager for controlling the LEDs from CoolLED. Each LaserManager instance controls one LED.

Manager properties:

  • rs232device – name of the defined rs232 communication channel through which the communication should take place

  • channel_index – laser channel (A to H)

class NidaqLaserManager

LaserManager for analog-value NI-DAQ-controlled lasers.

Manager properties: None

Positioner managers

class MHXYStageManager

PositionerManager for control of a Marzhauser XY-stage through RS232 communication.

Manager properties:

  • rs232device – name of the defined rs232 communication channel through which the communication should take place

class NidaqPositionerManager

PositionerManager for analog-value NI-DAQ-controlled positioners.

Manager properties:

  • conversionFactor – float value

  • minVolt – minimum voltage

  • maxVolt – maximum voltage

class PiezoconceptZManager

PositionerManager for control of a Piezoconcept Z-piezo through RS232 communication.

Manager properties:

  • rs232device – name of the defined rs232 communication channel through which the communication should take place

RS232 managers

class RS232Manager

A general-purpose RS232 manager that together with a general-purpose RS232Driver interface can handle an arbitrary RS232 communication channel, with all the standard serial communication protocol parameters as defined in the hardware control configuration.

Manager properties:

  • port

  • encoding

  • recv_termination

  • send_termination

  • baudrate

  • bytesize

  • parity

  • stopbits

  • rtscts

  • dsrdtr

  • xonxoff

Available signal designers

Scan designers

class BetaScanDesigner

Scan designer for X/Y/Z stages that move a sample.

Designer params:

  • return_time – time to wait between lines for the stage to return to the first position of the next line, in seconds.

class GalvoScanDesigner

Scan designer for scan systems with galvanometric mirrors.

Designer params: None

TTL cycle designers

class BetaTTLCycleDesigner

TTL cycle designer for camera-based applications where each pulse scheme is one frame.

Designer params: None

class PointScanTTLCycleDesigner

Line-based TTL cycle designer, for point-scanning applications. Treats input ms as lines.

Designer params: None

Adding support for more devices

There are three main device types that ImSwitch’s hardware control module supports: detectors, lasers and positioners. In order to add support for a new detector, laser or positioner, a corresponding device manager class must be implemented in ImSwitch’s code.

How device managers are implemented

Detector support is implemented in device manager classes derived from the abstract base class DetectorManager. The corresponding parent class for lasers is LaserManager, and for positioners it is PositionerManager. These derived classes are placed in the detectors, lasers and positioners sub-modules respectively in the imswitch.imcontrol.model.managers module.

The required constructor signature for the device managers is __init__(deviceInfo, name, **lowLevelManagers). deviceInfo is the DetectorInfo, LaserInfo or PositionerInfo object which represents the device’s entry in the setup file (see the hardware control setup page for further information). Inside it, the managerProperties dict field may contain manager-specific properties. name is a unique name that is used to identify the device, which is defined by the key of the device’s entry in the setup file. lowLevelManagers is a dict containing objects that facilitate low-level device interaction, which are documented here. Note that super().__init__ has a different signature, depending on which base class is used.

When creating a new device manager, you will need to implement all the abstract methods and properties defined in the base class. You should avoid overriding non-abstract properties. Overriding non-abstract methods is generally fine, but you should make sure that they continue to work as expected. The device manager class must be placed in a .py file with the same name as the class, in the appropriate location as outlined above. No other action is required for the device manager to be available to use; it will automatically be managed by a multi-manager as outlined in the paper.

You can find a simple example of a positioner manager implementation here.

Base class documentation

DetectorManager

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorManager(detectorInfo, name: str, fullShape: Tuple[int, int], supportedBinnings: List[int], model: str, *, parameters: Optional[Dict[str, DetectorParameter]] = None, actions: Optional[Dict[str, DetectorAction]] = None, croppable: bool = True)

Abstract base class for managers that control detectors. Each type of detector corresponds to a manager derived from this class.

abstract __init__(detectorInfo, name: str, fullShape: Tuple[int, int], supportedBinnings: List[int], model: str, *, parameters: Optional[Dict[str, DetectorParameter]] = None, actions: Optional[Dict[str, DetectorAction]] = None, croppable: bool = True) None
Parameters:
  • detectorInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • fullShape – Maximum image size as a tuple (width, height).

  • supportedBinnings – Supported binnings as a list.

  • model – Detector device model name.

  • parameters – Parameters to make available to the user to view/edit.

  • actions – Actions to make available to the user to execute.

  • croppable – Whether the detector image can be cropped.

property actions: Dict[str, DetectorAction]

Dictionary of available actions.

property binning: int

Current binning.

abstract crop(hpos: int, vpos: int, hsize: int, vsize: int) None

Crop the frame read out by the detector.

property croppable: bool

Whether the detector supports frame cropping.

finalize() None

Close/cleanup detector.

abstract flushBuffers() None

Flushes the detector buffers so that getChunk starts at the last frame captured at the time that this function was called.

property forAcquisition: bool

Whether the detector is used for acquisition.

property forFocusLock: bool

Whether the detector is used for focus lock.

property frameStart: Tuple[int, int]

Position of the top left corner of the current frame as a tuple (x, y).

property fullShape: Tuple[int, int]

Maximum image size as a tuple (width, height).

abstract getChunk() ndarray

Returns the frames captured by the detector since getChunk was last called, or since the buffers were last flushed (whichever happened last). The returned object is a numpy array of shape (numFrames, height, width).

abstract getLatestFrame() ndarray

Returns the frame that represents what the detector currently is capturing. The returned object is a numpy array of shape (height, width).

property image: ndarray

Latest LiveView image.

property model: str

Detector model name.

property name: str

Unique detector name, defined in the detector’s setup info.

property parameters: Dict[str, DetectorParameter]

Dictionary of available parameters.

abstract property pixelSizeUm: List[int]

The pixel size in micrometers, in the format [z, y, x]. z is typically set to 1.

setBinning(binning: int) None

Sets the detector’s binning.

setParameter(name: str, value: Any) Dict[str, DetectorParameter]

Sets a parameter value and returns the updated list of parameters. If the parameter doesn’t exist, i.e. the parameters field doesn’t contain a key with the specified parameter name, an AttributeError will be raised.

property shape: Tuple[int, int]

Current image size as a tuple (width, height).

abstract startAcquisition() None

Starts image acquisition.

abstract stopAcquisition() None

Stops image acquisition.

property supportedBinnings: List[int]

Supported binnings as a list.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorAction(group: str, func: callable)

An action that is made available for the user to execute.

func: callable

The function that is called when the action is executed.

group: str

The group to place the action in (does not need to be pre-defined).

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorParameter(group: str, value: Any, editable: bool)

Abstract base class for detector parameters that are made available for the user to view/edit.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorNumberParameter(group: str, value: float, editable: bool, valueUnits: str)

Bases: DetectorParameter

A detector parameter with a numerical value.

editable: bool

Whether it is possible to edit the value of the parameter.

group: str

The group to place the parameter in (does not need to be pre-defined).

value: float

The value of the parameter.

valueUnits: str

Parameter value units, e.g. “nm” or “fps”.

class imswitch.imcontrol.model.managers.detectors.DetectorManager.DetectorListParameter(group: str, value: str, editable: bool, options: List[str])

Bases: DetectorParameter

A detector parameter with a value from a list of options.

editable: bool

Whether it is possible to edit the value of the parameter.

group: str

The group to place the parameter in (does not need to be pre-defined).

options: List[str]

The available values to pick from.

value: str

The value of the parameter.

LaserManager

class imswitch.imcontrol.model.managers.lasers.LaserManager.LaserManager(laserInfo, name: str, isBinary: bool, valueUnits: str, valueDecimals: int, isModulated: bool = False)

Abstract base class for managers that control lasers. Each type of laser corresponds to a manager derived from this class.

abstract __init__(laserInfo, name: str, isBinary: bool, valueUnits: str, valueDecimals: int, isModulated: bool = False) None
Parameters:
  • laserInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • isBinary – Whether the laser can only be turned on and off, and its value cannot be changed.

  • valueUnits – The units of the laser value, e.g. “mW” or “V”.

  • valueDecimals – How many decimals are accepted in the laser value.

  • isModulated – Whether the laser can be frequency modulated.

finalize() None

Close/cleanup laser.

property freqRangeInit: int

The initial frequency of the laser modulation.

property freqRangeMax: int

The minimum frequency of the laser modulation.

property freqRangeMin: int

The minimum frequency of the laser modulation.

property isBinary: bool

Whether the laser can only be turned on and off, and its value cannot be changed.

property isModulated: bool

Whether the laser supports frequency modulation.

property name: str

Unique laser name, defined in the laser’s setup info.

abstract setEnabled(enabled: bool) None

Sets whether the laser is enabled.

setModulationDutyCycle(dutyCycle: int) None

Sets the laser modulation duty cycle.

setModulationEnabled(enabled: bool) None

Sets wether the laser frequency modulation is enabled.

setModulationFrequency(frequency: int) None

Sets the laser modulation frequency.

setScanModeActive(active: bool) None

Sets whether the laser should be in scan mode (if the laser supports it).

abstract setValue(value: Union[int, float]) None

Sets the value of the laser.

property valueDecimals

How many decimals are accepted in the laser value.

property valueRangeMax: float

The maximum value that the laser can be set to.

property valueRangeMin: float

The minimum value that the laser can be set to.

property valueRangeStep: float

The default step size of the value range that the laser can be set to.

property valueUnits: str

The units of the laser value, e.g. “mW” or “V”.

property wavelength: int

The wavelength of the laser.

PositionerManager

class imswitch.imcontrol.model.managers.positioners.PositionerManager.PositionerManager(positionerInfo, name: str, initialPosition: Dict[str, float])

Abstract base class for managers that control positioners. Each type of positioner corresponds to a manager derived from this class.

abstract __init__(positionerInfo, name: str, initialPosition: Dict[str, float])
Parameters:
  • positionerInfo – See setup file documentation.

  • name – The unique name that the device is identified with in the setup file.

  • initialPosition – The initial position for each axis. This is a dict in the format { axis: position }.

property axes: List[str]

The list of axes that are controlled by this positioner.

finalize() None

Close/cleanup positioner.

property forPositioning: bool

Whether the positioner is used for manual positioning.

property forScanning: bool

Whether the positioner is used for scanning.

abstract move(dist: float, axis: str)

Moves the positioner by the specified distance and returns the new position. Derived classes will update the position field manually. If the positioner controls multiple axes, the axis must be specified.

property name: str

Unique positioner name, defined in the positioner’s setup info.

property position: Dict[str, float]

The position of each axis. This is a dict in the format { axis: position }.

abstract setPosition(position: float, axis: str)

Adjusts the positioner to the specified position and returns the new position. Derived classes will update the position field manually. If the positioner controls multiple axes, the axis must be specified.

Available low-level managers

lowLevelManagers[‘nidaqManager’]

class imswitch.imcontrol.model.managers.NidaqManager.NidaqManager(setupInfo)

For interaction with NI-DAQ hardware interfaces.

setAnalog(target, voltage, min_val=-1, max_val=1)

Function to set the analog channel to a specific target to a certain voltage

setDigital(target, enable)

Function to set the digital line to a specific target to either “high” or “low” voltage

lowLevelManagers[‘rs232sManager’]

class imswitch.imcontrol.model.managers.RS232sManager.RS232sManager(rs232deviceInfos, **lowLevelManagers)

RS232sManager is an interface for dealing with RS232 devices. It is a MultiManager for RS232 devices.

RS232Manager instances for individual RS232 devices can be accessed by rs232sManager['your_rs232_device_name'].

class imswitch.imcontrol.model.managers.rs232.RS232Manager.RS232Manager(rs232Info, name, **_lowLevelManagers)

A general-purpose RS232 manager that together with a general-purpose RS232Driver interface can handle an arbitrary RS232 communication channel, with all the standard serial communication protocol parameters as defined in the hardware control configuration.

Manager properties:

  • port

  • encoding

  • recv_termination

  • send_termination

  • baudrate

  • bytesize

  • parity

  • stopbits

  • rtscts

  • dsrdtr

  • xonxoff

query(arg: str) str

Sends the specified command to the RS232 device and returns a string encoded from the received bytes.

write(arg: str)

Sends the specified command to the RS232 device.

Global-level functions

getLogger(self) logging.LoggerAdapter

Returns a logger instance that can be used to print formatted messages to the console.

getScriptDirPath(self) str

Returns the path to the directory containing the running script.

getWaitForSignal(self, signal: imswitch.imcommon.framework.qt.Signal, pollIntervalSeconds: float = 1.0) Callable[[], NoneType]

Returns a function that will wait for the specified signal to emit. The returned function will continuously check whether the signal has been emitted since its creation. The polling interval defaults to one second, and can be customized.

importScript(self, path: str) Any

Imports the script at the specified path (either absolute or relative to the main script) and returns it as a module variable.

api.imcontrol

class api.imcontrol

These functions are available in the api.imcontrol object.

getDetectorNames() List[str]

Returns the device names of all detectors. These device names can be passed to other detector-related functions.

getLaserNames() List[str]

Returns the device names of all lasers. These device names can be passed to other laser-related functions.

getPositionerNames() List[str]

Returns the device names of all positioners. These device names can be passed to other positioner-related functions.

getPositionerPositions() Dict[str, Dict[str, float]]

Returns the positions of all positioners.

loadScanParamsFromFile(filePath: str) None

Loads scanning parameters from the specified file.

movePositioner(positionerName: str, axis: str, dist: float) None

Moves the specified positioner axis by the specified number of micrometers.

runScan() None

Runs a scan with the set scanning parameters.

saveScanParamsToFile(filePath: str) None

Saves the set scanning parameters to the specified file.

setDetectorBinning(detectorName: str, binning: int) None

Sets binning value for the specified detector.

setDetectorParameter(detectorName: str, parameterName: str, value: Any) None

Sets the specified detector-specific parameter to the specified value.

setDetectorROI(detectorName: str, frameStart: Tuple[int, int], shape: Tuple[int, int]) None

Sets the ROI for the specified detector. frameStart is a tuple (x0, y0) and shape is a tuple (width, height).

setDetectorToRecord(detectorName: Union[List[str], str, int], multiDetectorSingleFile: bool = False) None

Sets which detectors to record. One can also pass -1 as the argument to record the current detector, or -2 to record all detectors.

setLaserActive(laserName: str, active: bool) None

Sets whether the specified laser is powered on.

setLaserValue(laserName: str, value: Union[int, float]) None

Sets the value of the specified laser, in the units that the laser uses.

setLiveViewActive(active: bool) None

Sets whether the LiveView is active and updating.

setLiveViewCrosshairVisible(visible: bool) None

Sets whether the LiveView crosshair is visible.

setLiveViewGridVisible(visible: bool) None

Sets whether the LiveView grid is visible.

setPositioner(positionerName: str, axis: str, position: float) None

Moves the specified positioner axis to the specified position.

setPositionerStepSize(positionerName: str, stepSize: float) None

Sets the step size of the specified positioner to the specified number of micrometers.

setRecFilename(filename: Optional[str]) None

Sets the name of the file to record to. This only sets the name of the file, not the full path. One can also pass None as the argument to use a default time-based filename.

setRecFolder(folderPath: str) None

Sets the folder to save recordings into.

setRecModeScanOnce() None

Sets the recording mode to record a single scan.

setRecModeScanTimelapse(lapsesToRec: int, freqSeconds: float, timelapseSingleFile: bool = False) None

Sets the recording mode to record a timelapse of scans.

setRecModeSpecFrames(numFrames: int) None

Sets the recording mode to record a specific number of frames.

setRecModeSpecTime(secondsToRec: Union[int, float]) None

Sets the recording mode to record for a specific amount of time.

setRecModeUntilStop() None

Sets the recording mode to record until recording is manually stopped.

signals() Mapping[str, imswitch.imcommon.framework.qt.Signal]

Returns signals that can be used with e.g. the getWaitForSignal action. Currently available signals are:

  • acquisitionStarted

  • acquisitionStopped

  • recordingStarted

  • recordingEnded

  • scanEnded

They can be accessed like this: api.imcontrol.signals().scanEnded

snapImage() None

Take a snap and save it to a .tiff file at the set file path.

startRecording() None

Starts recording with the set settings to the set file path.

stepPositionerDown(positionerName: str, axis: str) None

Moves the specified positioner axis in negative direction by its set step size.

stepPositionerUp(positionerName: str, axis: str) None

Moves the specified positioner axis in positive direction by its set step size.

stopRecording() None

Stops recording.

mainWindow

class mainWindow

These functions are available in the mainWindow object.

setCurrentModule(self, moduleId: str) None

Sets the currently displayed module to the module with the specified ID (e.g. “imcontrol”).