C++ Passport MRZ Scanner: Recognize and Parse Travel Documents on Windows and Linux

Need to extract passport, ID, or visa data from the Machine Readable Zone (MRZ) in C++? The MRZ encodes the holder’s name, nationality, document number, date of birth, gender, and expiration date in a standardized format that machines can parse reliably. This tutorial walks you through building a cross-platform desktop MRZ scanner using the Dynamsoft Capture Vision C++ SDK — from CMake project setup to real-time webcam recognition — with full source code for Windows and Linux.

See the C++ MRZ Scanner in Action

What you’ll build: A cross-platform desktop MRZ scanner in C++ that reads and parses passport, ID, and visa machine-readable zones from image files or a live camera stream using the Dynamsoft Capture Vision SDK on Windows and Linux.

Key Takeaways

  • The Dynamsoft Capture Vision C++ SDK provides end-to-end MRZ recognition and parsing for TD1 (ID cards), TD2, and TD3 (passports) document types on both Windows and Linux.
  • A single CCaptureVisionRouter::Capture() call handles image loading, text-line detection, and MRZ field extraction — no manual OCR pipeline is needed.
  • OpenCV integration enables real-time MRZ scanning from a webcam with bounding-box overlay and parsed-field display.
  • The same CMake project compiles on Windows (MSVC/MinGW) and Linux (GCC) without platform-specific code changes.

Common Developer Questions

  • How do I read passport MRZ data from an image in C++ on Windows and Linux?
  • What C++ SDK supports real-time MRZ recognition from a webcam stream?
  • How do I parse TD1, TD2, and TD3 machine-readable zone fields using the Dynamsoft Capture Vision SDK?

Prerequisites

Step 1: Set Up the CMake Project for Cross-Platform MRZ Recognition

This section will guide you through setting up a CMake project to implement MRZ recognition using the Dynamsoft Capture Vision SDK in C++ on both Windows and Linux. We will cover two examples: the first demonstrates loading images from files, and the second shows capturing and processing images from a camera. Both examples include recognizing MRZ data and extracting relevant information.

To get started, create two source files: main.cpp for loading images from files, and maincv.cpp for capturing images from a camera. Then, create a CMakeLists.txt file in your project directory with the following configuration:

cmake_minimum_required (VERSION 3.8)
project (main)
MESSAGE( STATUS "PROJECT_NAME: " ${PROJECT_NAME} )

option(ENABLE_OPENCV "Build with OpenCV" OFF)
MESSAGE(STATUS "Build with OpenCV: ${ENABLE_OPENCV}")

if (CMAKE_HOST_WIN32)
    set(WINDOWS 1)
elseif(CMAKE_HOST_UNIX)
    set(LINUX 1)
endif()

# Set RPATH
if(CMAKE_HOST_UNIX)
    SET(CMAKE_CXX_FLAGS "-std=c++11 -O3 -Wl,-rpath=$ORIGIN")
    SET(CMAKE_INSTALL_RPATH "$ORIGIN")
    SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()

# Add search path for include and lib files
MESSAGE( STATUS "CPU architecture ${CMAKE_SYSTEM_PROCESSOR}" )
if(WINDOWS)
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/win/bin/") 
    else()
        link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/win/lib/") 
    endif()
elseif(LINUX)
    if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
        MESSAGE( STATUS "Link directory: ${PROJECT_SOURCE_DIR}/../sdk/platforms/linux/" )
        link_directories("${PROJECT_SOURCE_DIR}/../sdk/platforms/linux/")
    endif()
endif()
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/../sdk/include/")

# Add the executable
if (ENABLE_OPENCV)
    find_package(OpenCV REQUIRED)
    add_executable(${PROJECT_NAME} maincv.cpp)
    if(WINDOWS)
        if(CMAKE_CL_64)
            target_link_libraries (${PROJECT_NAME} "DynamsoftCorex64" "DynamsoftLicensex64" "DynamsoftCaptureVisionRouterx64" "DynamsoftUtilityx64" ${OpenCV_LIBS})
        endif()
    else()
        target_link_libraries (${PROJECT_NAME} "DynamsoftCore" "DynamsoftLicense" "DynamsoftCaptureVisionRouter" "DynamsoftUtility" pthread ${OpenCV_LIBS})
    endif()
