multimodal-ai-lab/VETO

VETO: Towards Protecting Images From Frontier AI Editing

5

stars

7

commits

Python

primary language

Jul 31, 2026

updated

jonasgrebe.github.io/research/projects/veto/

README

🛡️ VETO: Towards Protecting Images From Frontier AI Editing

arXiv HF Space VetoBench MIT License

Official PyTorch implementation
"VETO: Towards Protecting Images From Frontier AI Editing"

VETO Teaser
The two contributions: (1) A new anti-edit protection called VETO for modern reference-based image-editing models, and (2) an anti-edit benchmark VetoBench to stress-test these protections against new open-frame capabilities.
VETO Method
Method overview: VETO's objective disrupts the attention between the reference image x and the canvas c by maximizing their entropy in early double-stream MMDiT blocks of modern image-editing models like FLUX.2.

Table of contents


📦 Setup

Installation

pip install uv
uv sync

Models

Weights are downloaded from Hugging Face on first run (accept model licenses):

protection.modelHugging Face model
flux2diffusers/FLUX.2-dev-bnb-4bit
fibo_editbriaai/Fibo-Edit

Dataset

VetoBench is our anti-edit benchmark released on Hugging Face. It contains 300 images across three categories (100 each). Every category is split evenly into 50 closed-frame and 50 open-frame edits:

  • Closed-frame — modify the original scene in place.
  • Open-frame — extract referenced entities or traits and recontextualize them in synthesized scenes.
CategoryImagesClosed-frameOpen-frameDescription
General1005050Everyday scenes without harmful intent
Defamation1005050Edits that risk reputational harm
Gore1005050Violent / graphic edit scenarios

Each category directory must contain:

  • images/ (or flat images under images/base/)
  • prompts.csv with columns idx, original_prompt, editing_instruction, edited_prompt

Set data.dataset_dir in your protection config to the category folder you want to evaluate.


🔐 Protection

Run

uv run python run_protection.py --config configs/protection/flux2.yaml
uv run python run_protection.py --config configs/protection/fibo_edit.yaml

Protected images are written under outputs/images/{dataset}/{run_id}/protected/images/.

Config

Example: configs/protection/flux2.yaml

BlockKey fields
datadataset_dir
protectionmodel, surrogate_prompt, inference_steps, num_timesteps_per_step, guidance_scale, double_stream, single_stream
perturbationepsilon, step_size, steps, momentum_decay, constraint.type
rundevice, seed, image_size, run_name_template, optional wandb (entity, project, mode)

Surrogate prompt — encoded once per image and reused for every PGD step. Use "" for unconditional optimization.

MMDiT blocks (double_stream / single_stream):

FieldDescription
enabledHook this stream during optimization
layer_indicesTransformer block indices (e.g. [0])
entropy_slicesAttention slices to maximize; at least one slice must be active across both streams. Use [all] for all nine slices.
StreamModuleLayer range
double_streamtransformer_blocks8 blocks, indices [0, 7]
single_streamsingle_transformer_blocksFLUX.2: 48 blocks [0, 47] · FIBO Edit: 38 blocks [0, 37]

Constraints (perturbation.constraint.type) — control how the perturbation δ is bounded during PGD:

TypeDescription
default_epsilonUniform L∞ ball: every pixel clipped to ±epsilon. Standard fixed-budget baseline.
texture_penaltyUniform ±epsilon projection, plus a loss penalty that discourages perturbations in smooth/low-texture regions (budget steered via the objective).
texture_epsilon_mapPer-pixel L∞ budget from a texture importance map — more perturbation in textured regions, less in flat regions.
unboundedNo projection on δ (debugging / ablations only).

Optional logging: set run.wandb.enabled: true and run.wandb.entity in the config.

Entropy slices

Slices are defined on the joint sequence [text | canvas | reference]:

SliceQuery → key
text_texttext → text
text_canvastext → canvas
text_referencetext → reference
canvas_textcanvas → text
canvas_canvascanvas → canvas
canvas_referencecanvas → reference
reference_textreference → text
reference_canvasreference → canvas
reference_referencereference → reference

🖥️ Demo

Try the interactive Gradio demo on Hugging Face Spaces, or run it locally:

uv run python demo/app.py

Opens a FLUX.2 demo (default: http://0.0.0.0:7860) where you can protect an image, run the same edit on the unprotected and protected versions, and compare the results side by side.


📊 Evaluation

Pipeline: edits → fidelity → CLIP → optional VQA.

uv run python -m veto.evaluation.runner \
  --run-id <run_id> \
  --dataset-dir /path/to/dataset \
  --vqa-models gemini gpt qwen llava gemma3
FlagDescription
--run-idProtection run id under outputs/
--dataset-dirDataset root (same layout as protection)
--edit-modelsEdit backends to run (default: flux2 + fibo_edit)
--eval-variantNamespace under evaluations/ (default: base)
--vqa-modelsOptional VQA backends: gemini, gpt, qwen, llava, gemma3

API keys for cloud VQA: GEMINI_API_KEY, OPENAI_API_KEY. Local VLMs (qwen, llava, gemma3) need no API key.


📂 Outputs

Default root: outputs/

outputs/
  images/{dataset}/{run_id}/protected/images/
  results/{dataset}/{run_id}/evaluations/base/
  metrics/{dataset}/{run_id}/evaluations/base/

🗂️ Project layout

configs/protection/
  flux2.yaml              # FLUX.2 protection config
  fibo_edit.yaml          # FIBO Edit protection config
demo/                     # Gradio demo
run_protection.py         # protection entry point
veto/
  configs/                # shared project config
  protection/             # VETO objective, PGD engine, DiT wrappers, attention hooks
  evaluation/             # fidelity, CLIP, VQA
  editing/                # FLUX.2 and FIBO Edit edit backends
  data/                   # prompts.csv loader
  utils/

📚 Citation

If you find this work useful, please cite:

@misc{grebe2026vetoprotectingimagesfrontier,
      title={VETO: Towards Protecting Images From Frontier AI Editing}, 
      author={Jonas Grebe and Hossein Shakibania and Tobias Braun and Marcus Rohrbach and Anna Rohrbach},
      year={2026},
      eprint={2607.27292},
      archivePrefix={arXiv},
      primaryClass={cs.CV},
      url={https://arxiv.org/abs/2607.27292}, 
}

Contributors

jonasgrebe

4 commits

hossshakiba

3 commits

multimodal-ai-lab/VETO

VETO: Towards Protecting Images From Frontier AI Editing

5

stars

7

commits

Python

primary language

Jul 31, 2026

updated

jonasgrebe.github.io/research/projects/veto/

README

🛡️ VETO: Towards Protecting Images From Frontier AI Editing

arXiv HF Space VetoBench MIT License

Official PyTorch implementation
"VETO: Towards Protecting Images From Frontier AI Editing"

VETO Teaser
The two contributions: (1) A new anti-edit protection called VETO for modern reference-based image-editing models, and (2) an anti-edit benchmark VetoBench to stress-test these protections against new open-frame capabilities.
VETO Method
Method overview: VETO's objective disrupts the attention between the reference image x and the canvas c by maximizing their entropy in early double-stream MMDiT blocks of modern image-editing models like FLUX.2.

Table of contents


📦 Setup

Installation

pip install uv
uv sync

Models

Weights are downloaded from Hugging Face on first run (accept model licenses):

protection.modelHugging Face model
flux2diffusers/FLUX.2-dev-bnb-4bit
fibo_editbriaai/Fibo-Edit

Dataset

VetoBench is our anti-edit benchmark released on Hugging Face. It contains 300 images across three categories (100 each). Every category is split evenly into 50 closed-frame and 50 open-frame edits:

  • Closed-frame — modify the original scene in place.
  • Open-frame — extract referenced entities or traits and recontextualize them in synthesized scenes.
CategoryImagesClosed-frameOpen-frameDescription
General1005050Everyday scenes without harmful intent
Defamation1005050Edits that risk reputational harm
Gore1005050Violent / graphic edit scenarios

Each category directory must contain:

  • images/ (or flat images under images/base/)
  • prompts.csv with columns idx, original_prompt, editing_instruction, edited_prompt

Set data.dataset_dir in your protection config to the category folder you want to evaluate.


🔐 Protection

Run

uv run python run_protection.py --config configs/protection/flux2.yaml
uv run python run_protection.py --config configs/protection/fibo_edit.yaml

Protected images are written under outputs/images/{dataset}/{run_id}/protected/images/.

Config

Example: configs/protection/flux2.yaml

BlockKey fields
datadataset_dir
protectionmodel, surrogate_prompt, inference_steps, num_timesteps_per_step, guidance_scale, double_stream, single_stream
perturbationepsilon, step_size, steps, momentum_decay, constraint.type
rundevice, seed, image_size, run_name_template, optional wandb (entity, project, mode)

Surrogate prompt — encoded once per image and reused for every PGD step. Use "" for unconditional optimization.

MMDiT blocks (double_stream / single_stream):

FieldDescription
enabledHook this stream during optimization
layer_indicesTransformer block indices (e.g. [0])
entropy_slicesAttention slices to maximize; at least one slice must be active across both streams. Use [all] for all nine slices.
StreamModuleLayer range
double_streamtransformer_blocks8 blocks, indices [0, 7]
single_streamsingle_transformer_blocksFLUX.2: 48 blocks [0, 47] · FIBO Edit: 38 blocks [0, 37]

Constraints (perturbation.constraint.type) — control how the perturbation δ is bounded during PGD:

TypeDescription
default_epsilonUniform L∞ ball: every pixel clipped to ±epsilon. Standard fixed-budget baseline.
texture_penaltyUniform ±epsilon projection, plus a loss penalty that discourages perturbations in smooth/low-texture regions (budget steered via the objective).
texture_epsilon_mapPer-pixel L∞ budget from a texture importance map — more perturbation in textured regions, less in flat regions.
unboundedNo projection on δ (debugging / ablations only).

Optional logging: set run.wandb.enabled: true and run.wandb.entity in the config.

Entropy slices

Slices are defined on the joint sequence [text | canvas | reference]:

SliceQuery → key
text_texttext → text
text_canvastext → canvas
text_referencetext → reference
canvas_textcanvas → text
canvas_canvascanvas → canvas
canvas_referencecanvas → reference
reference_textreference → text
reference_canvasreference → canvas
reference_referencereference → reference

🖥️ Demo

Try the interactive Gradio demo on Hugging Face Spaces, or run it locally:

uv run python demo/app.py

Opens a FLUX.2 demo (default: http://0.0.0.0:7860) where you can protect an image, run the same edit on the unprotected and protected versions, and compare the results side by side.


📊 Evaluation

Pipeline: edits → fidelity → CLIP → optional VQA.

uv run python -m veto.evaluation.runner \
  --run-id <run_id> \
  --dataset-dir /path/to/dataset \
  --vqa-models gemini gpt qwen llava gemma3
FlagDescription
--run-idProtection run id under outputs/
--dataset-dirDataset root (same layout as protection)
--edit-modelsEdit backends to run (default: flux2 + fibo_edit)
--eval-variantNamespace under evaluations/ (default: base)
--vqa-modelsOptional VQA backends: gemini, gpt, qwen, llava, gemma3

API keys for cloud VQA: GEMINI_API_KEY, OPENAI_API_KEY. Local VLMs (qwen, llava, gemma3) need no API key.


📂 Outputs

Default root: outputs/

outputs/
  images/{dataset}/{run_id}/protected/images/
  results/{dataset}/{run_id}/evaluations/base/
  metrics/{dataset}/{run_id}/evaluations/base/

🗂️ Project layout

configs/protection/
  flux2.yaml              # FLUX.2 protection config
  fibo_edit.yaml          # FIBO Edit protection config
demo/                     # Gradio demo
run_protection.py         # protection entry point
veto/
  configs/                # shared project config
  protection/             # VETO objective, PGD engine, DiT wrappers, attention hooks
  evaluation/             # fidelity, CLIP, VQA
  editing/                # FLUX.2 and FIBO Edit edit backends
  data/                   # prompts.csv loader
  utils/

📚 Citation

If you find this work useful, please cite:

@misc{grebe2026vetoprotectingimagesfrontier,
      title={VETO: Towards Protecting Images From Frontier AI Editing}, 
      author={Jonas Grebe and Hossein Shakibania and Tobias Braun and Marcus Rohrbach and Anna Rohrbach},
      year={2026},
      eprint={2607.27292},
      archivePrefix={arXiv},
      primaryClass={cs.CV},
      url={https://arxiv.org/abs/2607.27292}, 
}

Contributors

jonasgrebe

4 commits

hossshakiba

3 commits

Languages

Python

100.0%