from __future__ import annotations from dataclasses import dataclass import numpy as np # Локальные импорты внутри методов уменьшают риск циклов зависимостей @dataclass(frozen=True) class YcbcrImage512: """YCbCr-изображение 512x512.""" data: np.ndarray # uint8, shape (512, 512, 3) def __post_init__(self): arr = np.asarray(self.data, dtype=np.uint8) if arr.shape != (512, 512, 3): raise ValueError("Изображение должно быть 512x512x3 (YCbCr).") object.__setattr__(self, "data", arr) @classmethod def from_rgb(cls, rgb_img) -> "YcbcrImage512": """Создать из RgbImage512.""" from .color_space import ColorSpace from .rgb_image512 import RgbImage512 if not isinstance(rgb_img, RgbImage512): raise TypeError("Ожидается RgbImage512.") ycbcr = ColorSpace.rgb_to_ycbcr(rgb_img.to_array()) return cls(ycbcr) def to_rgb(self): """Преобразовать в RgbImage512.""" from .color_space import ColorSpace from .rgb_image512 import RgbImage512 rgb = ColorSpace.ycbcr_to_rgb(self.data) return RgbImage512.from_array(rgb) def get_channel(self, name: str) -> np.ndarray: """Вернуть 2D-компоненту канала.""" from .channel_view import ChannelView return ChannelView.get(self.data, name) def with_channel(self, name: str, component: np.ndarray) -> "YcbcrImage512": """Вернуть копию с заменённым каналом.""" from .channel_view import ChannelView new_ycbcr = ChannelView.set(self.data, name, component) return YcbcrImage512(new_ycbcr) def to_array(self) -> np.ndarray: """Вернуть массив uint8 512x512x3.""" return self.data