Repository navigation
v2.44.0 - A filmed ghost in a loop delivered as cut is given way, or named a take to film again; restoration reads the ghost; the RIFE lane runs every real-RIFE test
LatestUpgrading from 2.43.0: the restoration contract moves with the version. Source restoration is now metric source-restoration-v5 under policy key-protected-source-copy-one-step-cap-jump-ghost-guard-v1, and writes repair report schema_version 4 and comparison schema_version 6 (sprite_gen.video.restoration: METRIC, POLICY, REPAIR_SCHEMA, COMPARISON_SCHEMA); a consumer that checks these values raises all four with its pin. Nothing source-restoration-v4 (2.40.0 – 2.43.0) wrote is read as v5: a v4 repair report given to video-loop-compare is an input error, and a request whose baseline carries a v4 receipt in its loop report is shared unknown, restoration-receipt-policy-unsupported. A loop restored under v4 therefore goes on from its origin, not its v4 baseline: the origin itself is still a valid first request (the same five files as origin and baseline). Proposal IDs change with the versions. video-loop-compare without the origin and the source (same-sample-defects-v1) is unchanged.
- Fix: the Linux lane's
--rife-onlyruns every test of the real RIFE, not one file. It rantests/video/test_rife.pyalone, while--rife-unmeasuredskips every test that needs the realrife-ncnn-vulkan— so a real-RIFE test in another file (2.43.0's intests/video/test_crossfade.pyandtests/video/test_ghost_screen.py) was measured by neither run. Such a test now carries one mark,real_rife(registered inpyproject.toml):tests/conftest.pyskips the marked tests whererife.locate()finds no RIFE, andscripts/linux_lane.sh --rife-onlyrunspytest -m real_rifeover the whole suite, none skipped. A new real-RIFE test is run by--rife-onlyonce it is marked.docs/release.mdsays what each run measures. - The lane's RIFE-less run fails a skip that
--rife-onlywill not run.--rife-unmeasuredruns the suite aspytest --rife-unmeasured, wheretests/conftest.pyreads every skip — by a skip mark, in a fixture or the test body, or of a whole file at import: one whose test is not markedreal_rifefails by name, unlessNOT_RIFE_SKIPSin the same file names it as skipping for an input the lane does not give (theSPRITE_GEN_CHROMA_REAL_STILLSstills). Such a skip would be measured by neither run. tests/release/test_lane_rife_collection.pycollects the suite twice, once with a stand-in RIFE whererife.locate()looks (SPRITE_GEN_RIFE,PATH, the install root) and once with none: a test the two collections skip or collect differently and that is not markedreal_rifefails it by name — a test collected only where RIFE is reachable never reaches the RIFE-less run. It also runs a marked test and each unmarked way to skip without RIFE throughpytest --rife-unmeasured: the marked one skips, every other fails by name.- Correction to 2.43.0's cross-fade measure: "the windows step by half their size, so one lost pixel is counted in up to four of them" holds where the window is an even number of pixels wide. The step is half the width rounded down, so an odd-width window counts one pixel in up to nine. The
rife.crossfadedocstring anddocs/loop-repair.mdsection 4 now say "up to four of them (nine where the window is an odd number of pixels wide)". The measure is unchanged. - Output change: a filmed ghost with no clean frame beside it is a take to film again —
retakereasonfilmed-ghost. Since 2.43.0video-cycle-aligndelivers a filmed ghost only where the source frames on both sides of its time are ghosts too, and said so in a warning line alone: the loop'sretakestayednull, so a pipeline that films a direction again onretakedelivered the ghost. Such a loop is now named: its row'sretake(and the report's, andvideo-set's) readsreason: "filmed-ghost"withghost— the cellskept, their frames ofcycle.source/(sources), the highest reading (ghost_max) and the screen'slimit— andlimits.ghost_kept_min(one cell,align.RETAKE_GHOST_KEPT_MIN), with a line "film this direction again (filmed-ghost) — …".retakestays one object:reasonis the first that applies (held-drawings, thenfilmed-ghost), the newreasonslists every one (["held-drawings"]for a held loop as before), and the held numbers stand only where that reason applies. Eachghost_atcell that keeps its ghost says why (kept:no-clean-frame, under any--between, orbetween-rifewhere--between rifekept it beside a clean frame — the aligner's choice, not counted). The set is aligned as before (applied: true, exit 0). Docs:docs/loop-repair.mdsection 4 (filmed ghost),docs/video-pipeline.md. - Fix: a made frame given way at a cell that keeps its ghost no longer says the nearer source frame settled it. Its line said "so the nearer source frame was taken there" where that frame was itself a filmed ghost, delivered for want of a clean one; it now says "the nearer source frame, a filmed ghost, was kept there, the frames beside it being filmed ghosts too". A ghost
--between rifekept with no clean frame beside it is named "film this direction again", no longer told to align withautofor a clean frame that is not there. - Output change: a walk delivered as cut gives its filmed ghost way —
video-loop's jump repair stage. A walk filmed in one direction never meetsvideo-cycle-align, which takes a clean frame beside a filmed ghost, so its strip, GIF and WebP delivered the ghost as cut: 2.43.0 named it in awarning:line and changed no pixel, and only a band wide enough to read as a jump was replaced, by the jump repair, by chance. The ghost screen now reads the cut as filmed, before the jump repair (ghost_screennames a ghost the repair replaced, where it was read after it before), and each ghost it names is a round of that repair, first and outside its three-call budget (why: "ghost"; the jump rounds now saywhy: "jump"): RIFE's frame between the two frames beside it where both are clean and the frame has no fault (taken: "rife"), else the clean frame beside it — the one after, else the one before, the alignment's own choice at a time on a ghost (interpolation_quality.clean_beside,taken: {"source": k}) — else, both being filmed ghosts too, the ghost kept (taken: null). A ghost round also recordsneighbours,outcome(accepted,given-way,kept) andno_proposalwhere no frame was made. A cell given way is injump_repair.replaced, so the jumps are read after it and never made beside it, and the source projection reads it as a repair hint. The seam gate reads a cell given way to the frame beside it as filmed (seam_as_filmed, frames of the cut): that frame shown twice in a row is a step of nothing and a step of two, and at the wrap — a ghost at the cut's first frame given way to its second, or at its last given way to its first — the step of two would refuse a walk that closes ("the cycle does not close") and the step of nothing would pass a cut that does not. A loop that only gave its ghosts way to the frames beside them is gated on its source frames (seam_measurement: source-frames, as filmed); one with a frame made — RIFE's, for a jump or a ghost — on its rendered cells, the cell given way read on the strip the cut makes with that frame as filmed.--anchor body'sseam_ratio_after_anchorreads the wrap the same way. The report and its summary carryghost_given_way(frame,ghost,taken) andghost_kept(frame,ghost), and the strip metadata each where it is not empty —cycle/is the alignment'scycle.source/, whose screen no longer sees a ghost given way, so an alignment carries the record over. Awarning:line names each ("… a filmed ghost; given way to RIFE's frame between frames 9 and 11", "given way to frame 11 beside it, shown twice in a row", "kept … — film this direction again"). Without RIFE,--repair autostill gives each ghost way to the clean frame beside it and says a jump frame was not repaired only where a jump was left (unmade: the step, target and score the jump search stopped at;repair.repair_jumpswith no interpolator no longer raises on a jump);--repair onrefuses a ghost between two clean frames as it refuses a jump.--repair offand the states other than walk and run are unchanged. A loop with no filmed ghost is cut as before: the same cells and strip metadata, its report adding the two records empty. docs/loop-repair.md section 2. - Fix: a GIF or WebP that holds a frame shown twice in a row passes its check. Both formats hold two identical frames in a row as one frame of twice the delay, and
video-loopchecked its GIF and WebP for one frame per cell: a cut with two identical frames in a row — what giving a ghost way to the frame beside it leaves — failed (walk.gif failed verification: 19 frames, expected 20).video-loopandvideo-follownow check for one frame per run of identical cells, asvideo-cycle-alignalready did (loop.shown_runs), and the restoration's playback check (playback.verify) reads such a run as one frame held for the sum of its delays (in hundredths of a second in the GIF, as it stores it); itsstrip_indicesstill lists every scheduled cell. - Output change: source restoration reads the ghost — the ghost guard. A cell
video-loopgave a filmed ghost way is a cell the jump repair replaced, and its source frame is the ghost: the source's pixels clear any fault the cell carries, the smear and outline faults measure a frame only against its neighbours, and partial coverage was read only beyond the source reference's own, which is the ghost — sosource-restoration-v4could propose the cell and accept the ghost back. Now no cell whose source frame the cut's ghost screen names (interpolation_quality.ghost_screenon the cropped source frames, at their own size, as the cut read them) is a target; every final cell's faults carry its own ghost reading where the screen reads the loop; and the comparison's newaxes.ghostreads each cell's ownrife.ghoston final pixels (baseline,candidate,reference,threshold0.003,filmed— the screen), any rise of the excess over the limitregressedwithghost:regressed. A loop the screen does not read is not judged (reason: "loop-drawn-part-covered"). docs/loop-comparison.md. - Contract versions: repair report schema 4, comparison schema 6, metric
source-restoration-v5, policykey-protected-source-copy-one-step-cap-jump-ghost-guard-v1. A restored cell's pixels are made as in v4. Evidence and receipts ofsource-restoration-v4(2.40.0 – 2.43.0) are refused, never read as v5: a v4 repair report given tovideo-loop-compareis an input error, and a request whose baseline carries a v4 receipt in its loop report is sharedunknown,restoration-receipt-policy-unsupported. The origin itself is still a valid first request. Proposal IDs change with the versions.video-loop-comparewithout the origin and the source (same-sample-defects-v1) is unchanged. tests/video/test_ghost_given_way.pydraws the walk oftests/video/test_cycle_align.pywith a band of 0.3 coverage beside the foot: a small one, four times the ghost limit and no jump, delivered as cut before and now given way to RIFE's frame between its neighbours (a stand-in that draws the walk's true frame) or, without RIFE, to the frame after it, the GIF and WebP holding it as one frame; two identical frames in a row passing the check; three ghosts in a row, the middle one kept and named; a wide band that also reads as a jump, named by the screen read before the repair;--repair offand--repair onwithout RIFE; an alignment reading the cut given way and carrying its record; with the default seam gate, the walk cut where its wrap is an ordinary step, its ghost at the cut's first frame given way without RIFE or with RIFE's frame a ghost too (refused before, 2.17 times the mean step), at its last given way to its first (read 0.0 before), both beside a frame made, ghosts at its first and last frames at once given way inward (refused before, 2.40), and a cut that does not close whose last frame gives way to its first (delivered before), under--anchor noneand--anchor body; under--anchor body, the walk's default, the strip'sseam_ratio_after_anchorreading a ghost given way at the cut's first frame, its last or both as--repair offreads it (read oncycle/before: 0.0 at the last); the side walker oftests/video/test_one_step_cut.pycut with the application's walk flags (--anchor motion-auto --size-hold auto --body-height 160), through a stand-in and through the real RIFE (markedreal_rife); and the cut's source projection proving every other cell and listing the cell given way, a restoration request reading its playback and proposing nothing there.tests/video/test_source_restoration.pyadds a source frame that is a filmed ghost the smear and outline faults pass, whose cell v4 proposed first: it is no target, and the ghost restored there regresses.tests/video/test_ghost_retake.pyreproduces on the synthetic legs oftests/video/test_ghost_screen.pya ghost kept at a whole time (three in a row), one kept at a time made between two (the stand-in RIFE, and the real one making each frame beside a ghost, markedreal_rife), and a held loop on twos that keeps ghosts too (both reasons,held-drawingsfirst); under--between rifeonly the ghost with no clean frame beside it is counted; the fault line names the kept cell as it is.
Install or update:
pip install --upgrade "sprite-gen @ git+https://github.com/aldegad/sprite-gen.git@v2.44.0"The wheel, the source archive and SHA256SUMS are attached. Full notes in CHANGELOG.md.