Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions pyclm_config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# MicroManager Configuration Path
config_path = "C:\\Program Files\\Micro-Manager-2.0\\Ti2MightexCrestSolaSpectra.cfg"
config_path = "C:\\Program Files\\Micro-Manager-2.0\\Ti2MightexCrestSolaSpectra_newlab.cfg"

# SLM Configuration
affine_transform = [[-0.289, 0.006, 959.025], [-0.012, -0.579, 1540.03]]
affine_transform = [[-0.29, -0.002, 939.891], [0.004, -0.579, 1505.235]]
slm_shape_h = 1140
slm_shape_w = 912
5 changes: 4 additions & 1 deletion src/pyclm/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def main():
args = process_args()
base_path = Path(args.directory)
run_pyclm(base_path, args.config)
run_pyclm(base_path, args.config, dry=args.dry)


def process_args():
Expand All @@ -16,6 +16,9 @@ def process_args():
parser.add_argument(
"--config", type=str, help="path to pyclm_config.toml file", default=None
)
parser.add_argument(
"--dry", action="store_true", help="run without executing the experiment"
)

return parser.parse_args()

Expand Down
19 changes: 15 additions & 4 deletions src/pyclm/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,29 @@
SegmentationProcess,
SLMBuffer,
)
from .core.real_core import RealMicroscopeCore
from .core.virtual_microscope.simulated_core import SimulatedMicroscopeCore
from .core.virtual_microscope.simulated_source import TimeSeriesImageSource

logger = logging.getLogger(__name__)


class Controller:
def __init__(self, config="MMConfig_demo.cfg"):
self.stop_event = Event()
self.core = CMMCorePlus()
def __init__(self, config="MMConfig_demo.cfg", dry=False):
if not dry:
# Applies if config specifies that a real microscope is in use
self.core = RealMicroscopeCore()
else:
# image_source = TimeSeriesImageSource.from_tiff_stack(Path("path/to/stack.tif"), loop=True)
image_source = TimeSeriesImageSource.from_folder(
Path("tif-source"), pattern="*.tif", loop=True
)
self.core = SimulatedMicroscopeCore(image_source, slm_device=None)
self.core.loadSystemConfiguration(config)
self.all_queues = AllQueues()

self.stop_event = Event()

self.microscope = MicroscopeProcess(
core=self.core, aq=self.all_queues, stop_event=self.stop_event
)
Expand Down Expand Up @@ -192,7 +204,6 @@ def run(self):
for f in future_to_process:
f.cancel()

self.core.unloadAllDevices()
self.all_queues.close()

logger.info("Controller run finished.")
45 changes: 45 additions & 0 deletions src/pyclm/core/core_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Any, Protocol


class MicroscopeCoreInterface(Protocol):
def loadSystemConfiguration(self, configuration): ...

# SLM-related
def getSLMDevice(self) -> str: ...
def getSLMHeight(self, device: str) -> int: ...
def getSLMWidth(self, device: str) -> int: ...
def setSLMImage(self, device: str, image: Any) -> None: ...

# Config/device properties
def setConfig(self, group: str, config: str) -> None: ...
def setProperty(self, label: str, name: str, value: Any) -> None: ...
def getProperty(self, label: str, name: str) -> str: ...
def getAllowedPropertyValues(
self, device: str, prop_name: str
) -> Sequence[str]: ...
def describe(self) -> str: ...
def setFocusDevice(self, label: str) -> None: ...
def getAvailableConfigGroups(self) -> Sequence[str]: ...
def getConfigGroupObject(
self, group: str, include_read_only: bool = False
) -> Any: ...

# Camera-related
def getCameraDevice(self) -> str: ...
def setExposure(self, exposure_ms: float) -> None: ...
def snapImage(self) -> None: ...
def getImage(self) -> Any: ...
def getPixelSizeUm(self) -> float: ...
def getROI(self): ...

# Stage/focus/positioning
def getZPosition(self) -> float: ...
def setPosition(self, z: float) -> None: ...
def setXYPosition(self, x: float, y: float) -> None: ...
def setAutoFocusOffset(self, offset: float) -> None: ...

# Synchronization
def waitForSystem(self) -> None: ...
6 changes: 5 additions & 1 deletion src/pyclm/core/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np
from pymmcore_plus import CMMCorePlus

from .core_interface import MicroscopeCoreInterface
from .datatypes import AcquisitionData, EventSLMPattern, StimulationData
from .events import AcquisitionEvent, UpdatePatternEvent, UpdateStagePositionEvent
from .experiments import ConfigGroup, DeviceProperty, PositionWithAutoFocus
Expand All @@ -19,7 +20,10 @@

class MicroscopeProcess(BaseProcess):
def __init__(
self, core: CMMCorePlus, aq: AllQueues, stop_event: Event | None = None
self,
core: MicroscopeCoreInterface,
aq: AllQueues,
stop_event: Event | None = None,
):
super().__init__(stop_event, name="microscope")
self.core = core
Expand Down
94 changes: 94 additions & 0 deletions src/pyclm/core/real_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import Any

from pymmcore_plus import CMMCorePlus

from .core_interface import MicroscopeCoreInterface


class RealMicroscopeCore(MicroscopeCoreInterface):
"""
Delegates everything to an internal CMMCorePlus.
"""

def __init__(self):
self._core = CMMCorePlus()

def loadSystemConfiguration(self, configuration):
self._core.loadSystemConfiguration(str(configuration))

# SLM-related
def getSLMDevice(self) -> str:
return self._core.getSLMDevice()

def getSLMHeight(self, device: str) -> int:
return self._core.getSLMHeight(device)

def getSLMWidth(self, device: str) -> int:
return self._core.getSLMWidth(device)

def setSLMImage(self, device: str, image: Any) -> None:
self._core.setSLMImage(device, image)

# Config/device properties
def setConfig(self, group: str, config: str) -> None:
self._core.setConfig(group, config)

def setProperty(self, label: str, name: str, value: Any) -> None:
self._core.setProperty(label, name, value)

def getProperty(self, label: str, name: str) -> str:
return self._core.getProperty(label, name)

def getAllowedPropertyValues(self, device: str, prop_name: str) -> Sequence[str]:
return self._core.getAllowedPropertyValues(device, prop_name)

def describe(self) -> str:
return self._core.describe()

def setFocusDevice(self, label: str) -> None:
self._core.setFocusDevice(label)

def getAvailableConfigGroups(self) -> Sequence[str]:
return self._core.getAvailableConfigGroups()

def getConfigGroupObject(self, group: str, include_read_only: bool = False):
return self._core.getConfigGroupObject(group, include_read_only)

# Camera-related
def getCameraDevice(self) -> str:
return self._core.getCameraDevice()

def setExposure(self, exposure_ms: float) -> None:
self._core.setExposure(exposure_ms)

def snapImage(self) -> None:
self._core.snapImage()

def getImage(self) -> Any:
return self._core.getImage()

def getPixelSizeUm(self) -> float:
return self._core.getPixelSizeUm()

def getROI(self):
return self._core.getROI()

# Stage/focus/positioning
def getZPosition(self) -> float:
return self._core.getZPosition()

def setPosition(self, z: float) -> None:
self._core.setPosition(z)

def setXYPosition(self, x: float, y: float) -> None:
self._core.setXYPosition(x, y)

def setAutoFocusOffset(self, offset: float) -> None:
self._core.setAutoFocusOffset(offset)

# Synchronization
def waitForSystem(self) -> None:
self._core.waitForSystem()
Empty file.
Loading