base_camera#

Shared surface for camera-shaped devices.

Three concrete cameras converge on this base:

They have wildly different acquisition loops (mmcore-driven MDA vs Qt thread vs synthetic frame generator) so we don’t try to unify the run-loop here. What we DO unify is the surface every camera must expose:

  • Identity & cosmetic attrs the MDA GUI reads (name, viewer, auto_contrast, core, backend).

  • Output paths and metadata sidecar plumbing.

  • The standard lifecycle hooks (arm, set_writer, set_sequence, shutdown, status, calibration).

  • The DeviceSignals bundle that DataManager subscribes to (signals.started, signals.finished, signals.data).

Subclasses keep their backend-specific initialize, start, stop, save_data, and writer setup.

Design notes:

  • BaseCamera is a regular class – not a Protocol – so subclasses can multiply-inherit alongside Qt’s QThread (OpenCVCamera), pymmcore-plus’s DataProducer/HardwareDevice Protocols (MMCamera), and our pure-Python BaseDataProducer (MockFrameProducer).

  • We avoid BaseCamera.__init__ to dodge multiple-inheritance super() chains with QThread. Subclasses call _init_camera_surface() after whatever parent __init__ they need.

class mesofield.devices.base_camera.BaseCamera[source]#

Bases: object

Common camera surface (no Qt, no acquisition loop).

Subclasses call _init_camera_surface() from their __init__ after any superclass super().__init__() calls. Override set_writer(), set_sequence(), start(), stop(), save_data(), and get_data() as needed.

arm(config)[source]#

Per-run prep: set up the writer + (optionally) an MDA sequence.

The default body fits both MMCamera (which needs a sequence) and OpenCV/Mock (where set_sequence is a no-op). Subclasses override only when they need additional prep.

Parameters:

config (ExperimentConfig)

Return type:

None

set_writer(make_path)[source]#

Generate the camera’s output path and construct the writer.

Resolves self.output_path via make_path, then instantiates the writer that _make_writer() picks for self.file_type and copies the writer’s sidecar filename onto self.metadata_path for the AcquisitionManifest. Subclasses override only when they need additional plumbing on top (e.g. OpenCVCamera resetting its capture-loop timing).

Parameters:

make_path (Callable[[str, str, str, bool], str])

Return type:

None

set_sequence(build_mda)[source]#

Default no-op (only MMCamera with an MDA backend overrides this).

Parameters:

build_mda (Callable[[Any], Any])

Return type:

None

snap()[source]#

Capture a single frame outside any recording, return it as an ndarray.

Used by the GUI’s snap button. Implementations should NOT alter recording state – snap is preview-only – but they SHOULD call _save_snap_png() so each snap also lands a *_snap.png.

start_live()[source]#

Begin continuous live preview WITHOUT writing to disk.

Subscribers receive frames via image_ready (Qt) / signals.data (psygnal). No recording side-effects; pair with stop_live().

Return type:

None

stop_live()[source]#

End the continuous live preview started by start_live().

Return type:

None

property calibration: Dict[str, Any]#

Camera-specific constants worth recording in the AcquisitionManifest.

sidecars()[source]#

Extra sidecars beyond metadata_path. Cameras typically have none.

Return type:

list

shutdown()[source]#

Default cleanup: stop the camera. Subclasses extend if needed.

Return type:

None