How to Build a Cross-Platform C++ Passport MRZ Scanner for Windows and Linux

The Machine Readable Zone (MRZ) is a section on passports, IDs, visas, and other travel documents that encodes key personal information, such as the holder’s name, nationality, document number, date of birth, gender, and document expiration date, in a standardized format. In this article, you’ll learn how to use the Dynamsoft Capture Vision C++ SDK to develop a desktop MRZ scanner that accurately recognizes and extracts this information from various travel documents, providing a step-by-step guide to implementing a robust MRZ recognition solution.

MRZ Scanner Demo Video

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 the MRZ Data Parser Class

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 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 from a Live Camera Stream

  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

  • 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