Source code for fmusvid.core.frame

"""
Frame class for FMUS-VID.

This module provides the Frame class for handling individual video frames.
"""

from typing import Union, Tuple, Optional, Any
from pathlib import Path
import logging

logger = logging.getLogger(__name__)

[docs] class Frame: """ Class for handling individual video frames. This class provides methods for manipulating and saving frames. """
[docs] def __init__(self, frame_data: Any, backend: Any): """ Initialize a Frame object. Args: frame_data: Backend-specific frame data backend: Backend instance """ self._frame_data = frame_data self._backend = backend
[docs] def save(self, path: Union[str, Path], **kwargs) -> None: """ Save frame to file. Args: path: Output file path **kwargs: Save options Example: >>> frame.save("thumbnail.jpg", quality=95) """ self._backend.save_frame(self._frame_data, path, **kwargs)
[docs] def resize(self, width: Optional[int] = None, height: Optional[int] = None, keep_aspect: bool = True) -> 'Frame': """ Resize frame to specified dimensions. Args: width: Target width (None to auto-calculate from height) height: Target height (None to auto-calculate from width) keep_aspect: Maintain aspect ratio if only one dimension is specified Returns: New Frame object Example: >>> thumbnail = frame.resize(width=320) """ new_frame_data = self._backend.resize_frame( self._frame_data, width=width, height=height, keep_aspect=keep_aspect ) return Frame(new_frame_data, self._backend)
[docs] def crop(self, x: int, y: int, width: int, height: int) -> 'Frame': """ Crop frame to specified region. Args: x: X coordinate of top-left corner y: Y coordinate of top-left corner width: Width of crop region height: Height of crop region Returns: New Frame object Example: >>> cropped = frame.crop(100, 100, 400, 300) """ new_frame_data = self._backend.crop_frame( self._frame_data, x=x, y=y, width=width, height=height ) return Frame(new_frame_data, self._backend)
[docs] def grayscale(self) -> 'Frame': """ Convert frame to grayscale. Returns: New Frame object Example: >>> bw = frame.grayscale() """ new_frame_data = self._backend.grayscale_frame(self._frame_data) return Frame(new_frame_data, self._backend)
[docs] def blur(self, radius: float) -> 'Frame': """ Apply Gaussian blur with specified radius. Args: radius: Blur radius Returns: New Frame object Example: >>> blurred = frame.blur(5) """ new_frame_data = self._backend.blur_frame(self._frame_data, radius=radius) return Frame(new_frame_data, self._backend)
[docs] def brightness(self, factor: float) -> 'Frame': """ Adjust frame brightness. Args: factor: Brightness factor (1.0 = original, 1.5 = +50%, 0.5 = -50%) Returns: New Frame object Example: >>> brighter = frame.brightness(1.2) """ new_frame_data = self._backend.brightness_frame(self._frame_data, factor=factor) return Frame(new_frame_data, self._backend)
[docs] def contrast(self, factor: float) -> 'Frame': """ Adjust frame contrast. Args: factor: Contrast factor (1.0 = original, 1.5 = +50%, 0.5 = -50%) Returns: New Frame object Example: >>> higher_contrast = frame.contrast(1.2) """ new_frame_data = self._backend.contrast_frame(self._frame_data, factor=factor) return Frame(new_frame_data, self._backend)
[docs] def overlay(self, image, position: Tuple[int, int] = (0, 0), opacity: float = 1.0) -> 'Frame': """ Overlay an image on the frame. Args: image: Image to overlay (Frame or PIL Image) position: (x, y) coordinates for placement opacity: Opacity of overlay (1.0 = fully opaque) Returns: New Frame object Example: >>> with_logo = frame.overlay(logo, position=(20, 20), opacity=0.8) """ # If image is a Frame, extract its data if isinstance(image, Frame): image = image._frame_data new_frame_data = self._backend.overlay_frame( self._frame_data, image, position=position, opacity=opacity ) return Frame(new_frame_data, self._backend)
[docs] def add_text(self, text: str, position: Tuple[int, int], font: str = "Arial", size: int = 24, color: Union[str, Tuple[int, int, int]] = "white") -> 'Frame': """ Add text to the frame. Args: text: Text to add position: (x, y) coordinates for placement font: Font name size: Font size color: Text color (name or RGB tuple) Returns: New Frame object Example: >>> with_caption = frame.add_text("Hello World", position=(50, 50), size=32) """ new_frame_data = self._backend.add_text_to_frame( self._frame_data, text=text, position=position, font=font, size=size, color=color ) return Frame(new_frame_data, self._backend)
[docs] def to_pil(self): """ Convert frame to PIL Image. Returns: PIL Image object Example: >>> pil_image = frame.to_pil() >>> pil_image.show() """ return self._backend.frame_to_pil(self._frame_data)
[docs] def to_numpy(self): """ Convert frame to NumPy array. Returns: NumPy array (height, width, channels) Example: >>> import numpy as np >>> array = frame.to_numpy() >>> print(f"Shape: {array.shape}") """ return self._backend.frame_to_numpy(self._frame_data)
[docs] def __repr__(self) -> str: """String representation of the Frame object.""" try: # Try to get frame dimensions width, height = self._backend.get_frame_dimensions(self._frame_data) return f"Frame({width}x{height})" except Exception: return "Frame(unknown dimensions)"