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
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.
| Camada | Alvo | Como |
|---|---|---|
| A | Unicode invisível, espaços exóticos, bidi, caracteres de tag | Scripts Python determinísticos |
| B | Marcas d'água de texto estatísticas (amostragem de tokens) | Reescrita do agente + hook opcional rewrite_text.py |
| Arquivos | C2PA / EXIF / XMP / propriedades de documentos | PNG, 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):
| Ferramenta | Função |
|---|---|
c2patool | Inspecionar manifestos C2PA |
exiftool | Remoção de metadados residuais (especialmente PDF) |
qpdf | Reconstruçã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:
--offlineno 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 (transformerstrust_remote_codenunca é habilitado).WATERMARKS_MARKLLM_RLIMIT_AS=<bytes>(env, POSIX) aplica um limite de espaço de endereço ao subprocesso MarkLLM gerado porrewrite_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:
- 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.
- Motor opcional de remoção de pixels: seu ataque de regeneração
DiffusionPurificationé exposto comoclean_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. - 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
| Canal | Claude | Gemini/SynthID | OpenAI | Open-LLM |
|---|---|---|---|---|
| Texto Unicode / baseado em edição | Camada A | Camada A | Camada A | Camada A |
| Texto de amostragem estatística | Camada B melhor esforço | Camada B melhor esforço | Camada B se presente | Camada B melhor esforço |
| C2PA / metadados de arquivo | Sim (formatos listados) | Sim quando presente | Sim quando presente | Sim quando presente |
| Marcas de pixel em imagens | Fora do escopo | Pontuação SynthID opcional + remoção CtrlRegen (externo); detecção opcional do mesmo esquema MarkDiffusion + remoção DiffusionPurification (externo) | Fora do escopo | Remoção opcional CtrlRegen / MarkDiffusion (externo) |
| Backdoors de treinamento | Fora do escopo | Fora do escopo | Fora do escopo | Fora 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.
-
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.
-
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
| Formato | Inspecionar | Limpar |
|---|---|---|
| PNG / JPEG / WebP | Chunks C2PA / APP11 / RIFF C2PA, dicas XMP de IA | Remover segmentos de metadados |
| SVG | <metadata>, XMP | Remover blocos |
| Byte/XMP + ferramentas opcionais | exiftool depois qpdf; degradado sem ambos | |
| DOCX | docProps / customXml | Limpar props, remover customXml |
| ODT | meta.xml | Remover meta de gerador / meta tipo IA |
| HTML | meta, JSON-LD, data-ai* | Remover tags/atributos |
| Markdown | Chaves de IA no frontmatter YAML | Remover 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):
| Canal | O que removemos | O que pode permanecer | Verificação externa (exemplos) |
|---|---|---|---|
| C2PA com vinculação rígida / EXIF / XMP | Sim | Marcas de vinculação suave / pixel | c2patool, verificação de Content Credentials |
| Mídia classe SynthID | Remoção opcional de pixels (CtrlRegen externo); pontuação local caso contrário | Marca d'água de áudio/vídeo; marca d'água de pixel residual após remoção | Ferramentas do provedor (ex.: Google SynthID / detector Vertex onde oferecido); pontuador local opcional reverse-SynthID |
| Texto estatístico | Reescrita de melhor esforço | Marcas fortes após edição leve | Nenhum 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ção | Remove | Notas |
|---|---|---|
| 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/metadados | Proveniência de arquivo | Veja 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 aberto | Evitar re-marcação com o modelo de origem | Alternativa 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/MarkDiffusionexterno, Apache-2.0):markdiffusion_harness.pycom subcomandoswatermark/detect/purifypara nove esquemas de imagem (Tree-Ring, Ring-ID, ROBIN, WIND, SFW, Gaussian-Shading, GaussMarker, PRC, SEAL) clean_image.py --remove-pixel diffusionexecuta o ataque de regeneraçãoDiffusionPurificationdo MarkDiffusion como um motor alternativo de remoção de pixels (força conservadora padrão 0.3)- Bootstrap
setup_markdiffusion.sh(pin PyPI1.0.2; clone editável--checkoutem commit fixado) +requirements-markdiffusion.txt+Dockerfile.markdiffusione Makefilebootstrap-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ênciareferences/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.pycom subcomandosdetect/watermarkpara esquemas KGW e SynthID rewrite_text.py --markllm-schemeexecuta detecção antes/depois em torno de uma reescrita da Camada B (controlado por env; relatacleared)- Bootstrap
setup_markllm.sh+requirements-markllm.txt(dependências fixadas) +Dockerfile.markllme Makefilebootstrap-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_ASopcional no subprocesso de reescrita, torch fixado no Dockerfile e verificação de SHA de clone emDockerfile.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: adaptadorclean_ctrlregen.py+ bootstrapsetup_ctrlregen.sh(commit fixado, checkout esparso, venv, verificação SHA), além deDockerfile.ctrlregenemake bootstrap-ctrlregen/docker-ctrlregen-build/smoke-ctrlregen clean_image.py --remove-pixel ctrlregenexecuta remoção de metadados → remoção CtrlRegen → pontuação opcional reverse-SynthID antes/depois;inspect_image.pysugere 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-watermarknã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 usandoCtrlRegenEnginediretamente
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) eaudit_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/ZWJapó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_fneos.fchmodapenas 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.bakpelo mesmo caminho seguro — symlinks pré-posicionados (ex.: em/tmpou 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çalhoAuthorizationnunca pode ser reenviada a um host não validado; endpoints não loopback são negados por padrão (adesão com--allow-remoteouWATERMARKS_REWRITE_ALLOW_REMOTE=1); apenas esquemas http(s) são aceitos;--api-keyfoi removido — chaves são somente via ambiente comWATERMARKS_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_FSIZEaplicados 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 depip-audite 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.pyagora 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ão0.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.mdevendor-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 eminspect_image.py/clean_image.pycomREVERSE_SYNTHID_DIRou--synthid-dir - Bootstrap
setup_synthid.sh(dependências somente do scorer;--fullinstala requisitos upstream);Dockerfile.synthidmaismake docker-synthid-build/docker-synthid-help - Alvos
smoke-synthidebootstrap-synthidno 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_manifestnão sinaliza maisError: No claim found/No JUMBF data foundcomo 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 paracontentauth/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) epytest.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(substituiremove-claude-markssomente 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.pyopcional (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.pyunificados- Documentação multifornedor (Claude, Gemini/classe SynthID, OpenAI, LLM aberto)
- Scripts com prioridade para stdlib;
c2patool/exiftoolopcionais
Licença
MIT — veja LICENSE.
Referências
- Como o Claude marca conteúdo gerado por IA (Anthropic)
- Dathathri et al., Scalable watermarking for identifying large language model outputs (SynthID-Text, Nature 2024)
- Google AI for Developers, SynthID safeguards (documentação da API Gemini)
- C2PA / c2patool
- Kirchenbauer et al., A Watermark for Large Language Models
- THU-BPM/MarkLLM (toolkit unificado para avaliar algoritmos de marca d'água em LLMs)
- Pan et al., MarkDiffusion: An Open-Source Toolkit for Generative Watermarking of Latent Diffusion Models (JMLR) — o toolkit de incorporação que o harness opcional de marcas d'água de imagem deste repositório encapsula — código, documentação
- Zhang et al., Watermarks in the Sand: Impossibility of Strong Watermarking for Generative Models (ICML 2024)
- google-deepmind/synthid-text (referência de pesquisa; não usado para detecção aqui)
- aloshdenny/reverse-SynthID (referência de pesquisa)
- Liu et al., Image Watermarks are Removable Using Controllable Regeneration from Clean Noise (ICLR 2025) — o método de regeneração de pixels que o backend opcional CtrlRegen implementa — código
- Kassis & Hengartner, UnMarker: A Universal Attack on Defensive Image Watermarking (arXiv:2405.08363; IEEE S&P 2025) — um ataque universal de marca d'água comparado em uma métrica diferente da do CtrlRegen
- Goonatilake & Ateniese, Removing the Watermark Is Not Enough: Forensic Stealth in Generative-AI Watermark Removal (arXiv:2605.09203) — motiva o padrão de intensidade conservadora: a remoção ainda pode deixar vestígios forenses
- mertizci/noai-watermark (toolkit CLI/Python para remoção de SynthID/StableSignature/TreeRing e remoção de metadados de IA)
- 0xROOTPLS/DeSynth (remoção de SynthID para imagens OpenAI/Google)
- Institute of AI PM, AI Content Provenance and Watermarking: The PM's Guide to C2PA and SynthID (modelo industrial de duas camadas: C2PA + marca d'água imperceptível / vínculo suave; contexto da SB 942 / Art. 50 da Lei de IA da UE)