知識がなくても始められる、AIと共にある豊かな毎日。
AI Tool Review

Do AI Image Watermarks Survive Resizing and Compression? TrustMark and invisible-watermark Tested on 42 of Our Images

swiftwand

There are two broad ways to leave an “AI-generated” mark on an image made with AI. One is to write it into the file as metadata; C2PA manifests and XMP belong to this group. The other is a digital watermark embedded invisibly in the pixels themselves. This article is about the second: the AI image watermark.

This article contains affiliate links. As an Amazon Associate, we earn from qualifying purchases.

We embedded watermarks into 42 of this site’s article images with two tools. Adobe TrustMark embeds and reads the watermark with a trained model; the dwtDct method of invisible-watermark embeds it with plain computation and no training. We then counted whether the embedded value could still be read after the processing that commonly happens on the way to a blog: downscaling, JPEG compression, cropping and text overlays.

The results first. TrustMark (variant Q, default error correction BCH_5) read the value from all 42 images in 10 of the 11 conditions, including the unprocessed one. Only the condition “downscale to width 768, then save as JPEG at quality 82” came out at 41. It never judged a watermark to be present and then returned a wrong value, and it never reported “watermark present” on the original 42 images that carried no watermark.

dwtDct, by contrast, read only 18 of 42 images even with no processing, and 0 after downscaling to widths 1024 and 768, JPEG compression, cropping and edge trimming. At width 300 the image had too few pixels and the library refused to accept it. Every condition was run once, and the processing was reproduced locally with Pillow rather than by WordPress.

This article is not a search for ways to remove marks. We measured only processing that happens unintentionally in everyday work and did not test processing designed to remove a mark. The experiment used local copies of the article images; no published posts or images were changed. This site’s article images also do not carry digital watermarks.

The dates and framework of the EU rules on labeling AI-generated images are covered in Where “AI-Generated” Labels and Marks Disappear in Images and 3D Data (published 2026-11-09) and Update the Law Before You Memorise It: Japan AI Act, Copyright, and the EU AI Act (published 2026-10-04). This article makes no legal judgments and does not address whether readers have any obligations.

忍者AdMax

Metadata vs. Digital Watermarks: Write the “AI-Generated” Mark into the File, or Embed It in the Pixels

Metadata is information written to a defined place inside the file, separate from the image’s pixels. C2PA manifests, XMP, EXIF and PNG text chunks are examples. It can be read as long as it stays in the file, but it disappears when a process that opens and re-saves the image does not carry that place over. For this site’s article image pipeline, we counted at which stage metadata disappears in Where “AI-Generated” Labels and Marks Disappear in Images and 3D Data (published 2026-11-09). The procedure for actually writing and reading a C2PA manifest is covered in Adding and Reading Provenance on AI Images with C2PA (published 2026-11-10).

A digital watermark, on the other hand, embeds information by changing pixel values slightly. The official TrustMark FAQ says TrustMark changes only the pixels and does not touch the image’s metadata. The transparency component (the alpha channel) is also outside the watermark. In other words, embedding the watermark does not by itself rewrite metadata (whether metadata survives a save depends on how the file is saved). As long as the pixels remain, the watermark can survive a re-save, but once downscaling or compression changes the pixels, it may become unreadable. This article counts how far it survives.

The C2PA specification lays out how the two relate. C2PA divides the ways of binding an image to a manifest into two types. A hard binding links them with a value computed from the raw bytes of the file, and a manifest can hold at most one. A soft binding, by contrast, finds the manifest through a watermark or a fingerprint (a value computed from the image content). It is computed from the content rather than the raw bytes, and a manifest can hold zero or more. Within C2PA, a digital watermark is positioned as one kind of soft binding.

Soft Bindings in C2PA: A Watermark Is an Identifier for Finding a Lost Manifest

Section 2.3.14 of the C2PA Technical Specification (the 2.5 page) defines a soft binding as either a content identifier that is not statistically unique (such as a fingerprint) or an identifier embedded as an invisible watermark in the content it identifies. Section 12.2.3 then states the purpose: to allow content to be matched even when the underlying bits differ. For example, if the manifest is stripped from an image but a copy remains in a repository somewhere, the soft binding can be used to match the image and the manifest again.

