Skip to content

Software Setup โ€” ImSwitch, HIK SDK & Plugin

This page is the long form of the Get started list on the overview page: everything that has to be installed, in which order, and how to verify each step before moving on.

How the pieces fit together

The recording plugin does not talk to the camera directly. ImSwitch owns the camera and publishes every live frame into the napari viewer; the plugin reads its frames from that napari layer and drives the ESP32 (LEDs, sensors) itself over the serial port:

HIK MV-CS013-60GN โ”€โ”€GigEโ”€โ”€โ–ถ MVS SDK โ”€โ”€โ–ถ ImSwitch (HikCamManager)
                                             โ”‚
                                             โ–ผ  live image
                                        napari layer
                                             โ”‚  layer.data
                                             โ–ผ
                            Nematostella Timelapse Recording  โ”€โ”€โ–ถ HDF5 / Zarr
                                             โ”‚
                                             โ–ผ  USB serial (COMx)
                                          ESP32  โ”€โ”€โ–ถ IR / White LED, DHT22

Two consequences drive the whole installation:

  1. ImSwitch, napari and the plugin run in the same Python process, so they must be installed into the same environment โ€” here the conda environment imswitch21.
  2. All requirements must be installed before ImSwitch is started for the first time. Installing packages into a running environment does not retrofit them into the running ImSwitch/napari session, and a half-installed environment is the most common cause of "the plugin does not show up in the Plugins menu".

Prerequisites

Component Requirement
Operating system Windows 10/11, Linux or macOS (Windows is the reference setup)
Python 3.12 in a dedicated conda environment โ€” the analysis plugin requires โ‰ฅ 3.12, everything else is happy there
Camera Hik Robotics MV-CS013-60GN (GigE, NIR) โ€” see Hardware & Assembly
Camera driver Hikrobot MVS SDK (provides the runtime the HikCamManager binds to)
Network Gigabit NIC for the camera, camera and adapter in the same subnet
Controller ESP32 DevKit with the project firmware flashed

Step 1 โ€” Create the imswitch21 conda environment

conda create -n imswitch21 python=3.12 -y
conda activate imswitch21

Verify that the environment is really the active one before installing anything:

python -V          # Python 3.12.x
where python       # ...\envs\imswitch21\python.exe
python -V          # Python 3.12.x
which python       # .../envs/imswitch21/bin/python

Activate before every start

conda activate imswitch21 is required for every later start of ImSwitch, napari and for every pip install that belongs to this setup. A second environment with a partial install is the usual reason the plugin or the HIK camera silently disappears.


Step 2 โ€” Install the Hik Robotics SDK (MVS)

ImSwitch's HikCamManager is a thin wrapper around Hikrobot's machine-vision runtime. Without the SDK installed, ImSwitch starts but the detector stays empty.

  1. Download the MVS package for your operating system from the Hikrobot download center (Windows installer, Linux .tar.gz/.deb, macOS .pkg).
  2. Install it before ImSwitch. The package provides the MVS client application, the GenICam runtime, the GigE driver and the Python bindings (MvImport), and sets the environment variable the runtime is located through โ€” MVCAM_COMMON_RUNENV, pointing at the SDK's Development folder. (MVCAM_SDK_PATH may stay empty; that is not a problem.)
  3. Reboot (Windows) or open a new shell so those variables are visible inside the conda environment.

Verify the camera before touching ImSwitch

Open the MVS client that ships with the SDK. The camera must be listed and must open in live view there. If it does not, ImSwitch cannot open it either โ€” fix it here first:

Symptom Check
Camera not listed Camera IP and NIC IP in the same subnet (e.g. NIC 192.168.1.10, camera 192.168.1.101, mask 255.255.255.0)
Listed but unreachable Firewall blocking GigE discovery, or a second NIC on the same subnet
Frames drop or tear Enable jumbo frames (MTU 9000) on the camera NIC, use a Cat-6 cable, disable NIC power saving
Very dark image Expected โ€” the IR imager is dark without the LEDs running; use the plugin's LED control

macOS on Apple Silicon

The MVS SDK for macOS is x86_64-only. Run the whole stack in an x86_64 environment (Rosetta 2), otherwise the SDK libraries will not load.


Step 3 โ€” Install ImSwitch

This setup uses our own fork of ImSwitch, branch nematostella-rig. It is openUC2's ImSwitch 2.1.191 (commit 8b424d5) plus three commits this rig needs, and it provides both the HikCamManager detector and the UC2 ESP32Manager:

conda activate imswitch21
git clone -b nematostella-rig https://github.com/s1alknau/ImSwitch.git
cd ImSwitch
pip install -e .

Do not clone openUC2/ImSwitch directly

Three things fail with upstream:

  • The Qt GUI aborts on startup. Signals are assigned in __init__, but pyqtSignal only binds as a class attribute: AttributeError: 'pyqtSignal' object has no attribute 'connect'
  • fcntl is missing on Windows (imcontrol/model/io/session.py).
  • setLaserGalvo() does not exist. napari-lsft calls this endpoint over the HTTP API to start the light-sheet galvo at acquisition start.

Every change is documented commit by commit and mirrored as patch files under imswitch-patches/.

Choose a Qt binding and install the GUI stack

pip install -e . installs no GUI packages at all โ€” neither a Qt binding nor the layers ImSwitch's windows are built on. Pick a binding:

pip install "PySide6==6.8.3"   # Qt 6.8.3 - recommended
# or: pip install -e ".[PyQt5]"  # Qt 5.15.2, pulls the GUI stack below with it

PyQt5 shears the client area diagonally on some dual-GPU machines. With PySide6, site-packages/PyQt5 must be fully removed - pyqtgraph treats even an emptied directory as an installed binding and then picks the wrong one.

The binding alone is not enough. On the PySide6 path, add the packages ImSwitch imports on startup (the PyQt5 extra already contains them):

pip install qtpy napari pyqtgraph qdarkstyle
Package Where ImSwitch needs it
qtpy imcommon/framework/qt.py โ€” the abstraction layer over the binding, imported before anything else
napari imcommon/view/guitools/naparitools.py โ€” the camera view is an embedded napari viewer, and it is the layer this plugin reads
pyqtgraph plot widgets in imcontrol/view/widgets/
qdarkstyle imcommon/view/MultiModuleWindow.py โ€” the main window stylesheet

ModuleNotFoundError: No module named 'qtpy'

This is what a missing GUI stack looks like โ€” ImSwitch starts, prints its version and then dies during the import chain:

Starting ImSwitch using version:  2.1.191
  File ".../imswitch/imcommon/framework/qt.py", line 2, in <module>
    from qtpy import QtCore
ModuleNotFoundError: No module named 'qtpy'

Install the packages above (Step 5 also brings qtpy and napari in as plugin dependencies) and start ImSwitch again.

Decompile psygnal

The wheel ships mypyc-compiled modules. With those active, napari's plugin discovery dies inside ImSwitch:

TypeError: 'object' object is not subscriptable
  File "src\psygnal\_signal.py", line 1456, in resume
  File ".../npe2/_plugin_manager.py", line 301, in discover

The tell-tale sign is the log line Could not enable signal emission for logging: interpreted classes cannot inherit from compiled. psygnal ships an official switch for exactly this โ€” it renames the .pyd/.so files to *_BAK and is reversible with recompile():

python -c "import psygnal.utils; psygnal.utils.decompile()"
python -c "import psygnal; print(psygnal._compiled)"   # False

(openUC2 recommend pip install psygnal --no-binary :all: instead; that works too but has to build from source.)

Linux: Qt platform plugin

If the imports succeed but the GUI aborts with qt.qpa.plugin: Could not load the Qt platform plugin "xcb", install the system libraries the binding links against and make sure a display is available (echo $DISPLAY):

sudo apt install libxcb-xinerama0 libxcb-cursor0 libxkbcommon-x11-0 libegl1

Do not start ImSwitch yet

Install the plugin (Step 5) first โ€” every requirement has to be in place before the first start. ImSwitch creates its configuration folder on that first start; create it now so the setup file from Step 4 can already be placed:

mkdir -p ~/ImSwitchConfig/imcontrol_setups
~/ImSwitchConfig/         (C:\Users\<you>\ImSwitchConfig on Windows)
โ”œโ”€โ”€ imcontrol_setups/     โ† the setup JSON goes here (Step 4)
โ”œโ”€โ”€ config/               โ† modules.json lives here
โ””โ”€โ”€ ...

It is the home directory, not Documents

Older ImSwitch versions kept this folder under Documents/. This one resolves it to the home directory (storage_paths.py), unless --config-folder points elsewhere. If both folders exist on your machine, the one under Documents/ is a leftover and is ignored.

