Introduction
You have already *built* QR codes in the QR generator tutorial — now decode them. Upload any photo containing a QR code and the app extracts the embedded text or URL, draws a box around what it found, and hands you a copy button. It is a genuinely useful utility (ever gotten a QR in a screenshot?) and a friendly first step into computer vision.
The heavy lifting is one OpenCV function: QRCodeDetector, which finds the three finder squares, warps the code flat, and decodes the payload — all in a single call.
Features
Prerequisites
pip install streamlit opencv-python numpyStep 1: Create the Script
Save as qr_scanner.py:
import streamlit as st
import cv2
import numpy as np
st.set_page_config(page_title="QR Scanner", page_icon="🔍")
st.title("🔍 QR Code Scanner")
uploaded = st.file_uploader("Upload an image with a QR code", type=["png", "jpg", "jpeg"])
if uploaded:
bytes_data = uploaded.getvalue()
img = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR)
detector = cv2.QRCodeDetector()
retval, points, straight = detector.detectAndDecodeMulti(img)
if points is not None:
results = [r for r in retval if r]
annotated = img.copy()
for pt_set in points:
cv2.polylines(annotated, [pt_set.astype(int)], True, (0, 200, 0), 6)
st.image(cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), caption=f"{len(results)} code(s) found")
for i, text in enumerate(results, 1):
with st.expander(f"Result {i}: {'URL 🔗' if text.startswith('http') else 'Text 📝'}"):
st.code(text, language=None)
else:
st.error("No QR code found — try a sharper, straighter image.")Step 2: Run the App
streamlit run qr_scanner.pyGenerate a test code with the QR generator, screenshot it, and feed the screenshot back into this app — a satisfying full-circle demo.
How It Works
detectAndDecodeMulti returns three things: the decoded strings, the corner points of each code, and the rectified (straightened) binary images. The corner points matter more than people expect — cv2.polylines draws the green detection box through them, giving users visual proof the scan worked. OpenCV loads images in BGR order, so the app converts to RGB before handing the frame to st.image, or reds and blues swap.
The bytes path is deliberate: file_uploader gives you an in-memory buffer, np.frombuffer turns it into an array, and cv2.imdecode parses it into an image — no temp files, same pattern as the image uploader.
Detection quality is mostly image quality: QR codes carry error correction, but the detector still wants contrast, focus, and reasonably straight angles. That is why the failure message suggests a sharper, straighter photo rather than blaming the code.
Common Errors & Fixes
pillow-heif.cv2.GaussianBlur then cv2.threshold.opencv-python, but imports as cv2; check pip install opencv-python actually succeeded in your venv.Key Concepts
What to Try Next
streamlit-webrtc for live scanning.pyzbar (covers EAN/UPC product codes).accept_multiple_files=True and a results table — the file organizer shows table rendering.FAQ
Can it scan QR codes from a phone camera live?
Not with plain file_uploader — live camera needs streamlit-webrtc, which streams video frames into your Python function. The decoding logic above stays identical.
Why does my screenshot fail but the original works?
Screenshots compress and rescale, blurring the fine modules. Higher error-correction codes (level H, as used in the generator tutorial) survive this much better.
Is OpenCV the only option?
pyzbar is a popular alternative with excellent barcode support; OpenCV wins when you also want the corner geometry for drawing or cropping.