watermarks-remover

Remover marcas de proveniência de IA de vários fornecedores: higiene de texto Unicode, ganchos de reescrita estatística e metadados C2PA de PNG/JPEG/SVG/PDF/DOCX/HTML/MD

Documentação

_ _ _ ____ ___ ____ ____ _  _ ____ ____ _  _ ____    ____ ____ _  _ ____ _  _ ____ ____
| | | |__|  |  |___ |__/ |\/| |__| |__/ |_/  [__  __ |__/ |___ |\/| |  | |  | |___ |__/
|_|_| |  |  |  |___ |  \ |  | |  | |  \ | \_ ___]    |  \ |___ |  | |__|  \/  |___ |  \

watermarks-remover

CI Release Stars Forks

Habilidade de agente + scripts Python da stdlib para remover marcas de proveniência de IA de múltiplos fornecedores de texto e arquivos — para privacidade e higiene em conteúdo que você possui.

CamadaAlvoComo
AUnicode invisível, espaços exóticos, bidi, caracteres de tagScripts Python determinísticos
BMarcas d'água de texto estatísticas (amostragem de tokens)Reescrita do agente + hook opcional rewrite_text.py
ArquivosC2PA / EXIF / XMP / propriedades de documentosPNG, JPEG, WebP, SVG, PDF, DOCX, ODT, HTML, Markdown

Fornecedores / ecossistemas (nível de classe): Claude, Gemini / SynthID-Text, superfícies de proveniência OpenAI, marcas estilo Kirchenbauer open-LLM.

Última versão: v0.4.0

Caminho da habilidade: skills/remove-ai-marks/
(migração: anteriormente remove-claude-marks; alias de barra /remove-claude-marks ainda documentado)

Instalação (habilidade de agente)

# Grok Build / project-local
mkdir -p .grok/skills
ln -sfn "$(pwd)/skills/remove-ai-marks" .grok/skills/remove-ai-marks

# User-global Grok
mkdir -p ~/.grok/skills
ln -sfn "$(pwd)/skills/remove-ai-marks" ~/.grok/skills/remove-ai-marks

Invoque com /remove-ai-marks ou peça para "remover marcas d'água de IA / C2PA / marcas do Claude / texto classe SynthID."

Ferramentas de sistema opcionais (usadas automaticamente quando presentes):

FerramentaFunção
c2patoolInspecionar manifestos C2PA
exiftoolRemoção de metadados residuais (especialmente PDF)
qpdfReconstrução estrutural de PDF — necessária para uma remoção real de PDF (veja abaixo)

Os scripts principais precisam apenas da stdlib do Python 3.10+. Chamadas de modelo da Camada B são opcionais.

Uso rápido (scripts)

SCRIPTS=skills/remove-ai-marks/scripts

# Unified inspect / clean
python3 "$SCRIPTS/inspect_file.py" draft.md
python3 "$SCRIPTS/clean_file.py" draft.md -o draft.cleaned.md
python3 "$SCRIPTS/clean_file.py" photo.png -o photo.cleaned.png
python3 "$SCRIPTS/clean_file.py" notes.docx -o notes.cleaned.docx

# Text Layer A
python3 "$SCRIPTS/inspect_text.py" draft.md
python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats

# Layer B rewrite hook (default: print prompt only — no model required)
python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --strength paraphrase
# Optional local Ollama (loopback only by default — remote endpoints require
# WATERMARKS_REWRITE_ALLOW_REMOTE=1 or --allow-remote):
# WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \
#   python3 "$SCRIPTS/rewrite_text.py" draft.md -o draft.rewritten.md
# API keys are read from WATERMARKS_REWRITE_API_KEY only (never argv).

# Images
python3 "$SCRIPTS/inspect_image.py" shot.png
python3 "$SCRIPTS/clean_image.py" shot.png -o shot.cleaned.png

As ferramentas de texto recusam entrada binária

inspect_text.py, clean_text.py e rewrite_text.py operam em texto. Apontados para um .docx, .pdf ou imagem, eles costumavam decodificar os bytes comprimidos e relatar quaisquer codepoints que aparecessem — ruído que rastreia a compressão, não o conteúdo — e clean_text.py então escrevia esses bytes corrompidos de volta, destruindo o arquivo. Agora eles recusam entrada binária e indicam a ferramenta que lida com isso:

python3 "$SCRIPTS/inspect_text.py" report.docx
# refusing to treat report.docx as text: it looks like a ZIP container (DOCX, ODT, …).
# Use inspect_file.py / clean_file.py, which route by format,
# or pass --force-text to scan the raw bytes anyway.

A detecção é feita por número mágico mais uma proporção de bytes de controle, então texto em codificações diferentes de UTF-8 continua funcionando. --force-text substitui isso em todos os lugares.

