API Tutorials

Stratégies de résolution de CAPTCHA d'images multi-caractères

Sur une image de six caractères tordus, ce qui sépare une lecture juste d'une lecture fausse tient rarement à votre code : cela tient à ce que vous envoyez avec l'image. Longueur attendue, sensibilité à la casse, description de la déformation : ces indices orientent la reconnaissance bien plus efficacement qu'un filtre appliqué localement. La marche à suivre tient en trois temps : envoyez l'image en base64 à l'API CaptchaAI, ajustez les indices selon le type de déformation, et ne prétraitez qu'en dernier recours.


Les trois paramètres qui pèsent le plus

Avant d'écrire la moindre ligne, regardez ce que vous pouvez déclarer à la soumission. Trois champs font l'essentiel :

  • minLen / maxLen — la fourchette de longueur. Sur des captures de 5 à 7 caractères, la déclarer élimine les lectures parasites : une ligne de bruit prise pour un « l », un point pris pour un « i ».
  • regsense — la casse. À 1, la réponse conserve majuscules et minuscules ; sur un formulaire qui compare littéralement, c'est indispensable.
  • textinstructions — une phrase libre décrivant l'image. C'est le levier le plus sous-utilisé : « les caractères peuvent se toucher » ou « ignorez les lignes de fond » change concrètement la lecture.

À cela s'ajoutent language (jeu de caractères attendu), numeric (chiffres uniquement) et calc pour les images qui posent une opération à résoudre plutôt qu'un texte à recopier.


Classer vos CAPTCHA image multi-caractères par type de déformation

Le bon indice dépend de ce que vous avez en face. Classez d'abord vos captures :

Type Descriptif Difficulté
Texte propre Aucune distorsion, police uniforme Facile
Texte déformé Lettres tournées ou redimensionnées une à une Moyen
Lettres collées Les caractères se chevauchent ou se touchent Difficile
Multi-police Une police différente par caractère Difficile
Bruit + lignes Bruit de fond, lignes barrées Moyen
Variation de couleur Une couleur différente par caractère Moyen
Expression mathématique Chiffres et opérateurs, résultat attendu Moyen

Envoyer votre CAPTCHA image en base64 à l'API

Le socle ne change pas : un POST vers in.php avec method=base64, puis l'interrogation régulière de res.php jusqu'au texte. Les indices se greffent sur ce même payload.

import requests
import base64
import time
import os

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


def solve_complex_image(image_b64, hints=None):
    """Solve a complex multi-character image CAPTCHA."""
    payload = {
        "key": API_KEY,
        "method": "base64",
        "body": image_b64,
        "json": 1,
    }

    if hints:
        payload.update(hints)

    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    time.sleep(8)
    for _ in range(24):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()
        if data.get("status") == 1:
            return data["request"]
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("Solve timeout")

Gardez CAPCHA_NOT_READY comme seul cas de patience : tout autre code d'erreur doit remonter immédiatement. Un CAPTCHA image passe sous la barre des 0,5 s côté résolution ; le reste du temps observé vient de votre intervalle d'interrogation.


Adapter les indices à chaque cas

Lettres collées

C'est le cas qui met en défaut un OCR local : la segmentation échoue avant la reconnaissance. Déclarez la fourchette de longueur et prévenez que les glyphes se touchent :

def solve_connected_letters(image_path):
    """Solve CAPTCHA with connected/overlapping characters."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "textinstructions": "Characters may be connected or overlapping",
        "minLen": 4,
        "maxLen": 8,
    })

Bruit de fond et casse mixte

Quand l'image mêle lignes barrées et alternance majuscules/minuscules, combinez la casse stricte et une instruction qui écarte explicitement le décor :

def solve_noisy_mixed(image_path):
    """Solve CAPTCHA with background noise and mixed case."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "regsense": 1,         # Case-sensitive
        "language": 2,         # Latin characters
        "textinstructions": "Ignore background lines and noise",
    })

Polices mélangées

Une police différente par caractère fait chuter la précision des modèles entraînés sur une fonte unique. La fourchette de longueur redevient ici votre meilleur garde-fou :

def solve_multi_font(image_path):
    """Solve CAPTCHA using multiple fonts per character."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "textinstructions": "Each character may use a different font or style",
        "minLen": 5,
        "maxLen": 7,
    })

Prétraiter, mais seulement si nécessaire

Le prétraitement sert sur les images très bruitées ou à faible contraste : niveaux de gris, contraste, netteté, binarisation. Sur une image déjà lisible, il retire de l'information et dégrade le résultat — mesurez avant/après sur un même lot plutôt que de l'activer par principe.

# preprocess.py
from PIL import Image, ImageFilter, ImageEnhance
import io
import base64


def preprocess_for_ocr(image_path):
    """Preprocess image to improve OCR accuracy."""
    img = Image.open(image_path)

    # Convert to grayscale
    img = img.convert("L")

    # Increase contrast
    enhancer = ImageEnhance.Contrast(img)
    img = enhancer.enhance(2.0)

    # Sharpen
    img = img.filter(ImageFilter.SHARPEN)

    # Binarize (threshold)
    threshold = 128
    img = img.point(lambda p: 255 if p > threshold else 0)

    # Encode back to base64
    buffer = io.BytesIO()
    img.save(buffer, format="PNG")
    return base64.b64encode(buffer.getvalue()).decode("ascii")

Relancer en dégradant les contraintes

Une lecture ratée ne justifie pas de renvoyer trois fois la même requête. Dégradez les contraintes à chaque tentative : indices d'origine, puis sans instruction textuelle, puis contraintes relâchées. Signalez en parallèle les réponses fausses avec reportbad — c'est ce retour qui alimente la qualité sur vos gabarits récurrents.

# retry_strategy.py


def solve_with_retry(image_b64, hints, max_retries=3):
    """Retry solving with fallback strategies."""
    strategies = [
        hints,                                          # Original hints
        {**hints, "textinstructions": ""},              # Without instructions
        {**hints, "numeric": 0, "regsense": 0},        # Relaxed constraints
    ]

    for i, strategy in enumerate(strategies[:max_retries]):
        try:
            result = solve_complex_image(image_b64, strategy)
            return {"text": result, "strategy": i, "success": True}
        except RuntimeError:
            continue

    return {"text": None, "strategy": -1, "success": False}


def report_bad_answer(task_id):
    """Report incorrect answer for quality feedback."""
    requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY,
        "action": "reportbad",
        "id": task_id,
    }, timeout=10)

Journalisez l'index de la stratégie retenue avec la réponse : au bout de quelques centaines de lectures, vous saurez si votre premier jeu d'indices est bien calibré.


Un cas concret : extranet fournisseur et lots nocturnes

Une équipe produit lyonnaise récupère chaque nuit les fiches tarifaires d'un extranet fournisseur dont le formulaire de connexion affiche une image de six caractères, police unique mais fond strié. Le worker tourne sur une instance Scaleway à Paris, dans une fenêtre de deux heures.

Trois décisions suffisent : minLen=6 et maxLen=6 puisque la longueur est fixe, regsense=1 parce que le formulaire compare littéralement, et une textinstructions mentionnant les lignes de fond. Le prétraitement, testé puis abandonné, n'apportait rien sur ce gabarit. Côté capacité, l'équipe a calibré sur BASIC ($15/mois, 5 threads) puis est passée à STANDARD ($30/mois, 15 threads) quand le lot a doublé — la facturation portant sur le thread simultané, seule la parallélisation compte, jamais le volume d'images.

Côté RGPD, un réflexe à garder : les captures envoyées à un service tiers ne doivent contenir que le défi CAPTCHA, jamais la portion de page portant des identifiants ou des données personnelles. Recadrez à l'image et purgez les captures de diagnostic selon votre politique de rétention.


Dépannage

Problème Cause probable Correctif
Caractères manquants Lettres collées mal segmentées Décrire le chevauchement dans textinstructions
Caractères en trop Bruit interprété comme du texte Prétraiter l'image ou resserrer minLen/maxLen
Casse incorrecte Casse non conservée Passer regsense=1
Opération renvoyée telle quelle calc=1 absent Activer le mode calcul pour les images mathématiques
Échecs répétés sur un seul site Police propre au site Signaler les réponses fausses avec reportbad
Timeout côté client Boucle d'interrogation trop courte Allonger la fenêtre d'interrogation avant de relancer

FAQ

Comment traiter une image qui affiche une opération du type « 7 + 4 = » ?

Activez le mode calcul (calc=1) : la réponse renvoyée est le résultat, pas l'expression recopiée. Sans ce paramètre, vous recevez « 7 + 4 » et le formulaire refuse la soumission.

Combien de threads faut-il pour traiter un lot de plusieurs milliers d'images ?

Comptez en simultanéité, pas en volume : un thread traite une image à la fois, et chaque plan inclut des résolutions illimitées sur ses threads. BASIC ($15/mois, 5 threads) suffit à un lot nocturne séquentiel ; au-delà, dimensionnez sur le nombre de workers que vous voulez faire tourner en parallèle.

CaptchaAI prend-il en charge hCaptcha ou FunCaptcha pour les défis en images ?

Non — ces deux types ne sont pas pris en charge. Les CAPTCHA image et texte, les grilles d'images, reCAPTCHA v2 et v3, Cloudflare Turnstile et GeeTest v3 le sont ; CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta) restent en phase bêta.

Jusqu'à combien de caractères une image peut-elle contenir ?

Environ 20 caractères. En pratique, la très grande majorité tiennent entre 4 et 8 caractères, ce qui rend la fourchette minLen/maxLen facile à calibrer.


Guides connexes


Lisez vos images les plus tordues sans bricoler d'OCR maison — commencez avec CaptchaAI.

Les commentaires sont désactivés pour cet article.