Skip to content

Module wtracker.sim.view_controller

View Source
import cv2 as cv

import numpy as np

from wtracker.utils.frame_reader import FrameReader, FrameStream

class ViewController(FrameStream):

    """

    A class representing a view controller for a frame stream.

    This class allows for easy manipulation of the camera and microscope positions,

    and provides their corresponding views.

    Args:

        frame_reader (FrameReader): The frame reader object.

        camera_size (tuple[int, int], optional): The size of the camera frame.

        micro_size (tuple[int, int], optional): The size of the micro frame.

        init_position (tuple[int, int], optional): The initial position of the view.

    Attributes:

        frame_reader (FrameReader): The frame reader object.

        camera_size (tuple[int, int]): The size of the camera view (w, h).

        micro_size (tuple[int, int]): The size of the micro view (w, h).

        position (tuple[int, int]): The current position of the center of the view (x, y).

    """

    def __init__(

        self,

        frame_reader: FrameReader,

        camera_size: tuple[int, int] = (251, 251),

        micro_size: tuple[int, int] = (45, 45),

        init_position: tuple[int, int] = (0, 0),

    ):

        super().__init__(frame_reader)

        assert camera_size[0] >= micro_size[0]

        assert camera_size[1] >= micro_size[1]

        self._padding_size: tuple[int, int] = (camera_size[0] // 2, camera_size[1] // 2)

        self._camera_size = camera_size

        self._micro_size = micro_size

        self._position = init_position

        self.set_position(*init_position)

    def read(self) -> np.ndarray:

        """

        Read a frame from the frame reader and apply padding.

        Returns:

            np.ndarray: The padded frame.

        """

        frame = super().read()

        frame = cv.copyMakeBorder(

            src=frame,

            left=self._padding_size[0],

            right=self._padding_size[0],

            top=self._padding_size[1],

            bottom=self._padding_size[1],

            borderType=cv.BORDER_REPLICATE,

        )

        return frame

    @property

    def position(self) -> tuple[int, int]:

        """

        Get the current position of the view controller.

        Returns:

            tuple[int, int]: The current position (x, y).

        """

        return self._position

    @property

    def camera_size(self) -> tuple[int, int]:

        """

        Get the size of the camera view.

        Returns:

            tuple[int, int]: The size of the camera view (w, h).

        """

        return self._camera_size

    @property

    def micro_size(self) -> tuple[int, int]:

        """

        Get the size of the micro view.

        Returns:

            tuple[int, int]: The size of the micro view (w, h).

        """

        return self._micro_size

    @property

    def camera_position(self) -> tuple[int, int, int, int]:

        """

        Get the position of the camera view.

        Returns:

            tuple[int, int, int, int]: The position of the camera view (x, y, w, h).

        """

        w, h = self.camera_size

        x = self._position[0] - w // 2

        y = self._position[1] - h // 2

        return x, y, w, h

    @property

    def micro_position(self) -> tuple[int, int, int, int]:

        """

        Get the position of the micro view.

        Returns:

            tuple[int, int, int, int]: The position of the micro view (x, y, w, h).

        """

        w, h = self.micro_size

        x = self._position[0] - w // 2

        y = self._position[1] - h // 2

        return x, y, w, h

    def set_position(self, x: int, y: int):

        """

        Set the position of the view controller.

        Note, that the position is clamped to the frame size.

        Args:

            x (int): The x-coordinate of the position.

            y (int): The y-coordinate of the position.

        """

        x = np.clip(x, 0, self._frame_reader.frame_shape[1] - 1)

        y = np.clip(y, 0, self._frame_reader.frame_shape[0] - 1)

        self._position = (x, y)

    def move_position(self, dx: int, dy: int):

        """

        Move the position of the view controller by dx and dy.

        Args:

            dx (int): The amount to move in the x-direction.

            dy (int): The amount to move in the y-direction.

        """

        self.set_position(self._position[0] + dx, self._position[1] + dy)

    def _calc_view_bbox(self, w: int, h: int) -> tuple[int, int, int, int]:

        """

        Calculate the bbox of the view, while taking padding into account.

        Args:

            w (int): The width of the view.

            h (int): The height of the view.

        Returns:

            tuple[int, int, int, int]: The bounding box of the view (x, y, w, h).

        """

        x = self._position[0] + self._padding_size[0] - w // 2

        y = self._position[1] + self._padding_size[1] - h // 2

        return x, y, w, h

    def _custom_view(self, w: int, h: int) -> np.ndarray:

        """

        Get a custom view of the frame.

        Args:

            w (int): The width of the view.

            h (int): The height of the view.

        Returns:

            np.ndarray: The custom view of the frame.

        """

        x, y, w, h = self._calc_view_bbox(w, h)

        frame = self.read()

        slice = frame[y : y + w, x : x + h]

        return slice

    def camera_view(self) -> np.ndarray:

        """

        Get the camera view.

        Returns:

            np.ndarray: The camera view.

        """

        return self._custom_view(*self.camera_size)

    def micro_view(self) -> np.ndarray:

        """

        Get the micro view.

        Returns:

            np.ndarray: The micro view.

        """

        return self._custom_view(*self.micro_size)

    def visualize_world(self, line_width: int = 4, timeout: int = 1):

        """

        Visualize the world view with bounding boxes.

        Both the camera and micro views are visualized, along with the center point.

        Args:

            line_width (int): The width of the bounding box lines.

        """

        x_mid, y_mid, _, _ = self._calc_view_bbox(0, 0)

        x_cam, y_cam, w_cam, h_cam = self._calc_view_bbox(*self.camera_size)

        x_mic, y_mic, w_mic, h_mic = self._calc_view_bbox(*self.micro_size)

        world = self.read()

        if len(self._frame_reader.frame_shape) == 2:

            world = cv.cvtColor(world, cv.COLOR_GRAY2BGR)

        cv.rectangle(world, (x_cam, y_cam), (x_cam + w_cam, y_cam + h_cam), (0, 0, 255), line_width)

        cv.rectangle(world, (x_mic, y_mic), (x_mic + w_mic, y_mic + h_mic), (0, 255, 0), line_width)

        cv.circle(world, (x_mid, y_mid), 1, (255, 0, 0), line_width)

        cv.imshow("World View", world)

        cv.waitKey(timeout)

Classes

ViewController

class ViewController(
    frame_reader: wtracker.utils.frame_reader.FrameReader,
    camera_size: tuple[int, int] = (251, 251),
    micro_size: tuple[int, int] = (45, 45),
    init_position: tuple[int, int] = (0, 0)
)

A class representing a view controller for a frame stream.

This class allows for easy manipulation of the camera and microscope positions, and provides their corresponding views.

Attributes

Name Type Description Default
frame_reader FrameReader The frame reader object. None
camera_size tuple[int, int] The size of the camera frame. None
micro_size tuple[int, int] The size of the micro frame. None
init_position tuple[int, int] The initial position of the view. None
frame_reader FrameReader The frame reader object. None
camera_size tuple[int, int] The size of the camera view (w, h). None
micro_size tuple[int, int] The size of the micro view (w, h). None
position tuple[int, int] The current position of the center of the view (x, y). None
View Source
class ViewController(FrameStream):

    """

    A class representing a view controller for a frame stream.

    This class allows for easy manipulation of the camera and microscope positions,

    and provides their corresponding views.

    Args:

        frame_reader (FrameReader): The frame reader object.

        camera_size (tuple[int, int], optional): The size of the camera frame.

        micro_size (tuple[int, int], optional): The size of the micro frame.

        init_position (tuple[int, int], optional): The initial position of the view.

    Attributes:

        frame_reader (FrameReader): The frame reader object.

        camera_size (tuple[int, int]): The size of the camera view (w, h).

        micro_size (tuple[int, int]): The size of the micro view (w, h).

        position (tuple[int, int]): The current position of the center of the view (x, y).

    """

    def __init__(

        self,

        frame_reader: FrameReader,

        camera_size: tuple[int, int] = (251, 251),

        micro_size: tuple[int, int] = (45, 45),

        init_position: tuple[int, int] = (0, 0),

    ):

        super().__init__(frame_reader)

        assert camera_size[0] >= micro_size[0]

        assert camera_size[1] >= micro_size[1]

        self._padding_size: tuple[int, int] = (camera_size[0] // 2, camera_size[1] // 2)

        self._camera_size = camera_size

        self._micro_size = micro_size

        self._position = init_position

        self.set_position(*init_position)

    def read(self) -> np.ndarray:

        """

        Read a frame from the frame reader and apply padding.

        Returns:

            np.ndarray: The padded frame.

        """

        frame = super().read()

        frame = cv.copyMakeBorder(

            src=frame,

            left=self._padding_size[0],

            right=self._padding_size[0],

            top=self._padding_size[1],

            bottom=self._padding_size[1],

            borderType=cv.BORDER_REPLICATE,

        )

        return frame

    @property

    def position(self) -> tuple[int, int]:

        """

        Get the current position of the view controller.

        Returns:

            tuple[int, int]: The current position (x, y).

        """

        return self._position

    @property

    def camera_size(self) -> tuple[int, int]:

        """

        Get the size of the camera view.

        Returns:

            tuple[int, int]: The size of the camera view (w, h).

        """

        return self._camera_size

    @property

    def micro_size(self) -> tuple[int, int]:

        """

        Get the size of the micro view.

        Returns:

            tuple[int, int]: The size of the micro view (w, h).

        """

        return self._micro_size

    @property

    def camera_position(self) -> tuple[int, int, int, int]:

        """

        Get the position of the camera view.

        Returns:

            tuple[int, int, int, int]: The position of the camera view (x, y, w, h).

        """

        w, h = self.camera_size

        x = self._position[0] - w // 2

        y = self._position[1] - h // 2

        return x, y, w, h

    @property

    def micro_position(self) -> tuple[int, int, int, int]:

        """

        Get the position of the micro view.

        Returns:

            tuple[int, int, int, int]: The position of the micro view (x, y, w, h).

        """

        w, h = self.micro_size

        x = self._position[0] - w // 2

        y = self._position[1] - h // 2

        return x, y, w, h

    def set_position(self, x: int, y: int):

        """

        Set the position of the view controller.

        Note, that the position is clamped to the frame size.

        Args:

            x (int): The x-coordinate of the position.

            y (int): The y-coordinate of the position.

        """

        x = np.clip(x, 0, self._frame_reader.frame_shape[1] - 1)

        y = np.clip(y, 0, self._frame_reader.frame_shape[0] - 1)

        self._position = (x, y)

    def move_position(self, dx: int, dy: int):

        """

        Move the position of the view controller by dx and dy.

        Args:

            dx (int): The amount to move in the x-direction.

            dy (int): The amount to move in the y-direction.

        """

        self.set_position(self._position[0] + dx, self._position[1] + dy)

    def _calc_view_bbox(self, w: int, h: int) -> tuple[int, int, int, int]:

        """

        Calculate the bbox of the view, while taking padding into account.

        Args:

            w (int): The width of the view.

            h (int): The height of the view.

        Returns:

            tuple[int, int, int, int]: The bounding box of the view (x, y, w, h).

        """

        x = self._position[0] + self._padding_size[0] - w // 2

        y = self._position[1] + self._padding_size[1] - h // 2

        return x, y, w, h

    def _custom_view(self, w: int, h: int) -> np.ndarray:

        """

        Get a custom view of the frame.

        Args:

            w (int): The width of the view.

            h (int): The height of the view.

        Returns:

            np.ndarray: The custom view of the frame.

        """

        x, y, w, h = self._calc_view_bbox(w, h)

        frame = self.read()

        slice = frame[y : y + w, x : x + h]

        return slice

    def camera_view(self) -> np.ndarray:

        """

        Get the camera view.

        Returns:

            np.ndarray: The camera view.

        """

        return self._custom_view(*self.camera_size)

    def micro_view(self) -> np.ndarray:

        """

        Get the micro view.

        Returns:

            np.ndarray: The micro view.

        """

        return self._custom_view(*self.micro_size)

    def visualize_world(self, line_width: int = 4, timeout: int = 1):

        """

        Visualize the world view with bounding boxes.

        Both the camera and micro views are visualized, along with the center point.

        Args:

            line_width (int): The width of the bounding box lines.

        """

        x_mid, y_mid, _, _ = self._calc_view_bbox(0, 0)

        x_cam, y_cam, w_cam, h_cam = self._calc_view_bbox(*self.camera_size)

        x_mic, y_mic, w_mic, h_mic = self._calc_view_bbox(*self.micro_size)

        world = self.read()

        if len(self._frame_reader.frame_shape) == 2:

            world = cv.cvtColor(world, cv.COLOR_GRAY2BGR)

        cv.rectangle(world, (x_cam, y_cam), (x_cam + w_cam, y_cam + h_cam), (0, 0, 255), line_width)

        cv.rectangle(world, (x_mic, y_mic), (x_mic + w_mic, y_mic + h_mic), (0, 255, 0), line_width)

        cv.circle(world, (x_mid, y_mid), 1, (255, 0, 0), line_width)

        cv.imshow("World View", world)

        cv.waitKey(timeout)

Ancestors (in MRO)

  • wtracker.utils.frame_reader.FrameStream

Instance variables

camera_position

Get the position of the camera view.

camera_size

Get the size of the camera view.

index

The index of the current frame.

micro_position

Get the position of the micro view.

micro_size

Get the size of the micro view.

position

Get the current position of the view controller.

Methods

camera_view

def camera_view(
    self
) -> numpy.ndarray

Get the camera view.

Returns:

Type Description
np.ndarray The camera view.
View Source
    def camera_view(self) -> np.ndarray:

        """

        Get the camera view.

        Returns:

            np.ndarray: The camera view.

        """

        return self._custom_view(*self.camera_size)

can_read

def can_read(
    self
) -> 'bool'
View Source
    def can_read(self) -> bool:

        return self._idx >= 0 and self._idx < len(self._frame_reader)

micro_view

def micro_view(
    self
) -> numpy.ndarray

Get the micro view.

Returns:

Type Description
np.ndarray The micro view.
View Source
    def micro_view(self) -> np.ndarray:

        """

        Get the micro view.

        Returns:

            np.ndarray: The micro view.

        """

        return self._custom_view(*self.micro_size)

move_position

def move_position(
    self,
    dx: int,
    dy: int
)

Move the position of the view controller by dx and dy.

Parameters:

Name Type Description Default
dx int The amount to move in the x-direction. None
dy int The amount to move in the y-direction. None
View Source
    def move_position(self, dx: int, dy: int):

        """

        Move the position of the view controller by dx and dy.

        Args:

            dx (int): The amount to move in the x-direction.

            dy (int): The amount to move in the y-direction.

        """

        self.set_position(self._position[0] + dx, self._position[1] + dy)

progress

def progress(
    self,
    n: 'int' = 1
) -> 'bool'

Moves the current index forward by the specified number of steps.

Parameters:

Name Type Description Default
n int The number of steps to move forward. None

Returns:

Type Description
bool True if the index was successfully moved forward, False otherwise.
View Source
    def progress(self, n: int = 1) -> bool:

        """

        Moves the current index forward by the specified number of steps.

        Args:

            n (int): The number of steps to move forward.

        Returns:

            bool: True if the index was successfully moved forward, False otherwise.

        """

        return self.seek(self._idx + n)

read

def read(
    self
) -> numpy.ndarray

Read a frame from the frame reader and apply padding.

Returns:

Type Description
np.ndarray The padded frame.
View Source
    def read(self) -> np.ndarray:

        """

        Read a frame from the frame reader and apply padding.

        Returns:

            np.ndarray: The padded frame.

        """

        frame = super().read()

        frame = cv.copyMakeBorder(

            src=frame,

            left=self._padding_size[0],

            right=self._padding_size[0],

            top=self._padding_size[1],

            bottom=self._padding_size[1],

            borderType=cv.BORDER_REPLICATE,

        )

        return frame

reset

def reset(
    self
)

Resets the frame reader to the beginning of the steam.

View Source
    def reset(self):

        """

        Resets the frame reader to the beginning of the steam.

        """

        self.seek(-1)

seek

def seek(
    self,
    idx: 'int'
) -> 'bool'

Move the index to the specified position.

Parameters:

Name Type Description Default
idx int The index to seek to. None

Returns:

Type Description
bool True if the index is within the valid range, False otherwise.
View Source
    def seek(self, idx: int) -> bool:

        """

        Move the index to the specified position.

        Args:

            idx (int): The index to seek to.

        Returns:

            bool: True if the index is within the valid range, False otherwise.

        """

        self._idx = idx

        self.frame = None

        return self.can_read()

set_position

def set_position(
    self,
    x: int,
    y: int
)

Set the position of the view controller.

Note, that the position is clamped to the frame size.

Parameters:

Name Type Description Default
x int The x-coordinate of the position. None
y int The y-coordinate of the position. None
View Source
    def set_position(self, x: int, y: int):

        """

        Set the position of the view controller.

        Note, that the position is clamped to the frame size.

        Args:

            x (int): The x-coordinate of the position.

            y (int): The y-coordinate of the position.

        """

        x = np.clip(x, 0, self._frame_reader.frame_shape[1] - 1)

        y = np.clip(y, 0, self._frame_reader.frame_shape[0] - 1)

        self._position = (x, y)

visualize_world

def visualize_world(
    self,
    line_width: int = 4,
    timeout: int = 1
)

Visualize the world view with bounding boxes.

Both the camera and micro views are visualized, along with the center point.

Parameters:

Name Type Description Default
line_width int The width of the bounding box lines. None
View Source
    def visualize_world(self, line_width: int = 4, timeout: int = 1):

        """

        Visualize the world view with bounding boxes.

        Both the camera and micro views are visualized, along with the center point.

        Args:

            line_width (int): The width of the bounding box lines.

        """

        x_mid, y_mid, _, _ = self._calc_view_bbox(0, 0)

        x_cam, y_cam, w_cam, h_cam = self._calc_view_bbox(*self.camera_size)

        x_mic, y_mic, w_mic, h_mic = self._calc_view_bbox(*self.micro_size)

        world = self.read()

        if len(self._frame_reader.frame_shape) == 2:

            world = cv.cvtColor(world, cv.COLOR_GRAY2BGR)

        cv.rectangle(world, (x_cam, y_cam), (x_cam + w_cam, y_cam + h_cam), (0, 0, 255), line_width)

        cv.rectangle(world, (x_mic, y_mic), (x_mic + w_mic, y_mic + h_mic), (0, 255, 0), line_width)

        cv.circle(world, (x_mid, y_mid), 1, (255, 0, 0), line_width)

        cv.imshow("World View", world)

        cv.waitKey(timeout)