mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-09-03 19:10:41 +02:00
Record pipeline-lattice reframing and registered-v3 phase lock
Document that the pixel route detects an origin-anchored lattice rather than the watermark, and record the registered-v3 two-pixel phase-lock measurement: a 2px diagonal crop killed all 36 tested detections (28 foreign-generator, 8 Google) with recovery only at offsets that are multiples of four. Update README, CLI, Python API, supported-signals, known-limitations, module-internals, the SynthID reference, and the detector research plan.
This commit is contained in:
@@ -30,7 +30,7 @@ removal.
|
||||
| Goal | Command | GPU |
|
||||
| --- | --- | --- |
|
||||
| Find provenance signals and watermarks | `identify` | No |
|
||||
| Detect the SynthID pixel carrier in the calibrated image-size range | `detect-synthid` | No |
|
||||
| Detect a generation-pipeline pixel lattice, experimental, not the watermark | `detect-synthid` | No |
|
||||
| Verify supported OpenAI SynthID from pixels with the official remote API | `verify-openai-synthid` | No |
|
||||
| Remove known visible AI marks | `visible` | No |
|
||||
| Erase a region you select | `erase` | No |
|
||||
@@ -78,31 +78,54 @@ Inspect an image:
|
||||
remove-ai-watermarks identify image.png
|
||||
```
|
||||
|
||||
Detect the supported SynthID pixel carrier after installing the pixel runtime:
|
||||
Signed provenance is the supported route for SynthID and `identify` reads it.
|
||||
The pixel route below is experimental and answers a different question. Install
|
||||
the pixel runtime first:
|
||||
|
||||
```bash
|
||||
uv tool install --force "remove-ai-watermarks[pixels]"
|
||||
remove-ai-watermarks detect-synthid image.png
|
||||
remove-ai-watermarks detect-synthid resized.png --register-scale
|
||||
remove-ai-watermarks detect-synthid native-period.png --fixed-period
|
||||
```
|
||||
|
||||
This detector is positive-only and limited to one measured carrier family in
|
||||
This is experimental and does not detect the SynthID watermark. The statistic it reports is
|
||||
destroyed by cropping off the tile grid — a seven-pixel crop on the large branch
|
||||
and a two-pixel crop on the ordinary-size registered-v3 route — while SynthID's
|
||||
published evaluation keeps 99.97% of its detection rate under aggressive crop
|
||||
and resize, so a positive identifies the generation pipeline rather than the
|
||||
mark. Measured on 2026-08-16: it accepted 8 of 11 fresh large Google generations
|
||||
and 29 of 223 images from other generators, and 0 of 6 when the same positives
|
||||
were cropped off the tile grid; a two-pixel crop also removed all 36 tested
|
||||
registered-v3 detections across foreign-generator and Google images. It is
|
||||
positive-only
|
||||
and limited to one measured lattice family in
|
||||
the [calibrated image-size range](docs/synthid.md#32-how-our-tool-detects-the-supported-carrier).
|
||||
The native default uses the fixed fold through 10 megapixels and a separately
|
||||
challenged opponent-color large-image branch above 10 through 18 megapixels;
|
||||
the large branch requires both sides to be at least 2,048 pixels. Both expect
|
||||
the recovered carrier at its measured 16-pixel sampling scale.
|
||||
`--register-scale` opts into a much slower bounded scale search
|
||||
for resized images from 250,000 through 10,000,000 decoded pixels, with both
|
||||
sides at least 64 pixels. Its measured positive scale range is approximately
|
||||
0.65 through 1.5; 0.5x resizes remain outside reliable detection. `identify`
|
||||
keeps the fast default. `not_detected` or `unsupported` is not a clean-image
|
||||
guarantee.
|
||||
The production default uses registered-v3 from 250,000 through 10,000,000
|
||||
decoded pixels with both sides at least 256 pixels. When that route abstains,
|
||||
a narrower opponent-registered-v1 fallback covers 1 through 10 megapixels,
|
||||
both sides at least 768 pixels, and carrier periods 7.9 through 12.0. Period-8
|
||||
candidates additionally require a decoded-pixel block-edge veto that separates
|
||||
the measured carrier from ordinary JPEG lattices. The
|
||||
separately challenged opponent-color large-v1 branch runs
|
||||
above 10 through 18 megapixels; large-v1 requires both sides to be at least
|
||||
2,048 pixels. Registered-v3 is a bounded scale search with a measured positive
|
||||
scale range of approximately 0.65 through 1.5. The fallback recovered 49/49
|
||||
losslessly resized views at scales 0.5 through 0.75 from seven official
|
||||
pixel-positive parents. The period-8 veto rejected all 1,790 previously crossing
|
||||
codec-lattice controls, while 350 matched 0.5x Picsum controls had no base
|
||||
crossing; the earlier period-band rule also accepted 0/1,000 frozen controls.
|
||||
`--fixed-period` explicitly selects the faster legacy 16-pixel diagnostic; it
|
||||
is not a production positive route. `--register-scale` forces the registered
|
||||
expert, including on geometry where the default would choose large-v1.
|
||||
`identify` uses the production router. `indeterminate` means no qualified local
|
||||
expert crossed its threshold; `unsupported` means no expert covers the input.
|
||||
Neither is a clean-image guarantee.
|
||||
|
||||
The large native branch is not recompression-robust: all seven official large
|
||||
positives fell below its frozen threshold after same-size JPEG-95 and JPEG-90
|
||||
re-encoding. Use it for original or losslessly copied pixels, and treat a miss
|
||||
after lossy transcoding as inconclusive.
|
||||
The large native and opponent-registered branches are not recompression-robust.
|
||||
The large branch lost all seven official positives after same-size JPEG-95 and
|
||||
JPEG-90; the fallback retained 0/63 JPEG-95, JPEG-85, and WebP-95 views. Use
|
||||
them for original or losslessly copied pixels, and treat a miss after lossy
|
||||
transcoding as inconclusive.
|
||||
|
||||
For supported OpenAI images, the optional official verifier provides a broader
|
||||
pixel-watermark verdict than the incomplete local OpenAI research signal:
|
||||
|
||||
+53
-21
@@ -57,36 +57,58 @@ remove-ai-watermarks identify image.png --no-visible
|
||||
```
|
||||
|
||||
Despite the historical option name, `--no-visible` skips all pixel detectors,
|
||||
including the supported SynthID carrier, visible marks, open DWT-DCT, and
|
||||
including the pipeline lattice described below, visible marks, open DWT-DCT, and
|
||||
TrustMark. Metadata inspection still runs.
|
||||
|
||||
## Detect the supported SynthID pixel carrier
|
||||
## Detect the generation-pipeline pixel lattice (experimental)
|
||||
|
||||
```bash
|
||||
remove-ai-watermarks detect-synthid image.png
|
||||
remove-ai-watermarks detect-synthid image.png --json
|
||||
remove-ai-watermarks detect-synthid resized.png --register-scale
|
||||
remove-ai-watermarks detect-synthid native-period.png --fixed-period
|
||||
```
|
||||
|
||||
The command returns one of `detected`, `not_detected`, or `unsupported`. The
|
||||
runtime detector covers one frozen periodic carrier family in the
|
||||
This route is experimental. Signed provenance, read by `identify` and confirmed
|
||||
against the provider by `verify-openai-synthid`, remains the supported way to
|
||||
establish SynthID. The command returns one of `detected`, `indeterminate`, or
|
||||
`unsupported`, and it does not detect the SynthID watermark: its statistic disappears when the image
|
||||
is cropped off the tile grid, and it changes when the generator's pipeline
|
||||
changes, so read a positive as evidence about the pipeline and never as a
|
||||
watermark claim. The JSON carries `identifies_watermark` and
|
||||
`tile_aligned_crop_required` for exactly this reason. The
|
||||
runtime detector covers one frozen periodic lattice family in the
|
||||
[calibrated image-size range](synthid.md#32-how-our-tool-detects-the-supported-carrier)
|
||||
and needs the `pixels` extra. The native default uses the fixed fold from
|
||||
1,000,000 through 10,000,000 decoded pixels and the separately challenged
|
||||
opponent-color large branch above 10,000,000 through 18,000,000 pixels when
|
||||
both sides are at least 2,048 pixels. It never resizes the input and does not
|
||||
register a carrier whose sampling period changed through spatial resampling.
|
||||
`--register-scale` enables a substantially slower bounded search over measured
|
||||
carrier periods for images from 250,000 through 10,000,000 decoded pixels, with
|
||||
both sides at least 64 pixels. It is opt-in and is not used by `identify`.
|
||||
The measured positive scale range is approximately 0.65 through 1.5; 0.5x
|
||||
resizes are not reliably detected.
|
||||
The native large branch is also codec-sensitive: same-size JPEG-95 and JPEG-90
|
||||
re-encoding reduced its seven official large positives from 7/7 to 0/7. A miss
|
||||
on a lossy re-encode is therefore inconclusive.
|
||||
It is positive-only: `not_detected` means the score stayed below this detector's
|
||||
threshold, while `unsupported` means the image geometry is outside its scope.
|
||||
Neither result proves that another SynthID epoch or payload is absent.
|
||||
and needs the `pixels` extra. The production default uses registered-v3 from
|
||||
250,000 through 10,000,000 decoded pixels with both sides at least 256 pixels.
|
||||
An opponent-registered-v1 fallback covers 1 through 10 megapixels, both sides
|
||||
at least 768 pixels, and selected carrier periods 7.9 through 12.0. Period-8
|
||||
candidates must also pass an opponent-color block-edge veto for the native JPEG
|
||||
lattice. The
|
||||
separately challenged opponent-color large-v1 branch covers above 10,000,000
|
||||
through 18,000,000 pixels when both sides are at least 2,048 pixels.
|
||||
Registered-v3 performs a bounded carrier-period search and independent split-
|
||||
patch confirmation. Its measured positive
|
||||
scale range is approximately 0.65 through 1.5. The fallback recovered 49/49
|
||||
lossless 0.5x-0.75x views from seven official positives. Its period-8 veto
|
||||
rejected 1,790 codec-lattice crossings, and 350 matched 0.5x controls produced no
|
||||
base crossing. The earlier period-band rule accepted 0/1,000 post-freeze Picsum
|
||||
controls. `identify` uses this production router.
|
||||
|
||||
`--fixed-period` explicitly selects the faster legacy fixed-v2 diagnostic below
|
||||
10 megapixels. It does not resize or register the carrier and is not a
|
||||
production positive route. `--register-scale` forces the registered-v3 cascade,
|
||||
including its opponent fallback, on geometry where the default would select
|
||||
large-v1.
|
||||
The native large and opponent-registered branches are codec-sensitive. The
|
||||
large branch fell from 7/7 to 0/7 after same-size JPEG-95 or JPEG-90; the
|
||||
fallback retained 0/63 JPEG-95, JPEG-85, and WebP-95 views. A miss on a lossy
|
||||
re-encode is therefore inconclusive.
|
||||
It is positive-only: `indeterminate` means the score stayed below this
|
||||
detector's threshold, while `unsupported` means the image geometry is outside
|
||||
its scope. Neither result proves that another SynthID epoch or payload is
|
||||
absent.
|
||||
JSON output includes the exact reason plus provider scope, backend, pixel
|
||||
preservation, and metadata-use audit fields.
|
||||
|
||||
## Verify OpenAI SynthID from pixels
|
||||
|
||||
@@ -110,6 +132,16 @@ Retention, `--acknowledge-upload` is mandatory. This command is never called by
|
||||
`identify`. `not_detected` means only that OpenAI's verifier did not recognize a
|
||||
supported watermark in this file; it is not proof of human authorship.
|
||||
|
||||
The built-in client bounds the request at 120 seconds and disables automatic
|
||||
SDK retries, so one acknowledgement cannot silently upload the media multiple
|
||||
times. A timeout, disconnect, malformed response, access failure, or rate limit
|
||||
is an error, never a negative watermark verdict. API failures expose status,
|
||||
error code, request id, `Retry-After`, and whether an explicit caller-controlled
|
||||
retry is appropriate through `OpenAIProvenanceError`; the verifier itself never
|
||||
retries an upload.
|
||||
The JSON result uses the same provider-scope, backend, pixel-preservation, and
|
||||
metadata-use audit fields as the local detector.
|
||||
|
||||
The Python API enforces the same boundary with the required explicit intent
|
||||
flag `verify_openai_synthid(path, acknowledge_upload=True)`.
|
||||
|
||||
|
||||
+65
-15
@@ -64,22 +64,72 @@ identity or exact texture, and each then runs the same face stage.
|
||||
`qwen-zimage` is the higher fidelity of the two. Both are large, slow, and may
|
||||
still alter small text or difficult faces.
|
||||
|
||||
### Local SynthID detection covers one image-carrier family
|
||||
### The experimental pixel route reads a pipeline lattice, not the watermark
|
||||
|
||||
Google does not publish the proprietary SynthID payload decoder. The project
|
||||
ships a positive-only detector for one measured periodic image carrier in a
|
||||
calibrated image-size range, plus provenance-based recognition
|
||||
from Google AI C2PA or current OpenAI C2PA with an explicit watermark action.
|
||||
It does not cover images outside the selected mode's size range, crop, strong
|
||||
JPEG compression, video, or future carrier epochs. Arbitrary dimensions inside
|
||||
the default range are supported only while the recovered carrier retains its
|
||||
measured 16-pixel sampling lattice. A spatial resize changes that period. The
|
||||
opt-in `detect-synthid --register-scale` mode searches a bounded
|
||||
fractional-period range for images from 250,000 through 10,000,000 pixels, but
|
||||
requires both sides to be at least 64 pixels. Its measured positive range is
|
||||
approximately scale 0.65 through 1.5; a 0.5 resize aliases the carrier with
|
||||
ordinary resize lattices and is not reliably detected. It is also slower. A
|
||||
`not_detected` or `unsupported` result is not a negative universal verdict, and
|
||||
Signed provenance is the primary and supported route for SynthID in this
|
||||
project; the pixel route described here is experimental. Google does not
|
||||
publish the proprietary SynthID payload decoder, and nothing
|
||||
in this project reproduces one. What the local pixel route detects is a
|
||||
periodic lattice anchored at the image origin: it is destroyed by a crop of
|
||||
seven pixels on the large branch and by a crop of two pixels on the
|
||||
ordinary-size registered-v3 route, whereas the published SynthID evaluation
|
||||
retains 99.97% of its detection rate under aggressive crop and resize. A
|
||||
positive therefore
|
||||
identifies the generation pipeline that leaves the lattice, and says nothing
|
||||
about whether the image carries a watermark. It also changes when a provider
|
||||
changes pipeline: output from `gemini-2.5-flash-image` scores at the level of
|
||||
unrelated photographs. The project additionally offers provenance-based
|
||||
recognition from Google AI C2PA or current OpenAI C2PA with an explicit
|
||||
watermark action, and that provenance route is the only one that speaks about
|
||||
a watermark at all.
|
||||
|
||||
The ordinary-size route shares the same phase lock. On 2026-08-16 a two-pixel
|
||||
diagonal crop removed all 28 in-geometry foreign-generator detections and all 8
|
||||
detected Google provenance positives from registered-v3, with the statistic
|
||||
recovering only at crop offsets that are multiples of four. The route therefore
|
||||
fires on other vendors' images because those pipelines leave a matching
|
||||
origin-anchored lattice, not because a watermark is present.
|
||||
|
||||
The published control rates for this route were all measured on photographs.
|
||||
Against other generators' output, which is the population a provenance tool is
|
||||
actually pointed at, the shipped runtime accepted 29 of 223 signed non-Google
|
||||
images on 2026-08-16: 13.0% overall, 24.1% on Adobe Firefly, with a top score
|
||||
of 3.01 against a threshold of 1.0. Treat a positive as evidence only when the
|
||||
alternative generators have been ruled out by other means.
|
||||
|
||||
Sensitivity has only ever been reported on images the route was calibrated
|
||||
against. The first out-of-distribution measurement was taken on 2026-08-16: 11
|
||||
images generated at 5632x3072 by `gemini-3.1-flash-image`, a model that did not
|
||||
exist when the large branch was calibrated, written straight from the API with
|
||||
no lossy-codec history and watermarked by Google's documented policy that all
|
||||
generated images carry SynthID. Eight were detected and three returned
|
||||
indeterminate, a rate of 72.7% with a one-sided 95% lower bound of 43.6%. The
|
||||
sample is small and one provider wide, so read it as an order of magnitude
|
||||
rather than an operating point.
|
||||
|
||||
Cropping the same images to 4096x2560 on the tile grid retained 4 of 6. Taking
|
||||
the identical crop seven pixels off the grid retained 0 of 6, with every score
|
||||
negative. A crop that is not tile-aligned removes the signal entirely, which is
|
||||
the sharpest way to see that this route does not read the watermark: the
|
||||
published SynthID evaluation keeps 99.97% of its detection rate under
|
||||
aggressive crop and resize.
|
||||
|
||||
The route's declared scope is narrower still. It does not cover images outside
|
||||
the selected mode's size range, crop, strong
|
||||
JPEG compression, video, or future carrier epochs. The production default
|
||||
searches a bounded fractional-period range from 250,000 through 10,000,000
|
||||
pixels and requires both sides to be at least 256 pixels. Its measured positive
|
||||
range is approximately scale 0.65 through 1.5. A precision-first opponent-color
|
||||
fallback covers only 1 through 10 megapixels, sides of at least 768 pixels, and
|
||||
periods 7.9 through 12.0; it recovered the measured lossless 0.5x-0.75x cohort.
|
||||
At period 8, an additional block-edge veto rejects the ordinary JPEG lattice;
|
||||
this does not make arbitrary crops or codecs supported. The fallback retained
|
||||
0/63 lossy JPEG/WebP views, so a lossy miss remains inconclusive. Above 10
|
||||
through 18
|
||||
megapixels, the separate large-v1 route requires native, lossless pixels and
|
||||
both sides at least 2,048 pixels. The fixed-v2 branch is retained only behind
|
||||
the explicit `detect-synthid --fixed-period` diagnostic. A
|
||||
`indeterminate` or `unsupported` result is not a negative universal verdict, and
|
||||
removal still requires the matching provider oracle for confirmation.
|
||||
|
||||
For important outputs:
|
||||
|
||||
+153
-17
@@ -451,7 +451,7 @@ metadata extraction from verdict logic:
|
||||
both extractors reach the same answer. It did not, and the record path silently
|
||||
reported no SynthID for images the file path flagged.
|
||||
- `identify` preserves the path-based API and adds the optional registered
|
||||
visible-mark, open invisible-watermark, and supported SynthID carrier
|
||||
visible-mark, open invisible-watermark, and generation-pipeline lattice
|
||||
detectors after extraction.
|
||||
|
||||
### SynthID periodic carrier detector
|
||||
@@ -468,17 +468,29 @@ ceiling does not require multiple three-channel float workspaces. The model hash
|
||||
is pinned by a test, and the unchanged operating threshold is
|
||||
`0.17357069773071196` through 10 megapixels.
|
||||
|
||||
The direct API returns `detected`, `not_detected`, or `unsupported`; the last is
|
||||
distinct because no resize is performed. The fixed branch is selected from
|
||||
1,000,000 through 10,000,000 decoded pixels. The frozen threshold accepted
|
||||
That fixed threshold is not production-qualified. A later source-fresh
|
||||
Open Images test-split challenge produced 5 crossings among the 211 images in
|
||||
its supported geometry. A precision-first `0.28` replacement is frozen as a
|
||||
research candidate after retaining all 12 available source-diverse native
|
||||
positives and rejecting those five crossings. It then failed the second
|
||||
untouched holdout at 1/213, with score `0.322542963`. The replacement is
|
||||
rejected and fixed-v2 remains an explicit diagnostic only.
|
||||
|
||||
The direct API returns `detected`, `indeterminate`, or `unsupported`. Passing
|
||||
`register_scale=False` selects fixed-v2 from 1,000,000 through 10,000,000
|
||||
decoded pixels as an explicit diagnostic. Its frozen threshold accepted
|
||||
none of 5,000 public COCO views balanced across every observed target geometry,
|
||||
and none of a separate 5,000-view challenge over 256 generated geometries
|
||||
covering every pair of modulo-16 edge remainders. The original 2048x2048
|
||||
verdicts and exact scores remain unchanged. Runtime matches do not attribute a
|
||||
provider. `identify` adds only positive matches as high-confidence
|
||||
evidence and never turns a local negative into a clean verdict.
|
||||
The result envelope also names the signal family, provider scope, backend,
|
||||
whether metadata contributed to the verdict, whether pixels were preserved,
|
||||
and an explicit reason for unsupported or indeterminate results. These fields
|
||||
are shared with the official OpenAI verifier's JSON boundary.
|
||||
|
||||
The native default selects `synthid-periodic-tile-large-v1` above 10 through 18
|
||||
The production router selects `synthid-periodic-tile-large-v1` above 10 through 18
|
||||
megapixels when both dimensions are at least 2,048 pixels. It evaluates all
|
||||
phase-aligned 2,048-square windows and combines the minimum fixed-template,
|
||||
Red-minus-Green, and Blue-minus-Yellow spatial correlations with the most
|
||||
@@ -532,7 +544,67 @@ final controls. Multiplying the canonical score by spectral-period confidence
|
||||
also accepted two. That baseline was rejected rather than shipped at its
|
||||
discovery threshold.
|
||||
|
||||
The opt-in `register_scale=True` path is the later calibrated successor. It
|
||||
The research-only
|
||||
[`synthid_affine_lattice_probe.py`](../scripts/synthid_affine_lattice_probe.py)
|
||||
adds split-confirm synchronization. It estimates complex harmonic coherence on
|
||||
one checkerboard of patches, confirms the selected period on the other, and
|
||||
reports amplitude-aware confirmation, a locally content-whitened multichannel
|
||||
code match, and phase-preserving and cyclically registered template scores. A
|
||||
0.1-pixel grid recovered the expected
|
||||
12.8, 14.4, 17.6, and 19.2 periods in 20/20 transformed views from five official
|
||||
positive parents. A provisional conjunction accepted none of 800 corresponding
|
||||
views from 200 oracle-negative parents, none of 469 OpenAI-labeled rows, and
|
||||
three of 276 broad non-Google rows whose TC260 or Samsung provenance prevents
|
||||
treating them as clean oracle negatives. This probe is not runtime routing:
|
||||
positive diversity is still inadequate, period 8 remains rejected, and the
|
||||
amplitude stage currently handles isotropic scale at zero rotation only. Full
|
||||
protocol and caveats are in the detector research plan.
|
||||
|
||||
The whitened match uses neighboring noncarrier bins to estimate complex
|
||||
Green/opponent-color covariance around every selected harmonic. It corrected a
|
||||
period-24 alias on one native official positive, giving the broad native search
|
||||
the correct period 16 on all five ordinary-size positives. It was retained only
|
||||
as a candidate reranker: its positive-to-negative margin was smaller than the
|
||||
existing spatial-template margin in both the native pilot and a locked 0.8
|
||||
resize challenge. Two of the three TC260/Samsung-provenance challenge crossings
|
||||
also matched the full whitened code more strongly than the weakest official
|
||||
large positive, so the pixel result identifies a compatible signal family, not
|
||||
a provider.
|
||||
|
||||
The same probe also exposes a payload-agnostic H5 confirmation. It estimates a
|
||||
complex harmonic vector independently on the two checkerboards and measures
|
||||
their fixed relative-phase inner product; the codeword need not match the known
|
||||
template. Free cyclic-shift selection was rejected because natural phase aliases
|
||||
overlapped the controls. The fixed all-harmonic statistic separated five native
|
||||
official positives from 130 controls with margin `0.1619`, and five 0.8-resized
|
||||
views from 200 fresh-parent controls with margin `0.1541`; all seven large
|
||||
official positives also passed the observed gap. It remains research-only
|
||||
because these tests supplied the period, reuse one negative source family, and
|
||||
contain only 12 independent positive parents.
|
||||
|
||||
The separate research-only
|
||||
[`synthid_cyclostationary_probe.py`](../scripts/synthid_cyclostationary_probe.py)
|
||||
measures full complex cross-channel spectral correlation at carrier shifts
|
||||
against neighboring-shift same-image nulls. It recognizes synthetic amplitude
|
||||
modulation and rejects both independent noise and a purely additive carrier.
|
||||
The real native pilot overlapped completely: five official-positive joint
|
||||
contrasts ranged from `-0.0027` to `0.0337`, while 130 controls reached `0.0593`.
|
||||
The cyclostationary branch is rejected rather than tuned on new frequency bands.
|
||||
|
||||
The affine-lattice research CLI also supports symmetric in-memory JPEG and
|
||||
top-left crop challenges. At fixed native period 16, codec-specific
|
||||
phase-preserving template thresholds retained all five repeated positive views
|
||||
and accepted none of 80 locked controls at JPEG qualities 95, 90, and 80. The
|
||||
locked margins declined from `0.1054` to `0.0257`; whitened and unknown-codeword
|
||||
scores were rejected for lossy JPEG. A separate registered crop conjunction
|
||||
retained all five views and accepted none of 80 controls after both 5% and 10%
|
||||
top-left crops, with registered-template margins above `0.22`. These are
|
||||
research pilots, not runtime experts: the period was supplied, positive parents
|
||||
were reused, and only one codec implementation and one negative family were
|
||||
tested.
|
||||
|
||||
The `register_scale=True` path is the calibrated ordinary-size production
|
||||
expert. It
|
||||
keeps the harmonic search, adds three-level scale-space consistency and quadrant
|
||||
agreement. Its calibrated decision requires the canonical winner to be the
|
||||
strongest spectral-period candidate, its three-way amplitude to cross the
|
||||
@@ -556,11 +628,51 @@ accepted seven, so fixed and registered results cannot safely be unioned. In
|
||||
overlapping geometry the registered decision remains the validated path;
|
||||
fixed-only evidence is a diagnostic rather than a universal-cascade positive.
|
||||
|
||||
The research-only router in `scripts/synthid_routed_expert_bank.py` encodes that
|
||||
precedence and always abstains on fixed-only evidence. Its three-observation
|
||||
schema keeps the fixed, registered, and large identities explicit. Registered
|
||||
and large crossings are positive routes only in their disjoint calibrated
|
||||
ranges; the bank never returns a clean-image verdict.
|
||||
A later source-fresh 3,000-image Open Images test-split challenge superseded the
|
||||
registered-v2 precision claim: it accepted 4 of 2,997 supported controls. The
|
||||
registered-v3 confirmation uses disjoint patch groups for phase, cyclic
|
||||
amplitude, and held-out-codeword evidence. It rejected all four crossings,
|
||||
retained 147 of 148 base-accepted source-diverse positives, and retained all 359
|
||||
base-accepted views in a dense 0.65-1.50 transform matrix over 12 independent
|
||||
parents. Frozen unchanged, it then accepted 0/2,996 controls from a second
|
||||
nonoverlapping Open Images cohort and 0/2,366 supported controls from a
|
||||
3,000-image COCO second-family challenge. Registered-v3 is now the default
|
||||
ordinary-size positive route. The exact gates, acquisition hashes, and rejected
|
||||
weak-signal rescue are recorded in the detector research plan.
|
||||
|
||||
Every one of those control rates is photographic. Against 223 corpus images
|
||||
whose C2PA names a non-Google generator, the unchanged entry point accepted 29
|
||||
(`0.130`, Adobe Firefly `0.241`, highest foreign score `3.01`), all from
|
||||
registered-v3. The branch reads a lattice shared across generation pipelines,
|
||||
which is why `identify` reports it as the experimental `pipeline_lattice`
|
||||
signal and never as a watermark.
|
||||
|
||||
The branch is also phase-locked to the image origin, exactly like the large
|
||||
expert. A two-pixel diagonal crop killed all 28 in-geometry foreign detections
|
||||
and all 8 detected Google provenance positives (maximum remaining scores
|
||||
`0.779` and `0.311` against the `1.0` threshold); the signal recovers only at
|
||||
offsets that are multiples of four. Registered-v3 therefore detects the same
|
||||
crop-destroyed generation-pipeline lattice as `large-v1`, on ordinary sizes.
|
||||
|
||||
When registered-v3 abstains, opponent-registered-v1 searches the same frozen
|
||||
template in Red-minus-Green and Blue-minus-Yellow space. It reranks three
|
||||
separated scale candidates with fixed RGB and two spatial opponent-color gates,
|
||||
then accepts only periods 7.9-12.0 on 1-10 megapixel rasters whose sides are at
|
||||
least 768 pixels. Period-8 candidates additionally require Red-Green and
|
||||
Blue-Yellow 8-pixel edge ratios no greater than `1.05`; this vetoes the
|
||||
deterministic JPEG block lattice without using container metadata. The final
|
||||
rule recovered 49/49 lossless 0.5x-0.75x views from seven official-positive
|
||||
parents. It rejected all 1,790 measured period-8 codec crossings, while 350
|
||||
identically resized controls had no base crossing and the earlier period-band
|
||||
rule accepted 0/1,000 post-freeze Picsum controls. Period 12.8 remains excluded,
|
||||
and lossy JPEG/WebP views remain inconclusive.
|
||||
|
||||
The runtime precedence is registered-v3, the bounded opponent fallback, then
|
||||
large-v1 above 10 megapixels. Passing
|
||||
`register_scale=False`, or the CLI's `--fixed-period`, selects the legacy fixed
|
||||
diagnostic explicitly. The research bank in
|
||||
`scripts/synthid_routed_expert_bank.py` keeps all three observations for audits.
|
||||
Neither runtime nor research routing returns a clean-image verdict.
|
||||
|
||||
An unchanged registered challenge from 10 to 18 megapixels retained only 1 of
|
||||
37 Google candidates and zero of 89 non-Google controls. Twenty-eight positives
|
||||
@@ -635,6 +747,17 @@ period-8 model retained 5 of 158 and zero controls, then 1 of 40 fresh OpenAI
|
||||
rows and 1 of 50 fresh controls. They neither explain the known oracle-positive
|
||||
miss nor justify runtime routing.
|
||||
|
||||
An image-level patch-bag model over nine normalized high-pass patches was the
|
||||
strongest later local OpenAI classifier. It accepted 324/1,364 development-test
|
||||
OpenAI rows with 4/3,373 heterogeneous controls, then transferred to 120/486
|
||||
OpenAI rows, 8/504 general controls, and 5/33 same-provider non-assertion rows
|
||||
in the later temporal challenge. Assertion enrichment over the same-provider
|
||||
stratum was not independently significant (`p = 0.151`, one-sided exact test).
|
||||
JPEG-95 reduced the unchanged conjunction to
|
||||
1/486 positives and zero controls. The native temporal hits were complementary
|
||||
to the signed period-8 hits, but the same-provider rate and codec collapse
|
||||
identify another export noiseprint rather than a runtime SynthID expert.
|
||||
|
||||
The separate OpenAI period-8 DTCWT component is persistent rather than tied to
|
||||
one short rollout: exact-generator asserted hits were 3/16, 46/365, and 26/200
|
||||
from May through July, with 75/581 overall versus 1/52 same-generator rows
|
||||
@@ -655,12 +778,12 @@ development positives and finished at 0/50 positives, 0/199 controls, and AUC
|
||||
0.374. Neither branch is part of runtime routing; full split and oracle details
|
||||
are in the detector research plan.
|
||||
|
||||
The separately measured geometry range remains 250,000 through 10,000,000
|
||||
decoded pixels with both sides at least 64 pixels. The default path and
|
||||
`identify` remain native-only and select either the fixed or large branch by
|
||||
geometry; scale registration stays opt-in. A 20-image real-corpus drift check was
|
||||
byte-identical after integration. The calibration history and caveats are in the
|
||||
linked detector research plan.
|
||||
The separately measured registered geometry range remains 250,000 through
|
||||
10,000,000 decoded pixels with both sides at least 256 pixels. The default path
|
||||
and `identify` use registered-v3, then the narrower opponent-registered-v1
|
||||
fallback in its 1-10 megapixel domain, and large-v1 above 10 megapixels. A
|
||||
20-image real-corpus drift check was byte-identical after the earlier v2 integration.
|
||||
The calibration history and caveats are in the linked detector research plan.
|
||||
|
||||
### Official OpenAI SynthID verifier
|
||||
|
||||
@@ -681,6 +804,19 @@ multipart file to `content_provenance_checks.create` and parses exactly one
|
||||
used as fallback evidence. Missing, duplicate, or unknown SynthID outcomes are
|
||||
errors rather than negative detections.
|
||||
|
||||
The default SDK client has a 120-second request timeout and zero automatic
|
||||
retries. One upload acknowledgement therefore authorizes at most one media
|
||||
transmission rather than inheriting the SDK's retry default. Request logs keep
|
||||
the endpoint, temporary basename, media type, byte count, timeout, retry policy,
|
||||
duration, HTTP status, error code, and request id when available, but omit the
|
||||
source path, image bytes, credentials, and decoded-pixel fingerprint.
|
||||
|
||||
`OpenAIProvenanceError` preserves the status, API error code, request id,
|
||||
`Retry-After` value, and a transient-only `retryable` flag. The library does not
|
||||
automatically act on that flag: an explicit caller invocation is required for
|
||||
every additional upload. Transport and schema failures remain errors rather
|
||||
than becoming `not_detected` or a local detector result.
|
||||
|
||||
The result remains provider-scoped and positive-evidence-only. `not_detected`
|
||||
does not mean human-created, and the official endpoint's published prohibition
|
||||
on repeated reverse-engineering or evasion queries prevents using this backend
|
||||
|
||||
+40
-17
@@ -12,7 +12,7 @@ path that still runs on CPU and combines `video` and `diffusion`. Add `heif`
|
||||
independently when path-based pixel APIs must decode HEIC, HEIF, or AVIF. See
|
||||
the complete [feature-extra matrix](installation.md#feature-extras).
|
||||
|
||||
## Detect the supported SynthID carrier
|
||||
## Detect the generation-pipeline lattice (experimental)
|
||||
|
||||
Install `remove-ai-watermarks[pixels]`, then call the lazy top-level API:
|
||||
|
||||
@@ -20,26 +20,48 @@ Install `remove-ai-watermarks[pixels]`, then call the lazy top-level API:
|
||||
import remove_ai_watermarks as raiw
|
||||
|
||||
result = raiw.detect_synthid("input.png")
|
||||
print(result.status) # "detected" | "not_detected" | "unsupported"
|
||||
print(result.status) # "detected" | "indeterminate" | "unsupported"
|
||||
print(result.score) # float for a supported image size, otherwise None
|
||||
print(result.threshold) # frozen operating point
|
||||
print(result.reason) # support or non-detection reason, otherwise None
|
||||
|
||||
# Opt in when the image may have been spatially resized.
|
||||
registered = raiw.detect_synthid("resized.png", register_scale=True)
|
||||
# Force the legacy native-period diagnostic only when auditing that expert.
|
||||
fixed_diagnostic = raiw.detect_synthid("native-period.png", register_scale=False)
|
||||
```
|
||||
|
||||
The detector is positive-only and covers one measured periodic carrier family
|
||||
This is experimental and is not a watermark detector. Signed provenance through
|
||||
`identify` is the supported SynthID route. Its statistic is destroyed by a seven-pixel
|
||||
crop while the published SynthID evaluation survives aggressive crop and
|
||||
resize, so a positive identifies the generation pipeline. The result exposes
|
||||
`identifies_watermark` and `tile_aligned_crop_required` so a caller cannot
|
||||
reach the wrong conclusion from `status` alone. It is positive-only and covers
|
||||
one measured periodic lattice family
|
||||
in the [calibrated image-size range](synthid.md#32-how-our-tool-detects-the-supported-carrier).
|
||||
Arbitrary dimensions are accepted inside the default range, but spatial
|
||||
resampling can change the carrier period. `register_scale=True` enables the
|
||||
slower scale-registered operating point over 250,000 through 10,000,000 decoded
|
||||
pixels, with both sides at least 64 pixels; the default remains the native-period
|
||||
detector used by `identify`. Its score is a normalized multi-gate statistic with
|
||||
a threshold of `1.0`, not the native detector's raw template correlation. Scale
|
||||
0.5 is outside its reliable positive range.
|
||||
`not_detected` means only that the selected model did not find its carrier;
|
||||
`unsupported` is kept separate from a negative result. Neither is proof that
|
||||
the image contains no SynthID watermark.
|
||||
The default and `identify` use registered-v3 over 250,000 through 10,000,000
|
||||
decoded pixels, with both sides at least 256 pixels. An opponent-registered-v1
|
||||
fallback covers 1 through 10 megapixels, sides of at least 768 pixels, and
|
||||
periods 7.9 through 12.0; period-8 candidates also require the opponent-color
|
||||
block-edge codec veto. Large-v1 covers 10 through 18 megapixels. Each score
|
||||
uses a threshold of `1.0`; registered-v3 also requires independent split-patch
|
||||
confirmation. The fallback is qualified for measured lossless 0.5x-0.75x
|
||||
views, not lossy retranscodes.
|
||||
`register_scale=True` forces the registered-v3 cascade, including its bounded
|
||||
opponent fallback, while `False` explicitly selects the legacy native-period
|
||||
fixed-v2 diagnostic below the large-image boundary.
|
||||
`indeterminate` means only that the selected local model did not find its
|
||||
carrier; `unsupported` means no local expert covers the input geometry. Neither
|
||||
is proof that the image contains no SynthID watermark.
|
||||
Both local and official OpenAI JSON results expose `signal_family`,
|
||||
`provider_scope`, `backend`, `metadata_used_for_verdict`, and
|
||||
`pixels_preserved`; the local result adds `identifies_watermark` and
|
||||
`tile_aligned_crop_required`, so callers do not need to infer the evidence boundary from
|
||||
the detector name.
|
||||
|
||||
Remote transport and response failures raise `OpenAIProvenanceError`. Its
|
||||
`status_code`, `error_code`, `request_id`, `retry_after`, and `retryable`
|
||||
attributes let a caller implement bounded backoff or a circuit breaker without
|
||||
turning an API outage into a false `not_detected` result. One function call still
|
||||
performs at most one upload.
|
||||
|
||||
## Remove visible marks
|
||||
|
||||
@@ -330,8 +352,9 @@ Timings and spatial artifacts are opt-in. Artifacts include image-identifying da
|
||||
such as a thumbnail and perceptual hash; aggregate feature families do not.
|
||||
|
||||
`identify_from_evidence` does not reopen the source file by default: it evaluates
|
||||
metadata only, and registered visible marks and pixel-backed invisible watermarks
|
||||
remain in the path-based `identify` call.
|
||||
metadata only, and the pixel-backed checks remain in the path-based `identify`
|
||||
call: registered visible marks, open invisible-watermark decoders, and the
|
||||
experimental generation-pipeline lattice signal.
|
||||
|
||||
Pass `image_path` together with `check_visible` or `check_invisible` to add those
|
||||
pixel detectors on top of the SAME evidence. That is how a caller asking one file
|
||||
|
||||
+38
-17
@@ -79,7 +79,9 @@ The inspection and stripping code handles signals in these groups:
|
||||
- xAI and Grok EXIF signature fields;
|
||||
- Samsung AI editing markers;
|
||||
- Hugging Face job metadata;
|
||||
- one positive-only SynthID periodic pixel carrier in a calibrated image-size range;
|
||||
- one positive-only generation-pipeline pixel lattice in a calibrated image-size
|
||||
range, experimental, which identifies the pipeline and not the SynthID
|
||||
watermark; signed provenance remains the supported SynthID route;
|
||||
- open Stable Diffusion style DWT-DCT watermarks with the `detect` extra;
|
||||
- Adobe TrustMark with the `trustmark` extra.
|
||||
|
||||
@@ -127,32 +129,51 @@ The `controlnet`, `sdxl`, `qwen` and `default` values were removed. A retired na
|
||||
is rejected at parse time rather than remapped onto a surviving profile.
|
||||
|
||||
Google does not publish the SynthID payload decoder. This project ships a
|
||||
positive-only detector for one measured periodic image-carrier family in a
|
||||
positive-only detector for one measured periodic image-lattice family in a
|
||||
calibrated image-size range, available through `detect-synthid`
|
||||
and the default pixel pass in `identify` when the `pixels` extra is installed.
|
||||
The unchanged fixed threshold accepted none of the public COCO views in both
|
||||
an observed-geometry challenge and a generated-geometry challenge covering all
|
||||
modulo-16 edge cases. Above 10 through 18 megapixels, the native default uses a
|
||||
That lattice is not the watermark. It is anchored at the image origin: a
|
||||
seven-pixel crop removes it from the large branch and a two-pixel crop removes
|
||||
it from registered-v3 (all 36 tested detections across foreign-generator and
|
||||
Google images), while the published SynthID evaluation survives aggressive crop
|
||||
and resize, so every control rate below describes a generation-pipeline
|
||||
signature and not watermark detection. Every rate quoted
|
||||
below was also measured on photographs; on 223 signed non-Google generator
|
||||
images the same runtime accepted 29, a rate of 13.0%. Sensitivity outside the
|
||||
calibration distribution was measured once, on 11 fresh 5632x3072 images from
|
||||
`gemini-3.1-flash-image`: 8 detected, 72.7%, one-sided 95% lower bound 43.6%.
|
||||
The same images cropped seven pixels off the tile grid returned 0 of 6.
|
||||
The default ordinary-size route uses registered-v3, including independent
|
||||
split-patch phase and codeword confirmation. It accepted none of 5,993
|
||||
supported controls across two nonoverlapping Open Images test cohorts and none
|
||||
of 2,366 supported controls in a second-family COCO challenge. A precision-first
|
||||
opponent-registered-v1 fallback covers 1 through 10 megapixels, sides of at
|
||||
least 768 pixels, and carrier periods 7.9 through 12.0. Period-8 candidates must
|
||||
also pass an opponent-color block-edge codec veto. It recovered 49/49 lossless
|
||||
0.5x-0.75x views from seven separate official positives. The veto rejected all
|
||||
1,790 measured period-8 codec crossings, 350 matched 0.5x controls had no base
|
||||
crossing, and the earlier period-band rule accepted 0/1,000 post-freeze
|
||||
controls. Above 10 through
|
||||
18 megapixels, the production router uses a
|
||||
separately challenged large branch over phase-aligned windows and opponent-color
|
||||
phase agreement; both sides must be at least 2,048 pixels. It retained all seven
|
||||
officially verified large Google pixel positives and accepted none of 2,637
|
||||
feature-unseen, decoded-pixel-unique natural controls. A smaller post-freeze
|
||||
Open Images acquisition also produced 0/41 detections. Arbitrary dimensions in
|
||||
the default calibrated ranges are accepted, but the input must retain the
|
||||
measured 16-pixel carrier scale. The large branch retained 0/7 official
|
||||
positives after either JPEG-95 or JPEG-90 re-encoding, so its native-size scope
|
||||
does not include lossy retranscodes. The
|
||||
opt-in `detect-synthid --register-scale` mode performs a slower bounded scale
|
||||
search over its separately measured 250,000-through-10,000,000-pixel range and
|
||||
requires both sides to be at least 64 pixels. Its measured positive scale range
|
||||
is approximately 0.65 through 1.5; 0.5x resizes are not reliably detected.
|
||||
`identify` does not enable that search. Neither detector attributes a provider
|
||||
locally.
|
||||
Open Images acquisition also produced 0/41 detections. Registered-v3 has a
|
||||
measured scale range of approximately 0.65 through 1.5; the narrower fallback
|
||||
adds the measured lossless 0.5x-0.75x range. The large branch retained 0/7
|
||||
official positives after either
|
||||
JPEG-95 or JPEG-90 re-encoding, and the opponent-registered fallback retained
|
||||
0/63 JPEG-95, JPEG-85, and WebP-95 views. Their scope does not include lossy
|
||||
retranscodes. `detect-synthid --fixed-period` exposes
|
||||
fixed-v2 only as a legacy diagnostic; its fresh-source false-positive rate
|
||||
disqualified it as a production positive route. No local production expert
|
||||
attributes a provider.
|
||||
|
||||
The tool also recognizes presence from supported provenance: Google AI C2PA
|
||||
under Google's all-media watermark policy, and current OpenAI C2PA carrying an
|
||||
explicit `c2pa.watermarked.*` action. Legacy OpenAI C2PA without that action
|
||||
does not assert SynthID. A pixel result of `not_detected` or `unsupported`
|
||||
does not assert SynthID. A local pixel result of `indeterminate` or `unsupported`
|
||||
remains inconclusive for other sizes, epochs, codecs, and payloads.
|
||||
|
||||
The optional `verify-openai-synthid` command is a separate official remote
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+207
-27
@@ -585,21 +585,32 @@ codecs, video, or future epochs. The evidence and gates are documented in
|
||||
|
||||
### 3.2 How our tool detects the supported carrier
|
||||
|
||||
`remove-ai-watermarks detect-synthid image.png` folds the image residual modulo
|
||||
16x16 and compares it with a frozen float64 template. It evaluates only native
|
||||
input, without resize. Exact-multiple dimensions retain the original folding
|
||||
path; non-divisible dimensions use count-correct modulo folding. The model and
|
||||
The heading is kept because README, `cli.md` and `python-api.md` link to this
|
||||
anchor, but the name is inherited and inaccurate. What the local experts read
|
||||
is a periodic lattice anchored at the image origin, measured on 2026-08-16 to
|
||||
vanish under a seven-pixel crop that the published SynthID evaluation survives
|
||||
at 99.97% TPR. Everything in this section describes that pipeline signature,
|
||||
not watermark recovery; the measurement is in the empirical log of
|
||||
[`synthid-detector-removal-plan.md`](synthid-detector-removal-plan.md).
|
||||
|
||||
`remove-ai-watermarks detect-synthid image.png --fixed-period` exposes the
|
||||
legacy fixed-v2 diagnostic. It folds the image residual modulo 16x16 and
|
||||
compares it with a frozen float64 template. It evaluates only native input,
|
||||
without resize. Exact-multiple dimensions retain the original folding path;
|
||||
non-divisible dimensions use count-correct modulo folding. The model and
|
||||
threshold remain frozen from the 2048x2048 experiment. Through 10 megapixels,
|
||||
the fixed threshold
|
||||
accepted none of 5,000 public COCO views balanced across every observed target
|
||||
geometry. A separate 5,000-view challenge used 256 generated geometries from one
|
||||
through 18 megapixels and covered every pair of width/height remainders modulo
|
||||
16; it also produced no accepted view. Fixed-branch support is therefore the
|
||||
challenged interval of 1,000,000 through 10,000,000 decoded pixels rather than
|
||||
an enumerated width-by-height registry. The original 2048x2048 scores remain
|
||||
exactly unchanged.
|
||||
16; it also produced no accepted view. Later fresh-source challenges invalidated
|
||||
that precision claim: fixed-v2 accepted 5/211 supported controls in one Open
|
||||
Images cohort, and a proposed `0.28` threshold still accepted 1/213 in the
|
||||
second. The branch is therefore diagnostic only and is never unioned into the
|
||||
production positive route. The original 2048x2048 scores remain exactly
|
||||
unchanged.
|
||||
|
||||
Above 10,000,000 through 18,000,000 decoded pixels, the native default selects
|
||||
Above 10,000,000 through 18,000,000 decoded pixels, the production default selects
|
||||
the separate `synthid-periodic-tile-large-v1` branch when both sides are at
|
||||
least 2,048 pixels. It scores every phase-aligned 2,048-square window without
|
||||
resizing. Every window must retain the combined template, Red-minus-Green, and
|
||||
@@ -646,15 +657,19 @@ less conservative per-period thresholds accepted five final controls. The
|
||||
runtime therefore detects arbitrary decoded dimensions only when the carrier
|
||||
retains its measured 16-pixel scale.
|
||||
|
||||
`remove-ai-watermarks detect-synthid image.png --register-scale` opts into the
|
||||
separately calibrated scale-registered detector. It samples the 30 strongest
|
||||
Through 10,000,000 decoded pixels, `remove-ai-watermarks detect-synthid
|
||||
image.png` selects the registered-v3 detector by default. `--register-scale`
|
||||
forces this branch even where the router would otherwise select large-v1. The
|
||||
registered expert samples the 30 strongest
|
||||
template harmonics over fractional periods, reconstructs the three strongest
|
||||
canonical candidates, and combines full-frame correlation with quadrant and
|
||||
three-level scale-space consistency. A positive result additionally requires
|
||||
the canonical winner to be the strongest spectral candidate, a calibrated
|
||||
threshold for its recovered period, and agreement in two independent
|
||||
high-frequency template bands. The reported score is the minimum normalized
|
||||
margin across these requirements; its threshold is `1.0`.
|
||||
high-frequency template bands. Registered-v3 then confirms base crossings on
|
||||
disjoint patch groups using origin-corrected harmonic phase, cyclic amplitude,
|
||||
and a held-out unknown-codeword statistic. The reported score preserves the
|
||||
base normalized margin only after confirmation; its threshold is `1.0`.
|
||||
|
||||
The final rule retained 229 of 355 source-disjoint transformed positives and
|
||||
accepted none of 499 previously untouched Open Images controls. It retained
|
||||
@@ -662,8 +677,9 @@ accepted none of 499 previously untouched Open Images controls. It retained
|
||||
A separate period-8 rescue was rejected because symmetric 0.5x control resizing
|
||||
reproduced the same lattice and fully overlapped the positive feature range.
|
||||
The measured input range is 250,000 through 10,000,000 decoded pixels.
|
||||
Both dimensions must also be at least 64 pixels so every canonical quadrant can
|
||||
retain one 16-by-16 carrier tile.
|
||||
Both dimensions must also be at least 256 pixels because the frozen positive
|
||||
confirmation requires complete disjoint 256-pixel patch groups. Narrower
|
||||
geometries need a separately calibrated adaptive-patch expert.
|
||||
|
||||
A later frozen 1,000-image Open Images reserve preserved zero registered
|
||||
detections. Only 81 of those images were large enough for the fixed expert, and
|
||||
@@ -673,12 +689,151 @@ is the validated decision and a fixed-only crossing remains ambiguous. The
|
||||
fixed expert is retained as a separately scoped carrier diagnostic, not as an
|
||||
independent recall addition to registered mode.
|
||||
|
||||
The research router in `scripts/synthid_routed_expert_bank.py` makes this policy
|
||||
explicit and always abstains on fixed-only evidence. It records fixed,
|
||||
registered, and large observations separately: the registered threshold is the
|
||||
positive route in its measured scale-search range, while the large threshold is
|
||||
the positive route in its disjoint native large-image range. It never claims
|
||||
absence because encoder-version coverage remains incomplete.
|
||||
Registered-v2 later accepted 4/2,997 supported controls in a source-fresh Open
|
||||
Images test cohort. The frozen v3 confirmation rejected all four while retaining
|
||||
147/148 base-accepted source-diverse positives and all 359 base-accepted views
|
||||
in a dense 0.65-1.50 scale matrix. Without changing the rule, v3 then accepted
|
||||
0/2,996 controls from a second nonoverlapping Open Images cohort and 0/2,366
|
||||
supported controls from a 3,000-image COCO second-family challenge. The two Open
|
||||
Images cohorts give a one-sided 95% zero-error false-positive upper bound of
|
||||
about 0.050%. These results qualify a precision-first positive route, not an
|
||||
absence verdict; the v2 sensitivity benchmark and scale-0.5 exclusion remain.
|
||||
|
||||
Those control rates are photographic. On 223 corpus images whose C2PA names a
|
||||
non-Google generator, the unchanged production entry point accepted 29, a rate
|
||||
of `0.130`, with `0.241` on Adobe Firefly and a highest foreign score of `3.01`
|
||||
against the `1.0` threshold. Every acceptance came from registered-v3. The
|
||||
lattice this branch reads is therefore shared across generation pipelines, and
|
||||
the photographic upper bound above does not describe the rate a user meets on
|
||||
AI-generated input. The branch is also phase-locked to the image origin: a
|
||||
two-pixel diagonal crop killed all 28 in-geometry foreign detections and all 8
|
||||
detected Google provenance positives, with the signal recovering only at
|
||||
offsets that are multiples of four. A mark that survived cropping would not do
|
||||
that; the route detects pipeline lattice on ordinary sizes as well as large
|
||||
ones.
|
||||
|
||||
An exact runtime-parity check subsequently applied the recorded transform to
|
||||
each of the 226 source-diverse assignments before invoking registered-v3. Two
|
||||
half-scale outputs became too small for the research confirmation grid; 224
|
||||
were scored. Registered-v3 detected 147/224 overall. All 40 supported 0.5x
|
||||
views missed, while the measured 0.65x-1.5x range detected 147/184 (79.9%). The
|
||||
unchanged registered-v2 base accepted 148 of those 184; v3 confirmation removed
|
||||
only one true base crossing. The remaining sensitivity loss is therefore
|
||||
period selection, amplitude, or high-band failure after resampling, not an
|
||||
overly strict confirmation gate.
|
||||
|
||||
A preregistered same-image-null follow-up compares the selected carrier on the
|
||||
two patch checkerboards with 16 neighboring periods from plus or minus 0.35 to
|
||||
2.0 pixels. On two combined Open Images development cohorts, a near-threshold
|
||||
base floor of 0.8, the existing frozen confirmation, and minimum joint
|
||||
coherence excess `0.05695968150082637` rescued 7 of the 36 sub-threshold
|
||||
0.65x-1.5x views. The frozen rule accepted none of the first 3,000 COCO
|
||||
controls. It remains research-only until the untouched final control partition
|
||||
and new positive parents pass unchanged. The final 2,000 COCO files contained
|
||||
1,598 registered-supported images and seven near-threshold candidates. Four
|
||||
candidates could not form the required two 256-pixel patch groups; the three
|
||||
scored candidates had same-image excess at most zero. The frozen recovery rule
|
||||
therefore accepted 0/1,598, as did registered-v3 itself. The negative-side gate
|
||||
passed, but the lack of unseen positive parents still keeps the rescue outside
|
||||
runtime.
|
||||
|
||||
Same-image contrast also failed to rescue spatial/spectral period mismatches.
|
||||
Across the two 3,000-file Open Images cohorts, 469 controls passed the
|
||||
preregistered mismatch prefilters and 212 passed frozen split confirmation.
|
||||
Their maximum joint excess, `0.1762448251`, exceeded the `0.1464860382` maximum
|
||||
of the five eligible transformed positive mismatches. The requirement that the
|
||||
canonical and spectral period winners agree remains part of registered-v3.
|
||||
|
||||
A later content-adaptive research branch uses the registered tile only as a
|
||||
synchronization basis. It standardizes every patch against all cyclic tile
|
||||
shifts, chooses one phase on a checkerboard of selection patches, and confirms
|
||||
the same phase on the disjoint checkerboard. A frozen joint standardized score
|
||||
of `2.5`, combined with matching periods, the existing confirmation, both base
|
||||
gate margins at least `0.45`, and one margin at least `1.0`, recovered 5 of 18
|
||||
scored amplitude/high-band failures. It accepted none of 88 first-cohort gate
|
||||
failures and none of 334 previously scored control candidates, but those are
|
||||
development results. Its separately started Picsum feature-holdout then
|
||||
accepted 0/350 decoded-pixel-unique controls unchanged. Only two controls
|
||||
reached the mechanism pre-gates and their maximum joint patch-shift score was
|
||||
`0.7029283` against `2.5`. The branch remains research-only pending unseen
|
||||
positive parents. That check then failed: 21 resized views at 0.65, 0.70, and
|
||||
0.75 from seven separate official pixel-positive large Gemini parents produced
|
||||
0/21 registered-v3 detections and 0/21 patch-phase rescues. Their surviving
|
||||
amplitude but inverted or weak RGB high-band response points to a different
|
||||
carrier epoch or representation. The patch branch is not promoted; the next
|
||||
test registers the separately measured large-v1 opponent-color signal.
|
||||
|
||||
The first opponent-color registration experiment then recovered 21/21 of
|
||||
those views and selected the exact transformed period every time. It searches
|
||||
7.5-14.5 pixels in the Red-minus-Green and Blue-minus-Yellow spectrum, then
|
||||
requires fixed RGB, Red-minus-Green spatial, and Blue-minus-Yellow spatial
|
||||
scores of at least `0.16`, `0.60`, and `0.55`. The same frozen candidate
|
||||
accepted 0/21 symmetrically resized views from seven large Open Images
|
||||
controls. The frozen candidate then accepted 0/350 decoded-pixel-unique Picsum
|
||||
controls with a maximum decision margin of `0.7778322`.
|
||||
|
||||
The unrestricted branch was not production-safe. It accepted 605/2,997 and
|
||||
587/2,996 controls from two wider Open Images cohorts; almost every crossing
|
||||
selected the period-8 or period-12.8 codec lattice. It also accepted one of
|
||||
3,964 COCO controls at period 10, below one megapixel, and 28/3,772
|
||||
non-Google/non-OpenAI Spaces controls, all at period 8 or 12.8. Lossy
|
||||
re-encoding destroyed the measured positive route: raw lossless resize retained
|
||||
21/21 views at scales 0.5, 0.625, and 0.75, while JPEG-95, JPEG-85, and WebP-95
|
||||
retained 0/63.
|
||||
|
||||
Those failures defined a narrower precision-first expert before a new control
|
||||
holdout: 1-10 megapixels, both sides at least 768 pixels, and a selected period
|
||||
from 8.1 through 12.0. It keeps the original three score thresholds and does
|
||||
not require the strongest spectral alias to equal the spatially reranked
|
||||
period. The latter restriction was removed after a public-API test showed that
|
||||
it reduced the 0.55x cohort from 7/7 to 1/7 while the geometry gate already
|
||||
excluded its only reserve control crossing. The final rule then accepted
|
||||
0/1,000 untouched Picsum controls, with a maximum unrestricted decision margin
|
||||
of `0.7953734`, and the production API detected 42/42 lossless views at scales
|
||||
0.55 through 0.75 from the seven official-positive parents. It now ships as
|
||||
`synthid-periodic-tile-opponent-registered-v1` after registered-v3 abstains.
|
||||
|
||||
A later period-8 codec-confound experiment extended that frozen expert without
|
||||
mistaking the JPEG grid for a watermark. Half-scale lossless views retained 7/7
|
||||
official positives at period 8, but ordinary JPEG controls produced the same
|
||||
large template correlation. Their decoded opponent-color edge geometry was
|
||||
different: every positive had both the Red-Green and Blue-Yellow native
|
||||
8-pixel edge ratio at or below `1.05`. The threshold was selected against 600
|
||||
development codec crossings, then rejected all 582 crossings in the held-out
|
||||
Open Images cohort, all 601 in the feature-unseen frozen reserve, and all seven
|
||||
in the heterogeneous Spaces challenge. Separately, 350 controls passed through
|
||||
the same lossless 0.5x resize had no base carrier crossing. The runtime now
|
||||
accepts periods 7.9-12.0 and applies this pixel-only veto through period 8.1.
|
||||
The production API consequently detects 49/49 measured lossless views at scales
|
||||
0.5-0.75 from the seven official-positive parents.
|
||||
|
||||
The same work did not recover lossy retranscodes. JPEG 4:2:0 creates a strong
|
||||
inverted period-8 template in positives and natural controls alike. A
|
||||
matched-transform 512-feature codec-residual discriminant accepted up to
|
||||
86/525 holdout controls and did not generalize across positive parents. Eighty
|
||||
block-DCT coherence variants also failed the precision-first gate: the best
|
||||
zero-holdout-error statistic retained only 2/21 JPEG views. These are rejected
|
||||
hypotheses, not hidden fallback branches.
|
||||
|
||||
The wider provider challenge produced a useful but non-attributive observation:
|
||||
the unrestricted score accepted 95/7,254 OpenAI files with an explicit SynthID
|
||||
provenance assertion, all at periods 10.70-10.80. It also accepted 4/287 OpenAI
|
||||
lineage files without that assertion, including three in the production period
|
||||
band. Missing assertion is not an oracle-negative watermark label, and the
|
||||
feature does not identify a provider. All 95 decoded rasters were unique and
|
||||
none overlapped the 94 hits from the earlier period-8 OpenAI candidate, so the
|
||||
union covered 189/7,259 asserted files. The new component was nevertheless
|
||||
codec-fragile: JPEG-95 and WebP-95 each reduced 95/95 native hits to 0/95.
|
||||
It may be a complementary carrier epoch or an associated export/resampling
|
||||
lattice; current labels cannot distinguish those causes. The local detector
|
||||
therefore remains provider-neutral; OpenAI-wide coverage still comes from the
|
||||
official pixel verifier.
|
||||
|
||||
Runtime routing now applies these policies directly and always abstains on
|
||||
fixed-only evidence. The research bank in
|
||||
`scripts/synthid_routed_expert_bank.py` still records fixed, registered, and
|
||||
large observations separately for audits. No route claims absence because
|
||||
encoder-version coverage remains incomplete.
|
||||
|
||||
Running the unchanged registered components directly above the declared range
|
||||
did not justify a larger ceiling. From 10 to 18 megapixels it accepted 1 of 37
|
||||
@@ -931,10 +1086,12 @@ the aligned result. Together with the failed third-carrier fit, this exhausts
|
||||
the current linear native 16-by-16 Google hypothesis as two jointly controllable
|
||||
states. It still does not replace a matching-provider oracle result.
|
||||
|
||||
This mode is not the default because the bounded search is materially slower;
|
||||
scale registration is explicit and `identify` keeps the native detector. The
|
||||
registered mode does not reliably detect 0.5x carriers and does not make the
|
||||
detector universal across crop, codecs, carrier states, or providers.
|
||||
The bounded search is materially slower, but registered-v3 is now the default
|
||||
ordinary-size route because fixed-v2 failed its fresh-source precision gate.
|
||||
Registered-v3 itself does not reliably detect 0.5x carriers. The later bounded
|
||||
opponent fallback covers the measured lossless 0.5x case with its codec-grid
|
||||
veto, but does not make the detector universal across crop, codecs, carrier
|
||||
states, or providers.
|
||||
|
||||
A crop-specific follow-up tested cube-root LMS and OKLab projections, all six
|
||||
DTCWT orientations, and explicit FFT phase-lock metrics after period-and-phase
|
||||
@@ -1185,8 +1342,9 @@ runtime until a new future holdout confirms it.
|
||||
A positive result identifies the carrier but does not attribute a provider.
|
||||
Provider identity still comes from provenance.
|
||||
|
||||
The command reports `not_detected` separately from `unsupported`. Both are
|
||||
inconclusive outside the measured carrier family and calibrated image-size range.
|
||||
The command reports `indeterminate` separately from `unsupported`. Both are
|
||||
inconclusive: the first means no qualified local expert crossed its threshold,
|
||||
and the second means no local expert covers the geometry.
|
||||
|
||||
The same modulo-folding method has been tested separately on a large,
|
||||
temporally split OpenAI-labeled corpus. Its strongest native-size template was
|
||||
@@ -1543,6 +1701,23 @@ the signed OpenAI period-8 candidate accepted 4 of 417 unique positives and 1
|
||||
of 454 general-control groups, the OpenAI full-image noiseprint accepted 68 of
|
||||
417, and the Google runtime detector accepted 153 of 211 unique positives.
|
||||
|
||||
A previously prepared image-level patch-bag hypothesis was then executed. It
|
||||
aggregates the mean, standard deviation, minimum, and maximum of nine learned
|
||||
embeddings from normalized high-pass luminance and opponent-color patches. The
|
||||
validation-selected model accepted 285 of 1,280 OpenAI rows above every
|
||||
validation control. Without changing its weights or threshold, it accepted 324
|
||||
of 1,364 development-test rows, 3 of 1,373 difficult controls, and 1 of 2,000
|
||||
COCO controls. The later August temporal challenge accepted 120 of 486 OpenAI
|
||||
rows, 8 of 504 general controls, and 5 of 33 same-provider rows without a
|
||||
watermark assertion. Assertion enrichment over that same-provider stratum was
|
||||
not independently significant (`p = 0.151`, one-sided exact test). Content
|
||||
grouping reduced those counts to 107 of 417, 4 of 453, and 4 of 30,
|
||||
respectively. A JPEG-95 conjunction retained only 1 of 486
|
||||
OpenAI rows and no controls. Its native hits did not overlap the frozen signed
|
||||
period-8 temporal hits. The model therefore reveals a complementary OpenAI
|
||||
export noiseprint, not a codec-robust or watermark-specific decoder, and does
|
||||
not enter runtime.
|
||||
|
||||
### 3.3 Official OpenAI pixel verification
|
||||
|
||||
`remove-ai-watermarks verify-openai-synthid image.png
|
||||
@@ -1559,6 +1734,11 @@ entry. The source is not modified. Tests deliberately cover C2PA-only positive
|
||||
responses, pixel mutation, surviving metadata, malformed response shapes, and
|
||||
documented access and rate-limit failures.
|
||||
|
||||
The default SDK client fixes a 120-second request timeout and disables
|
||||
automatic retries. One explicit acknowledgement therefore cannot silently
|
||||
transmit the sanitized raster more than once. Timeout and connection failures
|
||||
are errors, and request logs omit source paths and decoded-pixel hashes.
|
||||
|
||||
A live 2026-08-14 web-verifier smoke used the same sanitization invariant. Two
|
||||
metadata-stripped, pixel-identical OpenAI images at 1536 by 1024 and 1024 by
|
||||
1536 both returned `SynthID detected` with `Content Credentials not detected`.
|
||||
|
||||
Reference in New Issue
Block a user