Skip to main content

Encode and decode multi-spectral / multi-layer QR codes (RGB, UV, NIR) with optional ML-based separation.

Project description

multispecqr

PyPI - Version PyPI - Python Version

Multi-spectral QR codes — encode multiple independent data payloads in a single QR code image using color channels.

Features

  • 3-Layer RGB Mode: Encode 3 independent payloads using Red, Green, and Blue channels
  • Up to 9-Layer Palette Mode: Encode up to 9 independent payloads using adaptive color palettes
    • 1-6 layers: 64-color palette
    • 7-8 layers: 256-color palette
    • 9 layers: 512-color palette
  • Robustness Features: Adaptive thresholding, preprocessing, and color calibration for real-world images
  • ML-Based Decoder: Optional neural network-based decoder for improved robustness (requires PyTorch)
  • Full round-trip support: Encode and decode with high fidelity
  • Simple API: Easy-to-use Python functions for encoding and decoding
  • CLI included: Full-featured command-line interface for quick operations

Installation

pip install multispecqr

Quick Start

Python API

RGB Mode (3 layers)

Encode three separate pieces of data into a single QR code:

from multispecqr import encode_rgb, decode_rgb

# Encode three payloads
img = encode_rgb("Hello Red", "Hello Green", "Hello Blue", version=2)
img.save("rgb_qr.png")

# Decode back
decoded = decode_rgb(img)
print(decoded)  # ['Hello Red', 'Hello Green', 'Hello Blue']

Palette Mode (up to 9 layers)

Encode up to nine separate pieces of data:

from multispecqr import encode_layers, decode_layers

# Encode 6 payloads (uses 64-color palette)
data = ["Layer 1", "Layer 2", "Layer 3", "Layer 4", "Layer 5", "Layer 6"]
img = encode_layers(data, version=2)
img.save("palette_qr.png")

# Decode back
decoded = decode_layers(img, num_layers=6)
print(decoded)  # ['Layer 1', 'Layer 2', 'Layer 3', 'Layer 4', 'Layer 5', 'Layer 6']

# Encode 8 payloads (automatically uses 256-color palette)
data8 = ["A", "B", "C", "D", "E", "F", "G", "H"]
img8 = encode_layers(data8, version=3)

# Encode 9 payloads (automatically uses 512-color palette)
data9 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
img9 = encode_layers(data9, version=4)
decoded9 = decode_layers(img9, num_layers=9)

Robustness Features

For decoding real-world images (photos of printed QR codes):

from multispecqr import decode_rgb, decode_layers

# Use adaptive thresholding for uneven lighting
decoded = decode_rgb(img, threshold_method="otsu")

# Use preprocessing to reduce noise
decoded = decode_rgb(img, preprocess="denoise")

# Combine multiple options
decoded = decode_rgb(img, threshold_method="otsu", preprocess="blur")

Color Calibration

For accurate color matching when decoding photographed QR codes:

from multispecqr import (
    generate_calibration_card,
    compute_calibration,
    decode_layers
)

# 1. Generate and print a calibration card
card = generate_calibration_card()
card.save("calibration_card.png")

# 2. Photograph the printed card alongside your QR code
# 3. Load both the reference and photographed card
photographed_card = Image.open("photographed_card.jpg")

# 4. Compute calibration
calibration = compute_calibration(card, photographed_card)

# 5. Use calibration when decoding
decoded = decode_layers(qr_image, calibration=calibration)

ML-Based Decoder (Optional)

For improved robustness with noisy or distorted images, use the neural network-based decoder:

# Install with ML dependencies
pip install multispecqr[ml]
from multispecqr import decode_rgb, decode_layers

# Use ML-based decoding for RGB mode
decoded = decode_rgb(img, method="ml")

# Use ML-based decoding for palette mode
decoded = decode_layers(img, num_layers=6, method="ml")

The ML decoder uses a lightweight CNN to unmix color layers, providing better robustness for:

  • Images with compression artifacts (JPEG)
  • Photos with color distortion
  • Noisy or low-quality images

Note: The ML decoder requires training for optimal results. Out of the box, it provides basic functionality with untrained weights.

Command Line Interface

The CLI supports both RGB and palette modes with full control over QR code parameters.

Basic Usage

# Show help
python -m multispecqr --help
python -m multispecqr encode --help
python -m multispecqr decode --help

RGB Mode (default)

# Encode three payloads into an RGB QR code
python -m multispecqr encode "Red data" "Green data" "Blue data" output.png

# Decode an RGB QR code
python -m multispecqr decode output.png

Palette Mode (up to 9 layers)

# Encode up to 6 payloads using palette mode (64-color palette)
python -m multispecqr encode "L1" "L2" "L3" "L4" "L5" "L6" output.png --mode palette

# Encode 8 payloads (automatically uses 256-color palette)
python -m multispecqr encode "A" "B" "C" "D" "E" "F" "G" "H" output.png --mode palette

# Decode a palette QR code (specify number of layers)
python -m multispecqr decode output.png --mode palette --layers 6

Advanced Encoding Options

# Encode with higher QR version (more capacity) and error correction
python -m multispecqr encode "R" "G" "B" output.png --version 4 --ec H

# Scale up output image (10x larger for printing)
python -m multispecqr encode "R" "G" "B" output.png --scale 10

# Short form options
python -m multispecqr encode "R" "G" "B" output.png -v 4 -e H -m rgb -s 10

Robustness Options for Decoding

# Decode with adaptive thresholding (for uneven lighting)
python -m multispecqr decode image.png --threshold otsu

# Decode with preprocessing (for noisy images)
python -m multispecqr decode image.png --preprocess denoise

# Combine options
python -m multispecqr decode image.png -t adaptive_gaussian -p blur

# Output results as JSON (for scripting)
python -m multispecqr decode image.png --json

Calibration

# Generate a calibration card for color correction
python -m multispecqr calibrate calibration.png

# Generate with custom patch size
python -m multispecqr calibrate calibration.png --patch-size 30

Batch Processing

# Decode multiple images at once
python -m multispecqr batch-decode img1.png img2.png img3.png

# Batch decode with JSON output
python -m multispecqr batch-decode *.png --json

# Batch decode palette mode images
python -m multispecqr batch-decode *.png --mode palette --layers 6

CLI Options Reference

Encode command:

Option Short Description Default
--mode -m Encoding mode: rgb (3 layers) or palette (1-9 layers) rgb
--version -v QR code version (1-40). Higher = more capacity 4
--ec -e Error correction: L (7%), M (15%), Q (25%), H (30%) M
--scale -s Scale factor for output image 1

Decode command:

Option Short Description Default
--mode -m Decoding mode: rgb or palette rgb
--layers -l Number of layers to decode (palette mode, 1-9) 6
--threshold -t Thresholding: global, otsu, adaptive_gaussian, adaptive_mean global
--preprocess -p Preprocessing: none, blur, denoise none
--json -j Output results as JSON -

Calibrate command:

Option Description Default
--patch-size Size of color patches in pixels 50
--padding Padding between patches 5

Batch-decode command:

Option Short Description Default
--mode -m Decoding mode rgb
--layers -l Number of layers (palette mode) 6
--threshold -t Thresholding method (RGB mode only) global
--preprocess -p Preprocessing method none
--json -j Output results as JSON -

API Reference

Encoding Functions

encode_rgb(data_r, data_g, data_b, *, version=4, ec="M")

Encode three payloads into an RGB QR code using channel separation.

  • data_r, data_g, data_b (str): Payload strings for Red, Green, Blue channels
  • version (int): QR code version 1-40. Higher versions hold more data. Default: 4
  • ec (str): Error correction level - "L", "M", "Q", or "H". Default: "M"
  • Returns: PIL.Image.Image in RGB mode

encode_layers(data_list, *, version=4, ec="M")

Encode 1-9 payloads using adaptive color palettes.

  • data_list (list[str]): List of 1-9 payload strings
  • version (int): QR code version 1-40. Default: 4
  • ec (str): Error correction level. Default: "M"
  • Returns: PIL.Image.Image in RGB mode
  • Raises: ValueError if more than 9 payloads provided

Automatically selects the appropriate palette:

  • 1-6 layers: 64-color palette (6-bit encoding)
  • 7-8 layers: 256-color palette (8-bit encoding)
  • 9 layers: 512-color palette (9-bit encoding)

Decoding Functions

decode_rgb(img, *, threshold_method="global", preprocess=None, calibration=None, method="threshold")

