tituli.frame¶
The frame: what the layout engine is allowed to know about the picture.
This module is the answer to “is a text-only video a special case of text over video?” — yes, and the axis that unifies them is **how much the layout engine knows about the frame**, a ladder:
rung |
the Frame knows |
so the engine can |
|---|---|---|
only its size |
place by anchor; must assume the worst and put a scrim under text |
|
a solid background colour |
choose ink by contrast, no scrim |
|
the actual pixels |
sample luminance under a candidate box; choose ink; scrim only if the patch is mid-toned or busy |
|
|
rank candidate positions by how little they cover the subject |
A single Frame type carries all four rungs; every rung is optional and
the engine asks (luminance_under, overlap) rather than branching on kind.
The regions-to-avoid input is one keyword, avoid=, which takes boxes *or a
callable* image -> box(es) — the shape of burns.salient_box and of
burns.FacesDetector — so subject detection plugs in without a dependency.
>>> f = Frame.blank((1920, 1080))
>>> f.luminance_under(f.safe) is None # rung (a): unknown
True
>>> Frame.blank((1920, 1080), color="#fff").luminance_under(f.safe)
1.0
Functions
|
Scale |
|
Normalised boxes a delivery target keeps for itself. |
Classes
|
Size plus whatever else is known about the picture text will sit on. |
- class tituli.frame.Frame(width, height, color=None, image=None, avoid=(), reserved=(), samples=())[source]¶
Bases:
objectSize plus whatever else is known about the picture text will sit on.
- classmethod blank(size=(1920, 1080), *, color=None)[source]¶
Rung (a) when
coloris None, rung (b) when it is given.- Return type:
- classmethod from_image(image, *, avoid=None, size=None, delivery=None)[source]¶
Rung (c), and rung (d) when
avoidis given.imagemay also be a sequence of frames (the stills a camera move will pass through under the text): the first is what gets composited, all of them feedluminance_stats(), which then reports the worst instant — so a scrim is as light as the whole window allows, not as heavy as one frame demands.avoidis normalised boxes, or a callable taking the PIL image and returning one box or several (burns.salient_boxfits directly). It runs on every sampled frame.sizecover-fits the picture(s) to the frame first.deliverynames a target whose reserved zones ("youtube": subtitle band + control bar) placement must keep clear.- Return type:
- luminance_stats(box)[source]¶
(mean, stddev)of relative luminance underbox; None if unknown.A high stddev means a busy patch — even a well-contrasted mean will not keep every glyph readable, which is when a scrim earns its place.
- classmethod over(stills, *, avoid=None, size=None, delivery=None)[source]¶
The frame a caption will sit on over a time window of stills.
The named entry point for the sequence case: the first still is what gets composited, all of them are sampled, and
luminance_statsreports the worst instant — so a scrim over a Ken Burns move is as light as the whole window allows. (from_imageaccepts a sequence too; this name says what it is for.)- Return type:
- place(size, *, anchor='auto', region=None)[source]¶
Where to put a block of
size: a named anchor and its pixel box.anchormay be one name, a preference list, or"auto"(the default order). With rung (d) knowledge the candidate that least covers the subject wins; ties fall to preference order.>>> f = Frame.blank((1000, 500)) >>> f.place((200, 50), anchor="bottom")[0] 'bottom'