Pontuação opcional de pixels SynthID

inspect_image.py e clean_image.py podem reportar uma pontuação de confiança SynthID no domínio de pixels quando um checkout externo de aloshdenny/reverse-SynthID está disponível. O scoreador não é incluído: ele é carregado em tempo de execução do seu checkout, e seu código permanece sob a Licença de Pesquisa não comercial do projeto upstream.

Opção 1: bootstrap com um comando (sem Docker)

SCRIPTS=skills/remove-ai-marks/scripts

# Clones upstream, creates a venv, and installs scorer-only dependencies.
"$SCRIPTS/setup_synthid.sh"

# Score an image (default checkout: ~/reverse-SynthID).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/score_synthid.py" shot.png

# Or surface the score from inspect / clean (same venv Python).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/inspect_image.py" shot.png

setup_synthid.sh aceita --dir PATH, --ref REF e --full (instale o requirements.txt upstream completo, que adiciona torch/diffusers para o bypass de VAE upstream que este projeto não usa).

No Windows use setup_synthid.ps1 (-Dir, -Ref, -Full), que cria o venv em .venv\Scripts\ — o layout que image_meta.py já procura em os.name == "nt".

Opção 2: build Docker local

make docker-synthid-build
# Run unprivileged and with a read-only rootfs; the scorer only needs to read
# /data and write to stdout/tmp.
docker run --rm \
  --user "$(id -u):$(id -g)" \
  --read-only --tmpfs /tmp \
  -v "$(pwd):/data" \
  watermarks-remover-synthid-scorer /data/shot.png

A imagem é construída localmente a partir do código-fonte upstream no momento do build. Ela não é publicada, portanto não redistribui o código upstream.

A pontuação V4 usa artifacts/spectral_codebook_v4.npz do checkout upstream (~220 MB). Isso é somente detecção/pontuação — não remove marcas d'água de pixels.

Remoção opcional de pixels com CtrlRegen

Para marcas d'água de imagem no domínio de pixels (classe SynthID, StegaStamp, Tree-Ring, StableSignature), um backend externo opcional executa o pipeline CtrlRegen (regeneração controlável com ControlNet + IP-Adapter DINOv2). O backend é mertizci/noai-watermark, uma reimplementação mantida do método CtrlRegen do ICLR 2025 com mosaico automático.

O backend não é incluído e não traz arquivo LICENSE, então é tratado como todos-os-direitos-reservados: ele é clonado em um commit fixado e carregado em tempo de execução.

Bootstrap

SCRIPTS=skills/remove-ai-marks/scripts

# Clones upstream (pinned commit), creates a venv, installs torch + deps.
"$SCRIPTS/setup_ctrlregen.sh"

# Standalone removal (default checkout: ~/noai-watermark).
NOAI_WATERMARK_DIR=~/noai-watermark \
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_ctrlregen.py" shot.png -o shot.ctrlregen.png

No Windows use setup_ctrlregen.ps1 (mesmas flags que -Dir, -Ref, -Python); o venv fica em .venv\Scripts\, que clean_image.py já resolve. Ele escolhe o índice de wheels do torch pela capacidade de computação da GPU em vez da versão CUDA que nvidia-smi imprime — esse número é o máximo que o driver suporta, e drivers são compatíveis com versões anteriores, então derivar a tag do wheel a partir dele instala cu130 em uma placa Pascal cujos kernels foram removidos em cu128. O script força cu126 abaixo da capacidade de computação 7.5 e então verifica o resultado com torch.cuda.get_arch_list().

A partir de clean_image.py

NOAI_WATERMARK_DIR=~/noai-watermark \
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
  -o shot.cleaned.png --remove-pixel ctrlregen

Ordem das operações: remoção de metadados primeiro, depois remoção de pixels com CtrlRegen, e depois uma pontuação reversa opcional de SynthID antes/depois (quando REVERSE_SYNTHID_DIR também estiver definido).

A força é conservadora por padrão (--ctrlregen-strength 0.25), porque força maior remove mais marca d'água mas regenera mais da imagem. Presets documentados: 0.15 mínimo / 0.25 padrão / 0.35 balanceado / 0.5 agressivo / 0.7 máximo (o padrão do backend é 0,5). --ctrlregen-steps padrão é 50 (etapas de denoising efetivas ≈ etapas × força).

Tamanho da imagem (limite nativo 512×512)

CtrlRegen é um ControlNet 512×512 do Stable Diffusion 1.5. O backend resolve isso para entradas arbitrárias, então nenhum mosaico extra é exposto aqui:

  • ≤512 px: passagem única — centralizar/redimensionar para 512, regenerar, redimensionar de volta.
  • >512 px: mosaico sobreposto automático (tiles de 512 px, sobreposição de 192 px), largura/altura alinhadas a múltiplos de 8, depois emendas com mesclagem de cosseno.
  • Qualquer caminho: a saída é redimensionada para o tamanho original e com correspondência de cores com a imagem original.

