All posts
Tutorial5 min read

How to decode a QR code from an image or PDF

Decode QR codes from photos, screenshots, and PDFs using pyzbar, OpenCV, and AI. Covers URLs, Wi-Fi configs, vCards, payment requests, and multiple QR codes per image.

Decoding a QR code from an image — rather than a live camera — comes up constantly: QR codes in email screenshots, on event flyers you photographed from a distance, in PDF documents, on product packaging in product photography, or on printed receipts you need to batch-process. Here's how to do it programmatically and when to use each approach.

Method 1: pyzbar (Python, fast, local)

pyzbar is the Python binding for the zbar library — the fastest option for decoding QR codes from clean images.

  • pip install pyzbar pillow && brew install zbar # macOS
  • from pyzbar.pyzbar import decode; from PIL import Image
  • codes = decode(Image.open('image.png'))
  • for code in codes:
  • print(code.type, code.data.decode('utf-8'))
  • print('Position:', code.rect) # x, left, top, width, height

pyzbar works instantly on clean screenshots and decent-quality photos. It returns the raw decoded string plus the bounding rectangle of each code in the image. When multiple QR codes appear in the same image, all are returned.

Limitations: pyzbar fails on angled shots (more than ~30° rotation), very small QRs relative to the image, low-contrast codes (white QR on light background), and damaged or partially obscured codes. For these cases, preprocessing or a vision-model approach is needed.

Method 2: OpenCV for preprocessing difficult images

When pyzbar fails, image preprocessing often fixes it. OpenCV can improve contrast, correct perspective, and upscale low-resolution images before passing to pyzbar or the built-in QRCodeDetector.

  • pip install opencv-python
  • import cv2
  • img = cv2.imread('difficult.jpg')
  • gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  • # Increase contrast with CLAHE
  • clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
  • enhanced = clahe.apply(gray)
  • detector = cv2.QRCodeDetector()
  • value, pts, qr_code = detector.detectAndDecode(enhanced)
  • if value: print(value)

OpenCV's QRCodeDetector handles moderate rotation better than pyzbar because it explicitly estimates the QR code's geometry. For severely angled images (packaging photos taken at an angle), use OpenCV's perspective transform to warp the QR region flat before decoding.

Method 3: Decoding from a PDF

QR codes in PDFs require rasterizing the relevant pages to images first, then running a decoder on the images. pdf2image (Python, backed by poppler) handles this:

  • pip install pdf2image pyzbar
  • from pdf2image import convert_from_path; from pyzbar.pyzbar import decode
  • pages = convert_from_path('document.pdf', dpi=200) # higher DPI = better QR resolution
  • for i, page in enumerate(pages):
  • codes = decode(page)
  • for code in codes:
  • print(f'Page {i+1}:', code.data.decode('utf-8'))

DPI matters: a QR code printed at 1cm² in a PDF needs at least 150 DPI to decode reliably; 200-300 DPI is safer. Higher DPI slows conversion but dramatically improves decode success on small codes.

Method 4: AI extraction for damaged, angled, or cluttered images

For QR codes that are rotated significantly, partially damaged, very small relative to the overall image, or surrounded by visual clutter that confuses standard decoders, AI-based extraction handles cases that pyzbar and OpenCV miss.

Tool
QR Code Extractor
Upload any image or PDF containing QR codes — photos, screenshots, flyers, receipts — and decode every code in the image. Returns the decoded value and content type (URL, Wi-Fi, vCard, payment link, plain text) for each code. Multiple codes per image supported.

QR code content types

The decoded string is always a text value — what it means depends on the content type:

  • URL — starts with http:// or https://. Most event and product QR codes.
  • Wi-Fi — WIFI:T:WPA;S:NetworkName;P:Password;; — credentials for joining a network.
  • vCard — BEGIN:VCARD ... END:VCARD — contact information. Common on business cards.
  • SMS — SMSTO:+1234567890:Message body
  • Email — MAILTO:address@example.com?subject=Subject&body=Body
  • Geo — geo:37.7749,-122.4194 — coordinates for a map location.
  • Payment — varies by system: SEPA credit transfer (BCD format), UPI (India), PromptPay (Thailand), etc.
  • Plain text — anything else. Inventory codes, serial numbers, short messages.

Batch decoding QR codes from many images

For inventory sheets, event wristbands, or warehouse labels where dozens of QR codes need decoding at once:

  • from pathlib import Path; from pyzbar.pyzbar import decode; from PIL import Image; import csv
  • results = []
  • for path in Path('images/').glob('*.jpg'):
  • codes = decode(Image.open(path))
  • for c in codes: results.append({'file': path.name, 'type': c.type, 'value': c.data.decode()})
  • with open('decoded.csv', 'w') as f: csv.DictWriter(f, ['file','type','value']).writerows(results)

Frequently asked questions

How do I decode a QR code from an image in Python?+

Use pyzbar: pip install pyzbar pillow, then decode(Image.open('image.png')). It returns a list of decoded codes with their value and bounding rectangle. For difficult images (low resolution, angled), preprocess with OpenCV's CLAHE contrast enhancement first.

How do I decode a QR code from a PDF?+

Rasterize the PDF pages to images with pdf2image (convert_from_path at 200+ DPI), then run pyzbar on each page image. Higher DPI is important for small QR codes.

Why does pyzbar fail to decode some QR codes?+

pyzbar fails on rotated images (>30° off axis), very low resolution, low contrast (white QR on gray background), and partially damaged codes. Try OpenCV's QRCodeDetector for rotation, increase DPI for resolution issues, and apply CLAHE for contrast problems.

How do I decode multiple QR codes in a single image?+

pyzbar returns all codes found in the image — decode() returns a list. All codes are decoded in one call. The rect attribute gives each code's position in the image.

More on tutorial

Stop reading, start extracting

Drop a PDF or image into ExtractFox and get structured data back in seconds.

Try a free extraction →