Ready-made configurations

The openUC2/ImSwitchConfig repository contains further example setups. You can clone it into the ImSwitchConfig folder and drop the file from Step 4 in next to the others.


Step 4 โ€” Add the camera setup JSON to imcontrol_setups

The setup file for this imager lives in the repository at Json+cam_manager/example_uc2_ddorf_hik_imager_IR.json:

{
  "rs232devices": {
    "ESP32": {
      "managerName": "ESP32Manager",
      "managerProperties": {
        "host_": "192.168.43.129",
        "serialport": "COM4",
        "debug": 0,
        "baudrate": 115200
      }
    }
  },
  "detectors": {
    "WidefieldCamera": {
      "managerName": "HikCamManager",
      "managerProperties": {
        "isRGB": false,
        "cameraListIndex": 0,
        "cameraEffPixelsize": 3.45,
        "hikcam": {
          "exposure": 50.0,
          "gain": 0.0,
          "blacklevel": 0,
          "trigger_source": "Continous",
          "exposure_mode": "manual",
          "frame_rate": 30
        }
      },
      "forAcquisition": true
    }
  },
  "availableWidgets": ["Settings", "View", "Recording", "Image"]
}

Copy it into the setups folder:

copy "Json+cam_manager\example_uc2_ddorf_hik_imager_IR.json" "$env:USERPROFILE\ImSwitchConfig\imcontrol_setups\"
cp "Json+cam_manager/example_uc2_ddorf_hik_imager_IR.json" \
  ~/ImSwitchConfig/imcontrol_setups/

Then select it in ImSwitch as the active setup (Settings โ†’ setup file) and restart ImSwitch so the setup is loaded.

What the fields mean

Field Meaning
detectors.WidefieldCamera.managerName HikCamManager โ€” the MVS-SDK-backed detector. The napari layer that shows up later is named after this detector.
cameraListIndex Index into the list of detected HIK cameras. 0 is correct for a single camera; increase it if several are attached.
cameraEffPixelsize Effective pixel size in ยตm (3.45 ยตm for the MV-CS013), used for scale bars.
isRGB false โ€” the NIR sensor is monochrome.
hikcam.exposure Exposure in ms. The plugin reads the live value back from ImSwitch, so set exposure in ImSwitch, not in the plugin.
hikcam.gain / blacklevel Keep at 0 for calibrated recordings โ€” LED calibration assumes a fixed camera response.
hikcam.trigger_source Continous (free-running). LED/exposure synchronization is done by the ESP32 sync pulse, not by a hardware camera trigger.
hikcam.exposure_mode manual โ€” auto exposure would defeat the brightness validation during recording.
rs232devices.ESP32.serialport Serial port of the ESP32 โ€” COMx on Windows, /dev/ttyUSB0 on Linux.
rs232devices.ESP32.baudrate 115200, matching the project firmware.
availableWidgets Which ImSwitch panels are shown. Settings, View, Recording and Image are enough for this workflow.

Only one process can hold the ESP32 serial port

The plugin opens the ESP32 port itself, and serial ports are exclusive: if ImSwitch's ESP32 rs232 device already holds COM4, the plugin's Connect fails (and vice versa). If you drive the ESP32 exclusively from the plugin โ€” the normal case for this workflow โ€” either point ImSwitch's serialport at an unused port or remove the rs232devices block from your copy of the setup file. Also close any serial monitor (Arduino IDE, PuTTY) before connecting.


Step 5 โ€” Install the napari plugins

Before the first ImSwitch start

Do this step before starting ImSwitch for the first time. The plugins bring qtpy and napari in as dependencies, so a missing step here is the usual cause of the No module named 'qtpy' crash in Step 3.

None of the three plugins is published on PyPI โ€” all install from source, into the same imswitch21 environment:

conda activate imswitch21

Timelapse recording with the HIK camera and the ESP32 โ€” the plugin this page is about.

git clone https://github.com/s1alknau/Nematostella-time-series.git
cd Nematostella-time-series
pip install -e .
# optional: pip install -e ".[opencv]"   # Live Analysis tab (HoughCircles ROI)

zarr (Zarr recording format, read-while-write live analysis) is already a core dependency.

Activity and circadian analysis of the recorded HDF5/Zarr files โ€” napari-hdf5-activity.

git clone https://github.com/s1alknau/napari-hdf5-activity.git
cd napari-hdf5-activity
pip install -e ".[zarr]"

This is the plugin that sets the Python floor: it requires โ‰ฅ 3.12, which is why Step 1 creates a 3.12 environment.

Light-sheet control and reconstruction โ€” napari-lsft. It drives the galvo through ImSwitch's HTTP API, which is one of the reasons this rig needs the fork from Step 3.

git clone https://github.com/s1alknau/napari-lsft.git
cd napari-lsft
pip install -e ".[control,stream]"

control adds UC2-REST for direct ESP32 access, stream the Socket.IO live frame stream. daheng (Galaxy SDK) is only needed for a Daheng camera.

Verify that all three are importable and registered with napari:

python -c "import timeseries_capture, napari_hdf5_activity, napari_lsft; print('ok')"
python -c "from npe2 import PluginManager; pm = PluginManager(); pm.discover(); print([w.display_name for m in pm._manifests.values() for w in (m.contributions.widgets or [])])"

Expected widgets: Nematostella Timelapse Recording, HDF5 Activity Analysis, LSFT Reconstruction, LSFT Control & Acquisition.


Step 6 โ€” First launch

  1. Activate the environment and start ImSwitch:

    conda activate imswitch21
    imswitch
    
  2. Load the example_uc2_ddorf_hik_imager_IR setup.

  3. Start the live view โ€” a napari layer with the camera image appears (Live: WidefieldCamera or similar). The plugin auto-detects layers whose name contains Live:, Widefield, Camera or Detector.
  4. Open Plugins โ†’ Nematostella Timelapse Recording in the napari viewer.
  5. In the ESP32 Connection tab, click Connect โ€” the status turns green.
  6. Run an LED calibration before the first recording; for light/dark experiments follow the Circadian Protocol.
  7. Configure duration, interval and output folder, then start the recording.

Without a HIK camera

Steps 2โ€“4 are only needed for the ImSwitch/HIK GigE path. The plugin also runs in a plain napari session with the other supported camera adapters โ€” only the ESP32 is mandatory.

Dry run without hardware

The installation can be checked before the imager is built: with no camera attached, HikCamManager falls back to a mock camera, and ImSwitch starts into its GUI with the same setup file (log: Initialized camera, model: mock โ€ฆ init done). Point it at a scratch configuration so your real one stays untouched:

mkdir -p /tmp/ImSwitchTest/imcontrol_setups
cp Json+cam_manager/example_uc2_ddorf_hik_imager_IR.json /tmp/ImSwitchTest/imcontrol_setups/
imswitch --config-folder /tmp/ImSwitchTest

Optional: multi-camera configuration

If a camera_system.json is present, the plugin adds its multi-camera panels on startup. It is searched for in this order:

  1. camera_system.json in the current working directory
  2. ~/.imswitch/camera_system.json
  3. camera_system.json next to the installed package

Use camera_system_example.json as a template โ€” it lists per-camera IP, ESP32 port and the default recording configuration. Without such a file the plugin runs in single-camera mode.


Troubleshooting

