Skip to content

layout

The layout: which views each video carries, and which of them are mirrored.

A rig that folds several views into one frame (hexaview's two videos, each seeing the arena directly and twice through prisms) needs this one fact, apart from the geometry: it is fixed until the rig is rebuilt, where geometry is solved per project, and labeling and training crops need mirrored before any calibration exists. An empty layout is the 1:1 case, every view reading the video of its name.

layout = Layout({"cam": ["front", "back", "left"]}, mirrored=["left"])
layout.video_of("left")             # "cam"

Layout dataclass

Which views each video carries, and which of them are mirrored.

Attributes:

Name Type Description
views_by_video Mapping[str, Sequence[str]]

video name -> the views it carries, in order; empty for a 1:1 rig.

mirrored Sequence[str]

The views the apparatus reflects, in the order given.

Source code in src/deeperfly/layout.py
@dataclass(frozen=True)
class Layout:
    """Which views each video carries, and which of them are mirrored.

    Attributes:
        views_by_video: `video name -> the views it carries`, in order; empty for a
            1:1 rig.
        mirrored: The views the apparatus reflects, in the order given.
    """

    views_by_video: Mapping[str, Sequence[str]] = field(default_factory=dict)
    mirrored: Sequence[str] = ()

    def __post_init__(self) -> None:
        """Store both fields as tuples and refuse an inconsistent layout.

        Raises:
            ValueError: If a view is carried by two videos, or a mirrored name is not
                a view this layout knows.
        """
        by_video = {v: tuple(views) for v, views in self.views_by_video.items()}
        object.__setattr__(self, "views_by_video", by_video)
        object.__setattr__(self, "mirrored", tuple(dict.fromkeys(self.mirrored)))
        carrier: dict[str, str] = {}
        for video, views in by_video.items():
            for view in views:
                if view in carrier:
                    raise ValueError(
                        f"view {view!r} is carried by both "
                        f"{carrier[view]!r} and {video!r}"
                    )
                carrier[view] = video
        unknown = [v for v in self.mirrored if carrier and v not in carrier]
        if unknown:
            raise ValueError(
                "mirrored names no view of this layout: " + ", ".join(unknown)
            )

    def video_of(self, view: str) -> str:
        """The video a view's frames come from; the view's own name in a 1:1 rig."""
        return next((v for v, ws in self.views_by_video.items() if view in ws), view)

    def videos_of(self, views: Iterable[str]) -> list[str]:
        """The videos some views read, each once, in the order the views first need."""
        return list(dict.fromkeys(self.video_of(v) for v in views))

    def views_in(self, video: str) -> list[str]:
        """The views a video carries, in order; just the video in a 1:1 rig."""
        return list(self.views_by_video.get(video, (video,)))

    def is_mirrored(self, view: str) -> bool:
        """Whether the apparatus reflects this view (a prism view)."""
        return view in self.mirrored

    def frame_sizes(
        self, views: Iterable[str], videos: Mapping[str, Video]
    ) -> dict[str, tuple[int, int]]:
        """Each view's frame `(height, width)`: that of the video it reads."""
        return {view: videos[self.video_of(view)].size for view in views}

__post_init__

__post_init__() -> None

Store both fields as tuples and refuse an inconsistent layout.

Raises:

Type Description
ValueError

If a view is carried by two videos, or a mirrored name is not a view this layout knows.

Source code in src/deeperfly/layout.py
def __post_init__(self) -> None:
    """Store both fields as tuples and refuse an inconsistent layout.

    Raises:
        ValueError: If a view is carried by two videos, or a mirrored name is not
            a view this layout knows.
    """
    by_video = {v: tuple(views) for v, views in self.views_by_video.items()}
    object.__setattr__(self, "views_by_video", by_video)
    object.__setattr__(self, "mirrored", tuple(dict.fromkeys(self.mirrored)))
    carrier: dict[str, str] = {}
    for video, views in by_video.items():
        for view in views:
            if view in carrier:
                raise ValueError(
                    f"view {view!r} is carried by both "
                    f"{carrier[view]!r} and {video!r}"
                )
            carrier[view] = video
    unknown = [v for v in self.mirrored if carrier and v not in carrier]
    if unknown:
        raise ValueError(
            "mirrored names no view of this layout: " + ", ".join(unknown)
        )

frame_sizes

frame_sizes(
    views: Iterable[str], videos: Mapping[str, Video]
) -> dict[str, tuple[int, int]]

Each view's frame (height, width): that of the video it reads.

Source code in src/deeperfly/layout.py
def frame_sizes(
    self, views: Iterable[str], videos: Mapping[str, Video]
) -> dict[str, tuple[int, int]]:
    """Each view's frame `(height, width)`: that of the video it reads."""
    return {view: videos[self.video_of(view)].size for view in views}

is_mirrored

is_mirrored(view: str) -> bool

Whether the apparatus reflects this view (a prism view).

Source code in src/deeperfly/layout.py
def is_mirrored(self, view: str) -> bool:
    """Whether the apparatus reflects this view (a prism view)."""
    return view in self.mirrored

video_of

video_of(view: str) -> str

The video a view's frames come from; the view's own name in a 1:1 rig.

Source code in src/deeperfly/layout.py
def video_of(self, view: str) -> str:
    """The video a view's frames come from; the view's own name in a 1:1 rig."""
    return next((v for v, ws in self.views_by_video.items() if view in ws), view)

videos_of

videos_of(views: Iterable[str]) -> list[str]

The videos some views read, each once, in the order the views first need.

Source code in src/deeperfly/layout.py
def videos_of(self, views: Iterable[str]) -> list[str]:
    """The videos some views read, each once, in the order the views first need."""
    return list(dict.fromkeys(self.video_of(v) for v in views))

views_in

views_in(video: str) -> list[str]

The views a video carries, in order; just the video in a 1:1 rig.

Source code in src/deeperfly/layout.py
def views_in(self, video: str) -> list[str]:
    """The views a video carries, in order; just the video in a 1:1 rig."""
    return list(self.views_by_video.get(video, (video,)))