Connect Your Camera#
MagScope ships with a simulated camera so you can explore the interface immediately, but you can swap in a real camera by providing a small adapter class. This guide shows how to implement a CameraBase subclass for your hardware and register it with MagScope before launching the GUI.
Do you have a frame grabber? Cameras can be connected to a computer through either a standard built-in interface or through a frame grabber. If you have a frame grabber then you are really connecting MagScope with your frame grabber which is then interfacing with your camera. For the purpose of this tutorial we will just refer to everything as the camera. Make sure you know what you have before starting this tutorial.
0. Test your camera with Python#
Before you try to get your camera to work with MagScope you should just try to create a minimal test of it working with Python in general. Many manufacturers will provide Python bindings for free. This will include a guide on how to get your specific camera working. For example:
Alternatively, you can use third party libraries. Many of these will connect to a wide variety of scientific cameras. For example:
Harvesters This work with any camera that supports GenTL.
1. Implement a camera adapter#
Once you can get your camera to connect through any Python library you can use that to connect it to MagScope.
Every camera must subclass CameraBase.
At minimum, define the following attributes on the class:
widthandheight: Number of pixels in each dimension.bits: The number bits per pixel. Most cameras generate 8, 10, 12, or 16-bits.dtype: This needs to benumpyinteger dtype (numpy.uint8,numpy.uint16,numpy.uint32, ornumpy.uint64). It needs to be just large enough to fit the number of bits your camera generates. For example if your camera generates 8-bit data then this should benumpy.uint8. If it is 12-bit thennumpy.uint16.nm_per_px: The width of a pixel in nanometers with out any magnification. This is usually between 1000nm-10000nm.settings: list of setting names; must include"framerate"so the GUI can display and edit it
Implement the methods below to bridge between the device SDK and MagScope’s shared buffers:
connect(video_buffer): Open the hardware connection, allocate any SDK buffers, and stashvideo_bufferfor later writesfetch(): Pull the next frame from the device intovideo_bufferusingself.video_buffer.write_frame(...)release(): Return SDK buffers or handles after frames have been consumed. Not all cameras will need this.__getitem__/__setitem__: Read and update entries insettingsso the GUI can synchronize values
A minimal skeleton that wraps a vendor SDK might look like:
import numpy as np
import fake_sdk # you will need to replace this with a real SDK for your camera
from magscope.camera import CameraBase
class MyLabCamera(CameraBase):
width = 2048
height = 1024
bits = 12
dtype = np.uint16
nm_per_px = 5000
settings = ["framerate", "exposure"]
def __init__(self):
super().__init__()
self._sdk = fake_sdk.SDK()
self._settings = {"framerate": 30, "exposure": 10.0}
def connect(self, video_buffer):
self.video_buffer = video_buffer
self._sdk = self._sdk.connect_to_camera()
self.is_connected = True
def fetch(self):
image, timestamp = self._sdk.get_frame()
self.video_buffer.write_image_and_timestamp(image, timestamp)
def release(self):
pass
def __getitem__(self, name):
return self._settings[name]
def __setitem__(self, name, value):
self._settings[name] = value
self._sdk.update_setting(name, value)
For examples with real cameras take a look at the examples/cameras folder on GitHub.
2. Register the camera before starting MagScope#
Instantiate your adapter and assign it to the camera manager prior to calling magscope.scope.MagScope.start():
import magscope
from lab_camera import MyLabCamera
scope = magscope.MagScope()
scope.camera_manager.camera = MyLabCamera()
scope.start()
During startup the camera manager calls magscope.camera.CameraBase.connect() and immediately publishes all entries in settings to the GUI, so ensure your adapter populates defaults before start() runs.
3. Validate the connection#
Watch the console for warnings; if
magscope.camera.CameraBase.connect()raises an exception MagScope will stay in simulation mode and report the error.Confirm that the GUI reflects any custom settings you exposed in
settingsand that adjusting them updates your device through__setitem__.
python-microscope cameras#
If your camera is already available through python-microscope, MagScope can wrap it directly with magscope.PythonMicroscopeCamera.
Install the optional dependency first:
pip install magscope[python-microscope]
For local devices, prefer constructing the microscope device inside the camera manager process with device_factory:
import numpy as np
import magscope
from magscope import PythonMicroscopeCamera
from microscope.simulators import SimulatedCamera
scope = magscope.MagScope()
scope.camera_manager.camera = PythonMicroscopeCamera(
width=512,
height=512,
dtype=np.uint16,
bits=16,
nm_per_px=5000.0,
device_factory=SimulatedCamera,
)
scope.start()
For remote device-server cameras, provide the Pyro URI instead:
scope.camera_manager.camera = PythonMicroscopeCamera(
width=512,
height=512,
dtype=np.uint16,
bits=16,
nm_per_px=5000.0,
device_uri="PYRO:SomeCamera@127.0.0.1:8000",
)
PythonMicroscopeCamera reads frames from a microscope camera that exposes grab_next_data() or trigger_and_wait(). It always exposes a framerate setting to MagScope. If your microscope device also has settings you want in the GUI, map them with settings_map:
scope.camera_manager.camera = PythonMicroscopeCamera(
width=512,
height=512,
dtype=np.uint16,
bits=16,
nm_per_px=5000.0,
device_uri="PYRO:SomeCamera@127.0.0.1:8000",
settings_map={"framerate": "fps", "exposure": "exposure time"},
)