The same section 12.2.3 also says that, because the purposes differ, a soft binding must not be used as a hard binding. A digital watermark is therefore not proof that an image has not been tampered with. It is an identifier for looking up a lost manifest in a repository (a Manifest Repository, in the spec’s terms). Section 2.4.5 defines this repository as a place where manifests are stored and can be searched by their binding to content. Section 2.4.1 goes on to call a Content Credential that can be found in a repository through a soft binding a Durable Content Credential.

The way it is written into a manifest is also fixed. The label of a soft binding assertion is c2pa.soft-binding (16.3.7), and the algorithm name (alg) should be a name from the list C2PA maintains. A match is declared only when both the algorithm name and the value match. Section 16.3.7.5 also defines a web API, the soft binding resolution API, for retrieving a manifest from a soft binding value and similar inputs.

There are rules for recording the act of watermarking as well. The list of actions (16.4.1.1) includes c2pa.watermarked.bound, for a watermark inserted to create a soft binding, and c2pa.watermarked.unbound, for one inserted without creating a soft binding. Section 16.4.1.5 requires that a manifest using bound also include a soft binding assertion; without one, validation rejects it as assertion.action.softBindingMissing (14.10.4.2.4).

Here is the scope of this experiment. This site does not run a manifest repository. So even when a watermark is read, there is no manifest its value can look up. What this article checks is only whether the identifier embedded in the image can still be read after processing; we did not test the flow of looking up a manifest from the identifier.

The Algorithm List Had 57 Entries as of the 2026-10-28 Commit

The C2PA soft binding algorithm list is published on GitHub as JSON. As of the commit on October 28, 2026, it held 57 entries: 48 watermarks and 9 fingerprints. Of these, 32 include images among the content they read. The counts are our own tally of the JSON, and because the list keeps growing, we give the date.

Of the tools we tested, only TrustMark is registered in the list by its own developer. Adobe has registered four variants, Q, C, P and F, and Q was registered on 2024-05-17. F was registered on 2026-10-14 and is not among the variants in Python version 0.9.2. Conversely, variant B, which the Python version has, is not on the list. Meta’s PixelSeal and VideoSeal appear as third-party implementations, not registered by Meta. Other image entries include watermarks from Digimarc and Microsoft’s InvisMark, as well as Adobe’s fingerprint method.

Google’s SynthID and invisible-watermark, on the other hand, are not registered. This does not mean they are “not compatible with C2PA”; it only means their names are not on the list. Registration is also a procedure in which a person belonging to the company that owns the method, or a maintainer, submits a change proposal and the C2PA technical working group approves it; it does not certify performance. The spec does define 3D models (model) as one of the content types for the list, but at this point not a single entry included model.

Choosing the Tools: TrustMark Embeds with a Trained Model, dwtDct Uses No Training

When choosing a tool to embed an AI image watermark, the first thing we read was the license text itself. This site earns revenue, including through affiliate links, so licenses limited to non-commercial use are ruled out. The table below summarizes what we found after opening the LICENSE and README for each candidate.

CandidateCodeModel weightsRole in this article
Adobe TrustMark 0.9.2MITMIT (stated in the README)Main tool (variant Q)
invisible-watermark 0.2.0MITdwtDct has no weightsComparison (dwtDct)
Meta Watermark AnythingMITMIT weights, plus CC BY-NC 4.0 weights for the paper versionNot used
Meta VideoSeal 1.0.1MITMITNot tested this time
Google SynthID (images)——No tool published for embedding it in your own images

TrustMark 0.9.2: Code and Model Weights Are Both MIT

TrustMark is a watermark developed by Adobe and the University of Surrey. The 2023 arXiv version of the paper is titled “TrustMark: Universal Watermarking for Arbitrary Resolution Images.” The paper was later presented at ICCV 2025 under a different title; everything this article quotes directly from the paper comes from the arXiv version.

The LICENSE itself is MIT, and the README states that the MIT license covers not only the repository code but also the model files fetched on first run. The FAQ also says it can be used both commercially and non-commercially.

