Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
a9b69b2
Add camera base service class.
ehpor Oct 24, 2023
3c4955b
Add ZWO camera service that derives from base class.
ehpor Oct 24, 2023
05d039b
Import all base services inside __init__.py.
ehpor Oct 24, 2023
716c192
Improve function names.
ehpor Nov 27, 2023
1a6648d
Add FLIR camera service deriving from base class.
ehpor Nov 27, 2023
7101eb5
Fix name of new ZWO camera service.
ehpor Nov 27, 2023
7178ebf
Started moving the sci-img sign flip into the base class
alexisyslau Mar 12, 2025
726e38c
Fix some of flake8 issues
ivalaginja Jun 26, 2025
c23145b
Updated camera base class and its atrribute for image operation. Also…
alexisyslau Jun 26, 2025
1977e2a
Confirm property setters and getters and their usage, and comments
ivalaginja Jul 2, 2025
cab0ae5
Set offsets up for forward and inverse transforms
ivalaginja Jul 2, 2025
94d53a5
Finish inverse function
ivalaginja Jul 2, 2025
8b1d377
Don't think we need this
ivalaginja Jul 2, 2025
10f4932
Fix inverse transform
ivalaginja Jul 10, 2025
0335174
Add missing things to base class
ivalaginja Jul 11, 2025
409d649
Fix up especially new ZWO child class
ivalaginja Jul 11, 2025
bccd863
Sneak sneak
ivalaginja Jul 11, 2025
3931bf5
Linter fixes for base class and ZWO v2
ivalaginja Jul 11, 2025
13530d1
Use double quotes
ivalaginja Jul 11, 2025
7436282
Fix flir linter problem
ivalaginja Jul 11, 2025
22bffc8
Add missing but non-required input parameters to base class methods
ivalaginja Jul 11, 2025
519417f
Minor fixes in existing services
ivalaginja Jul 11, 2025
7c565ae
Start adapting new dummy camera service
ivalaginja Jul 11, 2025
223563c
Start creating Hamamatsu v2 service
ivalaginja Sep 12, 2025
1f59243
Fill in missing child class methods (empty for now)
ivalaginja Sep 12, 2025
992b7ac
Draft new AVT camera service
ivalaginja Sep 12, 2025
b0209e2
Add more properties and methods
ivalaginja Sep 17, 2025
ebce52f
Feed sensor width and height through hidden properties
ivalaginja Sep 22, 2025
314a5ca
Override monitor_temperature() method in Hamamatsu child class
ivalaginja Sep 22, 2025
5383453
Adapt make_property_helper() to include option to stop acquisitioin
ivalaginja Sep 23, 2025
e468c91
Declare stopped_acquisition flags in child class for Hamamatsu
ivalaginja Sep 23, 2025
fe39f95
Simplify property helper in Hamamatsu
ivalaginja Sep 23, 2025
231eeec
Invert property sequence
ivalaginja Sep 23, 2025
48ac13b
Both. offsets need both offsets as inputs in setters
ivalaginja Sep 23, 2025
641582a
width and height always need to be ints
ivalaginja Sep 23, 2025
b007264
Add NUM_FRAMES as class attribute
ivalaginja Sep 23, 2025
17040f3
Remove camera name from end_acquisition()
ivalaginja Sep 23, 2025
f652e1b
Be explicit for type in image datastream submission
ivalaginja Sep 23, 2025
0a95f7a
Move stopped acquisition flags to class attributes
ivalaginja Sep 23, 2025
d9b54fb
Add mutex to property functions
ivalaginja Sep 23, 2025
842ea21
Add exposure time property to base class
ivalaginja Sep 23, 2025
5a9d020
Return offsets as ints
ivalaginja Sep 23, 2025
010f8d6
Fix up ZWO service
ivalaginja Sep 23, 2025
bf7f8a3
Remove duplicate attribute in Hamamatsu v2 service
ivalaginja Sep 23, 2025
e4310b6
Remove HiCAT hack in ZWO v2
ivalaginja Sep 23, 2025
c5e3d35
More random updates
ivalaginja Sep 23, 2025
ae1f40d
Remove tuple crap
ivalaginja Sep 25, 2025
2415214
Initialize self.cam as None
ivalaginja Sep 25, 2025
12d3dcd
StoppedAcquisition context manager should only control flag, not hw
ivalaginja Sep 25, 2025
ba4a343
Add Ham and ZWO v2s to pyproject toml entry points
ivalaginja Sep 26, 2025
95780fb
force transform offset function to return nearest integer.
makidonlab Sep 26, 2025
4c156ba
coordinate transform should be w.r.t. sensor_width and sensor_height …
steigersg Sep 29, 2025
5c9a239
through experimentation of recovering my initial coordinates running …
steigersg Sep 29, 2025
f89d088
add hidden variables for offset x and offset y so they can be appropr…
makidonlab Mar 6, 2026
ec5f800
add test for transformation logic consistency
makidonlab Mar 6, 2026
5fea371
update offset transformation function
makidonlab Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
407 changes: 407 additions & 0 deletions catkit2/base_services/camera.py

Large diffs are not rendered by default.

127 changes: 127 additions & 0 deletions catkit2/services/allied_vision_camera_v2/allied_vision_camera_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
'''
This module contains a service for Allied Vision cameras.

This service is a wrapper around the Vimba X SDK.
It provides a simple interface to control the camera and acquire images.
'''

from __future__ import annotations
import contextlib

from catkit2.base_services.camera import CameraService, StoppedAcquisition

from vmbpy import (AllocationMode,
Camera, Frame, Stream,
FrameStatus,
PixelFormat,
VmbSystem,
VmbCameraError)


class AlliedVisionCamera(CameraService):
# Stopped acquisition flags for base class
width_requires_stopped_acquisition = True
height_requires_stopped_acquisition = True
offset_x_requires_stopped_acquisition = True
offset_y_requires_stopped_acquisition = True
exposure_time_requires_stopped_acquisition = False
gain_requires_stopped_acquisition = False

def __init__(self, exit_stack: contextlib.ExitStack):
'''
Create a new AlliedVisionCamera service.

Parameters
----------
exit_stack : contextlib.ExitStack
The exit stack to which this service should be added.
This is used to ensure that resources are properly cleaned up when the service is closed.
'''
super().__init__('allied_vision_camera_v2')
self.exit_stack = exit_stack

# dictionary to store the pixel format and the corresponding numpy dtype and vimba pixel format
self.pixel_formats = {
"Mono8": PixelFormat.Mono8,
"Mono12": PixelFormat.Mono12,
"Mono12Packed": PixelFormat.Mono12Packed,
"Mono14": PixelFormat.Mono14,
"Mono16": PixelFormat.Mono16,
}
self.current_pixel_format = None

def open(self):
pass

def close(self):
pass

def start_acquisition(self):
pass

def end_acquisition(self):
pass

def capture_image(self):
pass

def get_roi_width(self):
pass

def set_roi_width(self, width):
pass

def get_roi_height(self):
pass

def set_roi_height(self, height):
pass

def get_roi_offset_x(self):
pass

def set_roi_offset_x(self, offset_x):
pass

def get_roi_offset_y(self):
pass

def set_roi_offset_y(self, offset_y):
pass

def get_sensor_width(self):
pass

def get_sensor_height(self):
pass

def get_exposure_time(self):
pass

def set_exposure_time(self, exposure_time):
pass

def get_gain(self):
pass

def set_gain(self, gain):
pass

def get_temperature(self):
'''
Get the temperature of the camera.

This function gets the temperature of the camera.

Returns
-------
float:
The temperature of the camera in degrees Celsius.
'''
return self.cam.DeviceTemperature.get()


if __name__ == '__main__':
with contextlib.ExitStack() as main_exit_stack:
service = AlliedVisionCamera(main_exit_stack)
service.run()
17 changes: 9 additions & 8 deletions catkit2/services/dummy_camera/dummy_camera.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from catkit2.testbed.service import Service

import time
from hcipy import *
import hcipy
import numpy as np
import threading


class DummyCamera(Service):
def __init__(self):
super().__init__('dummy_camera')
Expand All @@ -16,16 +17,16 @@ def __init__(self):
self._offset_x = self.config['offset_x']
self._offset_y = self.config['offset_y']

self.flux = self.config['flux']

self.sensor_width = self.config['sensor_width']
self.sensor_height = self.config['sensor_height']

self.should_be_acquiring = threading.Event()
self.should_be_acquiring.set()

