# [Tuto] Watermarks IA : nettoyer les filigranes et réécrire localement un texte avec Ollama

- Link: https://dolys.fr/forums/topic/tuto-watermarks-ia-nettoyer-les-filigranes-et-reecrire-localement-un-texte-avec-ollama/
- Published: 2026-08-20T18:58:46+02:00
- Author: nam1962

Le terme watermark signifie littéralement filigrane. Dans un document classique, un filigrane peut être visible, comme un texte ou un logo placé en arrière-plan d&rsquo;une page. Dans le contexte des contenus générés par IA, le mot watermark peut désigner des mécanismes beaucoup moins visibles. On peut notamment rencontrer : des caractères Unicode invisibles ou inhabituels ; des métadonnées ajoutées au fichier ; des informations de provenance ; des structures particulières dans le document ; des signaux statistiques intégrés à la génération même du texte, par exemple dans la succession et le choix des tokens. Ces différents mécanismes sont souvent regroupés sous le terme de watermarks IA, ou filigranes IA en français, même s&rsquo;ils ne ressemblent pas nécessairement à un filigrane graphique traditionnel. Le but de ce tutoriel est donc de disposer de deux outils distincts : wr-clean fichier.md pour nettoyer les filigranes ou marques déterministes que l&rsquo;on peut directement retirer du fichier, et : wr-rewrite-gui pour effectuer une véritable réécriture locale avec Ollama lorsqu&rsquo;on souhaite également perturber un éventuel watermark statistique lié au choix des tokens. Il est important de distinguer ces deux familles. Un caractère Unicode invisible ou une métadonnée peut être détecté puis supprimé sans toucher au texte lui-même. Un watermark statistique fonctionne différemment. Il peut être lié à la manière dont le modèle choisit successivement ses mots ou ses tokens. Dans ce cas, il n&rsquo;existe pas nécessairement une chaîne cachée qu&rsquo;il suffirait d&rsquo;effacer. Il faut modifier suffisamment la formulation du texte pour produire une nouvelle succession de tokens. C&rsquo;est précisément la différence entre le nettoyage effectué par wr-clean et la réécriture effectuée avec Ollama. Tout le traitement présenté ici reste local sur la machine. 1. Principe de l&rsquo;installation Nous allons utiliser : Docker ; Ollama ; le projet watermarks-remover ; curl et jq ; YAD pour l&rsquo;interface graphique ; deux petits wrappers placés dans ~/.local/bin. L&rsquo;installation finale sera organisée ainsi : ~/.local/bin/ ├── wr-clean └── wr-rewrite-gui ~/Bureau/ └── wr-rewrite.desktop Le nettoyage déterministe sera réalisé par watermarks-remover dans Docker. La réécriture sera réalisée directement par Ollama installé sur la machine. 2. Installer les dépendances sous EndeavourOS Sous EndeavourOS ou Arch Linux : sudo pacman -S --needed \ git \ docker \ docker-compose \ python \ curl \ jq \ yad \ ollama On active Docker : sudo systemctl enable --now docker Et Ollama : sudo systemctl enable --now ollama Pour pouvoir utiliser Docker sans sudo : sudo usermod -aG docker "$USER" Il faut ensuite fermer puis rouvrir la session utilisateur pour que le nouveau groupe soit pris en compte. On peut vérifier : docker --version ollama --version 3. Récupérer le projet watermarks-remover Créons un emplacement propre pour les sources : mkdir -p ~/.local/src cd ~/.local/src Puis clonons le dépôt : git clone https://github.com/guillaumemeyer/watermarks-remover.git On obtient : ~/.local/src/watermarks-remover/ Pour mettre le dépôt à jour plus tard : cd ~/.local/src/watermarks-remover git pull Le projet contient notamment des scripts destinés à inspecter, nettoyer et réécrire les fichiers. Dans l&rsquo;image Docker utilisée ici, on retrouve par exemple : /app/scripts/inspect_file.py /app/scripts/clean_file.py /app/scripts/rewrite_text.py 4. Installer watermarks-remover avec Docker On récupère l&rsquo;image : docker pull ghcr.io/guillaumemeyer/watermarks-remover:latest Puis on crée le conteneur en exposant son API uniquement en local : docker run -d \ --name watermarks-remover \ --restart unless-stopped \ -p 127.0.0.1:8765:8765 \ ghcr.io/guillaumemeyer/watermarks-remover:latest Vérification : docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' On doit obtenir une ligne proche de : watermarks-remover ghcr.io/guillaumemeyer/watermarks-remover:latest 127.0.0.1:8765-&gt;8765/tcp Quelques commandes utiles : docker stop watermarks-remover docker start watermarks-remover docker logs watermarks-remover Pour mettre l&rsquo;image à jour : docker pull ghcr.io/guillaumemeyer/watermarks-remover:latest 5. Créer la commande wr-clean Nous allons placer le script dans : ~/.local/bin/wr-clean Création : mkdir -p ~/.local/bin cat &gt; ~/.local/bin/wr-clean &lt;&lt;'EOF' #!/usr/bin/env bash # wr-clean : nettoie un fichier via watermarks-remover set -euo pipefail API="http://127.0.0.1:8765/clean" if [[ $# -lt 1 ]]; then echo "Usage: wr-clean &lt;fichier&gt; [fichier_sortie]" echo echo "Exemples :" echo " wr-clean draft.md" echo " wr-clean photo.png photo.cleaned.png" echo " wr-clean notes.docx notes.propre.docx" exit 1 fi INPUT="$1" if [[ ! -f "$INPUT" ]]; then echo "Erreur : $INPUT n'existe pas" exit 1 fi if [[ $# -ge 2 ]]; then OUTPUT="$2" elif [[ "$INPUT" == *.* ]]; then OUTPUT="${INPUT%.*}.cleaned.${INPUT##*.}" else OUTPUT="${INPUT}.cleaned" fi echo "→ Nettoyage de $INPUT ..." PAYLOAD=$( jq -n \ --arg file "$(base64 -w0 "$INPUT")" \ --arg name "$(basename "$INPUT")" \ '{file:$file, name:$name}' ) RESULT=$( curl -sS \ -X POST "$API" \ -H 'Content-Type: application/json' \ -d "$PAYLOAD" ) if printf '%s' "$RESULT" | jq -e '.ok == true' &gt;/dev/null; then printf '%s' "$RESULT" | jq -r '.cleaned' | base64 -d &gt; "$OUTPUT" echo "✓ Fichier nettoyé → $OUTPUT" else echo "Erreur :" printf '%s' "$RESULT" | jq . exit 1 fi EOF chmod +x ~/.local/bin/wr-clean Vérifions que le script est bien trouvé : command -v wr-clean On doit normalement obtenir quelque chose comme : /home/utilisateur/.local/bin/wr-clean 6. Utiliser wr-clean Pour un fichier Markdown : wr-clean document.md Le script crée automatiquement : document.cleaned.md On peut également préciser le nom de sortie : wr-clean document.md document.propre.md Avec une image : wr-clean photo.png Avec un document Word : wr-clean notes.docx notes.propre.docx Le fichier original reste intact. Un point important : le message : ✓ Fichier nettoyé signifie que le traitement s&rsquo;est correctement déroulé. Cela ne signifie pas nécessairement que quelque chose a effectivement été supprimé. Pour vérifier si le fichier a été modifié : cmp -s document.md document.cleaned.md \ &amp;&amp; echo "Fichiers identiques" \ || echo "Le fichier a été modifié" Si les deux fichiers sont identiques, cela signifie simplement que le nettoyage déterministe n&rsquo;a trouvé aucune marque à retirer. 7. Pourquoi wr-clean ne suffit pas toujours Un watermark statistique n&rsquo;est pas nécessairement stocké dans un caractère caché ou dans une métadonnée. Il peut résulter du choix probabiliste des tokens effectué pendant la génération. Imaginons qu&rsquo;un modèle dispose de plusieurs mots possibles pour continuer une phrase. Un mécanisme de watermark peut favoriser certaines familles de tokens plutôt que d&rsquo;autres selon une règle déterminée pendant la génération. Le texte reste parfaitement lisible. Il n&rsquo;y a alors aucun caractère particulier à supprimer. Pour perturber ce type de signal, l&rsquo;une des approches possibles consiste à produire une nouvelle formulation du texte avec un autre modèle. C&rsquo;est le rôle de notre seconde commande. 8. Installer les modèles Ollama Sur une machine équipée de 16 Go de RAM, trois modèles raisonnables peuvent être utilisés : ollama pull qwen3.5:9b ollama pull ministral-3:8b ollama pull gemma4:12b On vérifie : ollama list Dans mes essais sur un Ryzen 5 5600H avec 16 Go de RAM : qwen3.5:9b environ 59 secondes ministral-3:8b environ 66 secondes gemma4:12b environ 4 min 24 s pour une petite réécriture réalisée avec un modèle chargé à froid. qwen3.5:9b constitue donc ici le meilleur compromis entre qualité et vitesse. 9. Tester Ollama directement On peut créer un petit texte de test : cat &gt;/tmp/rewrite-test.txt &lt;&lt;'EOF' Réécris ce texte en français naturel en modifiant nettement la formulation, l'ordre des propositions et les connecteurs, sans modifier les informations. Réponds uniquement avec le texte réécrit. Je tiens d'abord à saluer la qualité de votre synthèse. Elle est claire, documentée, et les questions que vous posez sont bien posées. EOF Puis tester Qwen : time curl -s http://127.0.0.1:11434/api/chat \ -H 'Content-Type: application/json' \ -d "$(jq -n \ --arg model 'qwen3.5:9b' \ --arg prompt "$(cat /tmp/rewrite-test.txt)" \ '{ model:$model, messages:[{role:"user",content:$prompt}], think:false, stream:false, keep_alive:0 }')" \ | jq -r '.message.content' Deux options importantes apparaissent ici : "think": false et : "keep_alive": 0 think:false désactive le raisonnement lorsque le modèle le prend en charge. Pour une simple réécriture, cela évite de perdre du temps dans une phase de raisonnement inutile. keep_alive:0 demande à Ollama de décharger le modèle une fois le traitement terminé. C&rsquo;est pratique sur une machine disposant de 16 Go de RAM. 10. Créer l&rsquo;interface graphique wr-rewrite-gui On place là encore le script dans : ~/.local/bin/ Création : cat &gt; ~/.local/bin/wr-rewrite-gui &lt;&lt;'EOF' #!/usr/bin/env bash set -u TITLE="Watermark Rewriter" OLLAMA_URL="http://127.0.0.1:11434" LOGFILE="$HOME/.local/state/wr-rewrite.log" mkdir -p "$(dirname "$LOGFILE")" log() { echo "[wr-rewrite] $(date '+%Y-%m-%d %H:%M:%S') $*" \ &gt;&gt; "$LOGFILE" 2&gt;/dev/null || true } error_box() { yad \ --error \ --center \ --width=600 \ --title="$TITLE" \ --text="$1" } for cmd in yad curl jq; do command -v "$cmd" &gt;/dev/null 2&gt;&amp;1 || { echo "$cmd est requis" exit 1 } done if ! curl -sf "$OLLAMA_URL/api/tags" &gt;/dev/null 2&gt;&amp;1; then error_box "Ollama ne répond pas sur $OLLAMA_URL" exit 1 fi CHOICE=$( yad \ --title="$TITLE" \ --center \ --width=720 \ --height=430 \ --form \ --separator="|" \ --text="&lt;big&gt;&lt;b&gt;Réécriture locale&lt;/b&gt;&lt;/big&gt; Réécriture via Ollama avec raisonnement désactivé." \ --field="Fichier:FL" "$HOME/Bureau/" \ --field="Modèle:CB" \ "qwen3.5:9b!ministral-3:8b!gemma4:12b" \ --field="Méthode:CB" \ "paraphrase!humanize" \ --field="Température:NUM" \ "0.7!0.1..1.2!0.1" \ --button="Annuler":1 \ --button="Réécrire":0 ) [ $? -ne 0 ] &amp;&amp; exit 0 IFS='|' read -r INPUT MODEL STRENGTH TEMPERATURE _ &lt;&lt;&lt; "$CHOICE" if [ ! -f "$INPUT" ]; then error_box "Fichier introuvable : $INPUT" exit 1 fi case "$STRENGTH" in paraphrase) INSTRUCTION="Réécris le texte suivant en français naturel en utilisant une formulation nettement différente au niveau lexical et syntaxique. Modifie l'ordre des propositions lorsque cela reste naturel, varie les connecteurs, la longueur des phrases et les tournures. Préserve strictement tous les faits, nombres, noms, dates, références et affirmations. N'ajoute ni ne retire aucune information. Réponds uniquement avec le texte réécrit." ;; humanize) INSTRUCTION="Réécris le texte suivant comme s'il avait été rédigé naturellement à partir de zéro. Varie le rythme, la longueur des phrases, les formulations et les transitions. Préserve strictement tous les faits, nombres, noms, dates, références et affirmations. N'ajoute ni ne retire aucune information. Réponds uniquement avec le texte réécrit." ;; esac if [[ "$INPUT" == *.* ]]; then OUTPUT="${INPUT%.*}.rewritten.${INPUT##*.}" else OUTPUT="${INPUT}.rewritten" fi PROMPT="$INSTRUCTION --- $(cat "$INPUT")" log "input=$INPUT model=$MODEL strength=$STRENGTH output=$OUTPUT" TMP_RESPONSE=$(mktemp) TMP_ERROR=$(mktemp) START=$(date +%s) ( echo "10" echo "# Chargement de $MODEL..." curl -sS \ --max-time 1800 \ "$OLLAMA_URL/api/chat" \ -H 'Content-Type: application/json' \ -d "$( jq -n \ --arg model "$MODEL" \ --arg prompt "$PROMPT" \ --argjson temperature "$TEMPERATURE" \ '{ model: $model, messages: [ { role: "user", content: $prompt } ], think: false, stream: false, keep_alive: 0, options: { temperature: $temperature } }' )" \ &gt; "$TMP_RESPONSE" \ 2&gt; "$TMP_ERROR" if [ $? -ne 0 ]; then echo "FAIL" &gt; "${TMP_RESPONSE}.status" exit fi echo "85" echo "# Écriture du résultat..." jq -r '.message.content // empty' "$TMP_RESPONSE" &gt; "$OUTPUT" if [ ! -s "$OUTPUT" ]; then echo "FAIL" &gt; "${TMP_RESPONSE}.status" exit fi echo "OK" &gt; "${TMP_RESPONSE}.status" echo "100" echo "# Terminé" ) | yad \ --progress \ --title="$TITLE" \ --center \ --width=520 \ --auto-close \ --no-buttons \ --text="Réécriture en cours..." END=$(date +%s) DURATION=$((END - START)) STATUS=$(cat "${TMP_RESPONSE}.status" 2&gt;/dev/null || echo FAIL) if [ "$STATUS" != "OK" ]; then ERR=$(cat "$TMP_ERROR" 2&gt;/dev/null) error_box "La réécriture a échoué. ${ERR:-Réponse Ollama invalide.}" rm -f \ "$TMP_RESPONSE" \ "$TMP_ERROR" \ "${TMP_RESPONSE}.status" exit 1 fi rm -f \ "$TMP_RESPONSE" \ "$TMP_ERROR" \ "${TMP_RESPONSE}.status" MIN=$((DURATION / 60)) SEC=$((DURATION % 60)) yad \ --title="$TITLE" \ --center \ --width=620 \ --text="&lt;big&gt;&lt;b&gt;Réécriture terminée&lt;/b&gt;&lt;/big&gt; &lt;b&gt;Modèle :&lt;/b&gt; $MODEL &lt;b&gt;Méthode :&lt;/b&gt; $STRENGTH &lt;b&gt;Durée :&lt;/b&gt; ${MIN} min ${SEC} s &lt;b&gt;Sortie :&lt;/b&gt; $OUTPUT" \ --button="Ouvrir le dossier":2 \ --button="Ouvrir le fichier":3 \ --button="Fermer":0 case $? in 2) xdg-open "$(dirname "$OUTPUT")" &gt;/dev/null 2&gt;&amp;1 &amp; ;; 3) xdg-open "$OUTPUT" &gt;/dev/null 2&gt;&amp;1 &amp; ;; esac exit 0 EOF chmod +x ~/.local/bin/wr-rewrite-gui On peut lancer directement l&rsquo;interface : wr-rewrite-gui 11. Ce que propose l&rsquo;interface La fenêtre permet de sélectionner : Fichier Modèle Méthode Température Les modèles disponibles dans notre configuration sont : qwen3.5:9b ministral-3:8b gemma4:12b Deux modes sont proposés : paraphrase humanize Le mode paraphrase cherche surtout à modifier fortement le vocabulaire et la structure syntaxique. Le mode humanize privilégie une rédaction plus naturelle et moins mécanique. Le fichier original n&rsquo;est jamais écrasé. Par exemple : document.md produira : document.rewritten.md 12. Ajouter un lanceur XFCE sur le Bureau Pour éviter de lancer la commande depuis un terminal, on peut créer un raccourci sur le Bureau. Sur une installation française d&rsquo;XFCE : cat &gt; ~/Bureau/wr-rewrite.desktop &lt;&lt;EOF [Desktop Entry] Version=1.0 Type=Application Name=Watermark Rewriter Comment=Réécriture locale via Ollama Exec=$HOME/.local/bin/wr-rewrite-gui Icon=accessories-text-editor Terminal=false Categories=Utility; StartupNotify=true EOF chmod +x ~/Bureau/wr-rewrite.desktop On obtient alors une icône : Watermark Rewriter directement sur le Bureau. XFCE peut demander d&rsquo;autoriser le lanceur lors de sa première utilisation. 13. Organisation finale L&rsquo;installation ressemble finalement à ceci : ~/.local/src/ └── watermarks-remover/ ~/.local/bin/ ├── wr-clean └── wr-rewrite-gui ~/Bureau/ └── wr-rewrite.desktop Avec côté services : Docker └── watermarks-remover └── 127.0.0.1:8765 et : Ollama ├── qwen3.5:9b ├── ministral-3:8b └── gemma4:12b 14. Les deux opérations en pratique Pour nettoyer les marques directement présentes dans le fichier : wr-clean document.md Résultat : document.cleaned.md Pour effectuer une véritable réécriture : wr-rewrite-gui Résultat : document.rewritten.md Les deux opérations sont donc différentes : document.md | +--&gt; wr-clean | | | +--&gt; document.cleaned.md | +--&gt; wr-rewrite-gui | +--&gt; Ollama | +--&gt; document.rewritten.md 15. Nettoyer également le texte après réécriture On peut aller un peu plus loin. Après une réécriture par Ollama : document.rewritten.md on peut repasser le résultat dans wr-clean : wr-clean document.rewritten.md Cela produit : document.rewritten.cleaned.md La chaîne complète devient alors : texte original | v réécriture Ollama | v nouvelle succession de tokens | v nettoyage déterministe | v fichier final Cela permet de combiner les deux approches sans les confondre. 16. Vérifier la mémoire utilisée par Ollama Pour savoir si un modèle est actuellement chargé : ollama ps Grâce à : "keep_alive": 0 le modèle doit normalement être déchargé à la fin de la réécriture. On peut également arrêter manuellement un modèle : ollama stop qwen3.5:9b 17. Nettoyer sa bibliothèque Ollama Avec 16 Go de RAM, il est inutile d&rsquo;accumuler de nombreux gros modèles ayant le même usage. Lister les modèles : ollama list Supprimer un modèle : ollama rm nom-du-modele Par exemple : ollama rm ancien-modele:7b Les modèles peuvent être retéléchargés plus tard avec : ollama pull nom-du-modele 18. Limites à garder en tête Il faut distinguer une suppression déterministe d&rsquo;une tentative de perturbation statistique. Avec un caractère Unicode invisible, on peut généralement dire précisément : ce caractère était présent puis : il a été supprimé Avec un watermark statistique propriétaire, la situation est différente. Si l&rsquo;algorithme de détection, les paramètres ou les clés ne sont pas publics, on ne dispose pas forcément d&rsquo;un moyen de mesurer directement le signal avant et après la réécriture. La réécriture doit donc être comprise comme une méthode de modification profonde de la succession des tokens, pas comme une certification universelle. 19. Pourquoi faire tout cela en local ? L&rsquo;intérêt principal de cette installation est aussi ailleurs. Un document potentiellement confidentiel reste sur l&rsquo;ordinateur : document | +--&gt; Docker local | +--&gt; Ollama local Il n&rsquo;est pas nécessaire de l&rsquo;envoyer vers un site de paraphrase ou un service de nettoyage en ligne. On garde également : le fichier original ; le contrôle sur le modèle utilisé ; le contrôle sur la température ; les logs locaux ; la possibilité d&rsquo;inspecter les scripts ; une séparation claire entre nettoyage et réécriture. Conclusion Les watermarks IA, ou filigranes IA, ne désignent pas une seule technologie. Certains sont directement présents dans les fichiers et peuvent être nettoyés de manière déterministe. D&rsquo;autres peuvent être liés à la façon dont un modèle génère successivement ses tokens. Dans notre installation : wr-clean s&rsquo;occupe du premier cas, tandis que : wr-rewrite-gui utilise un autre modèle local pour produire une nouvelle rédaction. Avec Docker, Ollama et deux petits scripts dans ~/.local/bin, on obtient ainsi une chaîne de traitement locale, relativement simple à utiliser et entièrement sous notre contrôle.
