[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DC48W9A2A9R24VGFEFF652":3,"skill-same:01M6DC48W9A2A9R24VGFEFF652":106},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":35,"linkOnly":36,"markdownContent":37,"markdownBody":38,"frontMatter":39,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":14,"repositoryHtmlUrl":43,"repositoryHomepageUrl":6,"ownerHtmlUrl":44,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":45,"install":46,"resources":59},"01M6DC48W9A2A9R24VGFEFF652","video-watermark-removal",null,"Use when removing a static logo, watermark, or TV bug from a video seamlessly. PRIMARY method = ProPainter flow-guided temporal inpaint on a TIGHT logo-only mask (pulls REAL background pixels from neighbouring frames -> sharp, no soft haze), CHUNKED in time to fit a small GPU, with PER-FRAME presence gating so occluded frames stay pristine. big-LAMA is the fast SOFT fallback.","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34],"Video","video watermark removal","gpu","fast","flow","logo","use","only","bug","fallback","time","2026-11-10 10:12:30",false,"---\nname: video-watermark-removal\ndescription: Use when removing a static logo, watermark, or TV bug from a video seamlessly. PRIMARY method = ProPainter flow-guided temporal inpaint on a TIGHT logo-only mask (pulls REAL background pixels from neighbouring frames -> sharp, no soft haze), CHUNKED in time to fit a small GPU, with PER-FRAME presence gating so occluded frames stay pristine. big-LAMA is the fast SOFT fallback.\n---\n# Video Watermark \u002F Logo Removal (seamless)\n\nRemove a **static, semi-transparent logo\u002Fbug** from a video so cleanly it is not noticeable it was ever there. Proven on 41 vertical shorts (406x720) end-to-end. The OpusClip-specific instance (watermark spec, download, API) lives in basic-memory: `main\u002Fprojects\u002Freframe\u002Fopus-clip-watermark-removal-lama-inpaint-recipe-ops-lessons-...`. This skill is the **reusable technique**.\n\n## When to use \u002F when NOT\n- USE for: a fixed-position (or slowly-varying) opaque\u002Fsemi-transparent logo over real content (faces, hair, background), where the area behind it is small and not a critical detail.\n- DO NOT use for: moving\u002Fanimated watermarks (need ProPainter\u002FE2FGVI temporal inpaint), full-frame tiled marks, or marks over fine text you must recover (inpainting invents plausible texture, it cannot recover hidden detail).\n\n## Approach decision (pick fast, don't agonize)\n1. `ffmpeg delogo` — REJECT for anything visible. Interpolates from the box border → a blur patch. Obviously edited.\n2. **ProPainter, flow-guided temporal inpaint — PRIMARY (best quality).** Even for a STATIC logo, the subject moves slightly, so optical-flow propagation copies the REAL background that was exposed in neighbouring frames into the masked region — actual detail recovery, sharp, no soft haze, no flicker. This is the only thing that produced a truly seamless result on real faces\u002Fhair. Run it on a **cropped ROI** around the logo (e.g. ~280×200) so VRAM is tiny (~1.4 GB on a 6 GB card), and **chunk in time** (see v4 below) so RAM is bounded.\n3. **big-LAMA per-frame — FAST FALLBACK only.** Streams at constant memory, but it *hallucinates* the whole masked region every frame independently → the fill is visibly SOFTER than the surroundings and can flicker. A discerning viewer notices. Use only when ProPainter is unavailable or quality is not critical. (Lesson learned: shipping LAMA here was rejected by the user as \"clear image deterioration\".)\n\n> **The v4 win in one line:** ProPainter (temporal, real-pixel) + tight logo-shaped per-frame mask + presence gating + time-chunking + an NCC verification gate. Scripts: `scripts\u002Fpropaint_pipeline.py` (one clip), `scripts\u002Fpp_batch.sh` + `scripts\u002Fpp_daemon.sh` (batch), `scripts\u002Fpp_verify.py` (the gate). big-LAMA scripts (`inpaint_clip.py` etc.) are kept as the fallback.\n\n## Pipeline\n```\nlocalize logo  ->  TIGHT per-clip mask (white rect\u002Fshape on black, same WxH)\n  ->  per frame: ffmpeg decode rawvideo RGB\n                 LAMA fills masked region on a vertical CONTEXT BAND (mask bbox +- ~140px)\n                 FEATHERED composite of ONLY the masked region back onto the untouched frame\n  ->  x264 (crf 18) -> re-mux ORIGINAL audio (-c:a copy, -shortest)\n```\nEverything outside the mask stays bit-for-bit original. Keep source WxH and fps.\n\n### 1. Localize\n- If the logo is a **fixed overlay** (most SaaS clip tools render it at one position regardless of content): find it ONCE on a confident clip, reuse that mask for all. This is the common case — verify by eye on 2-3 clips.\n- General localizer (`scripts\u002Flocalize_wm.py` here is OpusClip-tuned but the method generalizes): sample N frames, build a **temporal-median** image (static logo persists, moving foreground averages out), multi-scale `cv2.matchTemplate` with `TM_CCOEFF_NORMED` on the logo's **alpha-brightness pattern** (mean-subtracted = background-DC invariant), restrict the search to the band where the logo lives, vote across frames. Low confidence -> DO NOT silently fall back to a big box (see Pitfall 1).\n\n### 2. Mask discipline (the part that makes or breaks it)\n- **TIGHT, logo-only.** Cover the logo + a few px, nothing more.\n- **Never let the mask touch a facial feature** (eyebrow, eye, mouth) or on-frame text you must keep (source chyrons, captions). Clamp the mask edges away from them. A logo over a forehead is trivial; the same generous box dropped onto a tighter-framed shot will cover an eyebrow and LAMA will smear it into mush.\n\n### 3. Inpaint one clip\n`scripts\u002Finpaint_clip.py \u003Cin.mp4> \u003Cmask.png> \u003Cout.mp4> [crf=18]` (needs `big-lama.pt`, see Env). Alters only the mask region, re-muxes original audio.\n\n### 4. Batch + heal transient GPU failures\n`scripts\u002Frun_batch.sh` (resumable: skips clips whose output exists) + `scripts\u002Fretry_until_complete.sh` (re-runs the batch up to N passes until every clip has an output).\n\n## Pitfalls (all hit in practice)\n1. **Generous fallback mask smears faces.** When localization confidence is low, a \"safe\" oversized box reaches into eyes\u002Feyebrows and the inpaint destroys them. FIX: since the logo position is fixed, swap in a tight standard mask from a confident clip and re-run. Tight-logo beats generous-box every time.\n2. **cuFFT_EXEC_FAILED \u002F \"CUDA error: unknown error\" are TRANSIENT, not OOM.** big-LAMA uses Fast Fourier Convolutions, so it leans on cuFFT; on a shared GPU these fail intermittently (~5-10% of clips) even with VRAM free — WSL2 passthrough instability under rapid CUDA-context churn. Each clip is a fresh process so one failure does not poison the next. FIX = a resumable RETRY loop, not a redesign. Confirm transient: the next clip after a failure succeeds.\n3. **Long GPU batches get hang-reaped as background tasks.** A `run_in_background` job that emits no stdout for minutes (one long clip) looks hung and gets killed. FIX: run the workload fully **detached inside WSL** (`setsid nohup bash pp_daemon.sh > log 2>&1 \u003C \u002Fdev\u002Fnull &`) via a fast-returning foreground call — it survives the launching bridge being killed because WSL2 keeps VM processes alive. Watch it with a **persistent Monitor** tailing the log for per-item DONE\u002FFAILED + a done-sentinel file. Do NOT put `wsl.exe` calls inside the Monitor's own loop (it errors the monitor). Also: a `setsid` daemon survives a naive `pkill` of the launcher — kill the whole process group \u002F the `pp_batch.sh` loop, not just leaf processes, or it keeps spawning clips.\n4. **ProPainter OOMs (SIGKILL) on long clips.** ProPainter loads the whole video + all-frame optical flow into RAM at once, so a 150 s \u002F ~4800-frame clip blows past 8 GB and gets SIGKILL'd — even though a 15 s segment runs fine. FIX: process each clip in **time-chunks** (`CORE≈450` frames) with a few **context frames** on each side for temporal propagation; composite only the core frames. Bounds RAM to ~2.2 GB. (`propaint_pipeline.py` does this.)\n5. **Static mask over a hook card \u002F intro smears the card text.** Many SaaS clip tools render a big text \"hook card\" over the first ~5 s of the top clips; the watermark is HIDDEN behind it there (no logo to remove). A mask applied to those frames inpaints the card text into mush. FIX: **per-frame presence gating** — localize the logo POSITION once (it's fixed), but per frame check NCC of the logo template at that fixed bbox; if it's below threshold (logo absent\u002Foccluded) emit an EMPTY mask so the frame is left pristine. Position is fixed; only presence varies — never do a per-frame position *search* (it's noisy and misplaces the mask → residual logo).\n6. **Never claim \"done\" on a single-frame check — use a residual GATE.** Build a clean, logo-SPECIFIC template (temporal-median of the logo over a dark-bg clip; a high-pass template that matches *any* white graphic will false-positive on channel branding like a \"G\" logo). Then for every output clip, sample across the WHOLE timeline and measure NCC of the template at the source-localized position; it must be LOW on every frame (logo gone). `scripts\u002Fpp_verify.py` is this gate (exit 1 on any residual). Do NOT use PSNR\u002FSSIM-vs-original (a good fill legitimately differs).\n\n## Secondary \u002F animated overlay marks — the cross-clip-average phase-mask method\nMany SaaS clip tools render TWO marks, not one: the static top bug (above) AND a SECONDARY animated mark — e.g. OpusClip's vertical \"Created with (.)\" on the LEFT edge (~t3-8.5s: the text holds, then the (.) icon scrolls UP and fades). The static-mask method fails on it (it moves), and a generous column smears any subject in that region. The fix exploits that the mark is a **FIXED-POSITION UI OVERLAY, identical (shape, screen coords, animation phase) across ALL clips** at a given playback time — only the CONTENT behind it differs per clip.\n\n- **Cross-clip averaging builds the mask.** For each window frame index k, average frame k over ALL clips: the overlay (present + time-aligned in every clip) stays sharp and white; content (different per clip) blurs to a dim mean (~60-110). Threshold the averaged frame at each k → the overlay footprint AT PHASE k (text + (.) wherever it is), isolated from content. Per-frame PHASE masks (mask each position only in the ~frames the mark is there) let ProPainter fill a small hole from clean neighbour frames → sharp, full-travel coverage, no smear. (`build_phase_masks.py`, `propaint_left_phase.py`.)\n- **Two compositing refinements that matter:** (a) fill the (.) ring — it thresholds to a HOLLOW ring; contour-fill + convex-hull → solid disk, else a faint outline survives; (b) brightness-match the fill to the local boundary ring (per-channel offset, clamped ±22) → kills the dark\u002Fbright seam when the mark sits over a smooth dark region (e.g. (.) over a suit), without Poisson instability.\n- **Per-clip timing variance is the trap.** The fade is NOT perfectly synced across clips (~±1s). The averaged mask is time-locked to the MAJORITY timing, so a clip whose mark fades LATE keeps a residual the mask misses. FIX for the outlier = a clip-specific mask: canonical phase masks + a SHORT past-union (~0.6s, tracks a scrolling icon's lag WITHOUT dragging the mask into now-changed regions — a long past-union inpaints unchanged content → a visible patch) + a static TOP-BAND for text that fades in place and lingers. **Stop masking a region once the animated element has left it**, else you inpaint unchanged background → a dark patch. (`build_mask_clip35.py`.)\n- **The one background nothing local can beat: a white mark over a SATURATED-WHITE ANIMATED background** (bright \"plexus\"\u002Fparticle graphic). This is the genuine unsolvable-without-source case. Three approaches tried, all fail (don't re-walk these):\n  - *ProPainter (flow inpaint):* the mark is present across the whole window → no clean temporal neighbour to copy; the animated bg defeats flow propagation → faint fade-tail trace.\n  - *Larger temporal context (big CTXF):* does NOT help (distant frames don't match the fast-moving bg) and can INTRODUCE a colour tint from mismatched far frames.\n  - *Alpha-unmixing* `bg=(obs-a*255)\u002F(1-a)` (the \"correct\" de-watermark for a semi-transparent overlay): fails here because (a) mark and bg share the saturated-white colour → the equation is degenerate where bg≈255, and (b) the fade lag is NON-UNIFORM (text vs icon fade at different rates) so no single glyph time-shift aligns → any misalignment subtracts white at the wrong place = a visible ghost (a forced lag manufactured a clear ghost in an already-clean region).\n  Root cause: when the watermark and an animated background share a colour, you cannot separate them locally — you need the un-watermarked SOURCE (paid tier). **FLAG it** (per \"remove fully, flag imperfect ones\") — don't grind: the prominent mark still removes cleanly, only the sub-second fade tail resists, and it's imperceptible at normal playback (visible only under 3x CLAHE + zoom).\n- **Verify the secondary mark SEPARATELY** from the top bug: an all-clips contact sheet of the mark-box crop at each clip's PEAK neutral-white frame (CLAHE-boosted) + a per-clip neutral-white score for triage. High score ≠ residual — it's usually legit broadcaster branding (lower-third chyron) or bright content; CONFIRM visually that no overlay glyph (the \"Created with\"\u002F(.) signature) appears. (`all_41_leftmark_qc.py`.)\n\n## QC (do not skip — \"seamless\" is the whole point)\n- Extract a **3x zoom of the watermark region**, before vs after, at >= 2 timestamps. The logo must be gone AND the fill must look like the surrounding texture.\n- Spot-check the **hard cases**: logo straddling a hairline\u002Fedge, and any shot with tighter framing or a busy background (where a fallback mask may have fired).\n- Build a **montage** of the region across spread-out clips to catch surprises in one glance.\n- **Programmatically validate every output** before delivery: it has video AND audio streams and a duration within ~0.7 s of the source (catches truncated\u002Fcorrupt encodes).\n\n## Env (WSL2 + CUDA, the tested setup)\n- WSL2 Ubuntu, Python venv (e.g. `~\u002Fwm_env`): `torch` (CUDA build, e.g. 2.6+cu124), `opencv-python-headless`, `numpy`. System `ffmpeg` with libx264.\n- Model: `big-lama.pt` TorchScript (~196 MB) at `~\u002Fwm_models\u002Fbig-lama.pt`, from the simple-lama-inpainting v0.1.0 GitHub release. Load the `.pt` directly with `torch.jit.load` — do NOT `pip install iopaint`\u002F`simple-lama-inpainting`, they pin Pillow\u003C10 \u002F numpy\u003C2 which fail to build on Python 3.12.\n- Perf reference: ~11 fps end-to-end, ~2.3 GB peak VRAM, ~60-90 min for ~40 clips of ~1 min each.\n\n## Reference scripts\nAll scripts here are **parametrized and non-breaking**: run them with **no args\u002Fenv** and every\npath\u002Fconstant resolves to the original job defaults; override any of them via the env knobs listed\nin each script's header comment (nothing points at a hardcoded personal directory anymore).\n\n**ProPainter pipeline (PRIMARY) + residual-QC gate:**\n- `scripts\u002Fpropaint_pipeline.py \u003Cin.mp4> \u003Cout.mp4> [start_sec] [dur_sec]` — one clip, chunked temporal inpaint. Env: `TPL_GRAY`, `PP` (ProPainter checkout, default `~\u002FProPainter`), `DET_THRESH`, `PRES_THRESH`, `CORE`, `CTXF`.\n- `scripts\u002Fpp_batch.sh` — resumable batch over `$SRC\u002F*.mp4` → `$OUT`. Env: `BASE`, `SRC`, `OUT`, `PIPELINE`, `VENV`, `LOG`.\n- `scripts\u002Fpp_daemon.sh` — detached retry daemon wrapping the batch (heals transient CUDA faults). Env: `BASE`, `SRC`, `OUT`, `BATCH`, `DONE`, `MAX`.\n- `scripts\u002Fpp_verify.py` — the \"prove it's clean\" NCC + sharpness gate (exit 1 on any residual). Env: `RG`, `QC`, `SRC`, `OUT`, `TPL_GRAY`, `NCC_FAIL`, `SHARP_MIN`.\n- `scripts\u002Fscan_residual.py` — thorough full-timeline residual scan; builds the clean template from `TPL_SRC` at `BBOX`. Env: `RG`, `QC`, `SRC`, `CLN`, `TPL_SRC`, `BBOX`.\n- `scripts\u002F_logo_template_example.png` — a **generic example** grayscale logo template (the OpusClip bug these scripts remove), for illustration only; the scripts do not load it by default — point `TPL_GRAY` at your own clean template.\n\n**big-LAMA fallback:** `scripts\u002Finpaint_clip.py`, `scripts\u002Frun_batch.sh`, `scripts\u002Fretry_until_complete.sh`, `scripts\u002Fdaemon_launch.sh` (set `BASE`\u002F`SRC`\u002F`MASK`\u002F`OUT`).\n\nThe committed defaults document the real razvan_gandu run (private repo). The secondary animated-mark helpers (`build_phase_masks.py`, `propaint_left_phase.py`, `build_mask_clip35.py`, `all_41_leftmark_qc.py`) are described above but not yet generalized into this skill.\n","# Video Watermark \u002F Logo Removal (seamless)\n\nRemove a **static, semi-transparent logo\u002Fbug** from a video so cleanly it is not noticeable it was ever there. Proven on 41 vertical shorts (406x720) end-to-end. The OpusClip-specific instance (watermark spec, download, API) lives in basic-memory: `main\u002Fprojects\u002Freframe\u002Fopus-clip-watermark-removal-lama-inpaint-recipe-ops-lessons-...`. This skill is the **reusable technique**.\n\n## When to use \u002F when NOT\n- USE for: a fixed-position (or slowly-varying) opaque\u002Fsemi-transparent logo over real content (faces, hair, background), where the area behind it is small and not a critical detail.\n- DO NOT use for: moving\u002Fanimated watermarks (need ProPainter\u002FE2FGVI temporal inpaint), full-frame tiled marks, or marks over fine text you must recover (inpainting invents plausible texture, it cannot recover hidden detail).\n\n## Approach decision (pick fast, don't agonize)\n1. `ffmpeg delogo` — REJECT for anything visible. Interpolates from the box border → a blur patch. Obviously edited.\n2. **ProPainter, flow-guided temporal inpaint — PRIMARY (best quality).** Even for a STATIC logo, the subject moves slightly, so optical-flow propagation copies the REAL background that was exposed in neighbouring frames into the masked region — actual detail recovery, sharp, no soft haze, no flicker. This is the only thing that produced a truly seamless result on real faces\u002Fhair. Run it on a **cropped ROI** around the logo (e.g. ~280×200) so VRAM is tiny (~1.4 GB on a 6 GB card), and **chunk in time** (see v4 below) so RAM is bounded.\n3. **big-LAMA per-frame — FAST FALLBACK only.** Streams at constant memory, but it *hallucinates* the whole masked region every frame independently → the fill is visibly SOFTER than the surroundings and can flicker. A discerning viewer notices. Use only when ProPainter is unavailable or quality is not critical. (Lesson learned: shipping LAMA here was rejected by the user as \"clear image deterioration\".)\n\n> **The v4 win in one line:** ProPainter (temporal, real-pixel) + tight logo-shaped per-frame mask + presence gating + time-chunking + an NCC verification gate. Scripts: `scripts\u002Fpropaint_pipeline.py` (one clip), `scripts\u002Fpp_batch.sh` + `scripts\u002Fpp_daemon.sh` (batch), `scripts\u002Fpp_verify.py` (the gate). big-LAMA scripts (`inpaint_clip.py` etc.) are kept as the fallback.\n\n## Pipeline\n```\nlocalize logo  ->  TIGHT per-clip mask (white rect\u002Fshape on black, same WxH)\n  ->  per frame: ffmpeg decode rawvideo RGB\n                 LAMA fills masked region on a vertical CONTEXT BAND (mask bbox +- ~140px)\n                 FEATHERED composite of ONLY the masked region back onto the untouched frame\n  ->  x264 (crf 18) -> re-mux ORIGINAL audio (-c:a copy, -shortest)\n```\nEverything outside the mask stays bit-for-bit original. Keep source WxH and fps.\n\n### 1. Localize\n- If the logo is a **fixed overlay** (most SaaS clip tools render it at one position regardless of content): find it ONCE on a confident clip, reuse that mask for all. This is the common case — verify by eye on 2-3 clips.\n- General localizer (`scripts\u002Flocalize_wm.py` here is OpusClip-tuned but the method generalizes): sample N frames, build a **temporal-median** image (static logo persists, moving foreground averages out), multi-scale `cv2.matchTemplate` with `TM_CCOEFF_NORMED` on the logo's **alpha-brightness pattern** (mean-subtracted = background-DC invariant), restrict the search to the band where the logo lives, vote across frames. Low confidence -> DO NOT silently fall back to a big box (see Pitfall 1).\n\n### 2. Mask discipline (the part that makes or breaks it)\n- **TIGHT, logo-only.** Cover the logo + a few px, nothing more.\n- **Never let the mask touch a facial feature** (eyebrow, eye, mouth) or on-frame text you must keep (source chyrons, captions). Clamp the mask edges away from them. A logo over a forehead is trivial; the same generous box dropped onto a tighter-framed shot will cover an eyebrow and LAMA will smear it into mush.\n\n### 3. Inpaint one clip\n`scripts\u002Finpaint_clip.py \u003Cin.mp4> \u003Cmask.png> \u003Cout.mp4> [crf=18]` (needs `big-lama.pt`, see Env). Alters only the mask region, re-muxes original audio.\n\n### 4. Batch + heal transient GPU failures\n`scripts\u002Frun_batch.sh` (resumable: skips clips whose output exists) + `scripts\u002Fretry_until_complete.sh` (re-runs the batch up to N passes until every clip has an output).\n\n## Pitfalls (all hit in practice)\n1. **Generous fallback mask smears faces.** When localization confidence is low, a \"safe\" oversized box reaches into eyes\u002Feyebrows and the inpaint destroys them. FIX: since the logo position is fixed, swap in a tight standard mask from a confident clip and re-run. Tight-logo beats generous-box every time.\n2. **cuFFT_EXEC_FAILED \u002F \"CUDA error: unknown error\" are TRANSIENT, not OOM.** big-LAMA uses Fast Fourier Convolutions, so it leans on cuFFT; on a shared GPU these fail intermittently (~5-10% of clips) even with VRAM free — WSL2 passthrough instability under rapid CUDA-context churn. Each clip is a fresh process so one failure does not poison the next. FIX = a resumable RETRY loop, not a redesign. Confirm transient: the next clip after a failure succeeds.\n3. **Long GPU batches get hang-reaped as background tasks.** A `run_in_background` job that emits no stdout for minutes (one long clip) looks hung and gets killed. FIX: run the workload fully **detached inside WSL** (`setsid nohup bash pp_daemon.sh > log 2>&1 \u003C \u002Fdev\u002Fnull &`) via a fast-returning foreground call — it survives the launching bridge being killed because WSL2 keeps VM processes alive. Watch it with a **persistent Monitor** tailing the log for per-item DONE\u002FFAILED + a done-sentinel file. Do NOT put `wsl.exe` calls inside the Monitor's own loop (it errors the monitor). Also: a `setsid` daemon survives a naive `pkill` of the launcher — kill the whole process group \u002F the `pp_batch.sh` loop, not just leaf processes, or it keeps spawning clips.\n4. **ProPainter OOMs (SIGKILL) on long clips.** ProPainter loads the whole video + all-frame optical flow into RAM at once, so a 150 s \u002F ~4800-frame clip blows past 8 GB and gets SIGKILL'd — even though a 15 s segment runs fine. FIX: process each clip in **time-chunks** (`CORE≈450` frames) with a few **context frames** on each side for temporal propagation; composite only the core frames. Bounds RAM to ~2.2 GB. (`propaint_pipeline.py` does this.)\n5. **Static mask over a hook card \u002F intro smears the card text.** Many SaaS clip tools render a big text \"hook card\" over the first ~5 s of the top clips; the watermark is HIDDEN behind it there (no logo to remove). A mask applied to those frames inpaints the card text into mush. FIX: **per-frame presence gating** — localize the logo POSITION once (it's fixed), but per frame check NCC of the logo template at that fixed bbox; if it's below threshold (logo absent\u002Foccluded) emit an EMPTY mask so the frame is left pristine. Position is fixed; only presence varies — never do a per-frame position *search* (it's noisy and misplaces the mask → residual logo).\n6. **Never claim \"done\" on a single-frame check — use a residual GATE.** Build a clean, logo-SPECIFIC template (temporal-median of the logo over a dark-bg clip; a high-pass template that matches *any* white graphic will false-positive on channel branding like a \"G\" logo). Then for every output clip, sample across the WHOLE timeline and measure NCC of the template at the source-localized position; it must be LOW on every frame (logo gone). `scripts\u002Fpp_verify.py` is this gate (exit 1 on any residual). Do NOT use PSNR\u002FSSIM-vs-original (a good fill legitimately differs).\n\n## Secondary \u002F animated overlay marks — the cross-clip-average phase-mask method\nMany SaaS clip tools render TWO marks, not one: the static top bug (above) AND a SECONDARY animated mark — e.g. OpusClip's vertical \"Created with (.)\" on the LEFT edge (~t3-8.5s: the text holds, then the (.) icon scrolls UP and fades). The static-mask method fails on it (it moves), and a generous column smears any subject in that region. The fix exploits that the mark is a **FIXED-POSITION UI OVERLAY, identical (shape, screen coords, animation phase) across ALL clips** at a given playback time — only the CONTENT behind it differs per clip.\n\n- **Cross-clip averaging builds the mask.** For each window frame index k, average frame k over ALL clips: the overlay (present + time-aligned in every clip) stays sharp and white; content (different per clip) blurs to a dim mean (~60-110). Threshold the averaged frame at each k → the overlay footprint AT PHASE k (text + (.) wherever it is), isolated from content. Per-frame PHASE masks (mask each position only in the ~frames the mark is there) let ProPainter fill a small hole from clean neighbour frames → sharp, full-travel coverage, no smear. (`build_phase_masks.py`, `propaint_left_phase.py`.)\n- **Two compositing refinements that matter:** (a) fill the (.) ring — it thresholds to a HOLLOW ring; contour-fill + convex-hull → solid disk, else a faint outline survives; (b) brightness-match the fill to the local boundary ring (per-channel offset, clamped ±22) → kills the dark\u002Fbright seam when the mark sits over a smooth dark region (e.g. (.) over a suit), without Poisson instability.\n- **Per-clip timing variance is the trap.** The fade is NOT perfectly synced across clips (~±1s). The averaged mask is time-locked to the MAJORITY timing, so a clip whose mark fades LATE keeps a residual the mask misses. FIX for the outlier = a clip-specific mask: canonical phase masks + a SHORT past-union (~0.6s, tracks a scrolling icon's lag WITHOUT dragging the mask into now-changed regions — a long past-union inpaints unchanged content → a visible patch) + a static TOP-BAND for text that fades in place and lingers. **Stop masking a region once the animated element has left it**, else you inpaint unchanged background → a dark patch. (`build_mask_clip35.py`.)\n- **The one background nothing local can beat: a white mark over a SATURATED-WHITE ANIMATED background** (bright \"plexus\"\u002Fparticle graphic). This is the genuine unsolvable-without-source case. Three approaches tried, all fail (don't re-walk these):\n  - *ProPainter (flow inpaint):* the mark is present across the whole window → no clean temporal neighbour to copy; the animated bg defeats flow propagation → faint fade-tail trace.\n  - *Larger temporal context (big CTXF):* does NOT help (distant frames don't match the fast-moving bg) and can INTRODUCE a colour tint from mismatched far frames.\n  - *Alpha-unmixing* `bg=(obs-a*255)\u002F(1-a)` (the \"correct\" de-watermark for a semi-transparent overlay): fails here because (a) mark and bg share the saturated-white colour → the equation is degenerate where bg≈255, and (b) the fade lag is NON-UNIFORM (text vs icon fade at different rates) so no single glyph time-shift aligns → any misalignment subtracts white at the wrong place = a visible ghost (a forced lag manufactured a clear ghost in an already-clean region).\n  Root cause: when the watermark and an animated background share a colour, you cannot separate them locally — you need the un-watermarked SOURCE (paid tier). **FLAG it** (per \"remove fully, flag imperfect ones\") — don't grind: the prominent mark still removes cleanly, only the sub-second fade tail resists, and it's imperceptible at normal playback (visible only under 3x CLAHE + zoom).\n- **Verify the secondary mark SEPARATELY** from the top bug: an all-clips contact sheet of the mark-box crop at each clip's PEAK neutral-white frame (CLAHE-boosted) + a per-clip neutral-white score for triage. High score ≠ residual — it's usually legit broadcaster branding (lower-third chyron) or bright content; CONFIRM visually that no overlay glyph (the \"Created with\"\u002F(.) signature) appears. (`all_41_leftmark_qc.py`.)\n\n## QC (do not skip — \"seamless\" is the whole point)\n- Extract a **3x zoom of the watermark region**, before vs after, at >= 2 timestamps. The logo must be gone AND the fill must look like the surrounding texture.\n- Spot-check the **hard cases**: logo straddling a hairline\u002Fedge, and any shot with tighter framing or a busy background (where a fallback mask may have fired).\n- Build a **montage** of the region across spread-out clips to catch surprises in one glance.\n- **Programmatically validate every output** before delivery: it has video AND audio streams and a duration within ~0.7 s of the source (catches truncated\u002Fcorrupt encodes).\n\n## Env (WSL2 + CUDA, the tested setup)\n- WSL2 Ubuntu, Python venv (e.g. `~\u002Fwm_env`): `torch` (CUDA build, e.g. 2.6+cu124), `opencv-python-headless`, `numpy`. System `ffmpeg` with libx264.\n- Model: `big-lama.pt` TorchScript (~196 MB) at `~\u002Fwm_models\u002Fbig-lama.pt`, from the simple-lama-inpainting v0.1.0 GitHub release. Load the `.pt` directly with `torch.jit.load` — do NOT `pip install iopaint`\u002F`simple-lama-inpainting`, they pin Pillow\u003C10 \u002F numpy\u003C2 which fail to build on Python 3.12.\n- Perf reference: ~11 fps end-to-end, ~2.3 GB peak VRAM, ~60-90 min for ~40 clips of ~1 min each.\n\n## Reference scripts\nAll scripts here are **parametrized and non-breaking**: run them with **no args\u002Fenv** and every\npath\u002Fconstant resolves to the original job defaults; override any of them via the env knobs listed\nin each script's header comment (nothing points at a hardcoded personal directory anymore).\n\n**ProPainter pipeline (PRIMARY) + residual-QC gate:**\n- `scripts\u002Fpropaint_pipeline.py \u003Cin.mp4> \u003Cout.mp4> [start_sec] [dur_sec]` — one clip, chunked temporal inpaint. Env: `TPL_GRAY`, `PP` (ProPainter checkout, default `~\u002FProPainter`), `DET_THRESH`, `PRES_THRESH`, `CORE`, `CTXF`.\n- `scripts\u002Fpp_batch.sh` — resumable batch over `$SRC\u002F*.mp4` → `$OUT`. Env: `BASE`, `SRC`, `OUT`, `PIPELINE`, `VENV`, `LOG`.\n- `scripts\u002Fpp_daemon.sh` — detached retry daemon wrapping the batch (heals transient CUDA faults). Env: `BASE`, `SRC`, `OUT`, `BATCH`, `DONE`, `MAX`.\n- `scripts\u002Fpp_verify.py` — the \"prove it's clean\" NCC + sharpness gate (exit 1 on any residual). Env: `RG`, `QC`, `SRC`, `OUT`, `TPL_GRAY`, `NCC_FAIL`, `SHARP_MIN`.\n- `scripts\u002Fscan_residual.py` — thorough full-timeline residual scan; builds the clean template from `TPL_SRC` at `BBOX`. Env: `RG`, `QC`, `SRC`, `CLN`, `TPL_SRC`, `BBOX`.\n- `scripts\u002F_logo_template_example.png` — a **generic example** grayscale logo template (the OpusClip bug these scripts remove), for illustration only; the scripts do not load it by default — point `TPL_GRAY` at your own clean template.\n\n**big-LAMA fallback:** `scripts\u002Finpaint_clip.py`, `scripts\u002Frun_batch.sh`, `scripts\u002Fretry_until_complete.sh`, `scripts\u002Fdaemon_launch.sh` (set `BASE`\u002F`SRC`\u002F`MASK`\u002F`OUT`).\n\nThe committed defaults document the real razvan_gandu run (private repo). The secondary animated-mark helpers (`build_phase_masks.py`, `propaint_left_phase.py`, `build_mask_clip35.py`, `all_41_leftmark_qc.py`) are described above but not yet generalized into this skill.\n",{"name":5,"description":7},"main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Fskills\u002Fvideo-watermark-removal","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill video-watermark-removal",{"tool":52,"command":53},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill video-watermark-removal",{"tool":55,"command":56},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill video-watermark-removal","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002FSKILL.md",true,[60,66,70,74,78,82,86,90,94,98,102],{"path":61,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":64,"sourceHtmlUrl":65},"scripts\u002F_logo_template_example.png","SCRIPT","脚本",6660,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002F_logo_template_example.png",{"path":67,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":68,"sourceHtmlUrl":69},"scripts\u002Fdaemon_launch.sh",814,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fdaemon_launch.sh",{"path":71,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":72,"sourceHtmlUrl":73},"scripts\u002Finpaint_clip.py",5280,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Finpaint_clip.py",{"path":75,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":76,"sourceHtmlUrl":77},"scripts\u002Flocalize_wm.py",9253,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Flocalize_wm.py",{"path":79,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":80,"sourceHtmlUrl":81},"scripts\u002Fpp_batch.sh",1683,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fpp_batch.sh",{"path":83,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":84,"sourceHtmlUrl":85},"scripts\u002Fpp_daemon.sh",1476,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fpp_daemon.sh",{"path":87,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":88,"sourceHtmlUrl":89},"scripts\u002Fpp_verify.py",5418,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fpp_verify.py",{"path":91,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":92,"sourceHtmlUrl":93},"scripts\u002Fpropaint_pipeline.py",9786,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fpropaint_pipeline.py",{"path":95,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":96,"sourceHtmlUrl":97},"scripts\u002Fretry_until_complete.sh",1277,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fretry_until_complete.sh",{"path":99,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":100,"sourceHtmlUrl":101},"scripts\u002Frun_batch.sh",1857,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Frun_batch.sh",{"path":103,"resourceType":62,"resourceTypeDesc":63,"sizeBytes":104,"sourceHtmlUrl":105},"scripts\u002Fscan_residual.py",5420,"https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fvideo-watermark-removal\u002Fscripts\u002Fscan_residual.py",[107,128,156,183,197,213],{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":113,"publishedAt":35},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[114,115,116,117,118,119,120,121,122,30,123,124,125,126,127],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","context","repo","injection","execution","file",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":137,"publishedAt":35},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[134],{"categoryNo":135,"categoryName":136,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[138,139,140,141,142,143,144,145,146,147,26,148,149,150,30,151,152,153,154,155],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":168,"publishedAt":35},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[162,165],{"categoryNo":163,"categoryName":164,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[169,170,171,172,173,174,175,176,177,178,139,179,180,181,182],"review","visual","visual review","pages","web","false","iteration","take","check","slides","ui","screenshot","playwright","screenshots",{"skillNo":184,"name":185,"nameZh":6,"description":186,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":187,"categories":188,"keywords":189,"publishedAt":35},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[185,190,191,192,193,194,195,196],"setup","report","project","installation","project setup","issues","state",{"skillNo":198,"name":199,"nameZh":6,"description":200,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":201,"categories":202,"keywords":204,"publishedAt":35},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[203],{"categoryNo":135,"categoryName":136,"taxonomyType":21,"taxonomyTypeDesc":22},[199,205,119,206,207,208,30,209,210,211,212],"start issue","issue","ready","work on issue","user","work","work on","development",{"skillNo":214,"name":190,"nameZh":6,"description":215,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":216,"categories":217,"keywords":218,"publishedAt":35},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[190,192,153,219,194,220],"files","interactive"]