The latest version on PyPI is 0.9.2 (uploaded 2026-09-08), distributed only as a source distribution (sdist). It requires Python 3.8.5 or later. The main dependencies are PyTorch (2.3.1 or later) and torchvision (0.18.1 or later), with lightning, omegaconf, einops and others also installed. The metadata registered on PyPI for 0.9.2 has no OS classifier, so no target OS is stated (only the repository’s setup.py carries the “OS Independent” classifier). It uses CUDA or MPS when a GPU is available and runs on the CPU otherwise.

How it works, in brief: TrustMark embeds 100 bits in the image. Four of them are version bits; of those, two are reserved (unused) and the other two indicate the error-correction scheme. With the default BCH_5, 61 bits carry the value and 35 bits go to error correction (61 + 35 + 4 = 100). BCH_5 can correct up to 5 bit errors. The other schemes are BCH_4 (68 value bits, 28 correction bits), BCH_3 (75 and 21) and BCH_SUPER (40 and 56, correcting up to 8 bits), and the decoder detects the scheme automatically. When it decides the errors cannot be corrected, TrustMark returns an empty value as “no watermark.”

There are four variants, B, C, P and Q, and a watermark embedded with one variant cannot be read with another. The defaults are variant Q, 100% strength and BCH_5, and the official configuration notes say this is enough for most uses. The watermark component is generated at 256×256, upscaled to the original resolution and blended into the image. When the aspect ratio exceeds 2.0, the watermark goes only into the central square, but at this site’s 1280×720 (an aspect ratio of about 1.78), variant Q covers the whole image. The FAQ also explains that the watermark is spread across the whole image to make it more robust.

The robustness the FAQ describes is that the model was trained to withstand many non-editorial transformations, such as resharing online, screenshots and printing. The FAQ goes on to say that the histograms in the paper show high robustness to 18 kinds of noise from the Python library Kornia (including cropping from the four sides that removes up to about 20% of the area), and that it was designed to withstand JPEG compression. It also says images are handled reliably down to a short side of about 150 px, with no upper limit. All of this is the developer’s description; our measurements are a check on it.

TrustMark also ships watermark removal code in its repository (official FAQ). In this experiment we did not use the removal function and initialized TrustMark with the setting that does not load it (loadRemover=False). Besides Python, there are a read-only JavaScript version (using ONNX), a Rust version, a C++ runtime and a Python version for the HAILO-8L in the Raspberry Pi AI Kit. This article used only the Python version.

invisible-watermark 0.2.0: Officially Targets Linux; We Used dwtDct

invisible-watermark is a library published by ShieldMnt, and its LICENSE is MIT. The latest version on PyPI is 0.2.0 (a wheel from 2023-07-06), and the last commit in the repository is dated 2023-08-25. The package classifiers name Linux as the official target, and it says it was tested on Ubuntu 18.04. It has no GPU support. Its dependencies include OpenCV, PyTorch, Pillow, PyWavelets and numpy.

It offers three methods: dwtDct (the default), dwtDctSvd and rivaGan. According to the README, dwtDct embeds bits in the largest non-trivial coefficient among each block’s DCT (discrete cosine transform) coefficients. At the default strength setting it embeds only in a color component (the U of YUV), not in luminance. The user decides how many bits to embed; the README’s tests use 64 bits for the frequency methods. rivaGan ships a trained model (ONNX), but we did not use it because we had not checked the license of the upstream model or the terms of its training data. dwtDct and dwtDctSvd have no model weights.

Note that the developer documents the weaknesses itself. The PyPI page says these methods are not robust to resizing or to cropping that changes the aspect ratio. In its test table, “Resize 50%” and “crop 7×5” are Fail for the frequency methods, while JPEG compression is Pass. The README further says there is no guarantee that the original watermark can be read with 100% accuracy even without any processing, and lists screenshots of web pages and posters with a uniform background color as known cases where it does not work well.

The other constraint is image size. Images with fewer than 256×256 (65,536) pixels stop with an exception, both when embedding and when reading. We counted this case separately as “could not run” rather than “unreadable,” because the library does not accept the image (the check was made with the same condition before calling the library).

Candidates We Did Not Use: Watermark Anything, SynthID, VideoSeal

For Meta’s Watermark Anything, the code and the newer weights trained on SA-1B are MIT, and those can be used commercially. But the COCO weights evaluated in the paper are CC BY-NC 4.0, so this revenue-earning site does not use them. CC BY-NC 4.0 defines NonCommercial as not primarily intended for or directed toward commercial advantage or monetary compensation (§1(i)), and it grants the license for non-commercial purposes only (§2(a)(1)(A)). In addition, the repository was archived on 2026-08-01 and is now read-only. We left unmaintained tools out of this comparison.

