Machine Learning  

Gradio Visible Watermarking for Image, Video, and Chatbots

Abstract / Overview

Visible watermarking improves provenance and disclosure for AI media. Gradio adds built-in watermark support to Image, Video, and Chatbot components so that you can ship disclosure without custom front-end code. This article showcases a production-ready demo, which generates a QR watermark image, overlays a persistent watermark into exported images, displays a UI-level watermark on images and videos, and appends a copy watermark to chatbot messages. Assumption: Python 3.10+ with gradio, Pillow, and qrcode installed.

ChatGPT Image Sep 17, 2025, 11_59_21 AM

Direct answer

Use gr.Image(..., watermark=wm_img), gr.Video(..., watermark="wm.png"), and gr.Chatbot(..., watermark="Text"). The watermark appears bottom-right for Image and Video. For the Chatbot, the text is appended when users copy messages. (Gradio)

Conceptual Background

Watermarking has two forms here:

  • Display watermark: a visible overlay in the UI for images and videos. Users see it, but the base file is unchanged. (Gradio)

  • Copy watermark: a string appended when users copy chatbot messages, which improves attribution for downstream paste events. (Gradio)

Why it matters now

  • Gradio added native parameters for images, videos, and chat copy, lowering integration cost to one line per component. (Hugging Face)

  • Regulation and policy discussions continue to push for clear labeling of AI content. (European Parliament)

  • Deepfake exposure and fraud incidents are trending upward, which increases the value of provenance cues such as visible marks and copy attributions. (Regula)

Step-by-Step Walkthrough

Install dependencies

pip install gradio pillow qrcode[pil]

Prepare a QR watermark image
This creates a small, high-contrast PNG watermark.

# make_qr_watermark.py
from PIL import Image
import qrcode

def make_qr_watermark(text="Generated with YourApp.example", out_path="qr_watermark.png", box_size=4):
    qr = qrcode.QRCode(version=1, box_size=box_size, border=1)
    qr.add_data(text)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white").convert("RGBA")
    # optional: make white background transparent for better overlay
    datas = img.getdata()
    new_data = []
    for r, g, b, a in datas:
        if r > 240 and g > 240 and b > 240:
            new_data.append((255, 255, 255, 0))
        else:
            new_data.append((r, g, b, a))
    img.putdata(new_data)
    img.save(out_path)
    return out_path

if __name__ == "__main__":
    make_qr_watermark()

Persistently overlay a watermark into an image you export

Gradio’s UI watermark is visible but does not modify pixels. Many teams also want an exported image with pixels marked.

# overlay_persistent.py
from PIL import Image

def overlay_watermark(base_img: Image.Image, wm_img: Image.Image, margin=10):
    base = base_img.convert("RGBA")
    wm = wm_img.convert("RGBA")
    bx, by = base.size
    wx, wy = wm.size
    # place at bottom-right
    x = max(0, bx - wx - margin)
    y = max(0, by - wy - margin)
    composite = base.copy()
    composite.alpha_composite(wm, (x, y))
    return composite.convert("RGB")

Build the full Gradio demo

  • Tab 1: Upload an image, show a UI watermark, and offer a persistently watermarked download.

  • Tab 2: Display a video with a UI watermark.

  • Tab 3: Chatbot with a copy watermark for attribution.

# app.py
import gradio as gr
from PIL import Image
from overlay_persistent import overlay_watermark
from make_qr_watermark import make_qr_watermark

WM_PATH = make_qr_watermark("AI-generated media: example.com/provenance")

def persist_overlay_fn(img: Image.Image):
    wm = Image.open(WM_PATH)
    out = overlay_watermark(img, wm)
    return out

with gr.Blocks(title="Gradio Watermarking Demo") as demo:
    gr.Markdown("## Watermark your media and disclose provenance")

    with gr.Tab("Image"):
        with gr.Row():
            inp = gr.Image(label="Upload image", type="pil")
            out = gr.Image(label="Preview with UI watermark", interactive=False, watermark=WM_PATH)
        with gr.Row():
            btn_preview = gr.Button("Preview (UI watermark only)")
            btn_export = gr.Button("Export with pixels watermarked")
            download = gr.Image(label="Exported image (pixels watermarked)", interactive=False, show_download_button=True)

        def preview(img):
            # pass through; UI watermark is applied by the component parameter
            return img

        btn_preview.click(preview, inputs=inp, outputs=out)
        btn_export.click(persist_overlay_fn, inputs=inp, outputs=download)

    with gr.Tab("Video"):
        gr.Markdown("Video output shows a UI watermark in bottom-right.")
        vid_note = gr.Markdown("Upload or provide a URL to a short, browser-compatible video.")
        vid_in = gr.Video(label="Input video")
        # When used as output, Gradio can repackage streams. Here we simply echo input.
        vid_out = gr.Video(label="Watermarked (UI overlay)", watermark=WM_PATH)
        def passthrough(v):
            return v
        vid_in.change(passthrough, inputs=vid_in, outputs=vid_out)

    with gr.Tab("Chat"):
        gr.Markdown("Copy any assistant message; attribution is appended.")
        chat = gr.Chatbot(
            value=[{"role": "assistant", "content": "Ask about watermarking."}],
            type="messages",
            watermark="Generated by AI • example.com/provenance"
        )
        msg = gr.Textbox(label="Your message")
        def respond(history, message):
            history = history + [{"role": "user", "content": message}]
            # trivial echo; replace with your model call
            history += [{"role": "assistant", "content": f"Watermarking info: {message}"}]
            return history, ""
        msg.submit(respond, inputs=[chat, msg], outputs=[chat, msg])

