What is a QR Code?
A QR (Quick Response) code is a 2D barcode that can store up to 4,296 alphanumeric characters or 7,089 digits. Developed by Denso Wave in 1994, its omnidirectional readability and error correction made it the global standard for:
- URLs and landing pages — restaurant menus, print advertising
- WiFi credentials — automatic connection without typing passwords
- Mobile payments — PayPal, WeChat Pay, Venmo
- 2FA authentication — TOTP for apps like Google Authenticator
- Inventory and logistics — package and asset tracking
- vCard/contacts — sharing contact information
Error Correction Levels
| Level | Recovery | Recommended Use |
|---|---|---|
| L (Low) | 7% | Clean environments, screens |
| M (Medium) | 15% | General use |
| Q (Quality) | 25% | Industrial printing |
| H (High) | 30% | With overlaid logo, rough surfaces |
Installation
pip install qrcode[pil] segno pyzbar Pillow opencv-python-headless
Basic QR Generation with qrcode
import qrcode
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_H
from PIL import Image
from pathlib import Path
def basic_qr(data, filename, box_size=10, border=4):
"""
Generate a basic black-and-white QR code.
box_size: pixels per module (QR cell)
border: white border modules around the QR
"""
qr = qrcode.QRCode(
version=None, # auto-detect required version
error_correction=qrcode.constants.ERROR_CORRECT_M,
box_size=box_size,
border=border,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color='black', back_color='white')
img.save(filename)
size_kb = Path(filename).stat().st_size / 1024
print(f"QR generated: {filename} ({size_kb:.1f} KB)")
return img
# Basic examples
basic_qr('https://kaijuconverter.com', 'qr_web.png')
basic_qr('mailto:contact@company.com', 'qr_email.png')
basic_qr('WIFI:S:MyNetwork;T:WPA;P:MyPassword;;', 'qr_wifi.png')
# vCard (contact information)
vcard = '''BEGIN:VCARD
VERSION:3.0
FN:Alice Johnson
ORG:KaijuConverter
TEL:+1-555-0100
EMAIL:alice@company.com
URL:https://kaijuconverter.com
END:VCARD'''
basic_qr(vcard, 'qr_contact.png')
Colored QR Codes
def colored_qr(data, filename, front_color='#1a1a2e',
back_color='#f8f9fa', size=400):
"""Generate a QR code with custom corporate colors."""
qr = qrcode.QRCode(
version=None,
error_correction=qrcode.constants.ERROR_CORRECT_M,
box_size=10, border=4,
)
qr.add_data(data)
qr.make(fit=True)
def hex_to_rgb(hex_color):
h = hex_color.lstrip('#')
return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
img = qr.make_image(
fill_color=hex_to_rgb(front_color),
back_color=hex_to_rgb(back_color)
).convert('RGB')
img = img.resize((size, size), Image.LANCZOS)
img.save(filename, quality=95)
print(f"Colored QR: {filename}")
return img
# Dark mode palette
colored_qr('https://kaijuconverter.com', 'qr_dark.png',
front_color='#4cc9f0', back_color='#0d1b2a')
# Brand palette
colored_qr('https://kaijuconverter.com', 'qr_brand.png',
front_color='#f72585', back_color='#ffffff')
QR with Overlaid Logo
def qr_with_logo(data, logo_path, output, logo_fraction=0.25,
front_color='black', back_color='white'):
"""
Generate a QR code with a centered logo.
logo_fraction: fraction of total size (0.25 = 25%)
Always uses error_correction=H to compensate for logo area.
"""
qr = qrcode.QRCode(
version=None,
error_correction=ERROR_CORRECT_H, # H required when adding logo
box_size=12, border=4,
)
qr.add_data(data)
qr.make(fit=True)
qr_img = qr.make_image(
fill_color=front_color, back_color=back_color
).convert('RGB')
qr_w, qr_h = qr_img.size
logo = Image.open(logo_path).convert('RGBA')
logo_size = int(min(qr_w, qr_h) * logo_fraction)
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
# White background pad behind logo
pad = 20
bg = Image.new('RGB', (logo_size + pad, logo_size + pad), 'white')
qr_img.paste(bg, ((qr_w - logo_size - pad) // 2,
(qr_h - logo_size - pad) // 2))
# Paste logo centered
pos = ((qr_w - logo_size) // 2, (qr_h - logo_size) // 2)
mask = logo.split()[3] if logo.mode == 'RGBA' else None
qr_img.paste(logo, pos, mask=mask)
qr_img.save(output, quality=95)
print(f"QR with logo: {output}")
return qr_img
Generating SVG QR with segno
import segno
def qr_svg(data, output_svg, scale=10, dark='#1a1a2e', light='white'):
"""
Generate QR in SVG format (scalable, ideal for print).
segno is much more flexible than qrcode for output formats.
"""
qr = segno.make(data, error='m')
qr.save(output_svg, scale=scale, dark=dark, light=light)
print(f"QR SVG: {output_svg}")
return qr
def batch_qr(data_list, output_folder, prefix='qr', fmt='png'):
"""Generate multiple QR codes from a list."""
folder = Path(output_folder)
folder.mkdir(parents=True, exist_ok=True)
for i, (name, content) in enumerate(data_list, 1):
clean_name = ''.join(c if c.isalnum() else '_' for c in name)
output = folder / f"{prefix}_{i:04d}_{clean_name}.{fmt}"
qr = segno.make(content, error='m')
qr.save(str(output), scale=10, dark='black', light='white')
print(f"Batch generated: {len(data_list)} QR codes in '{output_folder}'")
# Examples
qr_svg('https://kaijuconverter.com', 'qr_vector.svg')
# Batch for inventory
products = [
('Product A', 'SKU-001|Name: Product A|Price: 29.99'),
('Product B', 'SKU-002|Name: Product B|Price: 49.99'),
('Product C', 'SKU-003|Name: Product C|Price: 15.99'),
]
batch_qr(products, 'product_qrs/')
Reading QR Codes with pyzbar
from pyzbar.pyzbar import decode
from PIL import Image
def read_qr_image(image_path):
"""Decode all QR (and barcode) codes from an image."""
img = Image.open(image_path)
codes = decode(img)
if not codes:
print(f"No codes found in {image_path}")
return []
results = []
for code in codes:
data = code.data.decode('utf-8')
rect = code.rect
print(f" Type: {code.type}")
print(f" Data: {data}")
print(f" Position: x={rect.left}, y={rect.top}, "
f"w={rect.width}, h={rect.height}")
results.append({'type': code.type, 'data': data, 'rect': rect})
return results
def read_qr_camera():
"""Read QR codes in real time from the camera using OpenCV."""
try:
import cv2
except ImportError:
print("pip install opencv-python")
return
detector = cv2.QRCodeDetector()
cap = cv2.VideoCapture(0)
print("Camera active — point at a QR code (press Q to quit)")
while True:
ret, frame = cap.read()
if not ret:
break
data, points, _ = detector.detectAndDecode(frame)
if data:
print(f"QR detected: {data}")
if points is not None:
import numpy as np
pts = points.astype(int).reshape(-1, 2)
cv2.polylines(frame, [pts], True, (0, 255, 0), 3)
cv2.imshow('QR Reader', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
# Read QR from file
results = read_qr_image('qr_web.png')
for r in results:
print(f"Content: {r['data']}")
Generating 2FA QR (TOTP)
import segno, urllib.parse
def totp_2fa_qr(issuer, account, secret, output='qr_2fa.png'):
"""
Generate a QR code for 2FA TOTP setup.
Compatible with Google Authenticator, Authy, etc.
secret: base32 key (e.g. 'JBSWY3DPEHPK3PXP')
"""
params = {
'secret': secret,
'issuer': issuer,
'algorithm': 'SHA1',
'digits': 6,
'period': 30,
}
query = urllib.parse.urlencode(params)
account_enc = urllib.parse.quote(account)
issuer_enc = urllib.parse.quote(issuer)
uri = f"otpauth://totp/{issuer_enc}:{account_enc}?{query}"
qr = segno.make(uri, error='h') # H = maximum redundancy for 2FA
qr.save(output, scale=10, dark='black', light='white')
print(f"2FA QR generated: {output}")
print(f"URI: {uri}")
return uri
Conclusion
Python provides two main libraries for generating QR: qrcode for basic customization with logos, and segno for output in multiple formats (SVG, PNG, EPS, PDF) with greater control. For decoding, pyzbar is the fastest option without OpenCV, while cv2.QRCodeDetector enables real-time reading from a camera. Always use error_correction=H when overlaying a logo: the QR can still be read even if up to 30% of its surface is obscured.
Related conversions
Frequent conversions across the catalogue: