Skip to content

Gradient Dense Code

A QR code whose modules hold levels, not bits

Standard QR geometry, but every data module is printed at one of 4, 8 or 16 gray levels — 2 to 3× the data in the same area.

  • 2–4

    Bits per module

  • 8,883B

    40-L capacity, 8 gray

  • 11

    Format revisions

  • 7

    Tests

01The problem

A QR module stores exactly one bit: black or white. The largest QR code tops out at 2,953 bytes, however good the printer and camera are.

02The approach

Keep everything that makes QR frameable — finders, timing lines, alignment and format areas, quiet zone — and let each data module carry 2–4 bits as calibrated gray (or RGB) levels, with a decoder built to survive real cameras.

03Architecture — the camera follows the data

How it works

> Decoding runs it backwards through lighting correction, k-means and deblur.

  1. Step 1: Payload

    zlib if smaller

  2. Step 2: Header

    format · length · CRC-16

  3. Step 3: Reed-Solomon

    QR L/M/Q/H shares

    Where the work happens

  4. Step 4: Interleave

    then whitening

  5. Step 5: Levels

    Gray-coded, 4 / 8 / 16

    Where the work happens

  6. Step 6: Place

    QR's zigzag order

04Perks — why it's built this way

The edge

  • 2–4 bits per module

    4, 8 or 16 levels per module; zlib is applied automatically when the payload compresses.

  • Self-describing

    The profile — levels, gray or RGB, ECC — is BCH-coded into QR's own format modules, so the decoder needs no settings.

  • Built for real cameras

    Homography plus a moving-least-squares residual field for lens and paper curl, lighting correction from known modules, per-channel k-means, and decision-feedback deblurring.

  • Still frames like QR

    OpenCV's standard QR detector locates the symbol — a test checks it.

05Tech — and what each part does

The stack

Python 3.12
Encoder, decoder, CLI
Reed-Solomon
QR-matched L/M/Q/H parity
BCH + CRC-16
Profile and payload integrity
OpenCV
Finder detection, webcam scan
k-means (1-D)
Level clustering per channel
zlib
Automatic compression

06Evidence

The proof

GDC · 4 grayStandard QR
Version 10 (57×57)
GDC · 4 gray: 421B
Standard QR: 213B
Version 40 (177×177)
GDC · 4 gray: 4,585B
Standard QR: 2,331B

Incompressible bytes at error-correction level M, same symbol version. From the repo's capacity table.

Published measurements — real numbers from the repo
A standard QR code beside a same-size Gradient Dense Code whose modules are drawn in several gray levels.
Screenshot — Same size, same geometry — modules hold levels
A simulated camera capture of a GDC symbol with perspective, blur and a warm cast, next to the recovered module grid.
Recovery from a simulated phone capture

07Known limits — stated plainly

Where it doesn't win

  • Only the GDC decoder reads the payload. Phone QR apps recognise the shape but not the levels.
  • Robustness numbers come from simulation; real printer and phone validation is the next step.
  • 16 levels and RGB need sharp, high-resolution captures — JPEG chroma subsampling hurts RGB in particular.