• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Aharoni-Lab / mio / 17891979514

21 Sep 2025 09:39AM UTC coverage: 80.519% (-0.04%) from 80.554%
17891979514

Pull #93

github

web-flow
Merge 8cada29db into d5059b23d
Pull Request #93: [cli] Add `mio config list`

43 of 47 new or added lines in 2 files covered. (91.49%)

5 existing lines in 4 files now uncovered.

1922 of 2387 relevant lines covered (80.52%)

4.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.27
/mio/models/frames.py
1
"""
2
Pydantic models for storing frames and videos.
3
"""
4

5
from pathlib import Path
6✔
6
from typing import List, Optional, Union
6✔
7

8
import cv2
6✔
9
import numpy as np
6✔
10
from pydantic import BaseModel, ConfigDict, Field
6✔
11

12
from mio.io import VideoWriter
6✔
13
from mio.logging import init_logger
6✔
14

15
logger = init_logger("model.frames")
6✔
16

17

18
class NamedBaseFrame(BaseModel):
6✔
19
    """
20
    Pydantic model to store an an image or a video together with a name.
21
    """
22

23
    name: str = Field(
6✔
24
        ...,
25
        description="Name of the video.",
26
    )
27
    model_config = ConfigDict(
6✔
28
        arbitrary_types_allowed=True,
29
    )
30

31
    def export(self, output_path: Union[Path, str], fps: int, suffix: bool) -> None:
6✔
32
        """
33
        Export the frame data to a file.
34
        The implementation needs to be defined in the derived classes.
35
        """
36
        raise NotImplementedError("Method not implemented.")
×
37

38

39
class NamedFrame(NamedBaseFrame):
6✔
40
    """
41
    Pydantic model to store an image or a video together with a name.
42
    """
43

44
    frame: Optional[np.ndarray] = Field(
6✔
45
        None,
46
        description="Frame data, if provided.",
47
    )
48

49
    def export(self, output_path: Union[Path, str], suffix: bool = False) -> None:
6✔
50
        """
51
        Export the frame data to a file.
52
        The file name will be a concatenation of the output path and the name of the frame.
53
        """
54
        output_path = Path(output_path)
6✔
55
        if self.frame is None:
6✔
56
            logger.warning(f"No frame data provided for {self.name}. Skipping export.")
×
57
            return
×
58
        if suffix:
6✔
59
            output_path = output_path.with_name(output_path.stem + f"_{self.name}")
6✔
60
        cv2.imwrite(str(output_path.with_suffix(".png")), self.frame)
6✔
61
        logger.info(
6✔
62
            f"Writing frame to {output_path}.png: {self.frame.shape[1]}x{self.frame.shape[0]}"
63
        )
64

65
    def display(self, binary: bool = False) -> None:
6✔
66
        """
67
        Display the frame data in a opencv window. Press ESC to close the window.
68

69
        Parameters
70
        ----------
71
        binary : bool
72
            If True, the frame will be scaled to the full range of uint8.
73
        """
74
        if self.frame is None:
×
75
            logger.warning(f"No frame data provided for {self.name}. Skipping display.")
×
76
            return
×
77

78
        frame_to_display = self.frame
×
79
        if binary:
×
80
            frame_to_display = cv2.normalize(
×
81
                self.frame, None, 0, np.iinfo(np.uint8).max, cv2.NORM_MINMAX
82
            ).astype(np.uint8)
83
        cv2.imshow(self.name, frame_to_display)
×
UNCOV
84
        while True:
×
85
            if cv2.waitKey(1) == 27:
×
86
                break
×
87
        cv2.destroyAllWindows()
×
88
        cv2.waitKey(1)  # Extra waitKey to properly close the window
×
89

90

91
class NamedVideo(NamedBaseFrame):
6✔
92
    """
93
    Pydantic model to store a video together with a name.
94
    """
95

96
    video: Optional[List[np.ndarray]] = Field(
6✔
97
        None,
98
        description="List of frames.",
99
    )
100

101
    def export(self, output_path: Union[Path, str], suffix: bool = False, fps: float = 20) -> None:
6✔
102
        """
103
        Export the frame data to a file.
104
        """
105
        if self.video is None or self.video == []:
6✔
106
            logger.warning(f"No frame data provided for {self.name}. Skipping export.")
×
107
            return
×
108
        output_path = Path(output_path)
6✔
109
        if suffix:
6✔
110
            output_path = output_path.with_name(output_path.stem + f"_{self.name}")
6✔
111
        if not all(isinstance(frame, np.ndarray) for frame in self.video):
6✔
112
            raise ValueError("Not all frames are numpy arrays.")
×
113
        writer = VideoWriter(
6✔
114
            path=output_path.with_suffix(".avi"),
115
            fps=fps,
116
        )
117
        logger.info(
6✔
118
            f"Writing video to {output_path}.avi:"
119
            f"{self.video[0].shape[1]}x{self.video[0].shape[0]}"
120
        )
121
        try:
6✔
122
            for frame in self.video:
6✔
123
                picture = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
6✔
124
                writer.write_frame(picture)
6✔
125
        finally:
126
            writer.close()
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc