Code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi
gi.require_version('Gegl', '0.4') # Gegl est utilisé pour les opérations de bas niveau sur les pixels
from gi.repository import Gegl
from gi.repository import GObject
from gi.repository import GLib
import sys
import math # Pour la fonction sqrt (racine carrée) dans le calcul de la différence de couleur
# Fonctions de traduction (standard pour les plugins GIMP)
def N_(message): return message
def _(message): return GLib.dgettext(None, message)
class CleanAndRegularize (Gimp.PlugIn):
    ## GimpPlugIn virtual methods ##
    # Cette méthode est appelée par GIMP au démarrage pour savoir quels plugins sont disponibles
    def do_query_procedures(self):
        # Retourne une liste des noms de procédures que ce plugin fournit
        return [ "plug-in-clean-and-regularize" ]
    # Cette méthode est appelée pour créer une instance de la procédure
    def do_create_procedure(self, name):
        # Crée une nouvelle procédure d'image
        procedure = Gimp.ImageProcedure.new(self, name,
                                            Gimp.PDBProcType.PLUGIN,
                                            self.run, None)
        # Définit les types d'images sur lesquels le plugin peut opérer
        procedure.set_image_types("*") # Peut opérer sur n'importe quel type d'image
        # Définit la sensibilité du plugin (nécessite un calque sélectionné)
        procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.DRAWABLE)
        # Définit le texte qui apparaîtra dans le menu GIMP
        procedure.set_menu_label(_("Nettoyer et Régulariser..."))
        # Définit le chemin dans le menu GIMP
        procedure.add_menu_path('<Image>/Filters/Ma_Regularisation/')
        # Ajoute la documentation (visible dans le navigateur de procédures)
        procedure.set_documentation(_("Nettoie une image en rendant transparents les pixels en dehors d'une couleur sélectionnée, puis régularise la forme restante."),
                                    _("Rend transparents les pixels en dehors d'une couleur sélectionnée et régularise la forme restante."),
                                    name)
        # Attribution de l'auteur
        procedure.set_attribution("Votre Nom", "Votre Nom", "2025")
        # Ajoute les paramètres de la procédure (ceux qui apparaissent dans la boîte de dialogue)
        procedure.add_argument_from_property(GObject.Property(
            "target-color", "Couleur à conserver",
            "La couleur cible à conserver dans l'image.",
            GObject.TYPE_JSUINT64, # Type de propriété pour la couleur (RGBA 64 bits)
            Gimp.RGB(255, 255, 255).to_rgba(), # Valeur par défaut (blanc)
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
        ))
        procedure.add_argument_from_property(GObject.Property(
            "tolerance", "Tolérance de couleur (0-255)",
            "La tolérance pour la comparaison des couleurs (0-255).",
            GObject.TYPE_INT, # Type entier
            30, # Valeur par défaut
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            minimum=0, maximum=255
        ))
        procedure.add_argument_from_property(GObject.Property(
            "operation-type", "Opération de régularisation",
            "Le type d'opération de régularisation à appliquer.",
            GObject.TYPE_INT, # Type entier pour l'option
            0, # Valeur par défaut (0 = Aucune)
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            # Les options sont définies par des paires valeur/label
            blurb_values=["Aucune", "Érosion de la sélection", "Dilatation de la sélection"]
        ))
        return procedure
    # La méthode 'run' est le cœur du plugin, exécutée lorsque l'utilisateur lance le plugin
    def run(self, procedure, run_mode, image, drawables, config, run_data):
        # Vérifie qu'un seul calque est sélectionné
        if len(drawables) != 1:
            msg = _("Procedure '{}' only works with one drawable.").format(procedure.get_name())
            error = GLib.Error.new_literal(Gimp.PlugIn.error_quark(), msg, 0)
            return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, error)
        else:
            drawable = drawables[0]
        # Récupère les valeurs des paramètres de la configuration
        target_color_rgba = config.get_property("target-color")
        # Convertit la couleur RGBA 64 bits en Gimp.RGB pour un accès facile
        target_color_gimp_rgb = Gimp.RGB()
        target_color_gimp_rgb.parse_rgba(target_color_rgba)
        
        target_r = target_color_gimp_rgb.red_int
        target_g = target_color_gimp_rgb.green_int
        target_b = target_color_gimp_rgb.blue_int
        tolerance = config.get_property("tolerance")
        operation_type = config.get_property("operation-type")
        # Début de l'opération GIMP (pour l'historique d'annulation)
        image.undo_group_start()
        Gimp.progress_init(_("Traitement de l'image..."))
        # Assurez-vous que le calque a un canal alpha pour la transparence
        if not drawable.has_alpha():
            drawable.add_alpha()
        # --- Étape 1 : Rendre transparent les pixels hors couleur cible ---
        width = drawable.get_width()
        height = drawable.get_height()
        
        # Utilisation de Gegl.Buffer pour un accès aux pixels plus performant et moderne
        # Obtient le buffer du calque (le contenu des pixels)
        buffer = drawable.get_buffer()
        # Crée un buffer d'ombre pour les modifications (meilleure pratique pour les plugins)
        shadow_buffer = drawable.get_shadow_buffer()
        # Accès direct aux pixels via Gegl.Buffer.get_pixel() et Gegl.Buffer.set_pixel()
        # C'est plus lent que les opérations GEGL natives, mais plus simple pour ce type de logique
        # Pour des performances extrêmes sur de très grandes images, il faudrait utiliser des opérations GEGL complètes.
        for y in range(height):
            Gimp.progress_update(float(y) / height) # Met à jour la barre de progression
            for x in range(width):
                # Récupère le pixel du buffer original
                pixel_data = buffer.get_pixel(x, y)
                # Les données de pixel sont un tuple (r, g, b, a) où chaque composante est un float de 0.0 à 1.0
                r, g, b, a = pixel_data[0], pixel_data[1], pixel_data[2], pixel_data[3]
                # Convertit les floats (0.0-1.0) en entiers (0-255) pour la comparaison de tolérance
                current_r = int(r * 255)
                current_g = int(g * 255)
                current_b = int(b * 255)
                
                # Calculer la différence de couleur (distance euclidienne)
                diff = math.sqrt((current_r - target_r)**2 + (current_g - target_g)**2 + (current_b - target_b)**2)
                if diff > tolerance:
                    # Rendre le pixel transparent (alpha = 0.0)
                    shadow_buffer.set_pixel(x, y, (r, g, b, 0.0))
                else:
                    # Conserver le pixel tel quel
                    shadow_buffer.set_pixel(x, y, (r, g, b, a))
        
        # Applique les modifications du buffer d'ombre au calque
        drawable.merge_shadow(True)
        # Met à jour la zone modifiée pour l'affichage
        drawable.update(0, 0, width, height)
        # --- Étape 2 : Régulariser la forme restante ---
        # Utilisation des fonctions PDB de GIMP pour les opérations de sélection et de remplissage
        if operation_type == 1: # Érosion de la sélection
            # Crée une sélection à partir du canal alpha du calque
            image.set_selection_mask(drawable.get_alpha_mask())
            # Rétrécit la sélection
            Gimp.selection_shrink(image, 2) # Rétrécir de 2 pixels
            # Inverse la sélection pour remplir l'extérieur
            image.invert_selection()
            # Remplir la sélection inversée avec de la transparence
            drawable.fill(Gimp.FillType.TRANSPARENT)
            # Désélectionne tout
            image.set_selection_mask(None)
        elif operation_type == 2: # Dilatation de la sélection
            image.set_selection_mask(drawable.get_alpha_mask())
            # Agrandit la sélection
            Gimp.selection_grow(image, 2) # Agrandir de 2 pixels
            image.invert_selection()
            drawable.fill(Gimp.FillType.TRANSPARENT)
            image.set_selection_mask(None)
            
        # Rafraîchir l'affichage de toutes les fenêtres GIMP
        Gimp.displays_flush()
        image.undo_group_end() # Fin de l'opération GIMP (pour l'historique d'annulation)
        Gimp.progress_end()
        # Retourne les valeurs de succès
        return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
# Enregistre le plugin dans GIMP
Gimp.main(CleanAndRegularize.__gtype__, sys.argv)