else()
    add_executable(${PROJECT_NAME} main.cpp)
    if(WINDOWS)
        if(CMAKE_CL_64)
            target_link_libraries (${PROJECT_NAME} "DynamsoftCorex64" "DynamsoftLicensex64" "DynamsoftCaptureVisionRouterx64" "DynamsoftUtilityx64" )
        endif()
    else()
        target_link_libraries (${PROJECT_NAME} "DynamsoftCore" "DynamsoftLicense" "DynamsoftCaptureVisionRouter" "DynamsoftUtility"  pthread)
    endif()
endif()

if(WINDOWS)
    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    "${PROJECT_SOURCE_DIR}/../sdk/platforms/win/bin/"      
    $<TARGET_FILE_DIR:main>)
else()
    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    "${PROJECT_SOURCE_DIR}/../sdk/platforms/linux/"      
    $<TARGET_FILE_DIR:main>)
endif()

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${PROJECT_SOURCE_DIR}/../sdk/DLR-PresetTemplates.json"
$<TARGET_FILE_DIR:main>/DLR-PresetTemplates.json)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${PROJECT_SOURCE_DIR}/../sdk/MRZ.json"
$<TARGET_FILE_DIR:main>/MRZ.json)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${PROJECT_SOURCE_DIR}/../sdk/ConfusableChars.data"
$<TARGET_FILE_DIR:main>/ConfusableChars.data)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:main>/CharacterModel
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/../sdk/CharacterModel"
$<TARGET_FILE_DIR:main>/CharacterModel)


add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:main>/ParserResources
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/../sdk/ParserResources"
$<TARGET_FILE_DIR:main>/ParserResources)

Explanation

  • CMake Configuration: The CMakeLists.txt file configures the build environment for both Windows and Linux, detecting the operating system and setting paths for libraries and includes accordingly.
  • Enabling OpenCV: The ENABLE_OPENCV option toggles the inclusion of OpenCV support. When enabled, the project compiles maincv.cpp for camera capture. Otherwise, it compiles main.cpp for loading images from files.

  • Library Linking: The project links with necessary Dynamsoft libraries (DynamsoftCore, DynamsoftLicense, DynamsoftCaptureVisionRouter, and DynamsoftUtility).

  • Resource Copying: After building, required DLLs and resource files (e.g., templates, models) are copied to the output directory using add_custom_command to ensure the application has all necessary assets.

Step 2: Implement MRZ Recognition and Data Parsing

Define a Class to Store and Parse MRZ Fields

Define an MRZResult class to store and parse the MRZ data from recognized results:

class MRZResult
{
public:
  string docId;
  string docType;
  string nationality;
  string issuer;
  string dateOfBirth;
  string dateOfExpiry;
  string gender;
  string surname;
  string givenname;

  vector<string> rawText;

