Génération de CAPTCHA arithmétique en image avec Python

La création d'un CAPTCHA arithmétique consiste à afficher une opération mathématique sous forme d'image et à demander à l'utilisateur de saisir le résultat. Cette approche repose sur la bibliothèque Pillow pour le rendu visuel et l'encodage base64 pour la transmission.

Un fichier de police au format .ttf est nécessaire pour le rendu du texte sur l'image.

Module de génération

La classe ci-dessous encapsule trois responsabilités distinctes : la création d'équations aléatoires, le rendu en image base64, et le décodage du résultat pour vérification.

import io
import random
import base64
from PIL import Image, ImageDraw, ImageFont


class GenerateurCaptchaArithmetique:
    """Génère des CAPTCHA basés sur des opérations mathématiques élémentaires."""

    CALCULS = {
        '+': lambda a, b: a + b,
        '-': lambda a, b: a - b,
        'x': lambda a, b: a * b,
        '/': lambda a, b: a // b
    }

    def creer_equation(self):
        """Produit une équation aléatoire avec sa solution."""
        n1 = random.randint(1, 20)
        n2 = random.randint(1, 20)
        operateur = random.choice(list(self.CALCULS.keys()))

        # Garantit un résultat entier pour la division
        if operateur == '/' and n2 != 0:
            n1 = n1 * n2

        texte = f"{n1} {operateur} {n2}"
        resultat = self.CALCULS[operateur](n1, n2)

        return texte, resultat

    def dessiner_image(self, texte):
        """Génère une image PNG encodée en base64 à partir du texte fourni."""
        largeur = 130
        hauteur = 50
        fond = (190, 205, 220)
        encre = (20, 20, 20)

        img = Image.new("RGB", (largeur, hauteur), fond)
        stylo = ImageDraw.Draw(img)
        typo = ImageFont.truetype("chemin_vers_police.ttf", 38)

        cadre = stylo.textbbox((0, 0), texte, font=typo)
        t_largeur = cadre[2] - cadre[0]
        t_hauteur = cadre[3] - cadre[1]

        origine_x = (largeur - t_largeur) // 2
        origine_y = (hauteur - t_hauteur) // 2

        stylo.text((origine_x, origine_y), texte, font=typo, fill=encre)

        memoire = io.BytesIO()
        img.save(memoire, format="PNG")
        encode = base64.b64encode(memoire.getvalue()).decode("utf-8")

        return f"data:image/png;base64,{encode}"

    def extraire_resultat(self, texte):
        """Décode et calcule le résultat d'une équation affichée."""
        parties = texte.split()
        gauche = int(parties[0])
        op = parties[1]
        droite = int(parties[2])

        return self.CALCULS[op](gauche, droite)

Intégration dans une vue Django REST

L'endpoint suivant expose le CAPTCHA via une requête GET. Il renvoie l'image encodée ainsi que la solution attendue, que le frontend peut stocker temporairement pour valider la réponse de l'utilisateur.

from rest_framework.views import APIView
from rest_framework.response import Response


class CaptchaEndpoint(APIView):
    def get(self, requete):
        generateur = GenerateurCaptchaArithmetique()
        equation, solution = generateur.creer_equation()
        image_base64 = generateur.dessiner_image(equation)

        return Response({
            "statut": 200,
            "message": "succès",
            "image": image_base64,
            "reponse": solution
        })

L'image générée affiche une équation du type 15 x 3 ou 8 + 12 centrée sur un fond coloré, prête à être intégrée dans une balise <img> via l'attribut src avec le préfixe data:image/png;base64,.

Étiquettes: Python Pillow CAPTCHA Django REST Framework Base64

Publié le 11 juillet à 06h40