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 = 'Track'
[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] READY = 0
[docs] RUNNING = 1
[docs] FINISHED = 2
[docs] class Units: # Meters
[docs] m = 1.
[docs] cm = 1e-2
[docs] mm = 1e-3
[docs] um = 1e-6
[docs] nm = 1e-9
# Newtons
[docs] N = 1.
[docs] mN = 1e-3
[docs] uN = 1e-6
[docs] nN = 1e-9
[docs] pN = 1e-12
[docs] fN = 1e-15
# Seconds sec = s = 1.
[docs] ms = 1e-3
[docs] us = 1e-6
[docs] ns = 1e-9
[docs] ps = 1e-12
[docs] fs = 1e-15
# 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()