I codici QR fotografici funzionano davvero? I nostri risultati del test di decodifica
Un codice QR che è una fotografia potrebbe sembrare che funzioni peggio. Abbiamo misurato il nostro con un decoder reale — sfocato, ridimensionato e letto come legge un lettore.
Novità qui: Risultati del test di decodifica della nostra implementazione photo QR, misurati con jsQR rispetto a un codice di controllo standard: nessuno dei 4.225 moduli letto male a ogni apertura del lettore da 0,10 a 0,45 moduli, tolleranza di ridimensionamento 3x identica e tolleranza di sfocatura di 4-5 pixel rispetto ai 6 del codice standard — il costo specifico e ridotto di trasformare un codice in un'immagine.
Un codice QR che è una fotografia sembra debba implicare un compromesso: carino da vedere, ma perdi alcune scansioni. Questo istinto è giusto per la maggior parte delle implementazioni, ma sbagliato su come effettivamente deve funzionare — e nessuno pubblica il numero effettivo di scansioni perse.
Ecco i nostri, dalla suite di test che doveva superare prima di spedire completamente la funzione.
# Cosa è un codice QR fotografico e cosa non è
Due cose molto diverse vengono chiamate con lo stesso nome.
AI QR art — il tipo basato su modelli di diffusione — genera un’immagine basata su un codice QR e spera che il risultato si decodifichi ancora. A volte lo fa. La modalità di errore è invisibile fino a quando qualcuno in un bar buio non punta un telefono di tre anni fa su di esso.
Un codice foto con diffusione d’errore è l’altro approccio e quello misurato qui. L’immagine è ridotta a un bit — nero o bianco per sub-pixel — per diffusione d’errore, con un vincolo cruciale: a ogni centro del modulo il dither è forzato al valore che l’encoder richiede, e l’errore che la forzatura crea (fino a circa il 95% del valore di un pixel, per un modulo bianco in una regione scura) viene spinto nei sub-pixel vicini che non sono stati ancora sogliati. L’immagine è fatta dello spazio tra i centri dei moduli.
Questa distinzione è il fulcro dell’articolo. Significa che il codice non consuma la sua correzione d’errore per la decorazione: ogni modulo trasporta ancora il valore dell’encoder, quindi la ridondanza completa rimane intatta per le pieghe, l’abbagliamento, un dito su un angolo — le cose per cui la correzione d’errore esiste.
# Il test
Tutto ciò che segue è riproducibile dal nostro repository, offline:
- Encoder: il nostro
packages/qr, correzione d’errore H, versione minimaminVersion: 12→ versione del simbolo 12, 65 moduli totali, 10 pixel per modulo, zona silenziosa di 4 moduli. - Stili: halftone (una vera fotografia diffusa) e mosaic (vengono disegnati solo i moduli, dimensionati dall’oscurità locale), rispetto a un codice semplice come controllo — stesso payload, stessa versione, stessa scala.
- Immagini: immagini di test procedurali, incluso un gradiente diagonale (appaiono tutti i toni) e grigio piatto medio, che è il caso peggiore perché ogni punto forzato ha un valore diverso dai suoi dintorni.
- Decoder: jsQR 1.4.0, un decoder QR reale, che lavora sui pixel effettivi.
- Degradazioni: downscale nearest-neighbour, box blur seguito da re-thresholding (una fotocamera fuori fuoco), e una lettura di apertura.
La suite su cui è costruito è packages/qr/test/dither.test.js. 39/39 test superati il 2026-08-31, e un codice che non si decodifica correttamente è un test fallito piuttosto che un avviso — questa regola è il motivo per cui la funzione esiste nella forma attuale. Le tabelle sottostanti sono stampate da draft/mostlyqr/seo/benchmarks/photo.js, che esegue gli stessi stili attraverso gli stessi degradamenti; nulla qui è stato trascritto.
# Zero moduli errati, a ogni apertura
La misura che conta di più non è se si è decodificato correttamente, ma se il lettore ha visto il valore corretto in ogni cella, perché un codice che si decodifica solo perché la correzione d’errore lo ha salvato è un codice che fallirà quando qualcos’altro andrà storto.
Un lettore conforme non campiona un pixel per modulo. Calcola la media di una piccola apertura circolare a ogni centro del modulo e applica una soglia a quella media. Quindi abbiamo fatto lo stesso, a raggi di apertura di 0,10, 0,25, 0,36 e 0,45 moduli (0,5 sarebbe l’intero modulo), e abbiamo contato quanti dei 4.225 moduli sarebbero stati letti in modo scorretto:
| Stile | Moduli errati, apertura 0,10 / 0,25 / 0,36 / 0,45 |
|---|---|
| codice semplice (controllo) | 0 / 0 / 0 / 0 |
| foto a mosaico | 0 / 0 / 0 / 0 |
| foto a mezzitoni (gradiente) | 0 / 0 / 0 / 0 |
| foto a mezzitoni (grigio piatto medio, caso peggiore) | 0 / 0 / 0 / 0 |
Nemmeno un modulo, in qualsiasi stile, a qualsiasi apertura, inclusa l’immagine del caso peggiore. Il codice foto e il codice semplice presentano al lettore gli stessi valori. La correzione d’errore è intatta e ancora completamente disponibile.
# Il costo: circa un passo di sfocatura
La correzione d’errore intatta non significa identico. Il dither cambia davvero l’aspetto dei pixel tra i centri, e una fotocamera gravemente fuori fuoco media quei pixel insieme. Quindi abbiamo spinto ogni stile fino a quando si è rotto:
| Stile | Massimo downscale ancora decodificato | Massimo raggio di sfocatura ancora decodificato |
|---|---|---|
| codice semplice (controllo) | 3× | 6 px |
| foto a mosaico | 3× | 5 px |
| foto a mezzitoni (grigio piatto medio) | 3× | 6 px |
| foto a mezzitoni (gradiente) | 3× | 4 px |
Il downscaling — il codice renderizzato più piccolo del nativo — è identico in ogni stile: ognuno ha sopravvissuto a 3× e ognuno ha fallito per la prima volta a 4×. (Il downscaling nearest-neighbour non è monotonico — alcuni si sono decodificati di nuovo a 5×, dove la griglia di campionamento si è allineata meglio — quindi la figura che riportiamo è il primo fallimento, non l’ultimo successo.)
La sfocatura è dove emerge il costo, e solo per lo stile a mezzitoni sull’immagine più difficile: un terzo meno margine rispetto al controllo semplice. I mezzitoni di un campo grigio piatto medio corrispondono esattamente al codice semplice, quindi questo è un costo dipendente dall’immagine piuttosto che una proprietà della tecnica. In termini semplici: un codice foto ha una tolleranza di fuoco inferiore di circa uno step rispetto a un codice semplice. È un costo reale e modesto, ed è la risposta onesta a “si scansionano?” — non “sì” e non “no”, ma “sì, con una tolleranza di fuoco inferiore di circa uno step”.
Lo stile a mosaico si colloca tra i due, il che ha senso: disegna solo i moduli, quindi c’è meno inchiostro tra i centri che una sfocatura possa spalmare.
# Perché la densità è il requisito reale
La prima versione di questa funzione era tecnicamente corretta ma l’aspetto era sbagliato, per una ragione degna di conoscenza se usi qualsiasi strumento foto-QR:
https://mqr.sh/AB12CD a correzione d’errore H — che è quello che un codice foto forza — si codifica in una griglia di 29 moduli. Sono 841 celle per una fotografia intera, e il risultato si legge come rumore piuttosto che come un’immagine: le persone concludono che la tecnica non funziona quando il vero problema è che non c’è abbastanza spazio per l’immagine. Forzare un simbolo più denso lo risolve:
| Versione minima | Simbolo | Moduli totali | Celle disponibili per l’immagine |
|---|---|---|---|
| nessuno (quello che il payload richiede) | v3 | 29 | 841 |
| 8 | v8 | 49 | 2.401 |
| 12 (il nostro predefinito) | v12 | 65 | 4.225 |
Cinque volte le celle della versione 12. Ogni strumento image-QR serio forza questo per lo stesso motivo, e la versione minima non deve mai scendere al di sotto di quella che il payload richiede — i dati vincono sempre sulla densità.
La conseguenza per la stampa è diretta, ed è il motivo per cui questo articolo si trova nello stesso cluster di quanti moduli il tuo payload ha bisogno: un codice 65-modulo ha bisogno di 73 moduli-larghezze di carta compresa la sua zona silenziosa. Con un modulo di 0,5 mm è 36,5 mm — quindi un codice foto è un poster, un menu, un pannello di imballaggio o uno schermo. Non è un biglietto da visita.
# Cosa non copre il test
Affermato chiaramente, perché un benchmark con limiti non dichiarati è peggio di nessuno:
- Un decoder, pixel sintetici. jsQR è un decoder reale ma non è una pipeline di fotocamera del telefono, e l’input è un output reso pulito piuttosto che una fotografia di una cosa stampata.
- Niente stampa, niente abbagliamento, niente prospettiva, niente accartocciamento. Ognuno di questi ha un costo in margine, e il costo differisce tra un codice foto e un codice semplice.
- Nessun colore. Questi sono codici in bianco e nero a 1 bit.
- La nostra stessa implementazione. Questo non dice nulla sui codici QR foto o AI-art di altri. Se sovrascrivono moduli e si affidano alla correzione d’errore per riparare il danno, si comporteranno molto diversamente, e il test equo sarebbe quello sopra eseguito su di loro.
# Quando usare uno
- Sì: poster, menu sotto vetro, pannelli di imballaggio, schermi, grafica espositiva, qualsiasi cosa stampata in grande.
- Probabilmente: una copertina di album, una sovraccoperta di libro, un’etichetta di prodotto grande — testalo.
- No: biglietti da visita, volantini di carta che vengono piegati, qualsiasi cosa sotto circa 35 mm, qualsiasi cosa scansionata con cattiva luce a distanza.
E se quello che vuoi non è una fotografia intera ma il tuo logo nel mezzo di un codice altrimenti ordinario, è una tecnica diversa con un budget molto più piccolo — abbiamo misurato quanto grande può essere un logo anche, e la risposta è più piccola di quello che pensa Internet.
I codici foto di MostlyQR sono l’implementazione misurata qui. Lo stesso nucleo dithering funziona nell’anteprima del browser e nel PNG reso dal server, quindi quello che vedi nel builder è quello che ottieni dalla stampa. La nota del builder afferma “meglio su schermi e stampe grandi — un codice foto è più difficile da leggere su carta piccola o accartocciata”, che dopo questi numeri sembra la cosa giusta da dire.
Domande frequenti
I codici QR fotografici si leggono in modo affidabile?
Nei nostri test, sì — con un costo misurabile. Leggendo i nostri codici fotografici come farebbe un lettore conforme, calcolando la media di un'apertura circolare al centro di ogni modulo, nessuno dei 4.225 moduli è stato letto male a nessuna apertura che abbiamo testato. Hanno tollerato lo stesso ridimensionamento 3x di un codice standard. L'unica differenza misurata era la sfocatura, e solo sull'immagine più difficile: il controllo standard ha sopportato un raggio di sfocatura di 6 pixel, lo stile mosaico 5 e lo stile mezzitoni 4 su una sfumatura (6 su grigio piatto). Questo è più o meno un passo della messa a fuoco della fotocamera, non una categoria diversa di affidabilità.
I codici QR artistici esauriscono la correzione degli errori?
Molte implementazioni lo fanno, ed è per questo che la tecnica ha una cattiva reputazione. La nostra no. Invece di sovrascrivere interi moduli con dati di immagine e fare affidamento su Reed-Solomon per riparare la differenza, forza un punto al centro di ogni modulo al valore richiesto dall'encoder e diffonde l'errore risultante nei sub-pixel circostanti. L'immagine viene pagata con lo spazio tra i centri dei moduli, quindi il budget completo di correzione degli errori è ancora disponibile per il mondo reale — per pieghe, riflessi e un dito sull'angolo.
Posso stampare un codice QR fotografico su un volantino?
No, non lo faremmo. Il nostro builder dice "meglio su schermi e stampe di grandi dimensioni", e lo pensiamo davvero: un codice fotografico ha bisogno di un simbolo denso (65 moduli attraverso il test qui sotto, contro 29 per lo stesso collegamento breve non forzato), quindi a dimensioni di volantino ogni modulo diventa molto piccolo molto velocemente. Poster, menu sotto vetro, pannelli di imballaggio e schermi sono i posti giusti. Un volantino di carta che viene piegato in una tasca no.
Perché il mio codice QR fotografico assomiglia a rumore invece di un'immagine?
Quasi sempre perché il simbolo è troppo grossolano. Un collegamento breve con correzione degli errori H si codifica in una griglia di 29 moduli — 841 celle, troppo poche per contenere un'immagine riconoscibile, quindi l'immagine non ha dove stare. Forzare un simbolo più denso lo risolve: la versione 8 dà 49 moduli e la versione 12 dà 65 (4.225 celle, cinque volte di più), e il nostro builder predefinisce la versione 12 per i codici fotografici esattamente per questo motivo. I dati vincono sempre, quindi un payload più lungo ottiene ancora un simbolo più grande.
- Quanto grande può essere il logo su un codice QR? L'abbiamo misurato"Correzione degli errori al 30%" non è un budget per il logo. Abbiamo fatto buchi sempre più grandi nei codici a ogni livello EC finché un decoder reale non ha rinunciato, e abbiamo registrato dove si è rotto.
- Quanto piccolo può essere stampato un codice QR? Le dimensioni, calcolateNon esiste un'unica dimensione minima per un codice QR. Abbiamo eseguito cinque payload reali attraverso il nostro encoder per i conteggi dei moduli, quindi li abbiamo convertiti in millimetri.