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.