Message / symptom Cause and fix
ModuleNotFoundError: No module named 'qtpy' while starting ImSwitch The GUI stack is missing โ€” pip install -e . installs no Qt packages. Install the binding and qtpy napari pyqtgraph qdarkstyle, see Step 3.
ModuleNotFoundError for napari, pyqtgraph or qdarkstyle Same cause, same fix โ€” or simply run Step 5 (the plugin pulls qtpy and napari in) before starting ImSwitch.
Fatal Python error: Segmentation fault in launchApp QtWebEngine without a shared GL context โ€” fixed in nematostella-rig, git pull the ImSwitch clone. See Segfault when the GUI starts.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" (Linux) Missing system libraries or no display โ€” see the note in Step 3.
Failed to load module: KeyError: / KeyError: 'WidefieldCamera' No camera attached: HikCamManager falls back to its mock, which used to reject the "Continous" spelling the real camera uses. Fixed in nematostella-rig โ€” git pull the ImSwitch clone. With the fix, the documented setup starts on the mock and the GUI comes up without hardware.
Modal dialog "It appears that Jupyter Notebook isn't where it usually is" The imnotebook module wants jupyter-lab on PATH. It is off by default in nematostella-rig; an existing ~/ImSwitchConfig/config/modules.json keeps its old value, so set it to ["imcontrol"].
pip check: imswitchuc2 requires pydantic==2.11.4, but you have 2.13.4 napari pulls the newer pydantic. The exact pin is relaxed in nematostella-rig; ImSwitch runs unchanged with 2.13.4.
Expecting value: line 1 column 1 / setup file was corrupted A BOM in the JSON โ€” see the warning in Segfault when the GUI starts.
TypeError: 'object' object is not subscriptable in npe2 / psygnal while ImSwitch builds its window The compiled psygnal breaks napari's plugin discovery. Run python -c "import psygnal.utils; psygnal.utils.decompile()", see Step 3.
Plugin missing in the Plugins menu Installed into a different environment. conda activate imswitch21, reinstall, restart napari/ImSwitch.
No camera layer found (ensure live view is started in ImSwitch) The live view is not running, or the layer name contains none of Live:, Widefield, Camera, Detector. Start the live view or rename the layer.
Zero frame from napari layer (n consecutive) The HIK acquisition buffer is in an inconsistent state. The plugin flushes the ImSwitch detector buffer automatically after 5 consecutive zero frames; if it keeps recurring, check MTU/cabling and restart the live view.
Camera missing in ImSwitch but visible in MVS Wrong cameraListIndex, or the MVS client still holds the camera โ€” close it, only one application can open a GigE camera at a time.
Camera missing in MVS as well Subnet or firewall problem โ€” see the table in Step 2.
ESP32 Connect fails, port busy Another process holds the port (ImSwitch rs232 device, serial monitor) โ€” see the warning in Step 4.
Exposure in the plugin looks wrong The plugin reads the exposure back from ImSwitch in ms; change it in ImSwitch's settings, not in the plugin.
Frames too dark or too bright Run an LED calibration and keep gain and blacklevel at 0 so the calibration stays valid.

Segfault when the GUI starts (Linux, PySide6)

ImSwitch loads, the window appears for a moment, then the process dies without a Python exception:

Fatal Python error: Segmentation fault

Current thread (most recent call first):
  File ".../qtpy/_utils.py", line 53 in possibly_static_exec
  File ".../imswitch/imcommon/applaunch.py", line 169 in launchApp
Extension modules: ... PySide6.QtWebEngineCore, PySide6.QtWebEngineWidgets ...

Fixed in the fork โ€” update your clone. QtWebEngine requires Qt.AA_ShareOpenGLContexts, and the attribute only takes effect while no QApplication exists yet. It used to sit in a PyQt5/PySide2-only block, while imnotebook โ€” which pulls QtWebEngineWidgets in โ€” is imported after the application object is created. On Qt 6 the web engine then came up without a shared GL context and took the process down as soon as app.exec_() ran. nematostella-rig now sets the attribute for every binding (patch 0003):

cd ImSwitch
git pull

If the clone was installed with pip install . instead of pip install -e ., reinstall afterwards โ€” a plain install copies the code into site-packages and does not follow the clone:

pip install -e .

nematostella-rig additionally ships imnotebook off by default, so a fresh installation never loads QtWebEngine in the first place.

Workaround without updating

In an existing configuration the module stays enabled โ€” modules.json is only created once. Set ~/ImSwitchConfig/config/modules.json to:

{
    "enabled": ["imcontrol"]
}

Upstream's default is ["imcontrol", "imscripting", "imnotebook"]; imscripting is dropped by ImSwitch at startup anyway.

Windows: do not write JSON with Set-Content -Encoding utf8

PowerShell 5.1 writes a BOM, and ImSwitch's JSON parser stops at it โ€” modules.json fails with Expecting value: line 1 column 1 (char 0), a setup file is reported as corrupted and has been reset to default values. Use an editor that saves UTF-8 without BOM, or [System.IO.File]::WriteAllText($path, $json, (New-Object System.Text.UTF8Encoding($false))). The copy command in Step 4 is unaffected โ€” it copies bytes.

If it still segfaults, the display stack is the next suspect:

echo $XDG_SESSION_TYPE              # "wayland" is worth ruling out
QT_QPA_PLATFORM=xcb imswitch        # force X11 instead of Wayland
LIBGL_ALWAYS_SOFTWARE=1 imswitch    # software OpenGL (napari/vispy on old iGPUs)

Whichever of the two makes the crash go away identifies the cause: the first points at the Wayland platform plugin, the second at the OpenGL driver.