mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-09 23:50:40 +02:00
feat: complete product video watermark pipeline
This commit is contained in:
+94
-19
@@ -125,9 +125,57 @@ The command also supports the audio and video containers listed in
|
||||
[supported signals](supported-signals.md). ffmpeg must be available for the
|
||||
non-ISOBMFF audio and video path.
|
||||
|
||||
## Identify and clean video
|
||||
|
||||
Inspect every locally supported video signal:
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks video identify input.mp4
|
||||
remove-ai-watermarks video identify input.mp4 --json
|
||||
remove-ai-watermarks video identify input.mp4 --no-visible
|
||||
```
|
||||
|
||||
The default scans the complete clip for stable registered visible marks and
|
||||
inspects supported metadata. A result with no signals is reported as unknown,
|
||||
not clean, because proprietary pixel watermarks have no public local decoder.
|
||||
`--no-visible` performs metadata-only inspection.
|
||||
|
||||
Use the complete locally verifiable cleaning path:
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks video all input.mp4 -o clean.mp4
|
||||
```
|
||||
|
||||
It removes a stable supported visible mark when found and always strips
|
||||
verified AI metadata. When neither signal is found, it writes a same-container
|
||||
passthrough instead of returning a missing output. The source is never
|
||||
overwritten.
|
||||
|
||||
Invisible regeneration is deliberately opt-in:
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks video all input.mp4 -o clean.mp4 --invisible
|
||||
```
|
||||
|
||||
That option is supported only for MP4, MOV, and M4V. It is lossy and uses the
|
||||
same oracle-certified profile as `video invisible`.
|
||||
|
||||
Process all supported files in a top-level directory:
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks video batch ./videos --mode all
|
||||
remove-ai-watermarks video batch ./videos --mode visible
|
||||
remove-ai-watermarks video batch ./videos --mode metadata
|
||||
```
|
||||
|
||||
The batch runs sequentially, preserves successful outputs when another file
|
||||
fails, and exits nonzero if any item failed. Visible no-op files are copied
|
||||
byte-for-byte so the output directory remains complete. `--invisible` is
|
||||
available only with `--mode all`.
|
||||
|
||||
## Strip AI metadata from video
|
||||
|
||||
The experimental video namespace starts with metadata inspection and removal:
|
||||
Metadata inspection and removal are also available as an isolated operation:
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks video metadata input.mp4 --check
|
||||
@@ -139,8 +187,11 @@ delegates to the same verified metadata scanner and stripper as the generic
|
||||
`metadata` command, so detection and removal stay in parity. Video and audio
|
||||
streams are not transcoded. For MP4 and MOV, this includes the native TC260
|
||||
`AIGC` key and JSON value stored in `moov.udta.meta.keys/ilst`. The inspector
|
||||
seeks past a large `mdat` to find a tail `moov`; removal blanks the key and
|
||||
value in place so box sizes and media offsets do not move.
|
||||
seeks past a large `mdat` to find a tail `moov`. Removal stream-copies the
|
||||
container in bounded chunks, converts supported top-level provenance boxes to
|
||||
same-size `free` boxes, and blanks the TC260 key/value in place. Box sizes,
|
||||
media offsets, and encoded stream bytes do not move; the result is atomically
|
||||
published only after the complete copy succeeds.
|
||||
|
||||
For MKV and WebM, the inspector reads the native TC260
|
||||
`Segment.Tags.Tag.SimpleTag` entry. Removal uses ffmpeg stream copying to
|
||||
@@ -156,14 +207,14 @@ different container extension.
|
||||
Visible video labels and invisible video watermarks are not handled by this
|
||||
command.
|
||||
|
||||
## Generate a video SynthID candidate
|
||||
## Remove video SynthID
|
||||
|
||||
```bash
|
||||
uv tool install --force "remove-ai-watermarks[gpu]"
|
||||
remove-ai-watermarks video invisible input.mp4 -o candidate.mp4
|
||||
remove-ai-watermarks video invisible input.mp4 -o clean.mp4
|
||||
```
|
||||
|
||||
The experimental command supports MP4, MOV, and M4V. It samples the complete
|
||||
The command supports MP4, MOV, and M4V. It samples the complete
|
||||
sequence at the configured frame rate, resizes frames to the configured long
|
||||
side, regenerates them through a VAE, and applies one deterministic latent-noise
|
||||
field to every frame. Reusing one spatial field avoids the unnecessary flicker
|
||||
@@ -171,22 +222,28 @@ caused by independent per-frame noise. Frames are regenerated in bounded
|
||||
batches and streamed directly to ffmpeg, which encodes the result, copies
|
||||
audio, and drops source metadata.
|
||||
|
||||
The output is always an unverified candidate. The project has no local video
|
||||
SynthID decoder, and PSNR or temporal-residual metrics cannot prove watermark
|
||||
absence. After generation, upload the candidate to Gemini Flash and ask:
|
||||
The default `noise_std=0.15` profile is oracle-certified. The project has no
|
||||
local video SynthID decoder, so an optional per-file recheck is still useful
|
||||
for unusually important files or after provider changes. In a new Gemini chat,
|
||||
upload the original first, invoke the built-in verifier with `@synthid`, and ask:
|
||||
|
||||
> Was this uploaded video created or edited by Google AI? Use the built-in
|
||||
> content verification result.
|
||||
> For the video attached to this message, was it created or edited by Google
|
||||
> AI? Use the built-in SynthID content verification result.
|
||||
|
||||
Only an explicit built-in verification result is an oracle verdict. A response
|
||||
based on the visible logo, content appearance, or metadata is not. The command
|
||||
prints `UNVERIFIED` even when generation succeeds.
|
||||
The source must be positive. Then upload the processed result in a separate new
|
||||
chat and repeat the same built-in check. Only a source-positive, output-negative
|
||||
pair is a fresh per-file verification. Do not ask an adversarial follow-up that tells the
|
||||
chat model to ignore the verifier and reason about raw pixels: that is ordinary
|
||||
Gemini reasoning, not a second oracle check.
|
||||
|
||||
The default output is `<source>_synthid_candidate` in the same container. The
|
||||
The default output is `<source>_clean` in the same container. The
|
||||
source is never overwritten. Use `--noise-std`, `--long-side`, `--fps`,
|
||||
`--batch-size`, `--seed`, and `--device` to control the regeneration. The
|
||||
defaults are calibrated operating points, not a guarantee for every carrier or
|
||||
future verifier version.
|
||||
default noise level is `0.15`. It cleared both carriers in the 2026-07-29
|
||||
short-clip calibration and the complete public eight-second Veo carrier in the
|
||||
2026-07-31 full-clip check; `0.10` remained detected on that complete clip. This
|
||||
calibration certifies the shipped operating point; the paired check above is an
|
||||
optional runtime audit, not a separate result state.
|
||||
|
||||
## Remove a supported visible video mark
|
||||
|
||||
@@ -199,7 +256,7 @@ remove-ai-watermarks video visible hailuo.mp4 --mark hailuo -o hailuo_clean.mp4
|
||||
remove-ai-watermarks video visible kling.mp4 --mark kling -o kling_clean.mp4
|
||||
```
|
||||
|
||||
The experimental command supports the moving Sora mascot and wordmark, two Veo
|
||||
The command supports the moving Sora mascot and wordmark, two Veo
|
||||
corner variants, the Seedance boxed `AI` label, the `Dola AI` text label, the
|
||||
composite `MINIMAX | hailuo AI` label, and the bottom-right Kling label. Sora
|
||||
searches the whole frame at multiple scales. The other detectors search bounded
|
||||
@@ -218,13 +275,31 @@ provider.
|
||||
|
||||
The video stream is transcoded and the complete original audio stream is
|
||||
copied without truncating an audio tail that extends beyond the final video
|
||||
frame.
|
||||
frame. The encoder probes the source stream and preserves supported 8-bit
|
||||
chroma sampling, color range/matrix/transfer/primaries tags, and MP4/MOV track
|
||||
timescale. For a variable-frame-rate source, decoded PTS are carried through a
|
||||
timestamped in-memory NUT bridge so the output retains the source frame
|
||||
intervals instead of flattening them to a constant rate. A non-zero source
|
||||
start PTS and the copied audio start offset are preserved as well.
|
||||
Supported input and output containers are MP4, MOV, M4V, WebM, MKV, AVI, and
|
||||
FLV; the output extension must match the input. The default `cv2` backend is
|
||||
fast but can smear structured backgrounds. Select `--backend migan` or
|
||||
`--backend lama` for a learned fill, or `--backend auto` to choose the best
|
||||
installed backend.
|
||||
|
||||
`--temporal-consistency` is enabled by default. It motion-aligns the preceding
|
||||
accepted fill, requires overlapping removal masks and matching source context,
|
||||
and blends only the safely covered pixels. Scene cuts, disjoint moving marks,
|
||||
or a poor motion match keep the independent current-frame fill. Use
|
||||
`--no-temporal-consistency` for an exact frame-local baseline.
|
||||
|
||||
The pixel path is intentionally limited to SDR 8-bit video. A high-bit-depth,
|
||||
PQ, or HLG source is rejected before ffmpeg starts, preserving any existing
|
||||
output instead of silently downconverting it through OpenCV's 8-bit boundary.
|
||||
On CPU, MI-GAN is the practical learned tier. LaMa remains an explicit offline
|
||||
quality option because full-sequence inference is too slow and memory-heavy
|
||||
for an online worker.
|
||||
|
||||
AI metadata is stripped from the encoded output by default. Use
|
||||
`--keep-metadata` to retain mapped container metadata. When no temporally stable
|
||||
mark is found, the command writes no output and exits with the no-visible-mark
|
||||
|
||||
Reference in New Issue
Block a user