Imagens muito grandes (ex.: 4K) produzem muitos tiles, então as execuções escalam com a contagem de tiles (mais lentas e com maior VRAM). Reduza a escala de entradas grandes quando for prático; o tamanho do tile e a sobreposição são fixados no upstream e não são expostos como flags.

Computação, modelos com acesso restrito e verificação

Espere ~10 GB de downloads de modelos; uma GPU é fortemente recomendada e execuções em CPU são lentas. Alguns modelos upstream têm acesso restrito, então exporte HF_TOKEN (somente env — nunca argv). clean_ctrlregen.py se recusa a instalar dependências automaticamente; execute setup_ctrlregen.sh primeiro.

Não há detector local para StegaStamp/Tree-Ring/StableSignature, então o único sinal local é a pontuação reversa de SynthID (um substituto). Quando disponível, clean_image.py --remove-pixel ctrlregen reporta essa pontuação antes/depois; a verificação oficial do Google SynthID continua sendo a autoridade final.

Docker

make docker-ctrlregen-build
docker run --rm -e HF_TOKEN="$HF_TOKEN" \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd):/data" \
  watermarks-remover-ctrlregen /data/shot.png -o /data/shot.ctrlregen.png

Verificação opcional de marcas d'água de texto com MarkLLM

Para experimentos controlados, um harness externo opcional envolve THU-BPM/MarkLLM (Apache-2.0) para marcar texto de teste com marca d'água e re-detectá-lo após uma reescrita da Camada B — ex.: provar que uma marca KGW (Kirchenbauer, sua linha "open-LLM") ou SynthID-Text (linha Gemini) desaparece sob sua reescrita. É um harness de verificação, não um oráculo: a detecção do MarkLLM é válida apenas contra a mesma configuração de esquema + chaves usadas na geração, e não pode certificar que um detector de fornecedor falhará.

O backend não é incluído. setup_markllm.sh clona o upstream em um commit fixado, cria um venv e instala dependências fixadas (torch + transformers); o modelo de pontuação (padrão facebook/opt-1.3b, Apache-2.0) é baixado do Hugging Face na primeira execução.

SCRIPTS=skills/remove-ai-marks/scripts

# Bootstrap (clones upstream, creates ~/MarkLLM/.venv, installs deps).
"$SCRIPTS/setup_markllm.sh"

# Generate watermarked + unwatermarked sample text under the KGW scheme.
MARKLLM_DIR=~/MarkLLM \
  ~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" watermark prompt.txt \
    --scheme kgw -o wm.txt -o2 plain.txt

# Detect the scheme mark in a text file.
MARKLLM_DIR=~/MarkLLM \
  ~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" detect wm.txt --scheme kgw --json

Verificação em torno de uma reescrita da Camada B: passe --markllm-scheme para rewrite_text.py (com --markllm-dir), e ele registra a detecção do MarkLLM antes/depois mais uma flag cleared:

export WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2
MARKLLM_DIR=~/MarkLLM \
  python3 "$SCRIPTS/rewrite_text.py" wm.txt -o wm.rewritten.txt \
    --markllm-scheme kgw --markllm-dir "$HOME/MarkLLM" --json-stats

Se o backend não estiver configurado ou suas dependências estiverem ausentes, a reescrita prossegue e o relatório observa que a verificação não estava disponível. Uma GPU é recomendada; execuções em CPU funcionam mas são lentas, e o download do modelo tem alguns GB.

Ajustes de endurecimento:

  • --offline no adaptador (ou qualquer execução MarkLLM) carrega o modelo de pontuação do cache do Hugging Face apenas — zero tráfego de rede; falha rapidamente se não estiver em cache. Código remoto personalizado nunca é executado (transformers trust_remote_code nunca é habilitado).
  • WATERMARKS_MARKLLM_RLIMIT_AS=<bytes> (env, POSIX) aplica um limite de espaço de endereço ao subprocesso MarkLLM gerado por rewrite_text.py. Desativado por padrão porque torch/CUDA geralmente precisa de grandes espaços de endereço.
  • Arquivos de configuração são limitados a 1 MiB; o checkout upstream e a imagem base são fixados por SHA/digest.

Docker

make docker-markllm-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data" \
  watermarks-remover-markllm detect /data/wm.txt --scheme kgw --json

Harness opcional de marcas d'água de imagem com MarkDiffusion

Para experimentos controlados em imagens, um harness externo opcional envolve THU-BPM/MarkDiffusion (Apache-2.0), um kit de ferramentas de marcação d'água generativa para modelos de difusão latente (ele insere marcas — não as remove). Usamos para três coisas:

  1. Harness de verificação (como o MarkLLM, mas para imagens): marque uma imagem de teste com um esquema, execute a remoção e re-detecte com a mesma configuração de esquema — ex.: prove que uma marca da classe Tree-Ring desaparece sob seu pipeline. É um harness de verificação, não um oráculo: a detecção exige o modelo gerador (e chaves para esquemas baseados em chaves), então não pode certificar que um detector de fornecedor falhará em uma imagem arbitrária.
  2. Motor opcional de remoção de pixels: seu ataque de regeneração DiffusionPurification é exposto como clean_image.py --remove-pixel diffusion, uma alternativa ao CtrlRegen. É regeneração cega (sem condicionamento ControlNet), então desvia o conteúdo da imagem mais que o CtrlRegen — força padrão conservadora (0.3), tratada como fallback/comparação, nunca como garantia.
  3. Detector local do mesmo esquema para marcas da classe Tree-Ring, preenchendo parcialmente a lacuna "sem detector local para StegaStamp/Tree-Ring/StableSignature" (ele cobre Tree-Ring/Ring-ID/Gaussian-Shading etc., não StegaStamp / StableSignature / SynthID-media).

O backend não é incluído. setup_markdiffusion.sh cria um venv e instala markdiffusion==1.0.2 do PyPI (fixado), com torch instalado do índice de plataforma correto; --checkout instala um clone editável em um commit fixado em vez disso. O modelo Stable Diffusion (padrão huanzi05/stable-diffusion-2-1-base) é baixado do Hugging Face na primeira execução.

SCRIPTS=skills/remove-ai-marks/scripts

# Bootstrap (PyPI pin default; creates ~/markdiffusion/.venv, installs deps).
"$SCRIPTS/setup_markdiffusion.sh"

# 1. Generate a Tree-Ring watermarked image (+ unwatermarked control).
echo "a red fox in snow" > /tmp/prompt.txt
MARKDIFFUSION_DIR=~/markdiffusion \
  ~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" watermark \
    /tmp/prompt.txt -o wm.png -o2 plain.png --scheme tr --json

# 2. Remove with the DiffusionPurification regeneration attack.
MARKDIFFUSION_DIR=~/markdiffusion \
  ~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" purify \
    wm.png -o wm.purified.png --purification-strength 0.3 --json

# 3. Re-detect with the SAME scheme config.
MARKDIFFUSION_DIR=~/markdiffusion \
  ~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" detect \
    wm.purified.png --scheme tr --detector-type l1_distance --json

Ou execute a purificação como parte do pipeline normal de imagens:

MARKDIFFUSION_DIR=~/markdiffusion \
  ~/markdiffusion/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
    -o shot.cleaned.png --remove-pixel diffusion

Ajustes de endurecimento espelham o harness MarkLLM: --offline carrega o modelo do cache do Hugging Face apenas (zero tráfego de rede, sem código remoto), HF_TOKEN é somente env (nunca argv), configurações de algoritmo são limitadas a 1 MiB, e o subprocesso recebe os mesmos limites de recursos mais altos que o CtrlRegen.

Docker

make docker-markdiffusion-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data" \
  watermarks-remover-markdiffusion detect /data/wm.png --scheme tr --json

A imagem instala um torch de CPU; usuários de CUDA devem executar setup_markdiffusion.sh no host em vez disso. Downloads de modelos ainda acessam o hub HF na primeira execução.

Matriz de cobertura

CanalClaudeGemini/SynthIDOpenAIOpen-LLM
Texto Unicode / baseado em ediçãoCamada ACamada ACamada ACamada A
Texto de amostragem estatísticaCamada B melhor esforçoCamada B melhor esforçoCamada B se presenteCamada B melhor esforço
C2PA / metadados de arquivoSim (formatos listados)Sim quando presenteSim quando presenteSim quando presente
Marcas de pixel em imagensFora do escopoPontuação SynthID opcional + remoção CtrlRegen (externo); detecção opcional do mesmo esquema MarkDiffusion + remoção DiffusionPurification (externo)Fora do escopoRemoção opcional CtrlRegen / MarkDiffusion (externo)
Backdoors de treinamentoFora do escopoFora do escopoFora do escopoFora do escopo

Detalhes: skills/remove-ai-marks/references/vendor-notes.md, mark-classes.md.


Como funciona a marcação de texto (resumo)

As marcas d'água de LLM modernas geralmente escondem um sinal em quais tokens são escolhidos (viés generativo / de amostragem), não apenas em caracteres invisíveis. Esquemas baseados em edição inserem regras de Unicode ou sinônimos. Esquemas de arquivo anexam C2PA ou metadados do gerador.

  • Camada A remove portadores Unicode baseados em edição (testável).
  • Camada B ataca marcas d'água de amostragem via reescrita pesada (melhor esforço; ataques padrão da literatura, como paráfrase / retro-tradução).
  • Limpadores de arquivo removem C2PA/XMP/props de contêineres suportados.

Até que os fornecedores publiquem detectores e chaves públicas, nenhuma ferramenta pode certificar honestamente "isso falha na verificação oficial." Os relatórios devem separar trabalho verificável de trabalho de melhor esforço.

Prefira um modelo não-origem para a Camada B (não reescreva texto do Claude com o Claude se você está tentando evitar re-marcação).


Aviso: o que custa remover uma marca d'água de texto

As marcas d'água de texto vivem na própria redação: o sinal está distribuído entre as escolhas de tokens, então quase toda frase carrega um pouco dele. Duas consequências decorrem disso, e são por isso que a Camada B é honestamente descrita como melhor esforço em vez de uma borracha mágica.

  1. Remoção significa reformulação, não reestruturação. Embaralhar parágrafos, mudar títulos ou pequenos retoques mal movem o sinal. Remover uma marca estatística exige reescrever uma fração substancial do texto — frase por frase, não seção por seção.

  2. A reformulação degrada o texto. Qualquer reescrita substitui as escolhas de palavras originais pelas do modelo de reescrita, o que achata tom, voz e precisão. Em texto de produção (SEO, marketing, trabalho de cliente), essa degradação é real e frequentemente visível para as pessoas que mais se importam com a escrita. É como pegar texto de um modelo de primeira linha e pedir a um modelo menos capaz para reescrevê-lo do zero: o resultado não pode exceder o teto do modelo de reescrita.

O que leva à pergunta honesta de ciclo completo:

Se o plano é reescrever o texto com um modelo mais barato de qualquer forma, por que pagar por um modelo premium em primeiro lugar? Gerar diretamente com o modelo mais barato é mais simples, mais barato e produz o mesmo — ou melhor — resultado final.

A Camada B faz sentido quando você especificamente quer o pensamento e o rascunho do modelo premium e aceita uma passada de reescrita para satisfazer um requisito de higiene ou privacidade — não como um caminho barato para texto sem marca.

Quando pular a Camada B:

  • Qualidade importa mais que higiene: use o caminho sem perdas — limpeza Unicode da Camada A mais os limpadores de metadados de arquivo — e mantenha a prosa original.
  • Reescrevendo de qualquer forma: use um modelo não-origem (reescrever com o modelo de origem pode re-marcar o texto) e lembre que o risco residual permanece — nenhuma ferramenta pode certificar que um detector do fornecedor falhará.

Formatos de arquivo

FormatoInspecionarLimpar
PNG / JPEG / WebPChunks C2PA / APP11 / RIFF C2PA, dicas XMP de IARemover segmentos de metadados
SVG<metadata>, XMPRemover blocos
PDFByte/XMP + ferramentas opcionaisexiftool depois qpdf; degradado sem ambos
DOCXdocProps / customXmlLimpar props, remover customXml
ODTmeta.xmlRemover meta de gerador / meta tipo IA
HTMLmeta, JSON-LD, data-ai*Remover tags/atributos
MarkdownChaves de IA no frontmatter YAMLRemover chaves + corpo da Camada A

Por que PDF precisa de qpdf, não apenas exiftool

O ExifTool escreve PDFs incrementalmente. exiftool -all= anexa um bloco %BeginExifToolUpdate que libera o objeto Info e remove /Info do trailer — mas os bytes de metadados originais permanecem no arquivo verbatim, e o próprio exiftool pode desfazer a edição com -PDF-update:all=. O comando sai com 0, os visualizadores não mostram metadados, e o arquivo fica maior, o que é o sinal revelador.

Para uma ferramenta de remoção de proveniência, isso é um vazamento silencioso, então clean_pdf segue a passada do exiftool com qpdf --linearize, que re-serializa o documento a partir do seu grafo de objetos e remove os objetos agora não referenciados. Sem qpdf instalado, a limpeza ainda roda, mas avisa:

warning: exiftool PDF edits are incremental — the original metadata bytes
remain recoverable; install qpdf for a structural rewrite

A remoção de marca d'água no domínio de pixels agora está disponível como um backend externo opcional CtrlRegen (veja acima); é um removedor regenerador, não uma garantia. Vinculação suave C2PA (marca d'água no conteúdo que pode re-vincular um manifesto remoto de Content Credentials após a remoção de metadados) permanece fora do escopo. Remover C2PA com vinculação rígida não limpa esses canais.

Risco residual após uma limpeza

Esta ferramenta relata remoções verificáveis (contagens Unicode, ações de metadados) e reescritas de melhor esforço da Camada B. Ela não pode certificar que detectores de fornecedores falharão.

