How to Build an AI-Powered MRZ Scanner and Barcode Reader with Dynamsoft Capture Vision

On October 14, 2025, Dynamsoft unveiled its most significant leap in computer vision yet — the AI-powered Dynamsoft Capture Vision (DCV) 3.2.1000. This release introduces groundbreaking deep learning models that transform how 1D barcodes and MRZs are detected and decoded, pushing the boundaries of accuracy, performance, and real-world reliability. In this article, we will walk through the process of evaluating these new capabilities using publicly available datasets and a Python-based comparison tool we previously developed.

What you’ll build: A Python-based benchmark comparison tool that evaluates Dynamsoft Capture Vision 3.2.1000’s AI deep learning models for 1D barcode recognition and MRZ passport scanning — tested against real-world public datasets.

Key Takeaways

  • Dynamsoft Capture Vision 3.2.1000 ships neural network–based localization and decoding models that successfully read blurred, low-resolution, and partially damaged 1D barcodes.
  • The MRZ deep learning model delivers 42.7% faster processing than the previous rule-based approach while improving region detection accuracy for passports and ID cards.
  • Benchmarking is performed by running each SDK version in a subprocess and comparing per-image results — the first cvr.capture() call warms up the AI model; only the second call’s timing is measured.
  • This workflow applies to any use case requiring reliable automated document scanning: border control, KYC onboarding, and logistics identity verification.

Common Developer Questions

  • How do I build an AI MRZ scanner in Python using Dynamsoft Capture Vision?
  • How much faster is the new deep learning MRZ model compared to the previous version?
  • Why does my first cvr.capture() call return a slow result when using the AI barcode model?

See the AI Models in Action

  • Blurred 1D Barcode Recognition

  • MRZ Recognition

Prerequisites

What the AI Models Detect: 1D Barcodes and MRZ

The core highlights of this release include:

  • Barcode: Deep neural network–based localization and decoding models designed to handle blurred, low-resolution, or partially damaged barcodes.
  • MRZ: A specialized deep learning model that delivers 42.7% faster processing with improved region detection accuracy for passport and ID recognition workflows.

Step 1: Extend the Comparison Tool to Support MRZ Mode

We previously developed a comparison tool to benchmark barcode recognition performance.
To extend this tool for MRZ evaluation, we made the following updates.

Step 1: Add a Mode-Switching Dropdown for Barcode and MRZ

mode_label = QLabel("Detection Mode:")
toolbar_layout.addWidget(mode_label)

self.detection_mode_combo = QComboBox()
self.detection_mode_combo.addItems(["Barcode", "MRZ"])
self.detection_mode_combo.setCurrentText("Barcode")
self.detection_mode_combo.currentTextChanged.connect(self.on_detection_mode_changed)
toolbar_layout.addWidget(self.detection_mode_combo)

def on_detection_mode_changed(self, mode: str):
    self.current_detection_mode = mode
    self.status_bar.showMessage(f"Detection mode changed to: {mode}")
    
    if hasattr(self, 'processing_thread') and self.processing_thread and self.processing_thread.isRunning():
        self.processing_thread.terminate()
        self.processing_thread.wait(1000)
        self.processing_thread = None
    
    self.progress_bar.setVisible(False)
    
    self.results.clear()
    self.results_table.setRowCount(0)
    self.image_files.clear()
    self.file_list.clear()
    self.new_files = []
    self.image_comparison.sdk1_scene.clear()
    self.image_comparison.sdk2_scene.clear()
    self.image_comparison.summary_label.setText(f"Detection mode: {mode} - Add images to begin comparison")
    self.image_comparison.sdk1_results_text.clear()
    self.image_comparison.sdk2_results_text.clear()
    
    if mode == "Barcode":
        self.results_table.setHorizontalHeaderLabels(["Image", "SDK 1", "SDK 2", "Barcode Δ", "Speed Δ"])
    elif mode == "MRZ":
        self.results_table.setHorizontalHeaderLabels(["Image", "SDK 1", "SDK 2", "MRZ Δ", "Speed Δ"])

Step 2: Update the Detection Script to Handle MRZ Mode in the Subprocess

