Pourquoi un code QR artistique peut échouer quand vous le scannez de trop près
Tout le monde s'attend à ce qu'un code QR échoue parce qu'il est trop petit, trop loin, trop sombre. Les codes QR photo ont aussi la défaillance inverse : ils peuvent se lire parfaitement à distance et échouer quand vous collez le téléphone dessus. Voici pourquoi, avec les chiffres de la mesure qui l'a révélé.
Comment un scanner décide de ce qui est sombre
Avant qu'un lecteur puisse chercher des modules, il doit transformer une photographie en noir et blanc. Un seuil global ne survit pas à l'éclairage réel — une ombre sur un coin noircirait la moitié du code — donc la plupart des décodeurs seuillent localement. ZXing, la bibliothèque open source dont descend encore une grande partie du scan de codes-barres, divise l'image en blocs de 8×8 pixels, calcule la moyenne de chaque bloc, la lisse avec les blocs voisins et l'utilise comme seuil pour ces 64 pixels. Un pixel plus sombre que le seuil de son bloc est sombre ; les autres sont clairs. Pour un code QR normal, c'est presque gratuit. Quelle que soit la taille du bloc, un module est plein, donc la moyenne d'un bloc est soit proche du sombre, soit proche du clair, soit entre les deux quand le bloc chevauche une frontière, et la décision est facile dans tous les cas. Le schéma est robuste, peu coûteux et a été largement copié, et c'est exactement pourquoi il compte : le décodeur que rencontre votre code imprimé sur le terrain peut très bien être un décodeur qui seuille de cette façon.
Ce qui change quand les modules sont faits de tramage
Dans un code tramé, un module n'est pas plein. C'est un petit cœur forcé entouré de pixels qui portent la photographie. Sa moyenne est juste ; sa texture n'est pas uniforme. Considérez maintenant la distance de scan dans la seule unité qui compte : les pixels de caméra par module. Tenez le téléphone assez loin pour qu'un module tombe sur 5–8 pixels, et un bloc de 8×8 chevauche des frontières de modules. La moyenne locale est en gros « ce module et ses voisins », le seuil se situe entre sombre et clair, et le code se lit. Rapprochez le téléphone pour qu'un module couvre 20 pixels, et un bloc de 8×8 tient maintenant entièrement dans un seul module. La moyenne du bloc est la texture locale de ce module. Le seuil suit le tramage au lieu du module — et le décodeur récupère le moucheté, pas le code. Rien n'a changé dans l'impression ; seulement le nombre de pixels du capteur que couvre chaque module, et avec lui la relation entre le bloc du décodeur et le module. C'est pourquoi la défaillance est une propriété de la distance plutôt que de la taille ou de l'éclairage.
Le chiffre
Nous l'avons mesuré en construisant le mode photo de SWAPQR : trois longueurs de charge utile, six types d'images, les deux polarités de couleur, trois réglages de balance, sept distances de scan simulées, décodés par deux bibliothèques indépendantes. La frontière est nette. Avec environ 20% de chaque module forcé — le réglage orienté photo — et avec environ 44% forcé, les codes se lisaient à 5–8 pixels par module et les lecteurs de classe ZXing devenaient peu fiables en gros plan, à 10–28 pixels par module. À environ 60% forcé et au-delà, ils se lisaient à toutes les distances. Dès que plus de la moitié environ de la surface d'un module est forcée en plein, tout bloc de 8×8 qui tombe dans le module atterrit surtout sur des pixels pleins, et l'effet disparaît. En dessous, non — et aucun réglage du biais de ton n'y remédie, parce qu'un seuil local soustrait de toute façon la moyenne locale. Quel que soit le ton vers lequel vous penchez un module, un bloc entièrement à l'intérieur mesurera ce penchant comme sa référence et le fera disparaître au seuillage. Le seul levier qui fonctionne est la part du module qui est pleine, et c'est pourquoi la commande de balance existe.
Décodeurs plus récents, et pourquoi ce n'est pas la fin de l'histoire
Les décodeurs plus récents s'en sortent mieux. Dans le même balayage, jsQR — qui utilise une stratégie de binarisation différente — a lu chaque configuration à chaque distance, et les scanners intégrés aux téléphones actuels sont encore plus solides. Mais « la plupart des téléphones y arriveront » n'est pas la même chose que « chaque lecteur y arrivera », et un code imprimé rencontre le lecteur qui se trouve pointé sur lui : un téléphone plus ancien, un scanner de point de vente, une borne, une application qui embarque une bibliothèque dérivée de ZXing. La balance orientée photo est donc honnête sur son compromis — le studio indique que les scanners des téléphones modernes la lisent à toute distance tandis que des lecteurs plus anciens et plus simples peuvent peiner en gros plan extrême — et la décision de l'endroit où se placer sur la balance dépend de qui scannera le code et depuis quelle distance. Elle dépend aussi de là où un téléphone finit naturellement : personne ne tient un téléphone à bout de bras devant une affiche, mais tout le monde le colle sur une carte de visite, et c'est le cas où un code orienté photo et un lecteur plus simple peuvent se rencontrer.
Que faire
Si le code va là où les gens scannent depuis une distance normale — une affiche, un chevalet de table, une étiquette de rayon — les réglages orientés photo conviennent ; à cette distance un module couvre une poignée de pixels de caméra et les blocs chevauchent les frontières comme ils le doivent. S'il va là où les gens colleront un téléphone dessus — un petit autocollant, une carte de visite, un écran — déplacez la balance vers Code, pour que plus de la moitié de chaque module soit pleine et que l'effet de gros plan disparaisse pour toutes les classes de lecteurs. Dans tous les cas, imprimez-en un et scannez-le avec un téléphone. Pas une capture d'écran sur un moniteur : la pièce réellement imprimée, à la taille réelle, depuis la distance que les gens utiliseront vraiment, et idéalement avec plus d'un téléphone. SWAPQR affiche cet avertissement dans le studio lui-même plutôt que dans les petits caractères, parce qu'un code qui échoue sur le terrain est pire qu'un code simple qui fonctionne, et parce que la solution est un curseur plutôt qu'une réimpression si vous l'attrapez avant la série.
| Part forcée de chaque module | Loin / normal (5–8 px par module) | Gros plan (10–28 px par module) |
|---|---|---|
| ~20% (orienté photo) | Se lit | Lecteurs de classe ZXing peu fiables |
| ~44% (milieu) | Se lit | Lecteurs de classe ZXing peu fiables |
| ~60% et au-delà (orienté code) | Se lit | Se lit |
| Tout réglage, classe jsQR et scanners des téléphones actuels | Se lit | Se lit dans le même balayage |
Questions fréquentes
Mon téléphone lit le code photo à toute distance. Pourquoi l'avertissement ?
Les scanners des téléphones actuels utilisent une binarisation plus solide et ont lu chaque configuration de notre balayage. L'avertissement concerne les lecteurs plus anciens ou plus simples qui seuillent par blocs de 8×8, qu'un code imprimé peut encore rencontrer.
Pourquoi éloigner le téléphone règle-t-il le problème ?
Plus loin, chaque module couvre moins de pixels de caméra, donc le bloc de 8×8 d'un décodeur chevauche plusieurs modules et sa moyenne se situe entre sombre et clair. De près, un bloc tombe dans un seul module et suit la texture du tramage à la place.
Augmenter le biais de ton aide-t-il en gros plan ?
Non. Un seuil local soustrait la moyenne locale, donc tout penchant uniforme à l'intérieur d'un module disparaît au seuillage. Seule la part du module forcée en plein change le résultat.
Quelle balance est sûre pour une carte de visite ?
Déplacez-la vers Code, pour que plus de la moitié environ de chaque module soit pleine. À ce stade, les lecteurs de classe ZXing lisent à toutes les distances dans notre mesure, et l'image reste visible, juste plus pâle.
Imprimez le carré une fois. Décidez plus tard où il mène. SWAPQR crée des codes QR statiques gratuitement, avec toutes les options de style, sans compte et sans filigrane. Un forfait payant rend un code dynamique : le carré imprimé reste le même pendant que vous changez sa destination, et vous voyez combien de fois il a été scanné, par jour et par appareil.
Créer un code QR dans le navigateur