Troubleshooting

L'image CAPTCHA renvoie une mauvaise réponse : optimisation de la qualité

Un moteur OCR ne renvoie que ce qu'il parvient à lire dans les pixels reçus. Quand CaptchaAI retourne une réponse erronée sur un CAPTCHA d'image, la cause se situe presque toujours en amont — dans l'image envoyée — et non dans la résolution. Toutes ces causes se corrigent côté client, dans l'ordre ci-dessous.

Règle de diagnostic : avant de suspecter le moteur, rejouez l'envoi avec le fichier exact que vous transmettez. Neuf fois sur dix, le défaut se voit à l'œil nu dans cette image.


Où se cache la mauvaise réponse : les causes fréquentes

Cause Fréquence Correctif
Image mal recadrée Très fréquent Capturez l'élément CAPTCHA en entier
Basse résolution ou compression Fréquent Envoyez une image de meilleure qualité
Encodage d'image incorrect Fréquent Vérifiez l'encodage base64
Indice de langue ou de type absent Occasionnel Ajoutez language ou textinstructions
Image périmée ou expirée Occasionnel Recapturez l'image juste avant la résolution

Les deux premières lignes concentrent la majorité des tickets. Traitez les causes dans cet ordre, du plus fréquent au plus rare :

  • Cadrage et capture de l'élément (correctifs 1 et 5) — la source d'erreur numéro un.
  • Encodage base64 (correctif 2) — invisible tant qu'on ne vérifie pas l'aller-retour.
  • Qualité et prétraitement (correctif 3) — pour les images pâles ou minuscules.
  • Indices de type et de langue (correctif 4) — chiffres contre lettres, casse, longueur.
  • Fraîcheur de l'image (correctif 6) — pour les CAPTCHA qui expirent vite.

Remontez cette liste plutôt que de tout tenter en même temps : dans neuf cas sur dix, le défaut se règle dès les deux premières étapes.


Correctif 1 : validez la qualité de l'image avant l'envoi

Filtrez l'image localement d'abord : ce contrôle repère les images trop petites, quasi blanches ou trop lourdes (plus de 600 Ko).

import base64
from io import BytesIO
from PIL import Image


def validate_captcha_image(image_path):
    """Check image quality before submitting to CaptchaAI."""
    img = Image.open(image_path)
    width, height = img.size
    issues = []

    # Minimum resolution
    if width < 50 or height < 20:
        issues.append(f"Too small: {width}x{height}px (min 50x20)")

    # Check if mostly blank
    pixels = list(img.getdata())
    if img.mode == "RGB":
        white_count = sum(1 for p in pixels if p[0] > 250 and p[1] > 250 and p[2] > 250)
    else:
        white_count = sum(1 for p in pixels if p > 250)

    blank_ratio = white_count / len(pixels)
    if blank_ratio > 0.95:
        issues.append(f"Image appears blank ({blank_ratio:.0%} white)")

    # File size check
    img_bytes = BytesIO()
    img.save(img_bytes, format="PNG")
    size_kb = img_bytes.tell() / 1024
    if size_kb < 1:
        issues.append(f"File too small ({size_kb:.1f} KB) — may be empty")
    if size_kb > 600:
        issues.append(f"File too large ({size_kb:.0f} KB) — submit under 600 KB")

    return issues


issues = validate_captcha_image("captcha.png")
if issues:
    for issue in issues:
        print(f"WARNING: {issue}")
else:
    print("Image quality OK")

Correctif 2 : fiabilisez l'encodage base64

Un charabia en sortie trahit presque toujours un encodage cassé — souvent parce qu'on encode le chemin du fichier plutôt que son contenu.

import base64


def encode_captcha(image_path):
    """Properly encode a CAPTCHA image to base64."""
    with open(image_path, "rb") as f:
        raw = f.read()

    encoded = base64.b64encode(raw).decode("ascii")

    # Verify round-trip
    decoded = base64.b64decode(encoded)
    assert decoded == raw, "Base64 encoding corrupted the image"

    return encoded


# WRONG — encoding a file path string
bad = base64.b64encode(b"captcha.png").decode()  # Encodes filename, not image!

# CORRECT — encoding file contents
with open("captcha.png", "rb") as f:
    good = base64.b64encode(f.read()).decode()

Correctif 3 : prétraitez l'image pour l'OCR

Une réponse proche mais fausse signale une image trop petite ou trop pâle : mettez-la à l'échelle, montez le contraste, renforcez la netteté. Une image standard passe très bien sans retouche.

Ne prétraitez que si l'image le justifie, car une retouche appliquée à une image déjà nette peut la dégrader :

  • Oui : image minuscule, contraste faible, aliasing visible ou compression JPEG agressive.
  • Non : image nette, bien contrastée, capturée directement depuis l'élément — laissez-la telle quelle.
from PIL import Image, ImageFilter, ImageEnhance
from io import BytesIO
import base64


def preprocess_captcha(image_path):
    """Improve image quality for better OCR accuracy."""
    img = Image.open(image_path)

    # Convert to RGB if needed
    if img.mode != "RGB":
        img = img.convert("RGB")

    # Upscale small images
    width, height = img.size
    if width < 200:
        scale = 200 / width
        img = img.resize(
            (int(width * scale), int(height * scale)),
            Image.LANCZOS,
        )

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

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

    # Convert to PNG bytes
    buffer = BytesIO()
    img.save(buffer, format="PNG")
    return base64.b64encode(buffer.getvalue()).decode()

Correctif 4 : précisez le type et la langue attendus

Un CAPTCHA à quatre chiffres traité comme du texte rend des lettres au lieu de chiffres. Chaque paramètre cadre un aspect précis de la réponse attendue :

  • numeric — restreint la sortie aux chiffres (1) ou aux lettres (2).
  • min_len / max_len — bornent le nombre de caractères, utile quand la longueur est connue.
  • language1 pour le cyrillique, 2 pour le latin ; laissez 0 par défaut sinon.
  • textinstructions — une consigne libre (casse exacte, caractères accentués) transmise avec l'image.

Renseignez seulement les indices dont vous êtes sûr : un max_len erroné tronque une réponse correcte.

import requests


def solve_image(api_key, image_base64, **hints):
    """Submit image CAPTCHA with quality hints."""
    data = {
        "key": api_key,
        "method": "base64",
        "body": image_base64,
        "json": 1,
    }

    # Add optional hints for better accuracy
    if "language" in hints:
        data["language"] = hints["language"]  # 0=default, 1=Cyrillic, 2=Latin
    if "textinstructions" in hints:
        data["textinstructions"] = hints["textinstructions"]
    if "numeric" in hints:
        data["numeric"] = hints["numeric"]  # 1=digits only, 2=letters only
    if "min_len" in hints:
        data["min_len"] = hints["min_len"]
    if "max_len" in hints:
        data["max_len"] = hints["max_len"]

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


# Example: Digits-only CAPTCHA, 4-6 characters
result = solve_image(
    "YOUR_API_KEY",
    encoded_image,
    numeric=1,
    min_len=4,
    max_len=6,
)

# Example: Case-sensitive text
result = solve_image(
    "YOUR_API_KEY",
    encoded_image,
    textinstructions="Case-sensitive, enter exactly as shown",
)

Correctif 5 : capturez uniquement l'élément CAPTCHA

Une capture de page recadrée à la main introduit marges, décalage et parfois un caractère coupé. Ciblez directement l'élément : la capture est nette et cadrée au pixel près.

from selenium import webdriver
from selenium.webdriver.common.by import By
import base64


def capture_captcha_element(driver, selector):
    """Screenshot only the CAPTCHA element, not the full page."""
    element = driver.find_element(By.CSS_SELECTOR, selector)

    # Element screenshot (better than page crop)
    png_bytes = element.screenshot_as_png

    # Verify it's not empty
    if len(png_bytes) < 500:
        raise ValueError("Screenshot too small — element may not be visible")

    return base64.b64encode(png_bytes).decode()


# Usage
driver = webdriver.Chrome()
driver.get("https://example.com")
image_b64 = capture_captcha_element(driver, "img#captchaImage")

Exemple côté production. Sur une VM headless (OVHcloud ou Scaleway), le code qui marche en local renvoie parfois des réponses fausses : le navigateur rend le CAPTCHA à une densité de pixels plus basse et l'image part trop petite. Ce cadrage serré a un bonus RGPD : il évite d'embarquer des données personnelles affichées ailleurs.


Correctif 6 : gérez les CAPTCHA dynamiques ou tournants

Certains CAPTCHA d'image expirent en quelques secondes ou changent à chaque affichage. Capturez et envoyez l'image dans la même foulée, sans la laisser « vieillir ».

import time


def solve_with_fresh_image(driver, api_key, captcha_selector):
    """Capture and solve CAPTCHA immediately to avoid expiry."""
    # Wait for CAPTCHA to load fully
    time.sleep(2)

    # Capture fresh
    element = driver.find_element(By.CSS_SELECTOR, captcha_selector)
    png_bytes = element.screenshot_as_png
    body = base64.b64encode(png_bytes).decode()

    # Submit immediately
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "base64",
        "body": body,
        "json": 1,
    }, timeout=30)
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(result.get("request"))

    task_id = result["request"]

    # Poll — image CAPTCHAs solve fast
    time.sleep(5)
    for _ in range(12):
        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(3)

    raise TimeoutError("Image solve timeout")

Tableau de dépannage express

Symptôme Diagnostic Correctif
Réponse incohérente (charabia) Encodage base64 incorrect Vérifiez l'encodage aller-retour
Réponse proche mais fausse Qualité d'image insuffisante Prétraitez : mise à l'échelle, netteté, contraste
Nombre de caractères erroné Indices de longueur absents Ajoutez les paramètres min_len / max_len
Lettres et chiffres mélangés Indice de type absent Ajoutez numeric=1 ou numeric=2
Réponse vide Image blanche ou corrompue Validez l'image avant l'envoi
Bonne réponse mais refus du site Sensibilité à la casse Ajoutez textinstructions pour la casse

FAQ

Pourquoi l'OCR renvoie-t-il le bon nombre de caractères mais des lettres fausses ?

L'image est lisible mais trop pâle ou trop petite pour distinguer des caractères proches (0/O, 1/l, 5/S). Le correctif 3 — mise à l'échelle, contraste et netteté — suffit dans ce cas.

Le prétraitement d'image est-il toujours nécessaire ?

Non. CaptchaAI traite très bien les images standard sans retouche. Réservez le prétraitement aux cas limites : images minuscules, faible contraste ou compression agressive.

CaptchaAI gère-t-il les CAPTCHA d'image sensibles à la casse ?

Oui, mais c'est votre site cible qui impose la casse exacte. Passez textinstructions pour que la réponse respecte majuscules et minuscules telles qu'affichées.

Combien de temps une image CAPTCHA reste-t-elle valide ?

Cela dépend du site : certaines expirent en quelques secondes. Capturez et envoyez l'image immédiatement (correctif 6) plutôt que de la stocker.

Pourquoi une réponse correcte est-elle refusée par le site cible ?

Le moteur a bien lu l'image, mais le formulaire attend un format précis. Vérifiez la casse avec textinstructions et assurez-vous de ne pas ajouter d'espaces autour de la valeur avant de la soumettre.


Guides connexes


Résolvez vos CAPTCHA d'image avec précision — essayez CaptchaAI.

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