Google’s SynthID needs to be described with care. What is open source is SynthID Text, for text, under the Apache 2.0 license, and the developer says it is not intended for production use. SynthID for images is embedded at generation time in outputs from Google and its partners. We found no guidance anywhere, on the official pages, the blog or the repositories, about a tool for embedding it in your own images.

On the checking side, Gemini, Search and Chrome can check for SynthID watermarks, and on 2026-10-07 SynthID Detector became generally available in English. Google’s blog says it can check whether images, video and audio were made with AI from Google or partners such as OpenAI, NVIDIA and Kakao.

As an example of a watermark in text, we touched on the watermark in Claude’s generated text in What Changed in Claude Fable 5.1 — Two Names, One Model, and a Cache Read at a Quarter of the Price (published 2026-09-21). This article deals only with image watermarks.

Meta’s VideoSeal 1.0.1 is also usable, with MIT code and models. Its dependencies are heavy, though (decord, a pinned version of timm and so on), and we did not install it this time. Since we did not test it, we did not compare its results either.

TrustMark and invisible-watermark both ship as Python libraries. If you want to try them on your own images, knowing how to batch-process files in Python makes a run over dozens of images quick.

Paperback, 3rd edition (No Starch Press, May 2025) on Amazon.com

Experimental Setup: Embed in 42 Article Images, Read After 11 Kinds of Processing

The input was the cover image (images/cover.png) of each article dated 2026-09-28 to 2026-11-08, 42 images in all. Every one is a 1280×720 PNG this site made with local image generation (Z-Image-Turbo, Apache 2.0), and the only selection criterion was the date range. How we generate illustrations locally, and how to read the model licenses, is covered in Covering Your Article Images Locally — ComfyUI, FLUX, and Reading the Licence Right (published 2026-08-27).

The embedded value was built for each image from the article’s date. For TrustMark we embedded a text string of sw plus the article’s month and day (for example sw1102, 6 characters). For invisible-watermark we embedded 8 bytes: sw, the month and day, and !! (64 bits). Each watermarked image was first saved as PNG, and the reloaded image was the starting point for processing. This site’s images are PNG, and PNG can be saved without changing the pixels, so this starts from a state in which saving does not alter the watermark.

Each image went through each of the following 11 conditions once. The downscale widths match the three aspect-preserving sizes among WordPress’s default image sizes (large fits within 1024, medium_large is width 768, and medium fits within 300), and the landscape images were shrunk to each of those widths. The defaults also include thumbnail (150×150), which is cropped to a square, but we did not test it. JPEG quality 82 is WordPress’s default when it writes JPEGs. By default, however, WordPress downscales PNGs as PNGs and does not convert them to JPEG. Since this site’s article images are PNG, the JPEG conditions stand in for cases where an image is uploaded as JPEG or converted to JPEG somewhere else. The processing was also reproduced locally with Pillow, not by WordPress itself.

ConditionProcessingSize after processing
NoneRead the watermarked PNG as is1280×720
Width 1024Downscale keeping the aspect ratio (Lanczos)1024×576
Width 768Same as above768×432
Width 300Same as above300×169
JPEG 82Save as JPEG at quality 82 and reload1280×720
JPEG 70JPEG at quality 701280×720
JPEG 50JPEG at quality 501280×720
Width 768 + JPEG 82Downscale to width 768, then JPEG at quality 82768×432
Center 90%Crop the center to 90% of the width and of the height1152×648
Trim right 10%Cut off the rightmost 10%1152×720
Text overlayUsing apply_text_overlay in this site’s comfyui/generate.py, draw a band 10% of the height at the bottom (black at 60%) with text (3.5% of the width)1280×720

The heights after downscaling follow from the original aspect ratio (720 × 1024 ÷ 1280 = 576, 720 × 768 ÷ 1280 = 432, and 720 × 300 ÷ 1280 = 168.75, rounded to 169). The center crop is 1280 × 0.9 = 1152 by 720 × 0.9 = 648, and the right trim leaves 1280 − 1280 × 0.1 = 1152. The text overlay is a feature of this site’s image generation script, but current article images do not use it.