script_content = f'''#!/usr/bin/env python3
import sys
import json
import time
import os
from dynamsoft_capture_vision_bundle import *

LICENSE_KEY = "LICENSE-KEY"

def process_image(image_path, detection_mode):
    
    
    # Initialize license
    error_code, error_message = LicenseManager.init_license(LICENSE_KEY)
    if error_code not in [EnumErrorCode.EC_OK, EnumErrorCode.EC_LICENSE_CACHE_USED]:
        return success", "detection_mode": detection_mode}}
    
    # Process image
    cvr = CaptureVisionRouter()
    
    # Select template based on detection mode
    if detection_mode == "Barcode":
        template = EnumPresetTemplate.PT_READ_BARCODES.value
    elif detection_mode == "Document":
        template = EnumPresetTemplate.PT_DETECT_AND_NORMALIZE_DOCUMENT.value
    elif detection_mode == "MRZ":
        template = "ReadPassportAndId"
    else:
        template = EnumPresetTemplate.PT_READ_BARCODES.value  # Default
    
    try:
        cvr.capture(image_path, template) # Trigger model loading the first time
    except Exception as e:
        pass  # Ignore first capture errors for model loading

    start_time = time.time()
    result = cvr.capture(image_path, template)
    processing_time = time.time() - start_time

    error_code = result.get_error_code()
    items = result.get_items()
    
    # Error -10005 often just means "no results found" for any detection mode
    # This is not necessarily a failure, just empty results
    if error_code != EnumErrorCode.EC_OK:
        if error_code == -10005:
            # No results found - return empty results but success
            pass  # Continue with empty items list
        else:
            return success", "detection_mode": detection_mode}}
    
    # Initialize result containers
    barcodes = []
    mrz_results = []
    document_results = []
    
    # Process items based on detection mode  
    for item in items:
        item_type = item.get_type()
        
        if detection_mode == "Barcode" and item_type == 2:  # Barcode item (CRIT_BARCODE = 2)
            barcode_data = text
            
            # Get location points
            try:
                location = item.get_location()
                if location and hasattr(location, 'points'):
                    barcode_data["points"] = [
                        [int(location.points[0].x), int(location.points[0].y)],
                        [int(location.points[1].x), int(location.points[1].y)],
                        [int(location.points[2].x), int(location.points[2].y)],
                        [int(location.points[3].x), int(location.points[3].y)]
                    ]
                else:
                    barcode_data["points"] = []
            except:
                barcode_data["points"] = []
            
            barcodes.append(barcode_data)
            
        elif detection_mode == "MRZ" and item_type == 4:  # Text line item (CRIT_TEXT_LINE = 4)
            text = item.get_text()
            mrz_data = text,  # Could be expanded to parse MRZ fields
                "points": []
            }}
            
            # Get location points
            try:
                location = item.get_location()
                if location and hasattr(location, 'points'):
                    mrz_data["points"] = [
                        [int(location.points[0].x), int(location.points[0].y)],
                        [int(location.points[1].x), int(location.points[1].y)],
                        [int(location.points[2].x), int(location.points[2].y)],
                        [int(location.points[3].x), int(location.points[3].y)]
                    ]
            except:
                mrz_data["points"] = []
            
            mrz_results.append(mrz_data)
    
    return success

if __name__ == "__main__":
    detection_mode = sys.argv[2] if len(sys.argv) > 2 else "Barcode"
    result = process_image(sys.argv[1], detection_mode)
    print(json.dumps(result))
'''

Note: The first call to cvr.capture() triggers AI model loading. To measure true image-processing time, the script calls cvr.capture() twice.

Step 2: Prepare Datasets and Run the Benchmark

To evaluate the new AI-powered models, we used two public datasets:

Benchmark 1D Barcode Recognition with Dataset3

Barcodes200909

We used the Dataset3 folder (images at 1152×864 resolution, taken with an older Nokia 7610 phone lacking autofocus) to benchmark 1D barcode recognition.

Detect 1D barcodes with AI models

Benchmark MRZ Recognition with the L3i Passport Dataset

L3i MRZ Dataset

Reference

K.B. Bulatov, E.V. Emelianova, D.V. Tropin, N.S. Skoryukina, Y.S. Chernyshova, A.V. Sheshkus, S.A. Usilin, Z. Ming, J.-C. Burie, M. M. Luqman, V.V. Arlazarov: “MIDV-2020: A Comprehensive Benchmark Dataset for Identity Document Analysis”, Computer Optics (submitted), 2021.

We used the aze_passport folder for MRZ recognition benchmarking.

Recognize MRZ with AI models

Common Issues & Edge Cases

  • First-capture timing inflation: The AI model loads on the first cvr.capture() call. Always call cvr.capture() once to warm up the model before starting your benchmark timer — the script already does this.
  • MRZ not detected on low-resolution scans: The deep learning model expects sufficient image resolution. Scans below ~200 DPI may fail to locate the MRZ zone; pre-process images with contrast enhancement or upscaling if recognition returns empty results.
  • Error code -10005 misread as failure: DCV returns -10005 when no results are found in an image — this is not a crash. Treat it as an empty result set and continue processing rather than halting the benchmark run.

Source Code

https://github.com/yushulx/python-barcode-qrcode-sdk/edit/main/examples/official/comparison_tool