  MRZResult FromParsedResultItem(const CParsedResultItem *item)
  {
    docType = item->GetCodeType();

    if (docType == "MRTD_TD3_PASSPORT")
    {
      if (item->GetFieldValidationStatus("passportNumber") != VS_FAILED && item->GetFieldValue("passportNumber") != NULL)
      {
        docId = item->GetFieldValue("passportNumber");
      }
    }
    else if (item->GetFieldValidationStatus("documentNumber") != VS_FAILED && item->GetFieldValue("documentNumber") != NULL)
    {
      docId = item->GetFieldValue("documentNumber");
    }

    string line;
    if (docType == "MRTD_TD1_ID")
    {
      if (item->GetFieldValue("line1") != NULL)
      {
        line = item->GetFieldValue("line1");
        if (item->GetFieldValidationStatus("line1") == VS_FAILED)
        {
          line += ", Validation Failed";
        }
        rawText.push_back(line);
      }

      if (item->GetFieldValue("line2") != NULL)
      {
        line = item->GetFieldValue("line2");
        if (item->GetFieldValidationStatus("line2") == VS_FAILED)
        {
          line += ", Validation Failed";
        }
        rawText.push_back(line);
      }

      if (item->GetFieldValue("line3") != NULL)
      {
        line = item->GetFieldValue("line3");
        if (item->GetFieldValidationStatus("line3") == VS_FAILED)
        {
          line += ", Validation Failed";
        }
        rawText.push_back(line);
      }
    }
    else
    {
      if (item->GetFieldValue("line1") != NULL)
      {
        line = item->GetFieldValue("line1");
        if (item->GetFieldValidationStatus("line1") == VS_FAILED)
        {
          line += ", Validation Failed";
        }
        rawText.push_back(line);
      }

      if (item->GetFieldValue("line2") != NULL)
      {
        line = item->GetFieldValue("line2");
        if (item->GetFieldValidationStatus("line2") == VS_FAILED)
        {
          line += ", Validation Failed";
        }
        rawText.push_back(line);
      }
    }

    if (item->GetFieldValidationStatus("nationality") != VS_FAILED && item->GetFieldValue("nationality") != NULL)
    {
      nationality = item->GetFieldValue("nationality");
    }
    if (item->GetFieldValidationStatus("issuingState") != VS_FAILED && item->GetFieldValue("issuingState") != NULL)
    {
      issuer = item->GetFieldValue("issuingState");
    }
    if (item->GetFieldValidationStatus("dateOfBirth") != VS_FAILED && item->GetFieldValue("dateOfBirth") != NULL)
    {
      dateOfBirth = item->GetFieldValue("dateOfBirth");
    }
    if (item->GetFieldValidationStatus("dateOfExpiry") != VS_FAILED && item->GetFieldValue("dateOfExpiry") != NULL)
    {
      dateOfExpiry = item->GetFieldValue("dateOfExpiry");
    }
    if (item->GetFieldValidationStatus("sex") != VS_FAILED && item->GetFieldValue("sex") != NULL)
    {
      gender = item->GetFieldValue("sex");
    }
    if (item->GetFieldValidationStatus("primaryIdentifier") != VS_FAILED && item->GetFieldValue("primaryIdentifier") != NULL)
    {
      surname = item->GetFieldValue("primaryIdentifier");
    }
    if (item->GetFieldValidationStatus("secondaryIdentifier") != VS_FAILED && item->GetFieldValue("secondaryIdentifier") != NULL)
    {
      givenname = item->GetFieldValue("secondaryIdentifier");
    }

    return *this;
  }

  string ToString()
  {
    string msg = "Raw Text:\n";
    for (size_t idx = 0; idx < rawText.size(); ++idx)
    {
      msg += "\tLine " + to_string(idx + 1) + ": " + rawText[idx] + "\n";
    }
    msg += "Parsed Information:\n";
    msg += "\tDocument Type: " + docType + "\n";
    msg += "\tDocument ID: " + docId + "\n";
    msg += "\tSurname: " + surname + "\n";
    msg += "\tGiven Name: " + givenname + "\n";
    msg += "\tNationality: " + nationality + "\n";
    msg += "\tIssuing Country or Organization: " + issuer + "\n";
    msg += "\tGender: " + gender + "\n";
    msg += "\tDate of Birth(YYMMDD): " + dateOfBirth + "\n";
    msg += "\tExpiration Date(YYMMDD): " + dateOfExpiry + "\n";

    return msg;
  }
};

