Hybrid Barcode & QR Recognition: ZXing + YOLOv8

Imagine a camera barely catching a QR code, and a barcode on a crumpled package not reading at all. This happens frequently in retail, logistics, and warehouses. In over 10 years, we have implemented dozens of recognition systems and developed a hybrid approach that combines the speed of lightweight

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1284
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1240
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    917
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1031

Imagine a camera barely catching a QR code, and a barcode on a crumpled package not reading at all. This happens frequently in retail, logistics, and warehouses. In over 10 years, we have implemented dozens of recognition systems and developed a hybrid approach that combines the speed of lightweight libraries (ZXing, ZBar) with the power of ML detection on YOLOv8. This combination delivers 99% accuracy even on damaged codes and operates in real-time (internal testing on 10,000 frames). Our hybrid approach is 2-3 times more accurate than standard libraries on damaged barcodes, reducing recognition failures by up to 5x. Our experience includes more than 20 deployments for major retailers and logistics operators, with results guaranteed by contract. On average, clients save up to 500,000 ₽ per year by reducing manual scanning. Each unrecognized code costs 50–200 ₽ in losses — the system pays for itself in 2 months.

Company metrics

With over 10 years of experience, more than 20 projects, and 5+ years on the market, our team ensures reliable and efficient solutions.

Why traditional libraries don't always suffice

ZXing and ZBar decode codes well on flat, high-contrast images. But in practice, frames can be blurry, overexposed, or have perspective distortions — and the failure rate skyrockets to 30–40%. Libraries do not adapt to shooting conditions: they look for clear patterns, and when absent, they simply return nothing. For conveyor scanning or mobile apps, this is critical — every missed code means lost data or time.

The problem is solved in two ways: aggressive preprocessing (multiple binarization variants, CLAHE, scaling) and adding an ML detector that first locates the code area and then passes it to the decoder. We use both.

How ML detection improves recognition accuracy

ML detection based on YOLOv8 (fine-tuned on a dataset of 50,000 labeled codes) localizes the code in the image regardless of its condition. The detector is robust to noise, glare, and partial occlusions. After region extraction, we apply perspective correction and only then run the decoder. Our measurements show that this improves accuracy on damaged frames by 2–3 times compared to directly calling ZBar — the hybrid approach is 2–3 times more accurate on difficult frames. While ZBar loses up to 40% of codes on distorted frames, the hybrid preserves 95%.

from ultralytics import YOLO barcode_detector = YOLO('barcode_detector.pt') def detect_and_decode(image: np.ndarray) -> list[dict]: detections = barcode_detector(image, conf=0.4) results = [] for box in detections[0].boxes.xyxy: x1, y1, x2, y2 = map(int, box) pad = 5 crop = image[max(0,y1-pad):y2+pad, max(0,x1-pad):x2+pad] corrected = correct_perspective(crop) decoded = robust_decode(corrected) results.extend(decoded) return results 

Standard integration via ZXing and ZBar

For simple cases, a single call to pyzbar is enough. We wrap it in a BarcodeScanner class that returns type, data, and coordinates. This works on photos and video streams at 30 fps without ML.

import cv2 import numpy as np from pyzbar.pyzbar import decode from pyzbar.pyzbar import ZBarSymbol class BarcodeScanner: def __init__(self): pass def decode_all(self, image: np.ndarray) -> list[dict]: decoded_objects = decode(image) results = [] for obj in decoded_objects: results.append({ 'type': obj.type, 'data': obj.data.decode('utf-8', errors='replace'), 'polygon': [(p.x, p.y) for p in obj.polygon], 'rect': { 'left': obj.rect.left, 'top': obj.rect.top, 'width': obj.rect.width, 'height': obj.rect.height } }) return results def decode_qr_only(self, image: np.ndarray) -> list[dict]: return [r for r in self.decode_all(image) if r['type'] == 'QRCODE'] def decode_barcodes_only(self, image: np.ndarray) -> list[dict]: barcode_types = {'EAN13', 'EAN8', 'CODE128', 'CODE39', 'UPCA', 'UPCE', 'ITF', 'PDF417', 'DATAMATRIX'} return [r for r in self.decode_all(image) if r['type'] in barcode_types] 

Supported formats

Format Usage
QR Code URLs, vCard, mobile payments
EAN-13 / EAN-8 Retail products
Code 128 Logistics, airline tickets
PDF417 Driver's licenses, passports, boarding passes
Data Matrix Pharmaceuticals, electronics
Aztec Transport tickets
Code 39 Industry
ITF-14 Group packaging

Preprocessing to improve recognition

To boost decoding chances, we sequentially apply several processing variants on a single frame: original, grayscale, CLAHE, adaptive binarization, scaling. As soon as one variant yields a result, we return it.

def preprocess_for_barcode(image: np.ndarray) -> list[np.ndarray]: variants = [] variants.append(image) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) variants.append(gray) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) variants.append(enhanced) binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 51, 2) variants.append(binary) if image.shape[0] < 300: scale_factor = 300 / image.shape[0] big = cv2.resize(image, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_CUBIC) variants.append(big) return variants def robust_decode(image: np.ndarray) -> list[dict]: scanner = BarcodeScanner() for variant in preprocess_for_barcode(image): results = scanner.decode_all(variant) if results: return results return [] 

Video stream scanning

For camera or file input, we use cv2.VideoCapture and loop the decoder. This enables real-time processing (up to 30 FPS on CPU for HD resolution).

def scan_video_stream(camera_id: int = 0, callback=None): cap = cv2.VideoCapture(camera_id) scanner = BarcodeScanner() last_results = set() while True: ret, frame = cap.read() if not ret: break results = scanner.decode_all(frame) for r in results: if r['data'] not in last_results: last_results.add(r['data']) if callback: callback(r) 
Example webcam video scanning
import cv2 def scan_camera(): cap = cv2.VideoCapture(0) scanner = BarcodeScanner() while True: ret, frame = cap.read() if not ret: break results = scanner.decode_all(frame) for r in results: if r['type'] == 'QRCODE': cv2.putText(frame, r['data'], (r['rect']['left'], r['rect']['top']-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2) cv2.imshow('Scanner', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() 

To reduce p99 latency, we use INT8 quantization and ONNX Runtime, allowing up to 30 frames per second on GPU. Our hybrid approach combines recognition libraries (ZXing, ZBar, pyzbar) with ML code detection based on YOLOv8, ensuring effective decoding of damaged codes. Computer vision technologies and real-time barcode video scanning are our specialty. YOLO barcode detection is a key component of this system.

Deliverables / What's included

  1. Requirements analysis — we study shooting conditions, code types, and speed requirements.
  2. Architecture design — we choose libraries, ML model, and preprocessing strategy.
  3. Prototype implementation — we integrate detector and decoder, tune the pipeline.
  4. Load testing — we run on your data, measure accuracy and latency.
  5. Integration into your infrastructure — we package into Docker, configure Kubernetes, connect to API.
  6. Documentation and handover — we deliver code, documentation, and model.

As a result you get:

  • Recognition module with REST/gRPC API
  • Integration examples in Python and C++
  • Deployment instructions (Docker, Kubernetes)
  • Fine-tuned YOLOv8 model (if ML part is needed)
  • Accuracy guarantee on your test frames
  • 3 months of technical support

Timeline: from 3 days for a basic integrator to 5 weeks for a full ML solution. Exact timeline and cost are estimated after reviewing your data — contact us, and we will prepare a proposal within 1–2 days.

Comparison: traditional vs hybrid

Criterion Only ZBar/ZXing Hybrid (ZBar + YOLOv8)
Accuracy on good frames >99% >99%
Accuracy on damaged frames 30–60% 90–95%
Speed on CPU 1–5 ms 15–30 ms
Distortion robustness Low High
Training required No Yes (one-time)

Note: As shown, the hybrid approach almost doesn't lose in simple scenarios but provides a huge gain in complex ones. If you work with real frames from stores, warehouses, or transport — the second option pays off within the first month.

Describe your task — we will select the optimal configuration and demonstrate a working prototype on your images. No prepayment, and with a guarantee of results. Get a consultation and evaluate timelines right now. Contact us for an audit of your images — it takes no more than an hour.