Source code for magscope.utils
from __future__ import annotations
from datetime import datetime
from enum import IntEnum, StrEnum
from typing import TYPE_CHECKING, Callable, Type
import magtrack
import numpy as np
from PyQt6.QtGui import QImage
from magscope.ipc_commands import Command
[docs]
class AcquisitionMode(StrEnum):
"""Enum for acquisition modes.
The UI redesign changed the enum values to match the new display labels. Existing scripts
that compare or pass raw strings should update old values such as ``"video (full)"`` to
the new values such as ``"Video (Full)"``. The old member names remain as aliases so
scripts using ``AcquisitionMode.FULL_VIDEO`` continue to resolve to the new canonical mode.
"""
[docs]
TRACK_AND_VIDEO_ROIS = 'Track and Video (ROIs)'
[docs]
TRACK_AND_VIDEO_FULL = 'Track and Video (Full)'
[docs]
VIDEO_ROIS = 'Video (ROIs)'
[docs]
VIDEO_FULL = 'Video (Full)'
[docs]
TRACK_AND_CROP_VIDEO = TRACK_AND_VIDEO_ROIS
[docs]
TRACK_AND_FULL_VIDEO = TRACK_AND_VIDEO_FULL
[docs]
CROP_VIDEO = VIDEO_ROIS
[docs]
FULL_VIDEO = VIDEO_FULL
[docs]
def crop_stack_to_rois(stack, rois):
rois = np.asarray(rois, dtype=np.int64)
# Pre-allocate space for cropped_stack
n_images = stack.shape[2]
n_rois = len(rois)
width = rois[0, 1] - rois[0, 0]
shape = (width, width, n_images, n_rois)
cropped_stack = np.ndarray(
shape, dtype=stack.dtype
) # width, width, frames, rois
# Crop
for i, roi in enumerate(rois):
cropped_stack[:, :, :, i] = (
stack[roi[0]:roi[1], roi[2]:roi[3], :]
)
return cropped_stack
[docs]
def numpy_type_to_qt_image_type(numpy_type):
NP2QT = {
np.uint8: QImage.Format.Format_Grayscale8,
np.uint16: QImage.Format.Format_Grayscale16
}
if numpy_type not in NP2QT:
raise ValueError(f"Unsupported bit type: {numpy_type}")
return NP2QT[numpy_type]
[docs]
def date_timestamp_str(timestamp):
date_str = datetime.today().strftime('%Y-%m-%d')
hour = (timestamp // 3600 % 24 - 5) % 24
minutes = timestamp // 60 % 60
seconds = timestamp // 1 % 60
milliseconds = timestamp % 1 * 1000
return f'{date_str} {hour:02.0f}-{minutes:02.0f}-{seconds:02.0f}.{milliseconds:03.0f}'
[docs]
class PoolVideoFlag(IntEnum):
[docs]
class Units:
# Meters
# Newtons
# Seconds
sec = s = 1.
# Directions
clockwise = cw = 1.
counterclockwise = ccw = -1.
[docs]
def register_script_command(command_type: type[Command]):
"""Decorator marking a method as callable from a MagScope script.
Each script command must be paired with the IPC :class:`~magscope.ipc_commands.Command`
that will be dispatched when the script executes that step. The decorator mirrors
:func:`magscope.ipc_commands.command_handler` by attaching metadata to the wrapped
function without constraining how it is collected.
"""
def decorator(meth: callable):
meth._scriptable = True
meth._script_command_type = command_type
return meth
return decorator
[docs]
def check_cupy() -> bool:
"""Return ``True`` when the CuPy package is usable."""
return magtrack.check_cupy()