| """PixelCursor: the spatio-temporal-semantic-identity selector that owns pixel mutation. |
| |
| Every studio operation that touches pixels routes through a PixelCursor. Artifacts |
| are immutable from outside this package; the only way to produce a new Artifact is |
| via cursor.write_* methods. |
| |
| Op-set (closed): |
| seek_* β reposition (returns new cursor) |
| range / at_frame / all_frames / propagate_temporal β temporal traversal |
| lock_identity β pin subject or motion identity |
| bind_motion_exemplar β few-shot motion attach (returns new cursor) |
| read_* β non-mutating queries (returns data) |
| write_* β mutation (returns new Artifact) |
| tell β introspect cursor state |
| """ |
| from __future__ import annotations |
| from dataclasses import dataclass, replace, field |
| from typing import Optional, Sequence, Tuple |
|
|
| import numpy as np |
|
|
| from .artifact import Artifact |
| from .selectors import Selector, Point, Region, Mask, Token, Layer, Anchor3D |
| from .scope import ( |
| TemporalScope, |
| FrameIndex, |
| FrameRange, |
| ALL_FRAMES, |
| DepthScope, |
| IdentityLock, |
| OpacityProfile, |
| ) |
|
|
|
|
| @dataclass(frozen=True, slots=True) |
| class PixelCursor: |
| artifact: Artifact |
| selector: Selector |
| temporal: TemporalScope |
| depth: Optional[DepthScope] = None |
| identity_lock: Optional[IdentityLock] = None |
| opacity: OpacityProfile = field(default_factory=lambda: OpacityProfile(kind="hard")) |
|
|
| |
|
|
| def seek_point(self, x: float, y: float, z: Optional[float] = None) -> "PixelCursor": |
| return replace(self, selector=Point(x=x, y=y, z=z)) |
|
|
| def seek_region(self, x: int, y: int, w: int, h: int) -> "PixelCursor": |
| return replace(self, selector=Region(x=x, y=y, w=w, h=h)) |
|
|
| def seek_mask(self, bitmap: np.ndarray, is_soft: bool = False) -> "PixelCursor": |
| return replace(self, selector=Mask(bitmap=bitmap, is_soft=is_soft)) |
|
|
| def seek_token(self, text: str, embedding_id: Optional[str] = None) -> "PixelCursor": |
| return replace(self, selector=Token(text=text, embedding_id=embedding_id)) |
|
|
| def seek_layer(self, layer_id: str) -> "PixelCursor": |
| return replace(self, selector=Layer(layer_id=layer_id)) |
|
|
| def seek_3d_anchor( |
| self, anchor_id: str, bbox: Tuple[float, float, float, float, float, float] |
| ) -> "PixelCursor": |
| return replace(self, selector=Anchor3D(anchor_id=anchor_id, bbox=bbox)) |
|
|
| |
|
|
| def at_frame(self, index: int) -> "PixelCursor": |
| return replace(self, temporal=FrameIndex(index=index)) |
|
|
| def range(self, start: int, end: int) -> "PixelCursor": |
| return replace(self, temporal=FrameRange(start=start, end=end)) |
|
|
| def all_frames(self) -> "PixelCursor": |
| return replace(self, temporal=ALL_FRAMES) |
|
|
| def propagate_temporal(self, end_frame: int, mode: str = "forward") -> "PixelCursor": |
| """Extend cursor temporal extent. Backend (e.g. CoTracker3, SAM2) does the actual traversal.""" |
| cur = self.temporal |
| if isinstance(cur, FrameIndex): |
| if mode == "backward": |
| new: TemporalScope = FrameRange(start=end_frame, end=cur.index + 1) |
| else: |
| new = FrameRange(start=cur.index, end=end_frame) |
| elif isinstance(cur, FrameRange): |
| new = FrameRange(start=cur.start, end=max(cur.end, end_frame)) |
| else: |
| new = cur |
| return replace(self, temporal=new) |
|
|
| |
|
|
| def lock_identity(self, lock: IdentityLock) -> "PixelCursor": |
| return replace(self, identity_lock=lock) |
|
|
| def bind_motion_exemplar( |
| self, exemplar_paths: Sequence[str], backend: str = "auto" |
| ) -> "PixelCursor": |
| from .ops import _dispatch |
|
|
| return _dispatch("bind_motion_exemplar", backend, self, exemplar_paths) |
|
|
| |
|
|
| def read_state(self) -> dict: |
| return self.tell() |
|
|
| def read_pixels(self) -> np.ndarray: |
| from .artifact import Image, FrameStack |
|
|
| a = self.artifact |
| if isinstance(a, Image): |
| return a.pixels |
| if isinstance(a, FrameStack): |
| t = self.temporal |
| if isinstance(t, FrameIndex): |
| return a.frames[t.index] |
| if isinstance(t, FrameRange): |
| return a.frames[t.start : t.end] |
| return a.frames |
| raise TypeError(f"read_pixels: unsupported artifact type {type(a).__name__}") |
|
|
| |
|
|
| def write_motion(self, motion_spec, backend: str = "auto") -> Artifact: |
| from .ops import _dispatch |
|
|
| return _dispatch("write_motion", backend, self, motion_spec) |
|
|
| def write_displacement(self, target_xy: Tuple[float, float], backend: str = "auto") -> Artifact: |
| from .ops import _dispatch |
|
|
| return _dispatch("write_displacement", backend, self, target_xy) |
|
|
| def write_inpaint(self, prompt: str, backend: str = "auto") -> Artifact: |
| from .ops import _dispatch |
|
|
| return _dispatch("write_inpaint", backend, self, prompt) |
|
|
| def write_keyframe(self, frame_index: int, pose_state, backend: str = "auto") -> Artifact: |
| from .ops import _dispatch |
|
|
| return _dispatch("write_keyframe", backend, self, frame_index, pose_state) |
|
|
| def write_condition(self, control_signal, backend: str = "auto") -> Artifact: |
| from .ops import _dispatch |
|
|
| return _dispatch("write_condition", backend, self, control_signal) |
|
|
| |
|
|
| def tell(self) -> dict: |
| return { |
| "selector": type(self.selector).__name__, |
| "selector_value": self.selector, |
| "temporal": self.temporal, |
| "depth": self.depth, |
| "identity_locked": self.identity_lock is not None, |
| "identity_source": (self.identity_lock.source if self.identity_lock else None), |
| "opacity_kind": self.opacity.kind, |
| "artifact": type(self.artifact).__name__, |
| } |
|
|
|
|
| def open_cursor( |
| artifact: Artifact, |
| selector: Optional[Selector] = None, |
| temporal: Optional[TemporalScope] = None, |
| ) -> PixelCursor: |
| if selector is None: |
| selector = Token(text="*all*") |
| if temporal is None: |
| temporal = ALL_FRAMES |
| return PixelCursor(artifact=artifact, selector=selector, temporal=temporal) |
|
|