We fixed the definition of “read” before running anything.

ResultTrustMarkinvisible-watermark (dwtDct)
ReadReturns “watermark present” and the string read matches the embedded value exactlyThe 8 bytes read match the embedded value exactly
UnreadableReturns “no watermark”At least one of the 8 bytes differs
Wrong valueReturns “watermark present” but the value differs (counted as unreadable, with the count reported separately)—
Could not run—The processed image has fewer than 65,536 pixels and the library does not accept it

For dwtDct we also recorded the fraction of the 64 bits that matched (the bit match rate below). The paper and the official FAQ report performance as bit accuracy, but the main figure in this article is whether an image’s whole value was read.

Separately, as a negative control, we had both tools read the original 42 images without watermarks, unprocessed, once each (added after the 11-condition run). The aim was to see whether “watermark present” appears on images without a watermark, and whether a value in the shape of the embedded value comes back.

The environment was a Windows 11 Pro PC (Intel Core Ultra 7 265F, 63.7 GB RAM, RTX 5070 Ti 16 GB), with trustmark 0.9.2 and invisible-watermark 0.2.0 installed in a Python 3.13.17 virtual environment. PyTorch was the CPU build (2.15.0+cpu), and the GPU was not used. TrustMark was initialized with variant Q and the default BCH_5. The key calls are below.

tm = TrustMark(verbose=False, model_type="Q", loadRemover=False)
marked = tm.encode(orig, "sw1102", MODE="text")
secret, present, schema = tm.decode(after, MODE="text")
# read: present is true and secret exactly matches the embedded string

enc = WatermarkEncoder()
enc.set_watermark("bytes", b"sw1102!!")
marked_bgr = enc.encode(bgr, "dwtDct")
got = WatermarkDecoder("bytes", 64).decode(after_bgr, "dwtDct")
# read: got exactly matches the embedded 8 bytes

Installing TrustMark on Windows Needed PYTHONUTF8=1

Neither TrustMark nor invisible-watermark officially says it runs on Windows. In fact, installing TrustMark on this PC failed once. TrustMark’s setup.py reads the README with the system’s default encoding, so on Japanese-locale Windows (default encoding cp932) it stopped with the following exception.

UnicodeDecodeError: 'cp932' codec can't decode byte 0x94

Reinstalling with the environment variable PYTHONUTF8=1 worked, and once installed, it ran on Windows. The command used for this run was the following (in Git Bash), with the CPU build of PyTorch installed beforehand. To do the same in PowerShell, set $env:PYTHONUTF8 = '1' first.

PYTHONUTF8=1 <venv python> -m pip install trustmark==0.9.2 invisible-watermark==0.2.0

invisible-watermark officially targets Linux, but it is distributed as an OS-agnostic wheel (py3-none-any), so it installed, and processing of all 42 images ran to completion on this PC as well.

TrustMark fetches its model weights on first run. No login is needed, and the decoder file for variant Q (decoder_Q.ckpt) was 47,652,460 bytes. The first run took 124.2 seconds to download and load; later loads took 0.7 seconds. Processing a single test image on the CPU took 0.12 seconds to embed and 0.06 seconds to read.

Results: TrustMark Read 41–42 Images in All 11 Conditions; dwtDct Read 18 Even Unprocessed

ProcessingTrustMark readTrustMark wrong valuedwtDct readdwtDct could not rundwtDct mean bit match rate
None42/42018/4200.7615
Width 102442/4200/4200.4773
Width 76842/4200/4200.5089
Width 30042/420—42—
JPEG 8242/4200/4200.5454
JPEG 7042/4200/4200.5365
JPEG 5042/4200/4200.5342
Width 768 + JPEG 8241/4200/4200.5164
Center 90%42/4200/4200.6049
Trim right 10%42/4200/4200.6395
Text overlay42/42019/4200.7705

TrustMark read the value from all 42 images in 10 of the 11 conditions. The only miss was one image in “Width 768 + JPEG 82,” and even there it returned “no watermark” rather than reading a different value. Across all 42 images × 11 conditions = 462 reads, there were 0 in which it judged a watermark present and returned a wrong value.

