跳转到主要内容
许多国家的官方旅行证件或身份证件都包含机器可读区 (MRZ) ,可确保更准确地处理文档数据。MRZ 包含 2 行或 3 行采用 OCR-B 字体的文本,其编写方式符合 ICAO Document 9303 (参见 ICAO 网站上的规范) 。 此场景用于在客户引导或身份验证流程中,从身份证件上的机器可读区提取数据。系统会识别文档图像中的 MRZ,并从中提取数据。提取出的数据包含多个字段,其中包括证件及其持有人的个人信息 (如证件类型、到期日期、持证人的名和姓等) 。您可以搜索这些字段、验证数据,并将其保存到外部文件中,以供进一步处理。 为了从 MRZ 中提取数据,通过扫描获得或以电子格式保存的图像文件通常需要经过多个处理阶段,每个阶段都有其各自的特点:
  1. 对扫描图像或照片进行预处理
您可以扫描带有 MRZ 的身份证件资料页,或为其拍照。使用移动设备数码相机拍摄的照片可能分辨率较低、质量较差。此外,图像在识别前可能还需要进行一些预处理。
  1. 从 MRZ 中提取数据
每幅图像最多只能捕获一个 MRZ。系统将识别并解析 2 行或 3 行中的每一行文本,以提取数据字段。部分字段以及整个 MRZ 都带有校验和,可帮助您验证数据。
  1. 导出到外部文件
您还可以将提取的数据保存为外部格式:支持 XML 和 JSON。 下面描述的过程已在适用于 Linux 和 Windows 的 MRZExtraction 代码示例中实现。

场景实现

本主题提供的代码示例仅适用于 Windows。
下面将详细介绍在此场景中使用 ABBYY FineReader Engine 12 的推荐方法。该方法采用了最适合此场景的处理设置。
要开始使用 ABBYY FineReader Engine,您需要创建 Engine 对象。Engine 对象是 ABBYY FineReader Engine 对象层次结构中的顶层对象,提供各种全局设置、一些处理方法,以及用于创建其他对象的方法。要创建 Engine 对象,可以使用 InitializeEngine 函数。另请参阅加载 Engine 对象的其他方法 (Win) 。

C#

public class EngineLoader : IDisposable
{
    public EngineLoader()
    {
        // 用 FREngine.dll 的完整路径、您的 Customer Project ID
        // 以及(如适用)Online License 令牌文件的路径和 Online License 密码初始化这些变量
        string enginePath = "";
        string customerProjectId = "";
        string licensePath = "";
        string licensePassword = "";
        // 加载 FREngine.dll 库
        dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
           
        try
        {
            if (dllHandle == IntPtr.Zero)
            {
                throw new Exception("Can't load " + enginePath);
            }
            IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine");
            if (initializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find InitializeEngine function");
            }
            IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
            if (deinitializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DeinitializeEngine function");
            }
            IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
            if (dllCanUnloadNowPtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DllCanUnloadNow function");
            }
            // 将指针转换为委托
            initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
                initializeEnginePtr, typeof(InitializeEngine));
            deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
                deinitializeEnginePtr, typeof(DeinitializeEngine));
            dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
                dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
            // 调用 InitializeEngine 函数
            // 并传入 Online License 文件路径和 Online License 密码
            int hresult = initializeEngine(customerProjectId, licensePath, licensePassword, 
                "", "", false, ref engine);
            Marshal.ThrowExceptionForHR(hresult);
        }
        catch (Exception)
        {
            // 释放 FREngine.dll 库
            engine = null;
            // 在调用 FreeLibrary 之前删除所有对象
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            FreeLibrary(dllHandle);
            dllHandle = IntPtr.Zero;
            initializeEngine = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            throw;
        }
    }
    // Kernel32.dll 函数
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // FREngine.dll 函数
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine(string customerProjectId, string licensePath, 
        string licensePassword, string tempFolder, string dataFolder, bool isSharedCPUCoresMode, 
        ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // 私有变量
    private FREngine.IEngine engine = null;
    // FREngine.dll 的 Handle
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}
您可以使用 Engine 对象的 LoadPredefinedProfile 方法,加载适用于此场景的处理设置。此方法使用设置配置文件的名称作为输入参数。更多信息,请参阅使用配置文件此场景的设置可通过预定义配置文件 MachineReadableZone 获取:
  • 启用对图像中所有文本的检测和提取 (不检测图片、矢量图形和表格) 。
  • 自动执行分辨率和几何校正。

C#

// 加载预定义配置文件
engine.LoadPredefinedProfile("MachineReadableZone");
如果您想更改处理设置,请使用相应的参数对象。更多信息,请参阅下方的特定任务的附加优化只有当您的 ABBYY FineReader Engine 许可证支持 MRZCapture 模块时,才能进行 MRZ 捕获。
要将图像加载到 FineReader Engine,您可以使用以下对象的方法:
Linux 和 Windows 用户可在使用 ABBYY FineReader Engine 进行并行处理中了解这两种方法各自的优缺点。当前主题重点介绍 FRDocument。
要将图像加载到 FRDocument 对象,请执行以下任一操作:所有这些方法都将 PrepareImageMode 对象作为输入参数,它可用于指定不同的图像预处理参数。调用 IEngine::CreatePrepareImageMode 方法创建此对象,然后根据需要更改其属性,并将其传递给图像打开方法。

