burns.rect#
The Ken Burns viewport rectangle — the render-agnostic geometric atom.
A Rect is a normalized region of interest (ROI) over a source image:
(x, y, w, h) with top-left origin, y-down, every component a fraction
of the image in [0, 1]. This is the convention shared by videopython’s
BoundingBox, CSS transform-origin semantics, and FFmpeg’s coordinate
origin — adopting it (rather than the older center+magnification spec) is what
lets one spec drive a Python renderer, a future JS/TS renderer, and a CSS
preview without each reinventing the pixel mapping.
Zoom is expressed as window-fraction (the visible window is a fraction of
the image) rather than magnification; the magnification a backend like FFmpeg
zoompan wants is the derived read-only Rect.zoom.
The rect is pure data: no I/O, no image needed to construct or interpolate one.
Only to_pixels() takes image dimensions, because that is the single point
where normalized geometry meets a concrete raster.
Classes
|
A normalized |
- class burns.rect.Rect(x, y, w, h)[source]#
Bases:
objectA normalized
(x, y, w, h)viewport over a source image.All four components are fractions of the image in
[0, 1]with a top-left origin.(x, y)is the top-left corner of the window;wandhare its width and height.Rect(0, 0, 1, 1)is the whole image.Examples
>>> full = Rect(0.0, 0.0, 1.0, 1.0) >>> full.center (0.5, 0.5) >>> full.zoom 1.0 >>> Rect.from_center_zoom(0.5, 0.5, 2.0) Rect(x=0.25, y=0.25, w=0.5, h=0.5)
- property aspect: float#
Aspect ratio of the window in normalized image units (
w / h).Note this is not the rendered-pixel aspect ratio unless the image is square — the pixel AR is
(w * img_w) / (h * img_h). The render path reconciles the window with the desired output AR via a cover-crop.
- clamped()[source]#
Slide the window inside the image without resizing it.
Clamps the top-left corner so the window “rides the wall” at an edge rather than shrinking — shrinking the box would change its aspect ratio and make the rendered frame breathe/stretch. Windows larger than the image in a dimension are centered in that dimension.
- Return type:
Examples
>>> Rect(0.8, 0.0, 0.5, 0.5).clamped() Rect(x=0.5, y=0.0, w=0.5, h=0.5) >>> Rect(-0.2, 0.3, 0.4, 0.4).clamped() Rect(x=0.0, y=0.3, w=0.4, h=0.4)
- classmethod from_center_zoom(cx, cy, zoom=1.0, *, aspect=1.0)[source]#
Build a rect from a pan center, a zoom, and a window aspect ratio.
This is the bridge from the older center+magnification mental model to the
(x, y, w, h)representation.zoomis window-fraction magnification (1.0= full image,> 1.0= zoomed in).aspectis the window’s normalizedw / h; the default1.0yields an isotropic window (w == h) whose rendered AR equals the source image’s — reproducing the legacy behavior exactly. The result is clamped to stay inside the image.- Return type:
Examples
>>> Rect.from_center_zoom(0.5, 0.5, 1.0) Rect(x=0.0, y=0.0, w=1.0, h=1.0) >>> Rect.from_center_zoom(0.75, 0.5, 2.0) # off-center zoom-in Rect(x=0.5, y=0.25, w=0.5, h=0.5)
- is_contained()[source]#
True when the window lies wholly inside the image.
- Return type:
Examples
>>> Rect(0.1, 0.1, 0.5, 0.5).is_contained() True >>> Rect(0.8, 0.0, 0.5, 0.5).is_contained() False
- lerp(other, t)[source]#
Linearly interpolate toward
otherbytin[0, 1].- Return type:
Examples
>>> Rect(0, 0, 1, 1).lerp(Rect(0.25, 0.25, 0.5, 0.5), 0.5) Rect(x=0.125, y=0.125, w=0.75, h=0.75)
- to_pixels(img_w, img_h)[source]#
Map the (clamped) window to an integer pixel box
(x0, y0, x1, y1).Clamps first so the box is always inside the raster, then rounds (not truncates) for symmetric integer conversion. The returned box is a half-open crop region suitable for
ndarray[y0:y1, x0:x1].Examples
>>> Rect(0.0, 0.0, 1.0, 1.0).to_pixels(100, 80) (0, 0, 100, 80) >>> Rect.from_center_zoom(0.5, 0.5, 2.0).to_pixels(100, 80) (25, 20, 75, 60)