Decode an RGB QR code back into three payloads.

  • img (PIL.Image.Image): RGB image to decode
  • threshold_method (str): Thresholding algorithm:
    • "global": Simple threshold at 128 (default, fastest)
    • "otsu": Otsu's automatic threshold selection
    • "adaptive_gaussian": Adaptive threshold with Gaussian weights
    • "adaptive_mean": Adaptive threshold with mean of neighborhood
  • preprocess (str | None): Optional preprocessing:
    • None or "none": No preprocessing
    • "blur": Gaussian blur to reduce noise
    • "denoise": Non-local means denoising
  • calibration (dict | None): Calibration data from compute_calibration()
  • method (str): Decoding method:
    • "threshold": Traditional threshold-based decoding (default)
    • "ml": ML-based decoder using neural network (requires PyTorch)
  • Returns: list[str] of 3 strings (R, G, B channels). Empty string for failed layers.
  • Raises: ValueError if image is not RGB mode; ImportError if method="ml" but PyTorch not installed

decode_layers(img, num_layers=None, *, preprocess=None, calibration=None, method="threshold")

Decode a palette-encoded QR code.

  • img (PIL.Image.Image): RGB image to decode
  • num_layers (int | None): Number of layers to decode (1-9). Default: 6
  • preprocess (str | None): Optional preprocessing (same options as decode_rgb)
  • calibration (dict | None): Calibration data from compute_calibration()
  • method (str): Decoding method:
    • "threshold": Traditional threshold-based decoding (default)
    • "ml": ML-based decoder using neural network (requires PyTorch)
  • Returns: list[str] of decoded strings. Empty string for failed layers.
  • Raises: ValueError if image is not RGB mode or num_layers > 9; ImportError if method="ml" but PyTorch not installed

Automatically selects the appropriate palette based on num_layers.

Calibration Functions

generate_calibration_card(patch_size=50, padding=5)

Generate a calibration card containing all 64 palette colors.

  • patch_size (int): Size of each color patch in pixels. Default: 50
  • padding (int): Padding between patches. Default: 5
  • Returns: PIL.Image.Image containing the calibration card

compute_calibration(reference, sample, *, patch_size=50, padding=5)

Compute color calibration from a reference and sample calibration card.

  • reference (PIL.Image.Image): Original calibration card (from generate_calibration_card())
  • sample (PIL.Image.Image): Photographed calibration card
  • Returns: dict containing calibration data (matrix, offset, method)

apply_calibration(img, calibration)

Apply color calibration to an image.

  • img (PIL.Image.Image): Input image to calibrate
  • calibration (dict): Calibration data from compute_calibration()
  • Returns: PIL.Image.Image with corrected colors

Palette Functions

palette_6()

Get the 64-color palette (6-layer) mapping bit-vectors to RGB colors.

  • Returns: dict[tuple[int, ...], tuple[int, int, int]]

inverse_palette_6()

Get the inverse 6-layer palette mapping RGB colors to bit-vectors.

  • Returns: dict[tuple[int, int, int], tuple[int, ...]]

palette_8()

Get the 256-color palette (8-layer) mapping bit-vectors to RGB colors.

  • Returns: dict[tuple[int, ...], tuple[int, int, int]]

palette_9()

Get the 512-color palette (9-layer) mapping bit-vectors to RGB colors.

  • Returns: dict[tuple[int, ...], tuple[int, int, int]]

How It Works

RGB Mode

Each payload is encoded as an independent monochrome QR code, then assigned to one color channel (R, G, or B). The decoder separates the channels using thresholding and decodes each independently.

Payload 1 → QR Layer → Red Channel   ─┐
Payload 2 → QR Layer → Green Channel ─┼→ Combined RGB Image
Payload 3 → QR Layer → Blue Channel  ─┘

Multi-Layer Palette Mode

Uses systematic color palettes to encode multiple binary layers in a single image. The library automatically selects the appropriate palette based on the number of layers.

6-Layer Mode (64 colors)

For 1-6 layers, uses a 64-color palette with 2 bits per channel:

  • Bits 0-1 → Red level: {0, 85, 170, 255}
  • Bits 2-3 → Green level: {0, 85, 170, 255}
  • Bits 4-5 → Blue level: {0, 85, 170, 255}

This creates 4³ = 64 unique colors with ~85 unit spacing between levels.

8-Layer Mode (256 colors)

For 7-8 layers, uses a 256-color palette with 3-3-2 bit distribution:

  • Bits 0-2 → Red level: 8 levels (0-255)
  • Bits 3-5 → Green level: 8 levels (0-255)
  • Bits 6-7 → Blue level: 4 levels (0-255)

This creates 8×8×4 = 256 unique colors with ~36 unit spacing on R/G channels.

9-Layer Mode (512 colors)

For 9 layers, uses a 512-color palette with 3 bits per channel:

  • Bits 0-2 → Red level: 8 levels
  • Bits 3-5 → Green level: 8 levels
  • Bits 6-8 → Blue level: 8 levels

This creates 8³ = 512 unique colors with ~36 unit spacing per channel.

N Payloads → N QR Layers → Pixel-wise bit-vectors → Adaptive palette → RGB Image

The decoder uses nearest-neighbor color matching to recover the bit-vectors, then reconstructs each layer.

Robustness Features

For real-world usage (photographed QR codes), the library provides:

  1. Adaptive Thresholding: Handles uneven lighting conditions

    • Otsu's method: Automatic threshold selection based on image histogram
    • Adaptive Gaussian/Mean: Local thresholding for varying illumination
  2. Preprocessing: Reduces image noise

    • Gaussian blur: Smooths out small noise artifacts
    • Non-local means denoising: Advanced noise reduction
  3. Color Calibration: Corrects for camera/display color differences

    • Generate a calibration card with all palette colors
    • Photograph the card under the same conditions as your QR code
    • Compute and apply color correction
  4. ML-Based Decoder (optional): Neural network-based color unmixing

    • Lightweight CNN architecture for layer separation
    • Trainable on synthetic data for improved robustness
    • Handles compression artifacts and color distortion

ML Decoder Architecture

The ML decoder uses a lightweight encoder-decoder CNN:

Input RGB Image (H x W x 3)
    ↓
Encoder: Conv layers → 32 → 64 channels
    ↓
Decoder: Conv layers → 32 channels
    ↓
Output: 6 layer masks (H x W x 6)

The network learns to unmix the 64-color palette back into 6 independent binary layers. It can be trained using generated synthetic data:

from multispecqr.ml_decoder import MLDecoder, generate_training_batch

# Create decoder
decoder = MLDecoder()

# Train for a few epochs
for epoch in range(10):
    loss = decoder.train_epoch(num_samples=100)
    print(f"Epoch {epoch}: loss = {loss:.4f}")

# Use trained decoder
from multispecqr import decode_layers
decoded = decode_layers(img, method="ml")

Requirements

Core dependencies:

  • Python 3.9+
  • opencv-python
  • qrcode[pil]
  • numpy
  • Pillow

Optional ML dependencies (for neural network decoder):

pip install multispecqr[ml]
  • torch (PyTorch)

License

multispecqr is distributed under the terms of the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multispecqr-0.2.0.tar.gz (22.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multispecqr-0.2.0-py3-none-any.whl (22.1 kB view details)

Uploaded Python 3

File details

Details for the file multispecqr-0.2.0.tar.gz.

File metadata

  • Download URL: multispecqr-0.2.0.tar.gz
  • Upload date:
  • Size: 22.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.2 Windows/11

File hashes

Hashes for multispecqr-0.2.0.tar.gz
Algorithm Hash digest
SHA256 329b017549447312102eb18ea4115c76931f39caa6e8401ec734eac678a9b49f
MD5 d1d9f68f3f83bd94f0ace1ef6a7a8947
BLAKE2b-256 d9bbed8785c34d98a386505e2a93188769f7f74265ea9ebe72f4fc671de12e1d

See more details on using hashes here.

File details

Details for the file multispecqr-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: multispecqr-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.2 Windows/11

File hashes

Hashes for multispecqr-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dcf2ec71c5c21558ea8c0b2c92a79553073aa2635117fc5c4d3274d19e47c236
MD5 4402e070ea2deae08901cc57fbc7827b
BLAKE2b-256 49a94d8b4f0c8fa5ea2ea18d0fcdcc9310f34a563309d6be3c85fd954de154cc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page