watermarks-remover
Elimina marcas de procedencia de IA de múltiples proveedores: higiene de texto Unicode, enlaces de reescritura estadística y metadatos C2PA de PNG/JPEG/SVG/PDF/DOCX/HTML/MD
Documentación
_ _ _ ____ ___ ____ ____ _ _ ____ ____ _ _ ____ ____ ____ _ _ ____ _ _ ____ ____
| | | |__| | |___ |__/ |\/| |__| |__/ |_/ [__ __ |__/ |___ |\/| | | | | |___ |__/
|_|_| | | | |___ | \ | | | | | \ | \_ ___] | \ |___ | | |__| \/ |___ | \
watermarks-remover
Habilidad de agente + scripts de Python de la biblioteca estándar para eliminar marcas de procedencia de IA de múltiples proveedores de texto y archivos, para privacidad e higiene en contenido que te pertenece.
| Capa | Objetivo | Cómo |
|---|---|---|
| A | Unicode invisible, espacios exóticos, bidi, caracteres de etiqueta | Scripts Python deterministas |
| B | Marcas de agua de texto estadísticas (muestreo de tokens) | Reescritura del agente + hook opcional rewrite_text.py |
| Archivos | C2PA / EXIF / XMP / propiedades de documentos | PNG, JPEG, WebP, SVG, PDF, DOCX, ODT, HTML, Markdown |
Proveedores / ecosistemas (a nivel de clase): Claude, Gemini / SynthID-Text, superficies de procedencia de OpenAI, marcas estilo Kirchenbauer de open-LLM.
Última versión: v0.4.0
Ruta de la habilidad: skills/remove-ai-marks/
(migración: anteriormente remove-claude-marks; el alias de barra /remove-claude-marks sigue documentado)
Instalación (habilidad 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
Invoca con /remove-ai-marks o pide "eliminar marcas de agua de IA / C2PA / marcas de Claude / texto clase SynthID".
Herramientas del sistema opcionales (se usan automáticamente si están presentes):
| Herramienta | Rol |
|---|---|
c2patool | Inspeccionar manifiestos C2PA |
exiftool | Eliminación residual de metadatos (especialmente PDF) |
qpdf | Reconstrucción estructural de PDF — requerida para una eliminación real de PDF (ver abajo) |
Los scripts principales solo necesitan la biblioteca estándar de Python 3.10+. Las llamadas al modelo de la capa B son opcionales.
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
Las herramientas de texto rechazan entrada binaria
inspect_text.py, clean_text.py y rewrite_text.py operan sobre texto. Si se apuntan a un .docx, .pdf o imagen, solían decodificar los bytes comprimidos y reportar los puntos de código que salieran — ruido que sigue a la compresión, no al contenido — y clean_text.py luego escribía esos bytes corruptos de vuelta, destruyendo el archivo. Ahora rechazan entrada binaria y nombran la herramienta que la maneja:
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.
La detección se basa en el número mágico más una proporción de bytes de control, por lo que el texto en codificaciones distintas de UTF-8 sigue funcionando. --force-text lo anula en todas partes.
Puntuación opcional de píxeles SynthID
inspect_image.py y clean_image.py pueden reportar una puntuación de confianza SynthID en el dominio de píxeles cuando hay disponible un checkout externo de aloshdenny/reverse-SynthID. El puntuador no está incluido: se carga en tiempo de ejecución desde tu checkout, y su código permanece bajo la Licencia de Investigación no comercial del proyecto upstream.
Opción 1: arranque con un comando (sin 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 acepta --dir PATH, --ref REF y --full (instala el requirements.txt completo upstream, que añade torch/diffusers para el bypass de VAE upstream que este proyecto no usa).
En Windows usa setup_synthid.ps1 (-Dir, -Ref, -Full), que crea el venv en .venv\Scripts\ — el diseño que image_meta.py ya busca en os.name == "nt".
Opción 2: compilación 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
La imagen se compila localmente desde el código fuente upstream en tiempo de compilación. No se publica, por lo que no redistribuye el código upstream.
La puntuación V4 usa artifacts/spectral_codebook_v4.npz del checkout upstream (~220 MB). Esto es solo detección/puntuación — no elimina marcas de agua de píxeles.
Eliminación opcional de píxeles CtrlRegen
Para marcas de agua de imágenes en el dominio de píxeles (clase SynthID, StegaStamp, Tree-Ring, StableSignature), un backend externo opcional ejecuta el pipeline CtrlRegen (ControlNet + DINOv2 IP-Adapter regeneración controlable). El backend es mertizci/noai-watermark, una reimplementación mantenida del método CtrlRegen de ICLR 2025 con teselado automático.
El backend no está incluido y no trae archivo LICENSE, por lo que se trata como todos los derechos reservados: se clona en un commit fijado y se carga en tiempo de ejecución.
Arranque
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
En Windows usa setup_ctrlregen.ps1 (mismos flags que -Dir, -Ref, -Python); el venv termina en .venv\Scripts\, que clean_image.py ya resuelve. Elige el índice de ruedas de torch según la capacidad de cómputo de la GPU en lugar de la versión de CUDA que nvidia-smi imprime — ese número es el máximo que soporta el driver, y los drivers son compatibles hacia atrás, por lo que derivar la etiqueta de rueda de él instala cu130 en una tarjeta Pascal cuyos kernels se eliminaron en cu128. El script fuerza cu126 por debajo de la capacidad de cómputo 7.5 y luego verifica el resultado con torch.cuda.get_arch_list().
Desde 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
Orden de operaciones: primero la eliminación de metadatos, luego la eliminación de píxeles CtrlRegen, y luego una puntuación opcional inversa de SynthID antes/después (cuando REVERSE_SYNTHID_DIR también está configurado).
La fuerza es conservadora por defecto (--ctrlregen-strength 0.25), porque una fuerza mayor elimina más marca de agua pero regenera más de la imagen. Preajustes documentados: 0.15 mínimo / 0.25 predeterminado / 0.35 equilibrado / 0.5 agresivo / 0.7 máximo (el predeterminado del backend es 0.5). --ctrlregen-steps predetermina a 50 (pasos de denoising efectivos ≈ pasos × fuerza).
Tamaño de imagen (límite nativo 512×512)
CtrlRegen es un ControlNet de Stable Diffusion 1.5 de 512×512. El backend resuelve esto para entradas arbitrarias, por lo que no se expone teselado adicional aquí:
- ≤512 px: una sola pasada — recorte/redimensionado al centro a 512, regenerar, redimensionar de vuelta.
- >512 px: teselado automático con solapamiento (teselas de 512 px, solapamiento de 192 px), ancho/alto alineados a múltiplos de 8, luego uniones con fusión coseno.
- Cualquier ruta: la salida se redimensiona al tamaño original y se iguala el color con la imagen original.
Las imágenes muy grandes (por ejemplo, 4K) producen muchas teselas, por lo que las ejecuciones escalan con el número de teselas (más lentas y mayor VRAM). Reduce la escala previa de entradas grandes cuando sea práctico; el tamaño de tesela y el solapamiento están codificados upstream y no se exponen como flags.
Cómputo, modelos restringidos y verificación
Espera ~10 GB de descargas de modelos; se recomienda encarecidamente una GPU y las ejecuciones en CPU son lentas. Algunos modelos upstream están restringidos, así que exporta HF_TOKEN (solo entorno — nunca argv). clean_ctrlregen.py se niega a instalar dependencias automáticamente; ejecuta setup_ctrlregen.sh primero.
No hay detector local para StegaStamp/Tree-Ring/StableSignature, por lo que la única señal local es la puntuación inversa de SynthID (un sustituto). Cuando esté disponible, clean_image.py --remove-pixel ctrlregen reporta esa puntuación antes/después; la verificación oficial de Google SynthID sigue siendo la autoridad 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
Verificación opcional de marcas de agua de texto MarkLLM
Para experimentos controlados, un arnés externo opcional envuelve THU-BPM/MarkLLM (Apache-2.0) para marcar texto de prueba y redetectarlo después de una reescritura de Capa B — por ejemplo, demostrar que una marca KGW (Kirchenbauer, tu fila "open-LLM") o SynthID-Text (fila Gemini) desaparece bajo tu reescritura. Es un arnés de verificación, no un oráculo: la detección de MarkLLM solo es válida contra la misma configuración de esquema + claves usadas en la generación, y no puede certificar que un detector de proveedor fallará.
El backend no está incluido. setup_markllm.sh clona upstream en un commit fijado, crea un venv e instala dependencias fijadas (torch + transformers); el modelo de puntuación (predeterminado facebook/opt-1.3b, Apache-2.0) se descarga de Hugging Face en la primera ejecución.
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
Verificación alrededor de una reescritura de Capa B: pasa --markllm-scheme a rewrite_text.py (con --markllm-dir), y registra la detección de MarkLLM antes/después más un 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
Si el backend no está configurado o faltan sus dependencias, la reescritura continúa y el informe señala que la verificación no estaba disponible. Se recomienda una GPU; las ejecuciones en CPU funcionan pero son lentas, y la descarga del modelo es de unos pocos GB.
Perillas de endurecimiento:
--offlineen el adaptador (o cualquier ejecución de MarkLLM) carga el modelo de puntuación solo desde la caché de Hugging Face — cero salida de red; falla rápido si no está en caché. El código remoto personalizado nunca se ejecuta (transformerstrust_remote_codenunca se habilita).WATERMARKS_MARKLLM_RLIMIT_AS=<bytes>(entorno, POSIX) aplica un límite de espacio de direcciones al subproceso MarkLLM generado porrewrite_text.py. Desactivado por defecto porque torch/CUDA generalmente necesita espacios de direcciones grandes.- Los archivos de configuración están limitados a 1 MiB; el checkout upstream y la imagen base están fijados 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
Arnés opcional de marcas de agua de imagen MarkDiffusion
Para experimentos controlados en imágenes, un arnés externo opcional envuelve THU-BPM/MarkDiffusion (Apache-2.0), un kit de herramientas de marcado generativo para modelos de difusión latente (incrusta marcas — no las elimina). Lo usamos para tres cosas:
- Arnés de verificación (como MarkLLM, pero para imágenes): marca una imagen de prueba con un esquema, ejecuta la eliminación y redetecta con la misma configuración de esquema — por ejemplo, demostrar que una marca de clase Tree-Ring se limpia bajo tu pipeline. Es un arnés de verificación, no un oráculo: la detección requiere el modelo generador (y claves para esquemas basados en claves), por lo que no puede certificar que un detector de proveedor fallará en una imagen arbitraria.
- Motor opcional de eliminación de píxeles: su ataque de regeneración
DiffusionPurificationse expone comoclean_image.py --remove-pixel diffusion, una alternativa a CtrlRegen. Es regeneración ciega (sin condicionamiento ControlNet), por lo que desvía más el contenido de la imagen que CtrlRegen — fuerza conservadora por defecto (0.3), tratada como respaldo/comparación, nunca como garantía. - Detector local de mismo esquema para marcas de clase Tree-Ring, llenando parcialmente el vacío de "sin detector local para StegaStamp/Tree-Ring/StableSignature" (cubre Tree-Ring/Ring-ID/Gaussian-Shading, etc., no StegaStamp / StableSignature / SynthID-media).
El backend no está incluido. setup_markdiffusion.sh crea un venv e instala markdiffusion==1.0.2 desde PyPI (fijado), con torch instalado desde el índice de plataforma correcto; --checkout instala un clon editable en un commit fijado en su lugar. El modelo de Stable Diffusion (predeterminado huanzi05/stable-diffusion-2-1-base) se descarga de Hugging Face en la primera ejecución.
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
O ejecuta la purificación como parte del pipeline de imágenes normal:
MARKDIFFUSION_DIR=~/markdiffusion \
~/markdiffusion/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
-o shot.cleaned.png --remove-pixel diffusion
Las perillas de endurecimiento reflejan el arnés MarkLLM: --offline carga el modelo solo desde la caché de Hugging Face (cero salida de red, sin código remoto), HF_TOKEN es solo entorno (nunca argv), las configuraciones de algoritmo están limitadas a 1 MiB, y el subproceso recibe los mismos límites de recursos más altos que 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
La imagen instala un torch de CPU; los usuarios de CUDA deberían ejecutar setup_markdiffusion.sh en el host en su lugar. Las descargas de modelos aún golpean el hub de HF en la primera ejecución.
Matriz de cobertura
| Canal | Claude | Gemini/SynthID | OpenAI | Open-LLM |
|---|---|---|---|---|
| Texto Unicode / basado en edición | Capa A | Capa A | Capa A | Capa A |
| Texto de muestreo estadístico | Capa B mejor esfuerzo | Capa B mejor esfuerzo | Capa B si está presente | Capa B mejor esfuerzo |
| C2PA / metadatos de archivo | Sí (formatos listados) | Sí cuando está presente | Sí cuando está presente | Sí cuando está presente |
| Marcas de píxeles en imágenes | Fuera de alcance | Puntuación SynthID opcional + eliminación CtrlRegen (externo); detección de mismo esquema MarkDiffusion opcional + eliminación DiffusionPurification (externo) | Fuera de alcance | Eliminación CtrlRegen / MarkDiffusion opcional (externo) |
| Puertas traseras de entrenamiento | Fuera de alcance | Fuera de alcance | Fuera de alcance | Fuera de alcance |
Detalles: skills/remove-ai-marks/references/vendor-notes.md, mark-classes.md.
Cómo funciona el marcado de texto (resumen)
Las marcas de agua de LLM modernas a menudo ocultan una señal en qué tokens se eligen (sesgo generativo / de muestreo), no solo en caracteres invisibles. Los esquemas basados en edición inyectan reglas de Unicode o sinónimos. Los esquemas de archivos adjuntan C2PA o metadatos del generador.
- Capa A elimina los portadores Unicode basados en edición (comprobable).
- Capa B ataca las marcas de agua de muestreo mediante reescritura intensiva (mejor esfuerzo; ataques estándar de la literatura como paráfrasis / retrotraducción).
- Limpiadores de archivos eliminan C2PA/XMP/props de contenedores compatibles.
Hasta que los proveedores publiquen detectores y claves públicas, ninguna herramienta puede certificar honestamente que "esto falla la verificación oficial". Los informes deben separar el trabajo verificable del de mejor esfuerzo.
Prefiere un modelo no originario para la Capa B (no reescribas texto de Claude con Claude si intentas evitar un nuevo marcado).
Aviso: qué cuesta eliminar una marca de agua de texto
Las marcas de agua de texto viven en la propia redacción: la señal se distribuye entre las elecciones de tokens, de modo que casi cada frase lleva un poco de ella. De ello se derivan dos consecuencias, y son la razón por la que la Capa B se describe honestamente como de mejor esfuerzo y no como una goma mágica.
-
Eliminar significa reformular, no reestructurar. Reordenar párrafos, cambiar encabezados o hacer retoques ligeros apenas mueve la señal. Eliminar una marca estadística requiere reescribir una fracción sustancial del texto — frase por frase, no sección por sección.
-
La reformulación degrada el texto. Cualquier reescritura sustituye las elecciones de palabras originales por las del modelo que reescribe, lo que aplana el tono, la voz y la precisión. En textos de producción (SEO, marketing, trabajo para clientes) esa degradación es real y a menudo visible para quienes más valoran la escritura. Es como tomar texto de un modelo de primer nivel y pedirle a un modelo menos capaz que lo reescriba desde cero: el resultado no puede superar el techo del modelo que reescribe.
Lo que lleva a la pregunta honesta de círculo completo:
Si de todos modos el plan es reescribir el texto con un modelo más barato, ¿por qué pagar por un modelo premium en primer lugar? Generar directamente con el modelo más barato es más simple, más económico y produce el mismo — o mejor — resultado final.
La Capa B tiene sentido cuando quieres específicamente el razonamiento y el borrador del modelo premium y aceptas una pasada de reescritura para cumplir un requisito de higiene o privacidad — no como una vía barata hacia texto sin marca.
Cuándo omitir la Capa B:
- La calidad importa más que la higiene: usa la vía sin pérdida — el saneamiento Unicode de la Capa A más los limpiadores de metadatos de archivo — y conserva la prosa original.
- Si de todos modos vas a reescribir: usa un modelo no originario (reescribir con el modelo originario puede volver a marcar el texto) y recuerda que el riesgo residual permanece — ninguna herramienta puede certificar que un detector del proveedor fallará.
Formatos de archivo
| Formato | Inspeccionar | Limpiar |
|---|---|---|
| PNG / JPEG / WebP | Fragmentos C2PA / APP11 / RIFF C2PA, pistas XMP de IA | Eliminar segmentos de metadatos |
| SVG | <metadata>, XMP | Eliminar bloques |
| Byte/XMP + herramientas opcionales | exiftool y luego qpdf; degradado sin ninguno de los dos | |
| DOCX | docProps / customXml | Limpiar props, eliminar customXml |
| ODT | meta.xml | Eliminar metadatos de generador / tipo IA |
| HTML | meta, JSON-LD, data-ai* | Eliminar etiquetas/atributos |
| Markdown | Claves de IA en frontmatter YAML | Eliminar claves + cuerpo de la Capa A |
Por qué PDF necesita qpdf, no solo exiftool
ExifTool escribe PDFs de forma incremental. exiftool -all= añade un
bloque %BeginExifToolUpdate que libera el objeto Info y elimina /Info del
trailer — pero los bytes de metadatos originales permanecen en el archivo tal cual, y
el propio exiftool puede deshacer la edición con -PDF-update:all=. El comando sale con
0, los visores no muestran metadatos y el archivo se vuelve más grande, que es la señal reveladora.
Para una herramienta de eliminación de procedencia, eso es una fuga silenciosa, así que clean_pdf sigue
a la pasada de exiftool con qpdf --linearize, que re-serializa el documento
desde su grafo de objetos y elimina los objetos ahora no referenciados. Sin qpdf
instalado, la limpieza sigue ejecutándose, pero lo indica:
warning: exiftool PDF edits are incremental — the original metadata bytes
remain recoverable; install qpdf for a structural rewrite
La eliminación de marcas de agua en el dominio de píxeles está ahora disponible como backend externo opcional CtrlRegen (ver arriba); es un removedor regenerador, no una garantía. El enlace suave C2PA (marca de agua en el contenido que puede re-vincular un manifiesto remoto de Content Credentials después de eliminar los metadatos) sigue fuera de alcance. Eliminar C2PA con enlace duro no limpia esos canales.
Riesgo residual después de una limpieza
Esta herramienta informa eliminaciones verificables (conteos Unicode, acciones de metadatos) y reescrituras de mejor esfuerzo de la Capa B. No puede certificar que los detectores del proveedor fallarán.
Para comprobar las señales residuales por tu cuenta (opcional, externo):
| Canal | Qué eliminamos | Qué puede permanecer | Comprobación externa (ejemplos) |
|---|---|---|---|
| C2PA con enlace duro / EXIF / XMP | Sí | Marcas de enlace suave / de píxeles | c2patool, verificación de Content Credentials |
| Medios clase SynthID | Eliminación de píxeles opcional (CtrlRegen externo); puntuación local en caso contrario | Marca de agua de audio/video; marca de agua de píxeles residual tras la eliminación | Herramientas del proveedor (p. ej. Google SynthID / detector Vertex donde se ofrezca); puntuador local opcional reverse-SynthID |
| Texto estadístico | Reescritura de mejor esfuerzo | Marcas fuertes tras edición ligera | Sin detector universal público; herramientas del proveedor cuando estén disponibles |
Contexto industrial de dos capas (C2PA + marca de agua imperceptible): guía del Institute of AI PM.
Opciones de eliminación (resumen)
| Opción | Elimina | Notas |
|---|---|---|
| Saneamiento Unicode (Capa A) | ZWSP, bidi, etiquetas, espacios exóticos, … | Valor predeterminado seguro para texto |
| Reescritura (Capa B) | Marcas de tokens estadísticas (mejor esfuerzo) | Siempre ofrecida por la skill; cuesta estilo — ver Aviso |
| Eliminación de contenedor/metadatos | Procedencia de archivo | Ver tabla de formatos |
| Eliminación de píxeles CtrlRegen (opcional) | Marcas de imagen en dominio de píxeles (clase SynthID, StegaStamp, Tree-Ring, StableSignature) | Backend externo; cómputo intensivo; intensidad predeterminada conservadora |
| Eliminación de píxeles DiffusionPurification (opcional) | Marcas de imagen en dominio de píxeles (clase Tree-Ring) | Backend MarkDiffusion; regeneración ciega (más deriva que CtrlRegen); intensidad predeterminada conservadora |
| Modelos locales de peso abierto | Evitar re-marcado con el modelo originario | Alternativa operativa |
Matriz: skills/remove-ai-marks/references/removal-matrix.md.
Ética y aviso
Ver skills/remove-ai-marks/references/ethics.md. Para privacidad e investigación sobre tu contenido — no fraude académico ni afirmaciones falsas de "escrito por humanos".
Uso responsable: Este proyecto es para contenido que posees o que estás autorizado a procesar. Los usuarios deben cumplir las regulaciones locales y usarlo de forma responsable. Los desarrolladores rechazan cualquier responsabilidad por un posible uso indebido por parte de los usuarios.
Pruebas
python3 -m venv .venv && .venv/bin/pip install pytest
.venv/bin/python -m pytest # or: make test
make smoke # quick CLI smoke on fixtures
Registro de cambios
Sin publicar
- Nuevo arnés opcional de marcas de agua de imagen MarkDiffusion (
THU-BPM/MarkDiffusionexterno, Apache-2.0):markdiffusion_harness.pycon subcomandoswatermark/detect/purifypara nueve esquemas de imagen (Tree-Ring, Ring-ID, ROBIN, WIND, SFW, Gaussian-Shading, GaussMarker, PRC, SEAL) clean_image.py --remove-pixel diffusionejecuta el ataque de regeneraciónDiffusionPurificationde MarkDiffusion como motor alternativo de eliminación de píxeles (intensidad conservadora predeterminada 0.3)- Arranque
setup_markdiffusion.sh(pin de PyPI1.0.2; clon editable--checkouten commit fijado) +requirements-markdiffusion.txt+Dockerfile.markdiffusiony objetivosbootstrap-markdiffusion/smoke-markdiffusion/docker-markdiffusion-build/docker-markdiffusion-helpdel Makefile - Pruebas basadas en mocks (
tests/test_markdiffusion_harness.py) — sin torch en CI; documento de referenciareferences/markdiffusion.md - Documentación: advertencia de verificación solo mismo-esquema (no un oráculo de detector del proveedor) y advertencia de deriva por regeneración ciega en README, SKILL.md,
removal-matrix.md,markdiffusion.md - Añadir inspección WebP solo con stdlib y limpieza de metadatos para fragmentos RIFF
C2PA, XMP, EXIF y perfil ICC - Nuevo arnés opcional MarkLLM (checkout
THU-BPM/MarkLLMexterno, Apache-2.0):detect_text_watermark.pycon subcomandosdetect/watermarkpara esquemas KGW y SynthID rewrite_text.py --markllm-schemeejecuta detección antes/después alrededor de una reescritura de la Capa B (controlado por variable de entorno; informacleared)- Arranque
setup_markllm.sh+requirements-markllm.txt(dependencias fijadas) +Dockerfile.markllmy objetivosbootstrap-markllm/smoke-markllm/docker-markllm-build/docker-markllm-helpdel Makefile - Pruebas basadas en mocks (
tests/test_markllm_detect.py, 21 casos) — sin torch en CI - Documentación: advertencia del arnés de verificación (solo misma configuración, no un oráculo de detector del proveedor) en README, SKILL.md,
removal-matrix.md,vendor-notes.md - Endurecimiento: carga de modelos solo desde caché
--offline(sin salida HF, sin código remoto), límite de configuración de 1 MiB,WATERMARKS_MARKLLM_RLIMIT_ASopcional en el subproceso de reescritura, torch fijado en el Dockerfile y verificación de SHA del clon enDockerfile.markllm
v0.4.0 — eliminación de píxeles, confianza de hallazgos, correcciones de Windows y falsos positivos
Eliminación de píxeles CtrlRegen opcional (backend externo)
- Eliminación opcional de marcas de agua en dominio de píxeles mediante un checkout
mertizci/noai-watermarkexterno: adaptadorclean_ctrlregen.py+ arranquesetup_ctrlregen.sh(commit fijado, checkout disperso, venv, verificación SHA), además deDockerfile.ctrlregenymake bootstrap-ctrlregen/docker-ctrlregen-build/smoke-ctrlregen clean_image.py --remove-pixel ctrlregenejecuta eliminación de metadatos → eliminación CtrlRegen → puntuación opcional antes/después reverse-SynthID;inspect_image.pysugiere la bandera ante una puntuación SynthID alta- Intensidad predeterminada conservadora
0.25(preajustes 0.15/0.25/0.35/0.5/0.7); el backend divide automáticamente en mosaicos el pipeline nativo de 512×512 para imágenes más grandes; el subproceso de torch recibe límites de recursos superiores configurables por entorno - El backend nunca se incluye:
noai-watermarkno incluye archivo LICENSE (tratado como todos los derechos reservados), y sus rutas de auto-instalación/reinicio se evitan usandoCtrlRegenEnginedirectamente
Confianza de hallazgos y auditorías agregadas
- Los hallazgos ahora se clasifican como
confirmed/probable/informational/likely_false_positive, expuestos en JSON de texto/imagen/contenedor e informes humanos - Nuevos informes agregados
audit_dir.py(árbol recursivo) yaudit_website.py(descubrimiento de sitemap + rastreo); documentados en SKILL.md
Correcciones de falsos positivos
- DOCX: escanear solo
docProps/customXml, no el cuerpo visible (#14) - Texto Capa A: conservar emojis
VS16/ZWJdespués de una base de emoji; nueva bandera paranoica--strip-emoji-glue(#22) - HTML: tratar las etiquetas de generador CMS como informativas, no como metadatos de IA (#13)
- PDF: excluir cargas útiles de flujo del escaneo de bytes marcadores de IA (#13)
- Los informes de inspección señalan rutas no compatibles/de mejor esfuerzo
Soporte de Windows
- Limitar las funciones
preexec_fnyos.fchmodsolo a POSIX para que las escrituras y las herramientas opcionales funcionen en Windows (#15, #23) - Reconfigurar stdio a UTF-8 para que los flujos redirigidos de Windows ya no fallen con Unicode invisible; tramo de CI de Windows + prueba de humo de CLI (#23)
Documentación y cadena de suministro
- Sección CtrlRegen del README + referencias de investigación (CtrlRegen, UnMarker, advertencia de sigilo forense), aviso de uso responsable; actualizaciones de SKILL/matriz/notas de proveedor/ética
- Configuración de Dependabot + CODEOWNERS de ruta de seguridad; actualización de scipy/numpy/opencv-python/scikit-learn/pywavelets y de la imagen base a Python 3.14-slim
- Pruebas CtrlRegen basadas en mocks (sin torch en CI)
v0.3.2 — endurecimiento de seguridad (escrituras seguras, cliente HTTP, cadena de suministro de CI)
- Escrituras de salida seguras y atómicas: cada limpiador ahora escribe mediante archivo temporal + renombrado atómico (
safe_write_bytes/safe_write_text), rechaza destinos con enlaces simbólicos y crea copias de seguridad.baka través de la misma ruta segura — los enlaces simbólicos precolocados (p. ej. en/tmpo directorios de descargas) ya no pueden redirigir una escritura limpia hacia un archivo arbitrario - Endurecimiento del cliente HTTP
rewrite_text.py: las redirecciones se rechazan por completo, por lo que una clave API en el encabezadoAuthorizationnunca puede reenviarse a un host no validado; los endpoints que no sean de bucle local están denegados por defecto (se activan con--allow-remoteoWATERMARKS_REWRITE_ALLOW_REMOTE=1); solo se aceptan esquemas http(s); se eliminó--api-key— las claves solo se configuran mediante variables de entorno a través deWATERMARKS_REWRITE_API_KEY - Límites de recursos: entrada máxima predeterminada de 1 GiB → 256 MiB, nuevo límite de stdin de 64 MiB, presupuesto zip DOCX/ODT de 512 MiB → 128 MiB, y
RLIMIT_AS/RLIMIT_FSIZEaplicados a los subprocesos de exiftool/c2patool/SynthID (todos los límites se pueden sobrescribir mediante variables de entorno) - Cadena de suministro: las acciones de CI fijadas por SHA con
permissions: contents: read, dependencias de desarrollo fijadas (requirements-dev.txt), un paso depip-audity un nuevo flujo de trabajo CodeQL; la imagen Docker ahora se ejecuta como un usuario sin privilegios con pip fijado - Dependencias del evaluador: Pillow actualizado de 10.4.0 → 12.3.0 (24 CVEs conocidos); uso de la API verificado contra el commit ascendente fijado
- Pruebas: 18 nuevas pruebas de regresión de seguridad (60 en total, todas pasan)
v0.3.1 — reescritura más sólida de la marca de agua estadística de la Capa B
- La paráfrasis predeterminada de
rewrite_text.pyahora realiza un ataque explícito de elección de palabras + sintaxis (orden de cláusulas, conectores, palabras de transición, límites de oraciones, palabras funcionales) en lugar de una reescritura genérica - Nueva
--strength humanize: pasada de "escribir como un humano" de cero disparos dirigida a frases formuladas de estilo IA - Nueva
--strength code: reescribe comentarios, docstrings y literales de cadena, y renombra identificadores locales preservando el comportamiento y los nombres de la API pública - La pasada estructural ahora emite "prosa humana variada y natural" en lugar del típico "estilo profesional claro" de la IA
- Nueva
--temperature(predeterminada0.9) para backends Ollama y compatibles con OpenAI - Nueva
--candidates N: genera N reescrituras y selecciona la más divergente léxicamente (distancia de Jaccard de bigramas) con una protección contra la deriva de longitud - Higiene de modelos más estricta: preferir modelos locales de peso abierto y evitar cualquier proveedor con marca de agua conocida, no solo el origen sospechoso
- El informe de riesgo residual ahora distingue texto corto/altamente predecible (riesgo menor) de prosa larga y de alta entropía (riesgo mayor)
- Documentación actualizada en
SKILL.md,removal-matrix.mdyvendor-notes.md; las pruebas cubren nuevos prompts, puntuación de divergencia y selección de candidatos
v0.3.0 — puntuación de píxeles SynthID opcional
- Evaluador opcional de SynthID en el dominio de píxeles mediante un checkout externo de
aloshdenny/reverse-SynthID(score_synthid.py); visible eninspect_image.py/clean_image.pyconREVERSE_SYNTHID_DIRo--synthid-dir - Arranque de
setup_synthid.sh(dependencias solo del evaluador;--fullinstala los requisitos ascendentes);Dockerfile.synthidmásmake docker-synthid-build/docker-synthid-help - Objetivos
smoke-synthidybootstrap-synthiddel Makefile - Pruebas para el adaptador del evaluador, la ruta de CLI no disponible, el análisis JSON y los errores en tiempo de ejecución
- Documentación: solo detección/puntuación (sin eliminación de píxeles); el código ascendente no se incluye y permanece bajo su Licencia de Investigación no comercial
v0.2.0 — corrección de falsos positivos de c2patool
image_meta.py:has_manifestya no marcaError: No claim found/No JUMBF data foundcomo manifiesto (error de precedencia de operadores: los marcadores negativos ahora vetam cada rama positiva)- Nueva
tests/test_c2patool_report.py(4 casos: sin reclamación, sin JUMBF, manifiesto genuino, herramienta ausente) - Documentación: corregidos los enlaces de
c2patool(el repositorio se movió acontentauth/c2pa-rs); se añadió un aviso sobre el costo de calidad de la eliminación de marcas de agua de texto
v0.1.0 — pulido del empaquetado + honestidad de procedencia
Makefile(test/smoke/install-skill) ypytest.ini- Muestras de fixtures para Markdown, HTML, SVG; prueba de limpieza degradada de PDF
- Documentación: modelo industrial de dos capas (C2PA de enlace duro vs. enlace blando / SynthID-media)
- Tabla de riesgo residual en el README + enlaces a herramientas de verificación externas
- Referencia: guía de C2PA/SynthID del Institute of AI PM
- El enlace blando y las marcas de agua de píxeles/audio/video quedan explícitamente fuera del alcance en skill/matrix/ethics
v0.0.1 — lanzamiento inicial multivendedor
- Skill de agente
remove-ai-marks(reemplaza aremove-claude-markssolo para Claude) - Capa A: Unicode invisible / bidi / caracteres de etiqueta / homoglifos de espacio (
inspect_text/clean_text) - Capa B: guía de reescritura +
rewrite_text.pyopcional (print-prompt, Ollama, compatible con OpenAI) - Archivos: eliminación de metadatos C2PA/IA para PNG, JPEG, SVG, PDF, DOCX, ODT, HTML, Markdown
inspect_file.py/clean_file.pyunificados- Documentación multivendedor (Claude, Gemini/clase SynthID, OpenAI, LLM abierto)
- Scripts basados primero en la biblioteca estándar;
c2patool/exiftoolopcionales
Licencia
MIT — consulte LICENSE.
Referencias
- Cómo marca Claude el contenido generado por IA (Anthropic)
- Dathathri et al., Scalable watermarking for identifying large language model outputs (SynthID-Text, Nature 2024)
- Google AI for Developers, SynthID safeguards (documentación de la API de Gemini)
- C2PA / c2patool
- Kirchenbauer et al., A Watermark for Large Language Models
- THU-BPM/MarkLLM (kit de herramientas unificado para evaluar algoritmos de marcas de agua en LLM)
- Pan et al., MarkDiffusion: An Open-Source Toolkit for Generative Watermarking of Latent Diffusion Models (JMLR) — el kit de herramientas de incrustación que envuelve el arnés opcional de marcas de agua de imágenes de este repositorio — código, documentación
- Zhang et al., Watermarks in the Sand: Impossibility of Strong Watermarking for Generative Models (ICML 2024)
- google-deepmind/synthid-text (referencia de investigación; no se usa para detección aquí)
- aloshdenny/reverse-SynthID (referencia de investigación)
- Liu et al., Image Watermarks are Removable Using Controllable Regeneration from Clean Noise (ICLR 2025) — el método de regeneración de píxeles que implementa el backend opcional CtrlRegen — código
- Kassis & Hengartner, UnMarker: A Universal Attack on Defensive Image Watermarking (arXiv:2405.08363; IEEE S&P 2025) — un ataque de marca de agua universal comparado con una métrica diferente a la de CtrlRegen
- Goonatilake & Ateniese, Removing the Watermark Is Not Enough: Forensic Stealth in Generative-AI Watermark Removal (arXiv:2605.09203) — motiva el valor predeterminado de intensidad conservadora: la eliminación aún puede dejar rastros forenses
- mertizci/noai-watermark (kit de herramientas CLI/Python para eliminación de SynthID/StableSignature/TreeRing y eliminación de metadatos de IA)
- 0xROOTPLS/DeSynth (eliminación de SynthID para imágenes de OpenAI/Google)
- Institute of AI PM, AI Content Provenance and Watermarking: The PM's Guide to C2PA and SynthID (modelo industrial de dos capas: C2PA + marca de agua imperceptible / enlace blando; contexto de SB 942 / Art. 50 de la Ley de IA de la UE)