demo.launch()

How it works in Gradio

  • gr.Image(..., watermark=...) draws a non-resized watermark 10px from the bottom and right. The base file is unchanged. (Gradio)

  • gr.Video(..., watermark=...) overlays a static image watermark at the bottom-right during playback. (Gradio)

  • gr.Chatbot(..., watermark="text") appends the string to copied messages after a blank line. This is a disclosure at the moment of copy. (Gradio)

Code / Snippets

Minimal one-liners if you already have outputs:

gr.Image(value=img, watermark="wm.png")
gr.Video(value="sample.mp4", watermark="wm.png")
gr.Chatbot(value=history, type="messages", watermark="Generated by AI • example.com")

Use Cases / Scenarios

  • Model demos on Spaces that must label AI outputs in the browser. (Hugging Face)

  • Media teams that need both a UI overlay for preview and a pixel-baked export for distribution.

  • Chat assistants that add attribution on copy to reduce unlabeled re-posts.

Limitations / Considerations

  • The Image/Video watermark in Gradio is a display overlay. It does not change the file. Use a Pillow overlay step to persist the mark as shown. (Gradio)

  • The watermark image is not resized. Prepare multiple sizes or detect the target size and choose a suitable variant. (Gradio)

  • SVG and GIF are not supported as watermark images for gr.Image. (Gradio)

  • Browser-compatible codecs are required for Video playback. (Gradio)

  • Chatbot watermark affects copied text only. It does not alter rendered content. (Gradio)

Fixes

  • Watermark too large on small outputs: Preprocess a smaller watermark or automatically resize before passing to watermark.

  • Users still share unmarked files: force persistent watermarking on export and disable raw downloads of unmarked outputs.

  • Video not playing: re-encode to H. 264 in MP4 or use supported containers. (Gradio)

Mermaid Diagram: end-to-end workflow

gradio-visible-watermarking-flow

Practical Enhancements

  • Auto-resize watermark to maintain a fixed relative size (e.g., 12% width).

  • Theming: swap watermark based on light/dark backgrounds.

  • Batch processing for image sets.

  • Embed source and model metadata into EXIF or PNG text chunks alongside the visual mark.

  • Add a provenance link through a QR watermark that resolves to a signed provenance page.

FAQs

  1. Does the watermark survive a file download from the Image preview?
    No. The preview overlay is cosmetic. Persist with Pillow if needed. (Gradio)

  2. Can I watermark animated GIFs?
    The watermark image cannot be GIF or SVG. You can still watermark a GIF preview in the UI, but persistent per-frame watermarking requires processing outside the component. (Gradio)

  3. Does Chatbot watermark alter the message content?
    It appends the watermark only when users copy. On-screen text remains unchanged. (Gradio)

  4. Why watermark at all?
    Policy pressure and rising deepfake incidents make disclosure a baseline expectation. Watermarks aid attribution and trust. (European Parliament)

Expert and data points

  • “Use visible watermarks.” The Hugging Face guidance reduces setup to a single parameter per component. (Hugging Face)

  • In 2024–2025, several reports documented sharp growth in deepfake incidents and business losses, reinforcing the case for clear labeling. (Regula)

Learning links (supplemental skills on C# Corner)

References

  • “Visible Watermarking with Gradio,” Sep 15, 2025. Details the single-parameter setup for watermarking Images, Videos, and Chat copy. (Hugging Face)

  • Gradio Image docs: watermark parameter behavior and constraints. (Gradio)

  • Gradio Video docs: watermark parameter and video format caveats. (Gradio)

  • Gradio Chatbot docs: watermark parameter for copy attribution. (Gradio)

  • Policy and trend context on labeling AI content and incident growth. (European Parliament)

Conclusion

You can ship visible watermarking in Gradio with one parameter per component. Combine UI overlays for previews with a Pillow overlay for exported assets. Add copy watermarking in Chatbot to preserve attribution at the moment of sharing. This dual approach covers live demos and durable files with minimal code and clear disclosure.