Source code for j5_zoloto.component

"""Classes for Fiducial Marker Camera."""

import logging
from abc import abstractmethod
from pathlib import Path
from typing import Generator, List, Optional, Type, Union

from j5.components.component import Component, Interface
from numpy import ndarray
from zoloto.marker import BaseMarker, EagerMarker, Marker, UncalibratedMarker

LOGGER = logging.getLogger(__name__)


[docs]class MarkerCameraInterface(Interface): """An interface containing the methods required for a marker camera."""
[docs] @abstractmethod def process_frame( self, identifier: int, *, frame: Optional[ndarray] = None, ) -> Generator[Union[UncalibratedMarker, Marker], None, None]: """ Get markers that the camera can see. :param identifier: Camera identifier, ignored. """ raise NotImplementedError # pragma: nocover
[docs] @abstractmethod def process_frame_eager( self, identifier: int, *, frame: Optional[ndarray] = None, ) -> Generator[EagerMarker, None, None]: """ Get markers that the camera can see. :param identifier: Camera identifier, ignored. """ raise NotImplementedError # pragma: nocover
[docs] @abstractmethod def save_annotated_image( self, identifier: int, file: Path, *, frame: Optional[ndarray] = None, ) -> None: """Save an annotated image to a file.""" raise NotImplementedError # pragma: nocover
[docs] @abstractmethod def get_visible_markers( self, identifier: int, *, frame: Optional[ndarray] = None, ) -> List[int]: """ Get a list of visible marker IDs. :param identifier: Camera identifier, ignored. :returns: List of marker IDs that were visible. """ raise NotImplementedError # pragma: nocover
[docs] @abstractmethod def capture_frame(self) -> ndarray: """ Get the raw image data from the camera. :returns: Camera pixel data """ raise NotImplementedError # pragma: nocover
[docs] @abstractmethod def close_camera(self, identifier: int) -> None: """Close the camera object.""" raise NotImplementedError # pragma: nocover
[docs]class MarkerCamera(Component): """ Camera that can identify fiducial markers. Additionally, it will do pose estimation, along with some calibration in order to determine the spatial positon and orientation of the markers that it has detected. """
[docs] def __init__( self, identifier: int, backend: MarkerCameraInterface, ) -> None: self._backend = backend self._identifier = identifier
[docs] @staticmethod def interface_class() -> Type[MarkerCameraInterface]: """Get the interface class that is required to use this component.""" return MarkerCameraInterface
@property def identifier(self) -> int: """An integer to identify the component on a board.""" return self._identifier
[docs] def see( self, *, eager: bool = True, frame: Optional[ndarray] = None, ) -> List[BaseMarker]: """ Capture an image and identify fiducial markers. :param eager: Process the pose estimations of markers immediately. :returns: list of markers that the camera could see. """ if eager: return list(self._backend.process_frame_eager(self._identifier, frame=frame)) else: return list(self._backend.process_frame(self._identifier, frame=frame))
[docs] def see_ids(self, *, frame: Optional[ndarray] = None) -> List[int]: """ Capture an image and identify fiducial markers. This method does not perform pose estimation, so is faster than ``see``. :returns: A list of IDs for the markers that were visible. """ return self._backend.get_visible_markers(self._identifier, frame=frame)
[docs] def capture(self) -> ndarray: """ Get the raw image data from the camera. :returns: Camera pixel data """ return self._backend.capture_frame()
[docs] def save(self, path: Union[Path, str], *, frame: Optional[ndarray] = None) -> None: """Save an annotated image to a path.""" if isinstance(path, str): path = Path(path) if not path.suffix: LOGGER.info("Unable to save image without file extension, assuming .jpg") path = path.with_suffix(".jpg") self._backend.save_annotated_image(self._identifier, path, frame=frame)
[docs] def close(self) -> None: """ Close the camera. The camera will no longer work after this method is called. """ self._backend.close_camera(self._identifier)