Add Hailuo, Kling, AVI, and FLV video coverage

This commit is contained in:
Victor Kuznetsov
2026-07-29 23:30:33 -07:00
parent 63cde7f32a
commit 07b678fa4c
17 changed files with 1017 additions and 189 deletions
+24 -16
View File
@@ -134,17 +134,20 @@ remove-ai-watermarks video metadata input.mp4 --check
remove-ai-watermarks video metadata input.mp4 --remove -o clean.mp4
```
Supported containers are MP4, MOV, M4V, WebM, and MKV. The operation 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.
Supported containers are MP4, MOV, M4V, WebM, MKV, AVI, and FLV. The operation
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.
For MKV and WebM, the inspector reads the native TC260
`Segment.Tags.Tag.SimpleTag` entry. Removal uses ffmpeg stream copying to
discard container tags and chapters without transcoding the streams.
AVI uses the normative `LIST/INFO/AIGC` chunk, while FLV uses the
`script.onMetaData.AIGC` AMF0 string. Their bounded readers skip media payloads,
and removal also uses ffmpeg stream copying.
When `-o` is omitted, the command writes `<source>_clean` with the same
extension. It never overwrites the source, and it rejects an output with a
@@ -192,22 +195,27 @@ remove-ai-watermarks video visible input.mp4 -o clean.mp4
remove-ai-watermarks video visible veo.mp4 --mark veo -o veo_clean.mp4
remove-ai-watermarks video visible seedance.mp4 --mark seedance -o seedance_clean.mp4
remove-ai-watermarks video visible dola.mp4 --mark dola -o dola_clean.mp4
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
corner variants, the Seedance boxed `AI` label, and the `Dola AI` text label.
Sora searches the whole frame at multiple scales. The other detectors search
bounded bottom-right regions with separate synthetic silhouettes. Every mark
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
lower-frame regions with separate synthetic silhouettes. Kling additionally
requires its bright low-saturation label near the frame edge. Every mark
requires a spatially recurring candidate across adjacent frames. Fixed marks
must also remain anchored instead of drifting with a scene object. Matching
provider provenance may relax the visual score, but metadata alone never
creates a detection. Clean API exports therefore remain untouched.
provider provenance may relax the visual score only for registered
provenance-aware marks; metadata alone never creates a detection.
The video stream is transcoded and the original audio stream is copied.
Supported input and output containers are MP4, MOV, M4V, WebM, and MKV; 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.
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.
AI metadata is stripped from the encoded output by default. Use
`--keep-metadata` to retain mapped container metadata. When no temporally stable
+18 -10
View File
@@ -207,17 +207,21 @@ strip supported AI provenance metadata without transcoding streams.
`video visible` and `remove_video_visible` additionally support the moving
Sora 2 mascot and wordmark, the current Veo four-point diamond, the legacy
`Veo` text, the Seedance boxed `AI` label, and the fixed `Dola AI` text.
Detection requires a recurring visual candidate across adjacent frames.
Seedance, Dola, and Veo candidates must remain anchored rather than drifting
with a scene object. Provider provenance can recover low-contrast runs only
after visual evidence exists, so metadata alone does not erase a clean API
export.
`Veo` text, the Seedance boxed `AI` label, the fixed `Dola AI` text, the Hailuo
MINIMAX/Hailuo composite label, and the bottom-right Kling label with its
version suffix. Detection requires a recurring visual candidate across
adjacent frames. Fixed-mark candidates must remain anchored rather than
drifting with a scene object. Kling also requires a bright low-saturation
candidate near the expected frame edge. Provider provenance can recover
low-contrast runs only after visual evidence exists for the marks that define a
provenance prior, so metadata alone does not erase a clean API export.
Historical Sora Turbo exports use a small OpenAI swirl in the corner rather
than the moving mascot-and-wordmark design; that earlier variant is not
detected by the `sora` video mark. Other provider video labels are not
supported yet. Google video SynthID has a candidate-producing VAE path, while
other proprietary invisible video watermarks have no registered attack.
detected by the `sora` video mark. Hailuo and Kling coverage is specific to the
verified lower-edge layouts; a new provider layout needs a separate calibrated
silhouette. Other provider video labels are not supported yet. Google video
SynthID has a candidate-producing VAE path, while other proprietary invisible
video watermarks have no registered attack.
Visible removal transcodes the video stream and copies audio. Its frame-local
fill is not a motion-aware video inpainting model. OpenCV can leave a visible
@@ -235,7 +239,11 @@ Native TC260 metadata in MP4/MOV is supported at its normative
`moov.udta.meta.keys/ilst` placement, including non-faststart files whose
`moov` follows a large media payload. MKV/WebM is supported at the normative
`Segment.Tags.Tag.SimpleTag` placement and uses ffmpeg for stream-copy removal.
The corresponding FLV and AVI native tags are not parsed yet.
AVI is supported at `LIST/INFO/AIGC`, and FLV at
`script.onMetaData.AIGC`; both use ffmpeg stream-copy removal. Stock ffmpeg can
write and strip the FLV form, but writing the nonstandard AVI child for fixture
generation requires dedicated muxer support, so the AVI reader is verified
against an exact synthetic RIFF structure.
The current ISOBMFF stripper reads an MP4, MOV, or M4V container into memory
before rewriting its metadata boxes. A streaming box copier is required before
+41 -21
View File
@@ -115,6 +115,13 @@ only a `Segment.Tags.Tag.SimpleTag` pairing `TagName=AIGC` with a JSON
`TagString` carrying a TC260 field. The existing ffmpeg stream-copy path removes
those container tags without transcoding the encoded streams.
[`noai/riff.py`](../src/remove_ai_watermarks/noai/riff.py) and
[`noai/flv.py`](../src/remove_ai_watermarks/noai/flv.py) implement the remaining
normative TC260 video placements. The RIFF walker reads only AVI
`LIST/INFO/AIGC` children. The FLV walker skips media tags and parses the AMF0
`script.onMetaData.AIGC` string. Both require a recognized TC260 JSON field and
use the verified ffmpeg stream-copy path for removal.
[`video_encoding.py`](../src/remove_ai_watermarks/video_encoding.py) owns the
raw-BGR ffmpeg command and pipe lifecycle shared by visible removal and
invisible regeneration. It centralizes container codecs, optional audio stream
@@ -140,34 +147,44 @@ helpers to build a matched control and candidate grid, preventing research and
shipped regeneration paths from drifting.
[`video_visible.py`](../src/remove_ai_watermarks/video_visible.py) implements
the first pixel stages for Sora, Veo, Seedance, and Dola. The Sora detector
searches a normalized frame with a fully synthetic mascot-and-text silhouette
at several scales. The Veo detector uses separate synthetic silhouettes for the
current four-point diamond and legacy `Veo` text. Seedance uses a synthetic
rounded boxed-`AI` silhouette, while Dola uses an OpenCV-font `Dola AI`
silhouette. All fixed-mark searches are bounded to the bottom-right area and
calibrated independently. A strong relocated Veo diamond may bypass the known
layout anchors, but weak free-corner matches never enter the temporal arbiter.
the first pixel stages for Sora, Veo, Seedance, Dola, Hailuo, and Kling. The
Sora detector searches a normalized frame with a fully synthetic
mascot-and-text silhouette at several scales. The Veo detector uses separate
synthetic silhouettes for the current four-point diamond and legacy `Veo`
text. Seedance uses a synthetic rounded boxed-`AI` silhouette, while Dola uses
an OpenCV-font `Dola AI` silhouette. Hailuo uses a synthetic waveform,
MINIMAX/Hailuo text, separator, and ring. Kling combines synthetic font
variants with a ring approximation of its swirl; the logo path rescues
wordmarks whose version or font differs, while the edge and white-label gates
reject recurring scene texture. All fixed-mark searches are bounded to the
expected lower-frame area and calibrated independently. A strong relocated Veo
diamond may bypass the known layout anchors, but weak free-corner matches never
enter the temporal arbiter.
Every per-frame result is untrusted. The provider-specific stabilization
wrappers share one recurrence implementation, while retaining separate visual
floors and minimum-run policy. Provenance can relax a low-contrast run only
after recurring visual evidence exists. Sora transition frames follow the
nearest confirmed moving position only with Sora provenance. Veo, Seedance,
and Dola additionally require candidates to remain anchored to the start of a
run. This rejects slowly drifting scene details that still have high
frame-to-frame overlap.
Dola, Hailuo, and Kling additionally require candidates to remain anchored to
the start of a run. This rejects slowly drifting scene details that still have
high frame-to-frame overlap. Hailuo and Kling do not infer provenance from
technical encoder tags; their confirmed public samples carried no provider
metadata.
Removal runs in a second decode pass. Sora, legacy Veo text, Dola text, and the
Seedance box use box masks. Seedance deliberately fills the complete localized
box: a synthetic outline mask passed repeat detection but left part of the real
translucent border visible during visual end-to-end review. The square Veo
diamond uses a synthetic shape mask so transparent corners do not erase
unrelated pixels. Every mask goes through the shared `watermark_registry.fill`
backends. ffmpeg encodes the changed video stream and copies optional audio.
The default OpenCV fill is the speed floor; structured backgrounds need MI-GAN
or LaMa for better reconstruction. Invisible video stages must continue to
reuse the image and metadata implementations rather than copying their logic.
Removal runs in a second decode pass. Sora, legacy Veo text, Dola text,
Seedance, Hailuo, and Kling use box masks. Seedance deliberately fills the
complete localized box: a synthetic outline mask passed repeat detection but
left part of the real translucent border visible during visual end-to-end
review. Hailuo expands beyond the matched core to cover both provider icons.
Kling expands around the wordmark or swirl to include the version and optional
`PRO` suffix. The square Veo diamond uses a synthetic shape mask so transparent
corners do not erase unrelated pixels. Every mask goes through the shared
`watermark_registry.fill` backends. ffmpeg encodes the changed video stream and
copies optional audio. The default OpenCV fill is the speed floor; structured
backgrounds need MI-GAN or LaMa for better reconstruction. Invisible video
stages must continue to reuse the image and metadata implementations rather
than copying their logic.
The inherited ISOBMFF metadata path currently reads the complete container into
memory; replacing that with a streaming box copier is a prerequisite for large
@@ -205,6 +222,9 @@ Key contracts:
`moov.udta.meta.keys/ilst` and blanked without changing box sizes.
- Native MKV/WebM TC260 `AIGC` entries are read from
`Segment.Tags.Tag.SimpleTag` and removed through the ffmpeg stream-copy path.
- Native AVI and FLV TC260 entries are read from `LIST/INFO/AIGC` and
`script.onMetaData.AIGC`, respectively, then removed through ffmpeg stream
copying.
- Supported non-ISOBMFF audio and video containers use ffmpeg stream copying.
- The low-level remover is fail-safe and can copy an undecodable file through
unchanged.
+21 -7
View File
@@ -130,7 +130,8 @@ as proof that metadata was removed.
## Inspect and strip video metadata
The experimental high level video API supports MP4, MOV, M4V, WebM, and MKV:
The experimental high level video API supports MP4, MOV, M4V, WebM, MKV, AVI,
and FLV:
```python
import remove_ai_watermarks as raiw
@@ -152,7 +153,9 @@ inspection recognizes the native TC260 `AIGC` entry in
`moov.udta.meta.keys/ilst`; its removal preserves container size and encoded
stream bytes. MKV/WebM inspection recognizes the corresponding
`Segment.Tags.Tag.SimpleTag` representation; its removal requires ffmpeg for a
stream-copy remux.
stream-copy remux. AVI inspection reads `LIST/INFO/AIGC`, and FLV inspection
reads `script.onMetaData.AIGC`; both use the same verified ffmpeg stream-copy
removal path.
## Generate a video SynthID candidate
@@ -211,16 +214,27 @@ dola_result = raiw.remove_video_visible(
"dola_clean.mp4",
mark="dola",
)
hailuo_result = raiw.remove_video_visible(
"hailuo.mp4",
"hailuo_clean.mp4",
mark="hailuo",
)
kling_result = raiw.remove_video_visible(
"kling.mp4",
"kling_clean.mp4",
mark="kling",
)
```
`remove_video_visible` scans the complete video before writing output. It
combines synthetic multi-scale visual matching with temporal consistency, so an
isolated lookalike in one frame is not enough to authorize inpainting. The
supported `mark` values are `sora`, `veo`, `seedance`, and `dola`. The Veo
detector recognizes the current four-point diamond and the legacy `Veo` text.
Seedance recognizes the boxed `AI` label, while Dola recognizes its compact
text label. Each variant has an independent synthetic silhouette and calibrated
temporal policy.
supported `mark` values are `sora`, `veo`, `seedance`, `dola`, `hailuo`, and
`kling`. The Veo detector recognizes the current four-point diamond and the
legacy `Veo` text. Seedance recognizes the boxed `AI` label, Dola recognizes
its compact text label, Hailuo recognizes the composite MINIMAX/Hailuo label,
and Kling recognizes its bottom-right logo, wordmark, and version suffix. Each
variant has an independent synthetic silhouette and calibrated temporal policy.
The returned `VideoVisibleResult` records the total, detected, and removed frame
counts plus any AI metadata that survived the output encode. The function
+9 -5
View File
@@ -37,6 +37,8 @@ when you can select the affected area yourself.
| `veo` | Current four-point diamond and legacy `Veo` text | Fixed bottom-right corner | Uses separate silhouettes and requires a recurring match; learned fill is preferable on structured backgrounds. |
| `seedance` | Boxed `AI` label | Fixed bottom-right corner | Requires an anchored recurring match; the full localized box is filled because a thinner synthetic shape mask leaves the real translucent rim behind. |
| `dola` | `Dola AI` text | Fixed bottom-right corner | Requires an anchored recurring match; ByteDance or BytePlus provenance can relax only an existing visual run. |
| `hailuo` | `MINIMAX | hailuo AI` composite label | Fixed lower edge | Uses a synthetic waveform, text, separator, and ring silhouette; the complete recurring label box is filled. |
| `kling` | Kling swirl, `KLING AI`, version, and optional `PRO` suffix | Fixed bottom-right edge | Combines a synthetic logo rescue with font variants, an edge gate, a white-label gate, and anchored temporal recurrence. |
Use `video visible` for this registry. It is separate from the image `visible`
command because selection is made over a sequence rather than one raster.
@@ -61,8 +63,9 @@ The inspection and stripping code handles signals in these groups:
- IPTC AI disclosure fields;
- PNG text chunks and embedded generation parameters;
- China TC260 AIGC labels in supported image placements and the normative
MP4/MOV `moov.udta.meta.keys/ilst` and MKV/WebM
`Segment.Tags.Tag.SimpleTag` placements;
MP4/MOV `moov.udta.meta.keys/ilst`, MKV/WebM
`Segment.Tags.Tag.SimpleTag`, AVI `LIST/INFO/AIGC`, and FLV
`script.onMetaData.AIGC` placements;
- xAI and Grok EXIF signature fields;
- Samsung AI editing markers;
- Hugging Face job metadata;
@@ -87,8 +90,8 @@ Metadata inspection and removal additionally have container paths for:
- JPEG XL metadata;
- MP4, MOV, M4V, and M4A;
- WebM, MKV, MKA, MP3, WAV, FLAC, OGG, OGA, Opus, and AAC when ffmpeg is
available.
- WebM, MKV, MKA, AVI, FLV, MP3, WAV, FLAC, OGG, OGA, Opus, and AAC when
ffmpeg is available.
JPEG image metadata stripping removes targeted metadata segments without
re-encoding the entropy coded image scan. PNG and WebP removal preserves pixel
@@ -135,7 +138,8 @@ not a universal clean verdict.
| Midjourney | None registered | No registered pixel decoder | EXIF, XMP, and IPTC signals |
| ByteDance generators | Doubao and Jimeng marks | No registered pixel decoder | TC260 AIGC and supported C2PA signals |
| Qwen | Qwen mark | No registered pixel decoder | TC260 AIGC |
| Kling | Kling mark | No registered pixel decoder | TC260 AIGC |
| Kling | Kling image and video marks | No registered pixel decoder | TC260 AIGC |
| Hailuo / MiniMax video | Hailuo composite video label | No registered pixel decoder | TC260 AIGC where present |
| Baidu | Baidu mark | No registered pixel decoder | TC260 AIGC |
| LibLibAI | LibLibAI mark | No registered pixel decoder | TC260 AIGC |
| RunningHub | RunningHub mark | No registered pixel decoder | TC260 AIGC |
+24 -2
View File
@@ -45,6 +45,12 @@ Native MKV/WebM is a sixth serialization. TC260-PG-20257A stores
`Segment.Tags.Tag.SimpleTag`. The bounded EBML reader skips cluster payloads;
the existing ffmpeg stream-copy path removes the tags without transcoding.
The same [TC260 video guide](https://www.tc260.org.cn/portal/article/303/4061772dcf684d8a96f395a4298e9e53)
defines two more native serializations. AVI stores an `AIGC` child in
`LIST/INFO`; FLV stores an AMF0 `AIGC` string under `script.onMetaData`. The
bounded RIFF and FLV readers validate the JSON field set and skip media
payloads. Removal remuxes either container through ffmpeg with stream copy.
- **HuggingFace-hosted job (caught by `metadata.huggingface_job`, surfaced by `identify` as the `hf_job` signal, MEDIUM confidence):** HuggingFace Jobs / Spaces can stamp generated PNGs with an `hf-job-id` tEXt chunk holding the job UUID. It marks the *hosting job*, not a model, so it lifts an Unknown verdict to a tentative AI via `hf_only` but never overrides a hard metadata signal. `_HF_JOB_CAVEAT` states the limit. Removal drops the chunk through the PNG metadata whitelist.
- **No detectable signal on some downloads:** Recraft exports and some hosted
FLUX surfaces can arrive without a supported local signal. Midjourney samples
@@ -56,8 +62,9 @@ the existing ffmpeg stream-copy path removes the tags without transcoding.
- **Built in the dated batch:** soft-binding vendor detection, IPTC Photo
Metadata AI-disclosure fields, C2PA detection and stripping for supported
ISOBMFF video, the optional Adobe TrustMark decoder, and temporally stabilized
visible Sora, Veo, Seedance, and Dola removal. Other visible video logos and
proprietary audio-watermark detection remain outside the package.
visible Sora, Veo, Seedance, Dola, Hailuo, and Kling removal. Other visible
video logos and proprietary audio-watermark detection remain outside the
package.
Metadata stripping for supported audio containers is a separate implemented
path.
@@ -101,6 +108,21 @@ every frame, then requires an anchored temporal run. This extra anchor check
was necessary because a moving clean scene detail could retain enough adjacent
overlap to pass a recurrence-only gate.
**Hailuo and Kling use larger fixed composite labels.** Verified Hailuo exports
carry a lower-edge waveform, `MINIMAX`, separator, Hailuo ring, and
`hailuo AI` text. Verified Kling exports carry a bottom-right swirl,
`KLING AI`, a changing version suffix, and sometimes `PRO`. The detectors use
only synthetic primitives and fonts. Hailuo expands the matched core to cover
the complete composite. Kling combines a version-independent text core with a
synthetic ring rescue, then requires the recurring candidate to reach the
expected frame edge and contain enough bright low-saturation pixels. Those
extra gates were added after clean Luma and PixVerse scene details passed shape
and temporal recurrence alone. The generic
[WatermarkRemover-AI](https://github.com/D-Ogi/WatermarkRemover-AI) project
instead uses Florence-2 to identify arbitrary watermarks before LaMa
inpainting. That is broader, but it carries a much heavier model and a less
auditable detection boundary than the provider-specific synthetic path here.
**The faint-visible-mark precision/recall wall is fundamental, not a heuristic artifact.** The visible-watermark-detection literature has moved to LEARNED segmentation / object-detection (WDNet WACV'21 arXiv:2012.07616; SLBR ACM MM'21, open code+weights; the PRCV'18 large-scale detector; Su et al. survey 2025), but three verified findings bound what a learned detector actually buys: (1) a claim that a confidence threshold "cleanly separates" true from false matches even with a learned CNN front-end was **REFUTED** in verification (arXiv:1705.08593) -- the precision/recall wall persists even with learned features. (2) Learned detectors need a LARGE, pattern-diverse labeled dataset trained on synthetic composites (PRCV'18: 60k images / 80 watermark classes; CLWD: 60k / 160 marks), and off-distribution degradation is a documented real axis (models trained on limited-pattern LVW transfer worse; diversity of training patterns drives generalization). (3) Inference is cheap (WDNet ~8 ms at 256x256) -- the cost is the data pipeline, not runtime. Net: a learned detector shifts the frontier but does NOT remove the wall; for a SINGLE mark the cheapest next step is a small patch classifier (real-sparkle vs false-positive) on top of the existing NCC localizer, not a full segmentation model. SLBR is a ready baseline. The current NCC + false-positive gate (core-ring brightness margin + gradient-NCC crispness + white-core saturation) is a sound operating point, and the residual miss is the information-theoretic wall the literature confirms.
**Visible-mark landscape beyond the registry.** Meta stamps a visible "Imagined with AI" mark (bottom-LEFT, a small symbol) on its OWN Meta AI / "Imagine" output; for third-party images it relies on C2PA / IPTC, not a visible mark. Samsung Galaxy AI additionally uses a **four-star icon** variant in a corner alongside the localized text wordmark `samsung_engine` calibrates (only the Italian text variant is covered) -- the icon is a distinct, uncovered variant. Every source agrees visible + metadata marks are trivially removable (crop / screenshot, ~2 s), which is the tool's premise.