Recognize MRZ Fields from Image Files

  1. Initialize the SDK with a valid license key.

     #include <stdio.h>
     #include <string>
     #include <vector>
     #if defined(_WIN32) || defined(_WIN64)
     #include <windows.h>
     #include <conio.h>
     #include <io.h>
     #else
     #include <cstring>
     #include <dirent.h>
     #include <sys/time.h>
     #endif
        
     #include <fstream>
     #include <streambuf>
     #include <iostream>
     #include <sstream>
        
     #include "DynamsoftCaptureVisionRouter.h"
     #include "DynamsoftUtility.h"
        
     using namespace std;
        
     using namespace dynamsoft::cvr;
     using namespace dynamsoft::dlr;
     using namespace dynamsoft::dcp;
     using namespace dynamsoft::license;
     using namespace dynamsoft::basic_structures;
     using namespace dynamsoft::utility;
    
     int main(int argc, char *argv[])
     {
     	printf("*************************************************\r\n");
     	printf("Welcome to Dynamsoft MRZ Demo\r\n");
     	printf("*************************************************\r\n");
     	printf("Hints: Please input 'Q' or 'q' to quit the application.\r\n");
        
     	int iRet = -1;
     	char szErrorMsg[256];
     	// Initialize license.
     	// Request a trial from https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform
     	iRet = CLicenseManager::InitLicense("LICENSE-KEY", szErrorMsg, 256);
     	if (iRet != EC_OK)
     	{
     		cout << szErrorMsg << endl;
     	}
     }
    
  2. Load the MRZ recognition template file.

     int errorCode = 1;
     	char errorMsg[512] = {0};
        
     	CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
     	errorCode = cvr->InitSettingsFromFile("MRZ.json", errorMsg, 512);
     	if (errorCode != EC_OK)
     	{
     		cout << "error:" << errorMsg << endl;
     		return -1;
     	}
    
  3. Load images from files and recognize MRZ data in an infinite loop. Press Q or q to quit the application.

     bool GetImagePath(char *pImagePath)
     {
     	std::string input;
     	while (true)
     	{
     		std::cout << "\n>> Step 1: Input your image file's full path:\n";
     		std::getline(std::cin, input);
    
     		input.erase(0, input.find_first_not_of(" \t\n\r\"\'")); 
     		input.erase(input.find_last_not_of(" \t\n\r\"\'") + 1); 
        
     		if (input == "q" || input == "Q")
     		{
     			return true; 
     		}
        
     		std::strncpy(pImagePath, input.c_str(), 511);
     		pImagePath[511] = '\0'; 
        
     		std::ifstream file(pImagePath);
     		if (file.good())
     		{
     			file.close();
     			return false; 
     		}
        
     		std::cout << "Please input a valid path.\n";
     	}
     }
    
    
     char pszImageFile[512] = {0};
     bool bExit = false;
     while (1)
     {
       bExit = GetImagePath(pszImageFile);
       if (bExit)
         break;
       float costTime = 0.0;
       int errorCode = 0;
      
       CCapturedResult *captureResult = cvr->Capture(pszImageFile);
       if (captureResult)
       {
         CParsedResult *parsedResult = captureResult->GetParsedResult();
         if (parsedResult)
         {
           for (int i = 0; i < parsedResult->GetItemsCount(); i++)
           {
             const CParsedResultItem *item = parsedResult->GetItem(i);
             MRZResult result;
             result.FromParsedResultItem(item);
             cout << result.ToString() << endl;
           }
           parsedResult->Release();
         }
      
         captureResult->Release();
       }
     }
      
     delete cvr, cvr = NULL;
     return 0;
    