Para verificar sinais residuais você mesmo (opcional, externo):

CanalO que removemosO que pode permanecerVerificação externa (exemplos)
C2PA com vinculação rígida / EXIF / XMPSimMarcas de vinculação suave / pixelc2patool, verificação de Content Credentials
Mídia classe SynthIDRemoção opcional de pixels (CtrlRegen externo); pontuação local caso contrárioMarca d'água de áudio/vídeo; marca d'água de pixel residual após remoçãoFerramentas do provedor (ex.: Google SynthID / detector Vertex onde oferecido); pontuador local opcional reverse-SynthID
Texto estatísticoReescrita de melhor esforçoMarcas fortes após edição leveNenhum detector universal público; ferramentas do fornecedor quando disponíveis

Contexto industrial de duas camadas (C2PA + marca d'água imperceptível): guia de PM do Institute of AI.


Opções de remoção (resumo)

OpçãoRemoveNotas
Limpeza Unicode (Camada A)ZWSP, bidi, tags, espaços exóticos, …Padrão seguro para texto
Reescrita (Camada B)Marcas estatísticas de token (melhor esforço)Sempre oferecida pela skill; custa estilo — veja Aviso
Remoção de contêiner/metadadosProveniência de arquivoVeja tabela de formatos
Remoção de pixels CtrlRegen (opcional)Marcas de imagem no domínio de pixels (classe SynthID, StegaStamp, Tree-Ring, StableSignature)Backend externo; computação pesada; força padrão conservadora
Remoção de pixels DiffusionPurification (opcional)Marcas de imagem no domínio de pixels (classe Tree-Ring)Backend MarkDiffusion; regeneração cega (mais deriva que CtrlRegen); força padrão conservadora
Modelos locais de peso abertoEvitar re-marcação com o modelo de origemAlternativa operacional

Matriz: skills/remove-ai-marks/references/removal-matrix.md.

Ética e aviso

Veja skills/remove-ai-marks/references/ethics.md. Para privacidade e pesquisa no seu conteúdo — não fraude acadêmica ou alegações falsas de "escrito por humano".

Uso responsável: Este projeto é para conteúdo que você possui ou está autorizado a processar. Os usuários devem aderir às regulamentações locais e usá-lo de forma responsável. Os desenvolvedores isentam-se de qualquer responsabilidade por uso indevido potencial pelos usuários.

Testes

python3 -m venv .venv && .venv/bin/pip install pytest
.venv/bin/python -m pytest          # or: make test
make smoke                          # quick CLI smoke on fixtures

Changelog

Não lançado

  • Novo harness opcional de marca d'água de imagem MarkDiffusion (THU-BPM/MarkDiffusion externo, Apache-2.0): markdiffusion_harness.py com subcomandos watermark / detect / purify para nove esquemas de imagem (Tree-Ring, Ring-ID, ROBIN, WIND, SFW, Gaussian-Shading, GaussMarker, PRC, SEAL)
  • clean_image.py --remove-pixel diffusion executa o ataque de regeneração DiffusionPurification do MarkDiffusion como um motor alternativo de remoção de pixels (força conservadora padrão 0.3)
  • Bootstrap setup_markdiffusion.sh (pin PyPI 1.0.2; clone editável --checkout em commit fixado) + requirements-markdiffusion.txt + Dockerfile.markdiffusion e Makefile bootstrap-markdiffusion / smoke-markdiffusion / docker-markdiffusion-build / docker-markdiffusion-help
  • Testes baseados em mock (tests/test_markdiffusion_harness.py) — sem torch no CI; documento de referência references/markdiffusion.md
  • Docs: ressalva de verificação apenas mesmo-esquema (não é oráculo de detector de fornecedor) e ressalva de deriva de regeneração cega no README, SKILL.md, removal-matrix.md, markdiffusion.md
  • Adicionar inspeção WebP apenas stdlib e limpeza de metadados para chunks RIFF C2PA, XMP, EXIF e perfil ICC
  • Novo harness opcional MarkLLM (checkout externo THU-BPM/MarkLLM, Apache-2.0): detect_text_watermark.py com subcomandos detect / watermark para esquemas KGW e SynthID
  • rewrite_text.py --markllm-scheme executa detecção antes/depois em torno de uma reescrita da Camada B (controlado por env; relata cleared)
  • Bootstrap setup_markllm.sh + requirements-markllm.txt (dependências fixadas) + Dockerfile.markllm e Makefile bootstrap-markllm / smoke-markllm / docker-markllm-build / docker-markllm-help
  • Testes baseados em mock (tests/test_markllm_detect.py, 21 casos) — sem torch no CI
  • Docs: ressalva do harness de verificação (apenas mesma-configuração, não é oráculo de detector de fornecedor) no README, SKILL.md, removal-matrix.md, vendor-notes.md
  • Endurecimento: carregamento de modelo apenas cache --offline (sem egress HF, sem código remoto), limite de config de 1 MiB, WATERMARKS_MARKLLM_RLIMIT_AS opcional no subprocesso de reescrita, torch fixado no Dockerfile e verificação de SHA de clone em Dockerfile.markllm

v0.4.0 — remoção de pixels, confiança de achados, correções de Windows e falsos positivos

Remoção de pixels CtrlRegen opcional (backend externo)

  • Remoção opcional de marca d'água no domínio de pixels via checkout externo mertizci/noai-watermark: adaptador clean_ctrlregen.py + bootstrap setup_ctrlregen.sh (commit fixado, checkout esparso, venv, verificação SHA), além de Dockerfile.ctrlregen e make bootstrap-ctrlregen / docker-ctrlregen-build / smoke-ctrlregen
  • clean_image.py --remove-pixel ctrlregen executa remoção de metadados → remoção CtrlRegen → pontuação opcional reverse-SynthID antes/depois; inspect_image.py sugere a flag em pontuação SynthID alta
  • Força padrão conservadora 0.25 (predefinições 0.15/0.25/0.35/0.5/0.7); o pipeline nativo 512×512 é automaticamente dividido em tiles pelo backend para imagens maiores; o subprocesso torch recebe limites de recursos maiores substituíveis por env
  • O backend nunca é empacotado: noai-watermark não inclui arquivo LICENSE (tratado como todos-os-direitos-reservados), e seus caminhos de código de auto-instalação/reinício são contornados usando CtrlRegenEngine diretamente

Confiança de achados e auditorias agregadas

  • Achados agora são classificados confirmed / probable / informational / likely_false_positive, expostos em JSON de texto/imagem/contêiner e relatórios humanos
  • Novos relatórios agregados audit_dir.py (árvore recursiva) e audit_website.py (descoberta de sitemap + rastreamento); documentados no SKILL.md

Correções de falsos positivos

  • DOCX: escanear apenas docProps/customXml, não o corpo visível (#14)
  • Texto Camada A: preservar emoji VS16/ZWJ após uma base de emoji; nova flag paranóica --strip-emoji-glue (#22)
  • HTML: tratar tags de gerador CMS como informativas, não metadados de IA (#13)
  • PDF: excluir payloads de stream da varredura de bytes de marcador de IA (#13)
  • Relatórios de inspeção observam caminhos não suportados/melhor esforço

Suporte Windows

  • Condicionar preexec_fn e os.fchmod apenas POSIX para que gravações e ferramentas opcionais rodem no Windows (#15, #23)
  • Reconfigurar stdio para UTF-8 para que streams redirecionados do Windows não levantem mais erro em Unicode invisível; perna de CI Windows + teste de fumaça CLI (#23)

Docs e cadeia de suprimentos

  • Seção README CtrlRegen + referências de pesquisa (CtrlRegen, UnMarker, ressalva de stealth forense), aviso de uso responsável; atualizações SKILL/matriz/notas-de-fornecedor/ética
  • Config Dependabot + CODEOWNERS de caminho de segurança; bump scipy/numpy/opencv-python/scikit-learn/pywavelets e imagem base para Python 3.14-slim
  • Testes CtrlRegen baseados em mock (sem torch no CI)

v0.3.2 — endurecimento de segurança (gravações seguras, cliente HTTP, cadeia de suprimentos CI)

  • Escritas de saída seguras e atômicas: todo cleaner agora grava via arquivo temporário + rename atômico (safe_write_bytes / safe_write_text), recusa destinos com symlink e cria backups .bak pelo mesmo caminho seguro — symlinks pré-posicionados (ex.: em /tmp ou pastas de download) não conseguem mais redirecionar uma escrita limpa para um arquivo arbitrário
  • Endurecimento do cliente HTTP rewrite_text.py: redirecionamentos são recusados de imediato, então uma chave de API no cabeçalho Authorization nunca pode ser reenviada a um host não validado; endpoints não loopback são negados por padrão (adesão com --allow-remote ou WATERMARKS_REWRITE_ALLOW_REMOTE=1); apenas esquemas http(s) são aceitos; --api-key foi removido — chaves são somente via ambiente com WATERMARKS_REWRITE_API_KEY
  • Limites de recursos: entrada máxima padrão de 1 GiB → 256 MiB, novo limite de 64 MiB para stdin, orçamento de zip DOCX/ODT de 512 MiB → 128 MiB, e RLIMIT_AS/RLIMIT_FSIZE aplicados a subprocessos de exiftool/c2patool/SynthID (todos os limites podem ser sobrescritos via ambiente)
  • Cadeia de suprimentos: ações de CI fixadas por SHA com permissions: contents: read, dependências de desenvolvimento fixadas (requirements-dev.txt), uma etapa de pip-audit e um novo fluxo de trabalho CodeQL; a imagem Docker agora roda como usuário sem privilégios com pip fixado
  • Dependências do scorer: Pillow atualizada de 10.4.0 → 12.3.0 (24 CVEs conhecidos); uso da API verificado contra o commit upstream fixado
  • Testes: 18 novos testes de regressão de segurança (60 no total, todos passando)

v0.3.1 — reescrita mais forte da marca d'água estatística da Camada B

  • A paráfrase padrão de rewrite_text.py agora executa um ataque explícito de escolha de palavras + sintaxe (ordem das orações, conectores, palavras de transição, fronteiras de frases, palavras funcionais) em vez de uma reescrita genérica
  • Nova --strength humanize: passagem zero-shot "escreva como um humano" direcionada a frases com estilo artificial típico de IA
  • Nova --strength code: reescreve comentários, docstrings e literais de string, e renomeia identificadores locais preservando o comportamento e os nomes da API pública
  • A passagem estrutural agora emite "prosa humana natural e variada" em vez do típico "estilo profissional claro" de IA
  • Nova --temperature (padrão 0.9) para backends Ollama e compatíveis com OpenAI
  • Nova --candidates N: gera N reescritas e seleciona a mais divergente lexicalmente (distância de bigrama Jaccard) com proteção contra deriva de comprimento
  • Higiene de modelo mais forte: prefere modelos locais de pesos abertos e evita qualquer fornecedor com marca d'água conhecida, não apenas a origem suspeita
  • Relato de risco residual agora distingue texto curto/altamente previsível (risco menor) de prosa longa e de alta entropia (risco maior)
  • Documentação atualizada em SKILL.md, removal-matrix.md e vendor-notes.md; testes cobrem novos prompts, pontuação de divergência e seleção de candidatos

v0.3.0 — pontuação opcional de pixels SynthID

  • Scorer opcional de SynthID no domínio de pixels via checkout externo de aloshdenny/reverse-SynthID (score_synthid.py); exibido em inspect_image.py / clean_image.py com REVERSE_SYNTHID_DIR ou --synthid-dir
  • Bootstrap setup_synthid.sh (dependências somente do scorer; --full instala requisitos upstream); Dockerfile.synthid mais make docker-synthid-build / docker-synthid-help
  • Alvos smoke-synthid e bootstrap-synthid no Makefile
  • Testes para o adaptador do scorer, o caminho de CLI indisponível, a análise de JSON e erros de tempo de execução
  • Documentação: apenas detecção/pontuação (sem remoção de pixels); o código upstream não é incluído e permanece sob sua Licença de Pesquisa não comercial

v0.2.0 — correção de falso positivo do c2patool

  • image_meta.py: has_manifest não sinaliza mais Error: No claim found / No JUMBF data found como manifest (erro de precedência de operador: os marcadores negativos agora vetam todos os ramos positivos)
  • Nova tests/test_c2patool_report.py (4 casos: sem declaração, sem JUMBF, manifest genuíno, ferramenta ausente)
  • Documentação: corrigidos links de c2patool (repositório movido para contentauth/c2pa-rs); adicionado aviso sobre o custo de qualidade da remoção de marca d'água de texto

v0.1.0 — polimento de empacotamento + honestidade de proveniência

  • Makefile (test / smoke / install-skill) e pytest.ini
  • Amostras de fixtures para Markdown, HTML, SVG; teste de limpeza degradada em PDF
  • Documentação: modelo de indústria duas camadas (C2PA com vínculo rígido vs. vínculo suave / SynthID-media)
  • Tabela de risco residual no README + links para ferramentas externas de verificação
  • Referência: guia de C2PA/SynthID do Institute of AI PM
  • Marcos d'água de vínculo suave e de pixels/áudio/vídeo explicitamente fora do escopo na skill/matriz/ética

v0.0.1 — lançamento inicial multifornedor

  • Skill de agente remove-ai-marks (substitui remove-claude-marks somente para Claude)
  • Camada A: Unicode invisível / bidi / caracteres de tag / homóglifos de espaço (inspect_text / clean_text)
  • Camada B: orientação de reescrita + rewrite_text.py opcional (print-prompt, Ollama, compatível com OpenAI)
  • Arquivos: remoção de metadados C2PA/AI para PNG, JPEG, SVG, PDF, DOCX, ODT, HTML, Markdown
  • inspect_file.py / clean_file.py unificados
  • Documentação multifornedor (Claude, Gemini/classe SynthID, OpenAI, LLM aberto)
  • Scripts com prioridade para stdlib; c2patool / exiftool opcionais

Licença

MIT — veja LICENSE.

Referências