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

CI Release Stars Forks

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.

CapaObjetivoCómo
AUnicode invisible, espacios exóticos, bidi, caracteres de etiquetaScripts Python deterministas
BMarcas de agua de texto estadísticas (muestreo de tokens)Reescritura del agente + hook opcional rewrite_text.py
ArchivosC2PA / EXIF / XMP / propiedades de documentosPNG, 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):

HerramientaRol
c2patoolInspeccionar manifiestos C2PA
exiftoolEliminación residual de metadatos (especialmente PDF)
qpdfReconstrucció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:

  • --offline en 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 (transformers trust_remote_code nunca se habilita).
  • WATERMARKS_MARKLLM_RLIMIT_AS=<bytes> (entorno, POSIX) aplica un límite de espacio de direcciones al subproceso MarkLLM generado por rewrite_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:

  1. 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.
  2. Motor opcional de eliminación de píxeles: su ataque de regeneración DiffusionPurification se expone como clean_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.
  3. 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

CanalClaudeGemini/SynthIDOpenAIOpen-LLM
Texto Unicode / basado en ediciónCapa ACapa ACapa ACapa A
Texto de muestreo estadísticoCapa B mejor esfuerzoCapa B mejor esfuerzoCapa B si está presenteCapa B mejor esfuerzo
C2PA / metadatos de archivoSí (formatos listados)Sí cuando está presenteSí cuando está presenteSí cuando está presente
Marcas de píxeles en imágenesFuera de alcancePuntuación SynthID opcional + eliminación CtrlRegen (externo); detección de mismo esquema MarkDiffusion opcional + eliminación DiffusionPurification (externo)Fuera de alcanceEliminación CtrlRegen / MarkDiffusion opcional (externo)
Puertas traseras de entrenamientoFuera de alcanceFuera de alcanceFuera de alcanceFuera 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.

  1. 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.

  2. 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

FormatoInspeccionarLimpiar
PNG / JPEG / WebPFragmentos C2PA / APP11 / RIFF C2PA, pistas XMP de IAEliminar segmentos de metadatos
SVG<metadata>, XMPEliminar bloques
PDFByte/XMP + herramientas opcionalesexiftool y luego qpdf; degradado sin ninguno de los dos
DOCXdocProps / customXmlLimpiar props, eliminar customXml
ODTmeta.xmlEliminar metadatos de generador / tipo IA
HTMLmeta, JSON-LD, data-ai*Eliminar etiquetas/atributos
MarkdownClaves de IA en frontmatter YAMLEliminar 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):

CanalQué eliminamosQué puede permanecerComprobación externa (ejemplos)
C2PA con enlace duro / EXIF / XMPMarcas de enlace suave / de píxelesc2patool, verificación de Content Credentials
Medios clase SynthIDEliminación de píxeles opcional (CtrlRegen externo); puntuación local en caso contrarioMarca de agua de audio/video; marca de agua de píxeles residual tras la eliminaciónHerramientas del proveedor (p. ej. Google SynthID / detector Vertex donde se ofrezca); puntuador local opcional reverse-SynthID
Texto estadísticoReescritura de mejor esfuerzoMarcas fuertes tras edición ligeraSin 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ónEliminaNotas
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/metadatosProcedencia de archivoVer 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 abiertoEvitar re-marcado con el modelo originarioAlternativa 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/MarkDiffusion externo, Apache-2.0): markdiffusion_harness.py con subcomandos watermark / detect / purify para nueve esquemas de imagen (Tree-Ring, Ring-ID, ROBIN, WIND, SFW, Gaussian-Shading, GaussMarker, PRC, SEAL)
  • clean_image.py --remove-pixel diffusion ejecuta el ataque de regeneración DiffusionPurification de MarkDiffusion como motor alternativo de eliminación de píxeles (intensidad conservadora predeterminada 0.3)
  • Arranque setup_markdiffusion.sh (pin de PyPI 1.0.2; clon editable --checkout en commit fijado) + requirements-markdiffusion.txt + Dockerfile.markdiffusion y objetivos bootstrap-markdiffusion / smoke-markdiffusion / docker-markdiffusion-build / docker-markdiffusion-help del Makefile
  • Pruebas basadas en mocks (tests/test_markdiffusion_harness.py) — sin torch en CI; documento de referencia references/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/MarkLLM externo, Apache-2.0): detect_text_watermark.py con subcomandos detect / watermark para esquemas KGW y SynthID
  • rewrite_text.py --markllm-scheme ejecuta detección antes/después alrededor de una reescritura de la Capa B (controlado por variable de entorno; informa cleared)
  • Arranque setup_markllm.sh + requirements-markllm.txt (dependencias fijadas) + Dockerfile.markllm y objetivos bootstrap-markllm / smoke-markllm / docker-markllm-build / docker-markllm-help del 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_AS opcional en el subproceso de reescritura, torch fijado en el Dockerfile y verificación de SHA del clon en Dockerfile.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-watermark externo: adaptador clean_ctrlregen.py + arranque setup_ctrlregen.sh (commit fijado, checkout disperso, venv, verificación SHA), además de Dockerfile.ctrlregen y make bootstrap-ctrlregen / docker-ctrlregen-build / smoke-ctrlregen
  • clean_image.py --remove-pixel ctrlregen ejecuta eliminación de metadatos → eliminación CtrlRegen → puntuación opcional antes/después reverse-SynthID; inspect_image.py sugiere 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-watermark no incluye archivo LICENSE (tratado como todos los derechos reservados), y sus rutas de auto-instalación/reinicio se evitan usando CtrlRegenEngine directamente

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) y audit_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/ZWJ despué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_fn y os.fchmod solo 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 .bak a través de la misma ruta segura — los enlaces simbólicos precolocados (p. ej. en /tmp o 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 encabezado Authorization nunca puede reenviarse a un host no validado; los endpoints que no sean de bucle local están denegados por defecto (se activan con --allow-remote o WATERMARKS_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 de WATERMARKS_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_FSIZE aplicados 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 de pip-audit y 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.py ahora 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 (predeterminada 0.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.md y vendor-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 en inspect_image.py / clean_image.py con REVERSE_SYNTHID_DIR o --synthid-dir
  • Arranque de setup_synthid.sh (dependencias solo del evaluador; --full instala los requisitos ascendentes); Dockerfile.synthid más make docker-synthid-build / docker-synthid-help
  • Objetivos smoke-synthid y bootstrap-synthid del 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_manifest ya no marca Error: No claim found / No JUMBF data found como 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ó a contentauth/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) y pytest.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 a remove-claude-marks solo 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.py opcional (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.py unificados
  • Documentación multivendedor (Claude, Gemini/clase SynthID, OpenAI, LLM abierto)
  • Scripts basados primero en la biblioteca estándar; c2patool / exiftool opcionales

Licencia

MIT — consulte LICENSE.

Referencias