self.pupil_grid = make_pupil_grid(128)
self.aperture = evaluate_supersampled(make_hicat_aperture(True), self.pupil_grid, 4)
self.wf = Wavefront(self.aperture)
self.pupil_grid = hcipy.make_pupil_grid(128)
self.aperture = hcipy.evaluate_supersampled(hcipy.make_hicat_aperture(True), self.pupil_grid, 4)
self.wf = hcipy.Wavefront(self.aperture)
self.wf.total_power = 1

self.images = self.make_data_stream('images', 'uint16', [self.sensor_height, self.sensor_width], 20)
Expand Down Expand Up @@ -64,17 +65,17 @@ def make_property_helper(name, read_only=False):

def get_image(self):
try:
focal_grid = make_focal_grid(8, np.array([self.sensor_width, self.sensor_height]) / 16)
focal_grid = hcipy.make_focal_grid(8, np.array([self.sensor_width, self.sensor_height]) / 16)
# focal_grid = focal_grid.shifted([-self._offset_x / 8, -self._offset_y / 8])

prop = FraunhoferPropagator(self.pupil_grid, focal_grid)
prop = hcipy.FraunhoferPropagator(self.pupil_grid, focal_grid)
img = prop(self.wf).power.shaped

img = img[self._offset_y:self._offset_y + self._height, self._offset_x:self._offset_x + self._width]

img = img * self.flux * self.exposure_time / 1e6

img = large_poisson(img)
img = hcipy.large_poisson(img)
img = np.clip(img, 0, 2**16 - 1).astype('uint16')
except Exception as e:
print(e)
Expand Down
103 changes: 103 additions & 0 deletions catkit2/services/dummy_camera_v2/dummy_camera_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import time
from hcipy import evaluate_supersampled, make_hicat_aperture, make_pupil_grid, Wavefront
import numpy as np

from catkit2.base_services.camera import CameraService


class DummyCamera(CameraService):
def __init__(self):
super().__init__('dummy_camera_v2')
self.log.debug('Dummy camera service started')
self.flux = self.config['flux']

self.pupil_grid = make_pupil_grid(128)
self.aperture = evaluate_supersampled(make_hicat_aperture(True), self.pupil_grid, 4)
self.wf = Wavefront(self.aperture)
self.wf.total_power = 1

self.make_command('repeater', self.repeater)

def open(self):
# Set up general camera properties.
super().open()

def close(self):
super().close()
self.camera.close()

# TODO - write all functions in here
def start_acquisition(self):
# TODO - write a function to start the acquisition
self.should_be_acquiring.set()
pass

def end_acquisition(self):
# TODO - write a function to start the acquisition
self.should_be_acquiring.clear()
pass

def monitor_temperature(self):
while not self.should_shut_down:
self.log.debug('Monitoring temperature')
temperature = self.get_temperature()
self.temperature.submit_data(np.array([temperature]))
self.sleep(0.1)

def get_temperature(self):
return np.sin(2 * np.pi * time.time() / 10)

def get_roi_width(self):
return self.config['width']

def set_roi_width(self, width):
return width

def get_roi_height(self):
return self.config['height']

def set_roi_height(self, height):
return height

def get_roi_offset_x(self):
pass

def set_roi_offset_x(self, offset_x):
pass

def get_roi_offset_y(self):
pass

def set_roi_offset_y(self, offset_y):
pass

def get_sensor_width(self):
return self.config['sensor_width']

def get_sensor_height(self):
return self.config['sensor_height']

def get_exposure_time(self):
pass

def set_exposure_time(self, exposure_time):
pass

def get_gain(self):
pass

def set_gain(self, gain):
pass


if __name__ == '__main__':
try:
service = DummyCamera()
service.run()

except Exception:
import traceback

print(traceback.format_exc())
input()

3 changes: 3 additions & 0 deletions catkit2/services/flir_camera/flir_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from catkit2.testbed.service import Service
from catkit2.testbed.tracing import trace_interval


def _create_property(flir_property_name, read_only=False, stopped_acquisition=True):
def getter(self):
with self.mutex:
Expand All @@ -32,6 +33,7 @@ def setter(self, value):

return property(getter, setter)


def _create_enum_property(flir_property_name, enum_name, stopped_acquisition=True):
def getter(self):
with self.mutex:
Expand Down Expand Up @@ -63,6 +65,7 @@ def setter(self, value):

return property(getter, setter)


class FlirCamera(Service):
NUM_FRAMES_IN_BUFFER = 20

Expand Down
Loading
Loading