Recognize MRZ Fields from a Live Camera Stream with OpenCV

  1. Initialize the Capture Vision SDK and open the camera with OpenCV.

     #include "opencv2/core.hpp"
     #include "opencv2/imgproc.hpp"
     #include "opencv2/highgui.hpp"
     #include "opencv2/videoio.hpp"
     #include "opencv2/core/utility.hpp"
     #include "opencv2/imgcodecs.hpp"
     #include <iostream>
     #include <vector>
     #include <chrono>
     #include <iostream>
     #include <string>
    
     #include "DynamsoftCaptureVisionRouter.h"
     #include "DynamsoftUtility.h"
    
     using namespace std;
     using namespace cv;
    
     using namespace dynamsoft::cvr;
     using namespace dynamsoft::dlr;
     using namespace dynamsoft::dcp;
     using namespace dynamsoft::license;
     using namespace dynamsoft::basic_structures;
     using namespace dynamsoft::utility;
    
     int main(int argc, char *argv[])
     {
       bool captured = false;
       cout << "Opening camera..." << endl;
       VideoCapture capture(0); // open the first camera
       if (!capture.isOpened())
       {
         cerr << "ERROR: Can't initialize camera capture" << endl;
         cout << "Press any key to quit..." << endl;
         cin.ignore();
         return 1;
       }
       int iRet = -1;
       char szErrorMsg[256];
       // Initialize license.
       // Request a trial from https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform
       iRet = CLicenseManager::InitLicense("LICENSE-KEY", szErrorMsg, 256);
       if (iRet != EC_OK)
       {
         cout << szErrorMsg << endl;
       }
     }
    
  2. Register callback functions for appending camera frames and receiving MRZ recognition results.

     class MyCapturedResultReceiver : public CCapturedResultReceiver
     {
     	virtual void OnRecognizedTextLinesReceived(CRecognizedTextLinesResult *pResult) override
     	{
     		std::lock_guard<std::mutex> lock(textResultsMutex);
     		textResults.clear();
        
     		const CImageTag *tag = pResult->GetOriginalImageTag();
        
     		if (pResult->GetErrorCode() != EC_OK)
     		{
     			cout << "Error: " << pResult->GetErrorString() << endl;
     		}
     		else
     		{
     			int lCount = pResult->GetItemsCount();
     			for (int li = 0; li < lCount; ++li)
     			{
     				TextResult result;
        
     				const CTextLineResultItem *textLine = pResult->GetItem(li);
     				CPoint *points = textLine->GetLocation().points;
     				result.textLinePoints.push_back(cv::Point(points[0][0], points[0][1]));
     				result.textLinePoints.push_back(cv::Point(points[1][0], points[1][1]));
     				result.textLinePoints.push_back(cv::Point(points[2][0], points[2][1]));
     				result.textLinePoints.push_back(cv::Point(points[3][0], points[3][1]));
        
     				result.id = tag->GetImageId();
     				textResults.push_back(result);
     			}
     		}
     	}
        
     	virtual void OnParsedResultsReceived(CParsedResult *pResult)
     	{
     		if (pResult == nullptr)
     		{
     			return;
     		}
        
     		const CImageTag *tag = pResult->GetOriginalImageTag();
        
     		if (pResult->GetErrorCode() != EC_OK)
     		{
     			cout << "Error: " << pResult->GetErrorString() << endl;
     		}
     		else
     		{
     			int lCount = pResult->GetItemsCount();
     			for (int i = 0; i < lCount; i++)
     			{
     				const CParsedResultItem *item = pResult->GetItem(i);
        
     				MRZResult result;
     				result.FromParsedResultItem(item);
     				cout << result.ToString() << endl;
        
     				if (textResults[0].id == tag->GetImageId())
     				{
     					std::lock_guard<std::mutex> lock(textResultsMutex);
     					textResults[0].info = result;
     				}
     			}
     		}
        
     		pResult->Release();
     	}
     };
        
     class MyVideoFetcher : public CImageSourceAdapter
     {
     public:
     	MyVideoFetcher() {};
     	~MyVideoFetcher() {};
     	bool HasNextImageToFetch() const override
     	{
     		return true;
     	}
     	void MyAddImageToBuffer(const CImageData *img, bool bClone = true)
     	{
     		AddImageToBuffer(img, bClone);
     	}
     };
    
     int errorCode = 1;
     char errorMsg[512] = {0};
      
     CCaptureVisionRouter *cvr = new CCaptureVisionRouter;
      
     MyVideoFetcher *fetcher = new MyVideoFetcher();
     fetcher->SetMaxImageCount(4);
     fetcher->SetBufferOverflowProtectionMode(BOPM_UPDATE);
     fetcher->SetColourChannelUsageType(CCUT_AUTO);
     cvr->SetInput(fetcher);
      
     CCapturedResultReceiver *capturedReceiver = new MyCapturedResultReceiver;
     cvr->AddResultReceiver(capturedReceiver);
    
  3. Set the MRZ template and start the MRZ recognition process. Press the C key to capture the recognized frame and display the MRZ data.

     errorCode = cvr->InitSettingsFromFile("MRZ.json", errorMsg, 512);
     if (errorCode != EC_OK)
     {
       cout << "error:" << errorMsg << endl;
     }
    
     errorCode = cvr->StartCapturing("", false, errorMsg, 512);
    
     if (errorCode != EC_OK)
     {
       cout << "error:" << errorMsg << endl;
     }
     else
     {
       int width = (int)capture.get(CAP_PROP_FRAME_WIDTH);
       int height = (int)capture.get(CAP_PROP_FRAME_HEIGHT);
    
       for (int i = 1;; ++i)
       {
         Mat frame;
         capture.read(frame);
         if (frame.empty())
         {
           cerr << "ERROR: Can't grab camera frame." << endl;
           break;
         }
         CFileImageTag tag(nullptr, 0, 0);
         tag.SetImageId(i);
         CImageData data(frame.rows * frame.step.p[0],
                 frame.data,
                 width,
                 height,
                 frame.step.p[0],
                 IPF_RGB_888,
                 0,
                 &tag);
         fetcher->MyAddImageToBuffer(&data);
    
         {
           std::lock_guard<std::mutex> lock(textResultsMutex);
           for (const auto &result : textResults)
           {
             if (!result.textLinePoints.empty())
             {
               for (size_t i = 0; i < result.textLinePoints.size(); ++i)
               {
                 cv::line(frame, result.textLinePoints[i],
                     result.textLinePoints[(i + 1) % result.textLinePoints.size()],
                     cv::Scalar(0, 0, 255), 2);
               }
    
               int x = 20;
               int y = 40;
    
               MRZResult mrzResult = result.info;
               string msg = "Document Type: " + mrzResult.docType;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Document ID: " + mrzResult.docId;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Surname: " + mrzResult.surname;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Given Name: " + mrzResult.givenname;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Nationality: " + mrzResult.nationality;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Issuing Country or Organization: " + mrzResult.issuer;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Gender: " + mrzResult.gender;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Date of Birth(YYMMDD): " + mrzResult.dateOfBirth;
               drawText(frame, msg.c_str(), x, y);
               y += 20;
               msg = "Expiration Date(YYMMDD): " + mrzResult.dateOfExpiry;
    
               if (captured)
               {
                 captured = false;
                 imshow("Captured Frame", frame);
               }
             }
           }
         }
    
         cv::putText(frame, "Press 'ESC' to quit. Press 'C' to capture.",
               cv::Point(10, 20), cv::FONT_HERSHEY_SIMPLEX,
               0.5, cv::Scalar(0, 255, 0), 2);
    
         imshow("MRZ Scanner", frame);
         int key = waitKey(1);
         if (key == 27 /*ESC*/)
           break;
         else if (key == char('c'))
         {
           captured = true;
         }
       }
       cvr->StopCapturing(false, true);
     }
    
     delete cvr, cvr = NULL;
     delete fetcher, fetcher = NULL;
     delete capturedReceiver, capturedReceiver = NULL;
    
     return 0;
    

Step 3: Build and Run the MRZ Scanner on Windows and Linux

mkdir build
cd build
cmake ..
cmake --build .

desktop mrz recognition in C++

Common Issues and Edge Cases When Building a C++ MRZ Scanner

  • License initialization fails at runtime: Ensure the license key string passed to CLicenseManager::InitLicense() is valid and not expired. A network connection is required for online license validation; offline activation requires a different workflow.
  • MRZ not detected on low-resolution or skewed images: The SDK expects a reasonably clear view of the MRZ zone. If recognition accuracy drops, pre-process the image with OpenCV (e.g., deskew, increase contrast) before passing it to CCaptureVisionRouter::Capture().
  • Missing model files cause silent failures: The build copies CharacterModel/, ParserResources/, and MRZ.json to the output directory. If any are missing, MRZ parsing returns zero results without an explicit error — verify these files exist next to the executable.

Source Code

https://github.com/yushulx/cmake-cpp-barcode-qrcode-mrz/tree/main/examples/mrz