C#

// 创建文档
FREngine.IFRDocument frDoc = engine.CreateFRDocument();
// 将图像文件添加到文档
document.AddImageFile( imagePath, null, null );
要从 MRZ 提取数据:
  1. [可选] 使用 Engine 对象的 CreateMrzProcessingParams 方法创建 MrzProcessingParams 对象。将其属性设置为所需的值。
  2. 调用 FRPage 对象的 ExtractMrz 方法,并将上一步中配置好的 MrzProcessingParams 对象作为输入参数传入;如果使用默认的 MRZ 捕获设置,只需传递 NULL。您将收到一个 MrzData 对象,其中包含从捕获的 MRZ 中解析出的信息。

C#

// 提取 MRZ
FREngine.IFRPage page = document.Pages.Item(0);
FREngine.IMrzData mrzData = page.ExtractMrz( null );
MrzData 对象包含从 MRZ 中提取的所有数据。您可以使用 GetLine 方法访问机读文本行,并使用 GetFieldGetFieldByType 方法遍历字段。可提取以下类型的字段:
  • 文档类型
  • 文档子类型
  • 签发国家
  • 姓氏
  • 文档编号
  • 国籍
  • 出生日期
  • 性别
  • 到期日期
  • 个人编号
  • 可选数据第 1 行
  • 可选数据第 2 行
MrzField 对象提供有关提取字段的完整信息。使用其 Text 属性获取字段值,使用 Region 属性获取字段在图像上的位置。要验证数据,请使用 MrzDataMrzField 对象的 Checksum、HasChecksum 和 IsChecksumVerified 属性提供的校验和。并非所有字段类型都支持校验和;提供 HasChecksum 属性,是为了让您在尝试获取校验和之前先检查其值。您可以使用 MrzField 对象的 InsertRemove 方法,手动编辑字段的识别文本。

C#

// 验证文档编号的校验和
FREngine.IMrzField documentNumberField = mrzData.GetFieldByType(FREngine.MrzFieldTypeEnum.MFT_DocumentNumber);
bool isNumberVerified = documentNumberField.IsChecksumVerified;
提取的数据可以保存为 XML 或 JSON 文件。要使用默认参数导出数据,请调用 MrzData 对象的 ExportToFile 方法,并将文件路径作为输入参数传入。要使用用户自定义参数导出数据,请调用 MrzData 对象的 ExportToFileEx 方法,并将指向 MrzJsonExportParams 对象或 MrzXmlExportParams 对象的指针作为输入参数传入。C#
// 保存为 XML 格式
mrzData.ExportToFile("C:\\ExtractedData.xml", FREngine.MrzExportFormatEnum.MEF_Xml);
完成 ABBYY FineReader Engine 的使用后,需要卸载 Engine 对象。为此,请使用导出的 DeinitializeEngine 函数。

C#

public class EngineLoader : IDisposable
{
    // 卸载 FineReader Engine
    public void Dispose()
    {
        if (engine == null)
        {
            // Engine 未加载
            return;
        }
        engine = null;
        // 在调用 FreeLibrary 之前删除所有对象
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        int hresult = deinitializeEngine();
 
        hresult = dllCanUnloadNow();
        if (hresult == 0)
        {
            FreeLibrary(dllHandle);
        }
        dllHandle = IntPtr.Zero;
        initializeEngine = null;
        deinitializeEngine = null;
        dllCanUnloadNow = null;
        // 清理完成后抛出异常
        Marshal.ThrowExceptionForHR(hresult);
    }
    // Kernel32.dll 函数
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // FREngine.dll 函数
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine( string customerProjectId, string LicensePath, string LicensePassword, , , , ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // 私有变量
    private FREngine.IEngine engine = null;
    // FREngine.dll 的Handle
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}

所需资源

您可以使用 FREngineDistribution.csv 文件,自动生成应用程序运行所需文件的列表。对于此场景下的处理,请在第 5 列 (RequiredByModule) 中选择以下值: Core Core.Resources Opening Opening, Processing Processing Processing.OCR Processing.OCR, Processing.ICR Processing.OCR.NaturalLanguages Processing.OCR.NaturalLanguages, Processing.ICR.NaturalLanguages Export Export, Processing 如果您修改了标准场景,请相应调整所需模块。您还需要指定应用程序所使用的界面语言、识别语言以及其他附加功能 (例如,如果需要打开 PDF 文件,则需使用 Opening.PDF) 。更多详情,请参阅 Working with the FREngineDistribution.csv File

其他优化

以下是帮助文件中的相关章节,您可以在其中找到有关为各个处理阶段设置参数的更多信息:

另请参阅

基本使用场景的实现