dwtDct, by contrast, read only 18 of 42 images with no processing. After downscaling to widths 1024 and 768, JPEG compression, cropping and edge trimming, it read 0 in every condition. Width 300 gives 300×169 = 50,700 pixels, short of 65,536, so none of the 42 could be run. After the text overlay it read 19, almost the same as the 18 with no processing.

The mean bit match rate stays between 0.4773 and 0.5454 after downscaling and JPEG. Guessing 64 bits at random matches about half of them on average (the paper also gives chance-level accuracy as 50%). In other words, after downscaling or JPEG, dwtDct returns values close to chance even bit by bit. Cropping and trimming come out a little higher, at 0.6049 and 0.6395, but no image matched its whole value.

The 42 Images Without Watermarks: Negative Control

When the original 42 images without watermarks were read unprocessed, TrustMark reported “watermark present” on 0/42. Together with the 0 wrong-value reads across the 11 conditions, this means that on these 42 images TrustMark never said “present” for an image without a watermark and never returned a wrong value for an image with one (each tested once).

dwtDct is a different story. It has no mechanism for deciding whether a watermark is present, and it returns 8 bytes every time, even for images without one. The 42 images produced 13 distinct values, and 0/42 had the shape of the embedded value (starting with sw and ending with !!). With dwtDct, then, the fact that a value came back is not evidence of a watermark. Only when the reader checks that the returned value matches a shape the embedder decided on can it be said to have been read.

Image Quality: Median PSNR of 44.49 dB for TrustMark and 38.89 dB for dwtDct

We measured the difference between each image before and after watermarking with PSNR (peak signal-to-noise ratio). PSNR is derived from the mean of the squared pixel differences between two images (the mean squared error) with the formula below. The higher the value, the closer the watermarked image is to the original.

PSNR (dB) = 10 × log10 (255² ÷ mean squared error)

The median over the 42 images was 44.49 dB for TrustMark and 38.89 dB for dwtDct. TrustMark’s official configuration notes give a typical PSNR of 43–45 dB for variant Q, and the median in this run fell within that range. On the same metric, dwtDct’s median is 44.49 − 38.89 = 5.60 dB lower than TrustMark’s. Because PSNR is set by the logarithm of the mean squared error, this gap corresponds to a mean squared error ratio of 10 to the power (5.60 ÷ 10) ≈ 3.6 times (a rough figure derived from the two medians).

So in this run, dwtDct changed the image more from the original and yet was less robust to processing. Note that by default dwtDct embeds only in a color component (U), while the PSNR here was computed over all three RGB channels together, so it does not show which component changed by how much. PSNR is also a numerical measure of pixel differences; whether a person can see the difference was not checked in this run.

Reading the Results: What Downscaling, JPEG, Cropping and Text Overlays Did

Downscaling: TrustMark Read 42 Even at Width 300; dwtDct Read 0 at Width 1024

TrustMark read all 42 at widths 1024, 768 and 300. At width 300 the short side is 169 px, just above the lower limit the FAQ gives (a short side of about 150 px). The paper (the 2023 arXiv version) says its model takes 256×256 images as input and that training and evaluation apply random resizing in the range 0.8 to 1.0 (aspect ratio 3/4 to 4/3) and a 244-out-of-256 crop to those images. The Python version of TrustMark, however, always shrinks the image to the model’s resolution (245×245 for variant Q) before reading. The downscaling ratio from the original width of 1280 therefore cannot be compared directly with the paper’s range.

According to the FAQ, the paper’s model was also trained on MIRFlickR images, while the distributed models were trained on Adobe Stock images. This article did not check which processing, at what strength, was applied when the distributed models were trained. The paper’s conditions cannot predict the distributed model’s results, and the paper’s numbers cannot be read as the distributed model’s performance. All this run can say is that the distributed variant Q model read this site’s 42 images down to width 300.

dwtDct, on the other hand, read 0 even after downscaling to width 1024 (1024 ÷ 1280 = 0.8×). That points the same way as the developer’s statement that it is not robust to resizing and its test table marking “Resize 50%” as Fail. In this run, a 0.8× downscale left not a single image readable, including the 18 that were readable without processing.

JPEG Compression: dwtDct Diverged from the Developer’s Test Table

TrustMark read all 42 at qualities 82, 70 and 50. The FAQ says it was designed to withstand JPEG compression, and the result does not contradict that. The paper’s supplementary material also includes a table by JPEG strength, but it does not say at what strength the distributed models were trained, so we do not cite it here.

dwtDct read 0 even at quality 82. This diverges from the developer’s test table (JPEG compression: Pass). The developer’s tests and this run differ in both images and conditions, and this run did not isolate why the results diverge. At least for this site’s article images, no image was readable after JPEG at quality 82, including those that were readable without processing.

Downscaling Plus JPEG: TrustMark Missed Just One Image

TrustMark read 42 under both “Width 768” and “JPEG 82” alone, but 41 when the two were applied in sequence. Still, each condition was run once and the difference is one image. These numbers do not support a claim like “combining them makes some percentage unreadable.” For the one image it could not read, TrustMark still returned “no watermark” and did not read a different value.

Cropping and Trimming: TrustMark Read 42 Even with 19% of the Area Removed

The center 90% crop leaves 0.9 × 0.9 = 0.81 of the area, removing 19%. That is close to the “cropping from the four sides that removes up to about 20% of the area” the FAQ cites from the paper’s evaluation, but that figure evaluates the paper’s model and cannot serve as a guide for the distributed model. Under this condition, TrustMark read all 42. Trimming the rightmost 10% gives 1152×720 and changes the aspect ratio from about 1.78 to 1152 ÷ 720 = 1.6. That condition also gave 42.

dwtDct read 0 in both. The developer says it is not robust to cropping that changes the aspect ratio, but in this run it could not read even the center crop, which keeps the aspect ratio. The mean bit match rates were 0.6049 and 0.6395, higher than after downscaling or JPEG, but not a single image matched its whole value.

Text Overlay: With a Band on the Bottom 10%, Both Stayed Close to Unprocessed

The text overlay draws a band and text on the bottom 10% of the image and re-saves it as PNG. TrustMark read all 42 and dwtDct read 19, nearly the same count as the 18 with no processing (one image that was readable without processing became unreadable, and two that were unreadable became readable). Only the pixels in the band change, and no compression like JPEG is added, so this was likely close to the unprocessed condition for both methods. We did not confirm that, however, and we did not test other band heights or colors.

dwtDct Failed on 24 Images Even Without Processing

Without processing, 24 images (42 − 18) were unreadable. To check whether saving was the cause, we took 8 images, 5 that were unreadable in the main run and 3 that were readable, and compared reading the array right after embedding with reading it after saving as PNG and reloading (the embedded value this time was swtest!!, different from the main run). All 8 images gave the same result both ways, so at least for these 8, saving was not the cause. We did not run this comparison on the other unreadable images. Of the 24 unreadable images, 13 returned a value of 0xff in all 8 bytes. As the README says, there is no guarantee of 100% accurate reading even without processing, and the method simply does not work on some images. This run did not analyze which kinds of images it fails on.

Does a Read Identifier Mean “AI-Generated”? Applying AI Image Watermarks to a Blog

Within the scope of this run, after processing that reproduced with Pillow the three aspect-preserving widths among WordPress’s default image sizes and the default JPEG quality of 82, TrustMark’s value was read in 41–42 images (the square-cropped thumbnail was not tested). An identifier embedded in the pixels can survive even when a re-save removes the metadata. dwtDct, by contrast, could not read 24 of the 42 images before any processing at all, and in this run it was not usable as a mark for images published on a blog.

Even so, the value that was read does not by itself mean “AI-generated.” What this experiment embedded was a short string chosen by this site, such as sw1102, and only the embedder knows what it means. Within C2PA, a watermark becomes useful when it is written into a manifest as a soft binding, that manifest is stored in a repository, and the manifest can be looked up from the value read. This site has no such repository, so nothing can be looked up from the values read in this experiment.

The role of the watermark comes down to three points.

  • A watermark is an identifier for finding, from an image whose manifest is gone, the manifest stored in a repository (spec 12.2.3)
  • A watermark is not proof of no tampering, and a soft binding must not stand in for a hard binding (same section)
  • To record a watermark as a soft binding in C2PA, include the c2pa.watermarked.bound action and the c2pa.soft-binding assertion as a pair (16.4.1.5)

From the standpoint of checking other people’s images, the picture is different again. According to Google, SynthID is embedded at generation time in outputs from Google and its partners and can be checked with Gemini, Search, Chrome or SynthID Detector. SynthID cannot, however, be used as a tool to embed into images you made yourself. In that respect its position differs from TrustMark, which you can embed into your own images and read yourself. If you embed an AI image watermark yourself, the value you read will not be a useful clue unless you also decide what to embed and where its meaning can be looked up.

A watermark is one piece of a larger set of concerns when you ship products built on generative models. For the wider picture of evaluation, guardrails and deployment, this is a solid reference.

USD 52.40 on Amazon.com (as of 2026/09/22)

What These Measurements Cannot Tell You

  • One run per condition, on 42 images. All are 1280×720 PNGs this site made with the same image generation model, selected only by date range. There is no basis for saying photos, or images from other models or at other sizes, would give the same results
  • The processing was reproduced locally with Pillow. These are not images WordPress actually downscaled and compressed. The downscaling filter (Lanczos) and the details of JPEG writing may not match the server’s image processing
  • Square-cropping downscales were not tested. WordPress’s default image sizes also include thumbnail (150×150), which is cropped to a square. The downscales in this run all preserve the aspect ratio, and cropping and trimming were limited to the center 90% and the right 10%
  • The negative control was a single unprocessed read of the 42 images without watermarks. TrustMark’s “watermark present” count was 0/42, but we did not count whether “present” can appear after images without a watermark are downscaled or compressed. dwtDct returns a value every time even for images without a watermark, so presence cannot be judged by whether a value came back
  • We do not set these figures side by side with those in the paper or the official FAQ. According to the official FAQ, the default variant Q can exceed 96% bit accuracy even under strong noise. This article, however, counts whether an image’s whole value was read, which is a different metric. The paper’s model and the distributed models were also trained on different data
  • Only TrustMark variant Q and dwtDct were used. TrustMark’s P, B and C, dwtDctSvd, rivaGan, VideoSeal and others were not tested
  • Processing designed to remove the mark was not tested. How the methods hold up against processing aimed at erasing marks, such as heavy noise, rotation or regeneration, is outside the scope of this article
  • The flow of looking up a manifest from the identifier was not tested. This site has no manifest repository
  • Image quality is the PSNR figure only, and whether a person can see the difference was not checked. Processing times come from processing a single test image on the CPU

Summary

An AI image watermark places an “AI-generated” mark somewhere other than the metadata: in the pixels. In this run, which embedded watermarks in 42 of this site’s article images and reproduced common blog processing with Pillow, TrustMark (variant Q), which embeds with a trained model, read the value from all 42 images in 10 of the 11 conditions and from 41 in the remaining one, and never returned a wrong value. It also never reported “present” on the 42 images without watermarks. Its median PSNR was 44.49 dB, within the official typical range.

dwtDct, which uses no training, read 18 with no processing and 0 after downscaling to widths 1024 and 768, JPEG, cropping and trimming, and the width-300 images were too small for the library to accept. Its weakness to downscaling, and the fact that it cannot be read reliably even without processing, point the same way as the developer’s own statements on the PyPI page and in the README. For JPEG compression, the results diverged from the developer’s test table.

Within C2PA, though, a read identifier leads to an “AI-generated” record only when it is bound to a manifest as a soft binding and the manifest can be looked up in a repository. A watermark is not proof of no tampering, and the spec says a soft binding must not stand in for a hard binding. If you leave a mark on your images, you need to think separately about what the C2PA manifest written to the file and the digital watermark embedded in the pixels each do, and where each can disappear. The procedure for writing and reading C2PA is covered in Adding and Reading Provenance on AI Images with C2PA (published 2026-11-10), and where metadata disappears in this site’s pipeline in Where “AI-Generated” Labels and Marks Disappear in Images and 3D Data (published 2026-11-09).

Sources

ブラウザだけでできる本格的なAI画像生成【ConoHa AI Canvas】
ABOUT ME
swiftwand
swiftwand
AIを使って、毎日の生活をもっと快適にするアイデアや将来像を発信しています。 初心者にもわかりやすく、すぐに取り入れられる実践的な情報をお届けします。 Sharing ideas and visions for a better daily life with AI. Practical tips that anyone can start using right away.
記事URLをコピーしました