Logo

dev-resources.site

for different kinds of informations.

Developing a Desktop MRZ Scanner for Passports, IDs, and Visas with Dynamsoft C++ Capture Vision SDK

Published at
9/6/2024
Categories
cpp
mrz
passport
ocr
Author
yushulx
Categories
4 categories in total
cpp
open
mrz
open
passport
open
ocr
open
Author
7 person written this
yushulx
open
Developing a Desktop MRZ Scanner for Passports, IDs, and Visas with Dynamsoft C++ Capture Vision SDK

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

Prerequisites

Setting Up a CMake Project for 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)
Enter fullscreen mode Exit fullscreen mode

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.

Implementing MRZ Recognition with Dynamsoft Capture Vision SDK

Setting Up MRZ Recognition Templates

To start implementing MRZ recognition, you'll first need to prepare a template file named MRZ.json. This template defines the settings for MRZ recognition, including parameters such as text length ranges, regular expression patterns, concatenation characters, and other configurations that guide the recognition process. Below is a sample MRZ.json file:

{
    "CaptureVisionTemplates": [
        {
            "Name": "default",
            "OutputOriginalImage": 0,
            "ImageROIProcessingNameArray": [
                "roi-mrz"
            ],
            "SemanticProcessingNameArray": [
                "sp-mrz"
            ],
            "Timeout": 1000000
        }
    ],
    "TargetROIDefOptions": [
        {
            "Name": "roi-mrz",
            "TaskSettingNameArray": [
                "task-mrz"
            ]
        }
    ],
    "TextLineSpecificationOptions": [
        {
            "Name": "tls-mrz-passport",
            "BaseTextLineSpecificationName": "tls-base",
            "StringLengthRange": [
                44,
                44
            ],
            "OutputResults": 1,
            "ExpectedGroupsCount": 1,
            "ConcatResults": 1,
            "ConcatSeparator": "",
            "SubGroups": [
                {
                    "StringRegExPattern": "(P[A-Z<][A-Z<]{3}[A-Z<]{39}){(44)}",
                    "StringLengthRange": [
                        44,
                        44
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{14}[0-9<][0-9]){(44)}",
                    "StringLengthRange": [
                        44,
                        44
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                }
            ]
        },
        {
            "Name": "tls-mrz-visa-td3",
            "BaseTextLineSpecificationName": "tls-base",
            "StringLengthRange": [
                44,
                44
            ],
            "OutputResults": 1,
            "ExpectedGroupsCount": 1,
            "ConcatResults": 1,
            "ConcatSeparator": "",
            "SubGroups": [
                {
                    "StringRegExPattern": "(V[A-Z<][A-Z<]{3}[A-Z<]{39}){(44)}",
                    "StringLengthRange": [
                        44,
                        44
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{14}[A-Z0-9<]{2}){(44)}",
                    "StringLengthRange": [
                        44,
                        44
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                }
            ]
        },
        {
            "Name": "tls-mrz-visa-td2",
            "BaseTextLineSpecificationName": "tls-base",
            "StringLengthRange": [
                36,
                36
            ],
            "OutputResults": 1,
            "ExpectedGroupsCount": 1,
            "ConcatResults": 1,
            "ConcatSeparator": "",
            "SubGroups": [
                {
                    "StringRegExPattern": "(V[A-Z<][A-Z<]{3}[A-Z<]{31}){(36)}",
                    "StringLengthRange": [
                        36,
                        36
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{8}){(36)}",
                    "StringLengthRange": [
                        36,
                        36
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                }
            ]
        },
        {
            "Name": "tls-mrz-id-td2",
            "BaseTextLineSpecificationName": "tls-base",
            "StringLengthRange": [
                36,
                36
            ],
            "OutputResults": 1,
            "ExpectedGroupsCount": 1,
            "ConcatResults": 1,
            "ConcatSeparator": "",
            "SubGroups": [
                {
                    "StringRegExPattern": "([ACI][A-Z<][A-Z<]{3}[A-Z<]{31}){(36)}",
                    "StringLengthRange": [
                        36,
                        36
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([A-Z0-9<]{9}[0-9][A-Z<]{3}[0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9<]{8}){(36)}",
                    "StringLengthRange": [
                        36,
                        36
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                }
            ]
        },
        {
            "Name": "tls-mrz-id-td1",
            "BaseTextLineSpecificationName": "tls-base",
            "StringLengthRange": [
                30,
                30
            ],
            "OutputResults": 1,
            "ExpectedGroupsCount": 1,
            "ConcatResults": 1,
            "ConcatSeparator": "",
            "SubGroups": [
                {
                    "StringRegExPattern": "([ACI][A-Z<][A-Z<]{3}[A-Z0-9<]{9}[0-9][A-Z0-9<]{15}){(30)}",
                    "StringLengthRange": [
                        30,
                        30
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([0-9]{2}[(01-12)][(01-31)][0-9][MF<][0-9]{2}[(01-12)][(01-31)][0-9][A-Z<]{3}[A-Z0-9<]{11}[0-9]){(30)}",
                    "StringLengthRange": [
                        30,
                        30
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                },
                {
                    "StringRegExPattern": "([A-Z<]{30}){(30)}",
                    "StringLengthRange": [
                        30,
                        30
                    ],
                    "BaseTextLineSpecificationName": "tls-base"
                }
            ]
        },
        {
            "Name": "tls-base",
            "CharacterModelName": "MRZ",
            "CharHeightRange": [
                5,
                1000,
                1
            ],
            "BinarizationModes": [
                {
                    "BlockSizeX": 30,
                    "BlockSizeY": 30,
                    "Mode": "BM_LOCAL_BLOCK",
                    "EnableFillBinaryVacancy": 0,
                    "ThresholdCompensation": 15
                }
            ],
            "ConfusableCharactersCorrection": {
                "ConfusableCharacters": [
                    [
                        "0",
                        "O"
                    ],
                    [
                        "1",
                        "I"
                    ],
                    [
                        "5",
                        "S"
                    ]
                ],
                "FontNameArray": [
                    "OCR_B"
                ]
            }
        }
    ],
    "LabelRecognizerTaskSettingOptions": [
        {
            "Name": "task-mrz",
            "ConfusableCharactersPath": "ConfusableChars.data",
            "TextLineSpecificationNameArray": [
                "tls-mrz-passport",
                "tls-mrz-visa-td3",
                "tls-mrz-id-td1",
                "tls-mrz-id-td2",
                "tls-mrz-visa-td2"
            ],
            "SectionImageParameterArray": [
                {
                    "Section": "ST_REGION_PREDETECTION",
                    "ImageParameterName": "ip-mrz"
                },
                {
                    "Section": "ST_TEXT_LINE_LOCALIZATION",
                    "ImageParameterName": "ip-mrz"
                },
                {
                    "Section": "ST_TEXT_LINE_RECOGNITION",
                    "ImageParameterName": "ip-mrz"
                }
            ]
        }
    ],
    "CharacterModelOptions": [
        {
            "DirectoryPath": "",
            "Name": "MRZ"
        }
    ],
    "ImageParameterOptions": [
        {
            "Name": "ip-mrz",
            "TextureDetectionModes": [
                {
                    "Mode": "TDM_GENERAL_WIDTH_CONCENTRATION",
                    "Sensitivity": 8
                }
            ],
            "BinarizationModes": [
                {
                    "EnableFillBinaryVacancy": 0,
                    "ThresholdCompensation": 21,
                    "Mode": "BM_LOCAL_BLOCK"
                }
            ],
            "TextDetectionMode": {
                "Mode": "TTDM_LINE",
                "CharHeightRange": [
                    5,
                    1000,
                    1
                ],
                "Direction": "HORIZONTAL",
                "Sensitivity": 7
            }
        }
    ],
    "SemanticProcessingOptions": [
        {
            "Name": "sp-mrz",
            "ReferenceObjectFilter": {
                "ReferenceTargetROIDefNameArray": [
                    "roi-mrz"
                ]
            },
            "TaskSettingNameArray": [
                "dcp-mrz"
            ]
        }
    ],
    "CodeParserTaskSettingOptions": [
        {
            "Name": "dcp-mrz",
            "CodeSpecifications": [
                "MRTD_TD3_PASSPORT",
                "MRTD_TD2_VISA",
                "MRTD_TD3_VISA",
                "MRTD_TD1_ID",
                "MRTD_TD2_ID"
            ]
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

The above template supports multiple MRZ document types, including MRTD_TD3_PASSPORT, MRTD_TD2_VISA, MRTD_TD3_VISA, MRTD_TD1_ID, and MRTD_TD2_ID.

Implementing MRZ Data Parsing

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;
  }
};
Enter fullscreen mode Exit fullscreen mode

Example 1: Recognize MRZ from 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=mrz
        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;
    

Example 2: Recognize MRZ from 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=mrz
      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;
    

Building and Running the MRZ Scanner on Windows and Linux

mkdir build
cd build
cmake ..
cmake --build .
Enter fullscreen mode Exit fullscreen mode

desktop mrz recognition in C++

Source Code

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

ocr Article's
30 articles in total
Favicon
Quick and Dirty Document Analysis: Combining GOT-OCR and LLama in Python
Favicon
Pixtral Large: Revolutionizing Multimodal AI with Superior Performance
Favicon
Say goodbye to tedious data entry! The future of OCR is here, and it’s smarter than ever!
Favicon
Unlocking Text from Embedded-Font PDFs: A pytesseract OCR Tutorial
Favicon
Streamlining Healthcare Paperwork with AI-Powered OCR
Favicon
NoisOCR: A Python Library for Simulating Post-OCR Noisy Texts
Favicon
AI-driven OCR Revolutionizes Intelligent Layout Analysis with 24+ Labels
Favicon
πŸ“„ OCR Reader, πŸ” Analyzer, and πŸ’¬ Chat Assistant using πŸ”Ž Zerox, 🧠 GPT-4o, powered by πŸš€ AI/ML API
Favicon
Qu'est-ce qu'OCRULUS ?
Favicon
Practical Approaches to Key Information Extraction (Part 1)
Favicon
OCR Data Extraction Software: Exploring the Latest Innovations in 2024
Favicon
Developing a Desktop MRZ Scanner for Passports, IDs, and Visas with Dynamsoft C++ Capture Vision SDK
Favicon
Streamlining Operations with Cloud OCR: Leading Use Cases in Business Automation
Favicon
Implementing Efficient Mobile OCR: A Developer’s Guide
Favicon
Automating VIN Code Recognition with OCR Technology
Favicon
OCR Solutions Uncovered: How to Choose the Best for Different Use Cases
Favicon
Steps to Develop an Angular Passport MRZ Reader & Scanner
Favicon
Mastering Text Extraction from Multi-Page PDFs Using OCR API: A Step-by-Step Guide
Favicon
Efficient Driver's License Recognition with OCR API: Step-by-Step Tutorial
Favicon
How to improve OCR accuracy ? | my 5-year experience
Favicon
I ask for help
Favicon
Mastering Parcel Scanning with C++: Barcode and OCR Text Extraction
Favicon
Difference Between OCR and ICR | A Complete Guide
Favicon
dvantages of iCustoms OCR: AI Precision for Streamlined Customs Processes
Favicon
5 C# OCR Libraries commonly Used by Developers
Favicon
Understand How to Transform Images into Text Easily
Favicon
OCR with tesseract, python and pytesseract
Favicon
Build a serverless EU-Driving Licences OCR with Amazon Textract on AWS
Favicon
Secure OCR and Biometrics Integration in Angular
Favicon
Removendo Dados Sensiveis de Images

Featured ones: