Por que um código QR artístico pode falhar quando você o lê de perto demais
Todo mundo espera que um código QR falhe por ser pequeno demais, longe demais, escuro demais. Os códigos QR com foto têm a falha oposta também: podem ser lidos perfeitamente à distância e falhar quando você encosta o celular neles. Eis o porquê, com os números da medição que descobriu isso.
Como um leitor decide o que é escuro
Antes que um leitor possa procurar módulos, ele precisa transformar uma fotografia em preto e branco. Um limiar global não sobrevive à iluminação real — uma sombra em um canto escureceria metade do código — então a maioria dos decodificadores aplica o limiar localmente. O ZXing, a biblioteca de código aberto da qual boa parte da leitura de códigos de barras ainda descende, divide a imagem em blocos de 8×8 pixels, calcula a média de cada bloco, suaviza-a contra os blocos vizinhos, e usa isso como limiar para aqueles 64 pixels. Um pixel mais escuro que o limiar do seu bloco é escuro; os demais são claros. Para um código QR normal isso é quase de graça. Seja qual for o tamanho do bloco, um módulo é sólido, então a média de um bloco é ou quase escura ou quase clara, ou fica entre as duas quando o bloco cavalga uma fronteira, e a decisão é fácil de qualquer forma. O esquema é robusto, barato e foi amplamente copiado, e é exatamente por isso que importa: o decodificador que o seu código impresso encontra em campo pode muito bem ser um que aplica o limiar dessa forma.
O que muda quando os módulos são feitos de dithering
Em um código com dithering, um módulo não é sólido. Ele é um pequeno núcleo forçado cercado por pixels que carregam a fotografia. Sua média está correta; sua textura não é uniforme. Agora considere a distância de leitura na única unidade que importa: pixels de câmera por módulo. Segure o celular longe o bastante para que um módulo caia em 5–8 pixels, e um bloco 8×8 cavalga as fronteiras entre módulos. A média local é aproximadamente "este módulo e seus vizinhos", o limiar fica entre escuro e claro, e o código é lido. Aproxime o celular para que um módulo cubra 20 pixels, e um bloco 8×8 agora cabe inteiramente dentro de um único módulo. A média do bloco é a textura local daquele módulo. O limiar segue o dithering em vez do módulo — e o decodificador recupera o pontilhado, não o código. Nada na impressão mudou; só o número de pixels do sensor que cada módulo cobre, e com ele a relação entre o bloco do decodificador e o módulo. É por isso que a falha é uma propriedade da distância, e não do tamanho ou da iluminação.
O número
Medimos isso ao construir o modo foto do SWAPQR: três comprimentos de conteúdo, seis tipos de imagem, ambas as polaridades de cor, três configurações de balanço, sete distâncias de leitura simuladas, decodificadas por duas bibliotecas independentes. A fronteira é limpa. Com cerca de 20% de cada módulo forçado — a configuração voltada para a foto — e com cerca de 44% forçado, os códigos foram lidos a 5–8 pixels por módulo e os leitores da classe ZXing ficaram pouco confiáveis de perto, a 10–28 pixels por módulo. Com cerca de 60% forçado e acima, foram lidos em todas as distâncias. Quando mais da metade aproximadamente da área de um módulo é forçada sólida, qualquer bloco 8×8 que caia dentro do módulo aterrissa principalmente em pixels sólidos, e o efeito desaparece. Abaixo disso, não desaparece — e nenhum ajuste do viés de tom resolve, porque um limiar local subtrai a média local de qualquer forma. Para qualquer tom que você incline um módulo, um bloco totalmente dentro dele vai medir essa inclinação como sua linha de base e eliminá-la pelo limiar. A única alavanca que funciona é a parcela do módulo que é sólida, e é por isso que o controle de balanço existe.
Decodificadores mais novos, e por que isso não é o fim da história
Decodificadores mais novos são melhores nisso. Na mesma bateria de testes, o jsQR — que usa uma estratégia de binarização diferente — leu todas as configurações em todas as distâncias, e os leitores embutidos nos celulares atuais são ainda mais fortes. Mas "a maioria dos celulares vai dar conta" não é o mesmo que "todo leitor vai dar conta", e um código impresso encontra qualquer leitor que seja apontado para ele: um celular mais antigo, um leitor de ponto de venda, um quiosque, um app que embute uma biblioteca derivada do ZXing. O balanço voltado para a foto é, portanto, honesto sobre sua troca — o estúdio observa que os leitores dos celulares modernos o leem a qualquer distância enquanto leitores mais antigos e simples podem ter dificuldade em close extremo — e a decisão de onde ficar no balanço depende de quem vai escanear o código e de que distância. Também depende de onde um celular naturalmente acaba: ninguém segura um celular à distância de um braço de um cartaz, mas todo mundo o encosta em um cartão de visita, e esse é o caso em que um código voltado para a foto e um leitor mais simples podem se encontrar.
O que fazer a respeito
Se o código vai para um lugar onde as pessoas escaneiam de uma distância normal — um cartaz, um display de mesa, uma etiqueta de prateleira — as configurações voltadas para a foto servem; a essa distância um módulo cobre um punhado de pixels de câmera e os blocos cavalgam as fronteiras como deveriam. Se vai para um lugar onde as pessoas vão encostar um celular nele — um adesivo pequeno, um cartão de visita, uma tela — mova o balanço em direção a Código, para que mais da metade de cada módulo seja sólida e o efeito de perto desapareça para toda classe de leitor. De qualquer forma, imprima um e escaneie com um celular. Não uma captura de tela em um monitor: a peça impressa de verdade, no tamanho real, da distância que as pessoas realmente vão usar, e de preferência com mais de um celular. O SWAPQR mostra esse aviso no próprio estúdio e não nas letras miúdas, porque um código que falha em campo é pior do que um código simples que funciona, e porque a correção é um controle deslizante em vez de uma reimpressão se você perceber antes da tiragem.
| Parcela forçada de cada módulo | Longe / normal (5–8 px por módulo) | De perto (10–28 px por módulo) |
|---|---|---|
| ~20% (voltado para a foto) | Lê | Leitores da classe ZXing pouco confiáveis |
| ~44% (meio) | Lê | Leitores da classe ZXing pouco confiáveis |
| ~60% e acima (voltado para o código) | Lê | Lê |
| Qualquer configuração, classe jsQR e leitores dos celulares atuais | Lê | Lê na mesma bateria de testes |
Perguntas frequentes
Meu celular lê o código com foto de qualquer distância. Por que o aviso?
Os leitores dos celulares atuais usam uma binarização mais forte e leram todas as configurações na nossa bateria de testes. O aviso é sobre leitores mais antigos ou simples que aplicam o limiar em blocos de 8×8, que um código impresso ainda pode encontrar.
Por que afastar o celular resolve?
Mais longe, cada módulo cobre menos pixels de câmera, então o bloco 8×8 de um decodificador cavalga vários módulos e sua média fica entre escuro e claro. De perto, um bloco cai dentro de um único módulo e segue a textura do dithering em vez disso.
Aumentar o viés de tom ajuda de perto?
Não. Um limiar local subtrai a média local, então qualquer inclinação uniforme dentro de um módulo é eliminada pelo limiar. Só a parcela do módulo que é forçada sólida muda o resultado.
Qual balanço é seguro para um cartão de visita?
Mova-o em direção a Código, para que mais da metade aproximadamente de cada módulo seja sólida. Nesse ponto os leitores da classe ZXing leem em todas as distâncias na nossa medição, e a imagem ainda fica visível, só mais tênue.
Imprima o quadrado uma vez. Decida depois para onde ele leva. O SWAPQR cria códigos QR estáticos de graça, com todas as opções de estilo, sem conta e sem marca d'água. Um plano pago torna um código dinâmico: o quadrado impresso continua o mesmo enquanto você muda o destino dele, e você vê quantas vezes ele foi escaneado, por dia e por dispositivo.
Criar um código QR no navegador