如何使用 Python 构建护照扫描仪,支持人像识别、MRZ 编码和文档检测

在本教程中,您将学习如何构建一个专业的桌面应用程序,用于读取护照和身份证上的机读区 (MRZ),并具备人像和文档识别功能。完成本指南后,您将拥有一个使用 PySide6 构建、并由 Dynamsoft Capture Vision SDK 提供支持的全功能 GUI 应用程序。

演示视频:具备人脸、磁条识别和文件检测功能的护照扫描仪

你将建造什么

一款跨平台桌面护照扫描仪,功能包括:

  • 从护照和身份证件(TD1、TD2、TD3格式)中实时识别MRZ
  • 旅行证件上的人脸/肖像检测和定位
  • 多种输入方式:图像文件、文件夹、摄像头视频流、拖放、剪贴板粘贴
  • 视觉叠加层显示文档边界、MRZ 位置和检测到的肖像
  • 解析后的数据展示,并结构化提取个人信息
  • 导出标准化文档图像的功能

为什么选择 Dynamsoft Capture Vision SDK?

Dynamsoft Capture Vision SDK 为文档处理提供了一套全面的解决方案,具有以下主要优势:

  • 一体化文档处理:将文档检测、MRZ识别和人像提取集成于单个SDK中
  • 高精度:先进的OCR引擎针对机器可读区域进行了优化
  • 中间结果访问:对处理流程的精细控制
  • 跨平台支持:兼容 Windows、Linux 和 macOS
  • 生产就绪:深受企业信赖的身份验证系统

先决条件

开始之前,请确保您已准备好:

  • 已安装 Python 3.9 或更高版本
  • 网络摄像头(可选,用于摄像头拍摄功能)
  • 用于测试的护照或身份证样本图像

获取试用许可证

Dynamsoft 提供免费试用许可证供评估:

  1. 访问Dynamsoft 许可证门户
  2. 选择“Capture Vision Suite (DCV)”作为产品
  3. 完成注册即可获得您的许可证密钥
  4. 试用许可证有效期为 30 天,可访问所有功能。

第一步:项目设置和依赖项

创建项目目录并安装所需的软件包:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#336666">mkdir </span>mrz_scanner_gui
<span style="color:#336666">cd </span>mrz_scanner_gui
python <span style="color:#330099"><strong>-m</strong></span> venv venv
<span style="color:#336666">source </span>venv/bin/activate  <span style="color:#0099ff"><em># On Windows: venv\Scripts\activate</em></span>
</code></span></span></span>

安装依赖项:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code>pip <span style="color:#336666">install </span>dynamsoft-capture-vision-bundle opencv-python PySide6
</code></span></span></span>

创建requirements.txt依赖管理文件:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code>dynamsoft-capture-vision-bundle
PySide6
opencv-python
</code></span></span></span>

步骤 2:了解 SDK 架构

Dynamsoft Capture Vision SDK 采用基于流水线的架构:

  1. CaptureVisionRouter:协调图像处理工作流程
  2. IntermediateResultManager:提供对中间处理阶段的访问
  3. IntermediateResultReceiver:用于捕获中间结果(例如倾斜校正后的图像和检测到的文本行)的回调接口
  4. 身份处理器:专门用于身份证明文件上人像区域检测的处理器

步骤三:创建数据模型

首先,定义用于存储 MRZ 结果的数据结构:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>from</strong></span> dataclasses <span style="color:#006699"><strong>import</strong></span> dataclass
<span style="color:#006699"><strong>from</strong></span> typing <span style="color:#006699"><strong>import</strong></span> List, Optional

<span style="color:#9999ff">@dataclass</span>
<span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>MRZResult</strong></span>:
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Stores parsed MRZ result data.</span><span style="color:#cc3300">"""</span>
    raw_lines: List[<span style="color:#336666">str</span>]
    doc_type: <span style="color:#336666">str</span>
    doc_id: <span style="color:#336666">str</span>
    surname: <span style="color:#336666">str</span>
    given_name: <span style="color:#336666">str</span>
    nationality: <span style="color:#336666">str</span>
    issuer: <span style="color:#336666">str</span>
    gender: <span style="color:#336666">str</span>
    date_of_birth: <span style="color:#336666">str</span>
    date_of_expiry: <span style="color:#336666">str</span>
    is_passport: <span style="color:#336666">bool</span>
    mrz_locations: List[<span style="color:#cc3300">'</span><span style="color:#cc3300">Quadrilateral</span><span style="color:#cc3300">'</span>]
    portrait_zone: Optional[<span style="color:#cc3300">'</span><span style="color:#cc3300">Quadrilateral</span><span style="color:#cc3300">'</span>] <span style="color:#555555">=</span> <span style="color:#336666">None</span>
</code></span></span></span>

该数据类捕获 MRZ 中的所有基本信息:原始文本行、解析的个人数据、文档位置和人像区域坐标。

步骤 4:实现人像检测及其中间结果

人像检测需要访问中间处理结果。创建一个接收器类:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>from</strong></span> dynamsoft_capture_vision_bundle <span style="color:#006699"><strong>import</strong></span> <span style="color:#555555">*</span>

<span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>:
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Container for intermediate results needed for portrait extraction.</span><span style="color:#cc3300">"""</span>
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">__init__</span>(self):
        self.deskewed_image_unit <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.localized_text_lines_unit <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.scaled_colour_img_unit <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.detected_quads_unit <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.recognized_text_lines_unit <span style="color:#555555">=</span> <span style="color:#336666">None</span>

<span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>MyIntermediateResultReceiver</strong></span>(IntermediateResultReceiver):
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Captures intermediate processing stages for portrait detection.</span><span style="color:#cc3300">"""</span>
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">__init__</span>(self, cvr: CaptureVisionRouter):
        <span style="color:#cc00ff">super</span>().<span style="color:#cc00ff">__init__</span>()
        self.cvr <span style="color:#555555">=</span> cvr
        self.unit_groups: Dict[<span style="color:#336666">str</span>, NeededResultUnit] <span style="color:#555555">=</span> {}
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">on_deskewed_image_received</span>(self, result, info):
        <span style="color:#006699"><strong>if</strong></span> info.is_section_level_result:
            <span style="color:#336666">id</span> <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
            <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(<span style="color:#336666">id</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
                self.unit_groups[<span style="color:#336666">id</span>] <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>()
            self.unit_groups[<span style="color:#336666">id</span>].deskewed_image_unit <span style="color:#555555">=</span> result
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">on_scaled_colour_image_unit_received</span>(self, result, info):
        <span style="color:#336666">id</span> <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
        <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(<span style="color:#336666">id</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
            self.unit_groups[<span style="color:#336666">id</span>] <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>()
        self.unit_groups[<span style="color:#336666">id</span>].scaled_colour_img_unit <span style="color:#555555">=</span> result
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">on_localized_text_lines_received</span>(self, result, info):
        <span style="color:#006699"><strong>if</strong></span> info.is_section_level_result:
            <span style="color:#336666">id</span> <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
            <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(<span style="color:#336666">id</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
                self.unit_groups[<span style="color:#336666">id</span>] <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>()
            self.unit_groups[<span style="color:#336666">id</span>].localized_text_lines_unit <span style="color:#555555">=</span> result
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">on_recognized_text_lines_received</span>(self, result, info):
        <span style="color:#006699"><strong>if</strong></span> info.is_section_level_result:
            <span style="color:#336666">id</span> <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
            <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(<span style="color:#336666">id</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
                self.unit_groups[<span style="color:#336666">id</span>] <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>()
            self.unit_groups[<span style="color:#336666">id</span>].recognized_text_lines_unit <span style="color:#555555">=</span> result
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">on_detected_quads_received</span>(self, result, info):
        <span style="color:#006699"><strong>if</strong></span> info.is_section_level_result:
            <span style="color:#336666">id</span> <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
            <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(<span style="color:#336666">id</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
                self.unit_groups[<span style="color:#336666">id</span>] <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>NeededResultUnit</strong></span>()
            self.unit_groups[<span style="color:#336666">id</span>].detected_quads_unit <span style="color:#555555">=</span> result
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">get_portrait_zone</span>(self, hash_id: <span style="color:#336666">str</span>) <span style="color:#555555">-></span> Optional[<span style="color:#cc3300">'</span><span style="color:#cc3300">Quadrilateral</span><span style="color:#cc3300">'</span>]:
        <span style="color:#cc3300">"""</span><span style="color:#cc3300">Extract portrait zone using IdentityProcessor.</span><span style="color:#cc3300">"""</span>
        <span style="color:#006699"><strong>if</strong></span> self.unit_groups.<span style="color:#cc00ff">get</span>(hash_id) <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
            <span style="color:#006699"><strong>return</strong></span> <span style="color:#336666">None</span>
        
        id_processor <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>IdentityProcessor</strong></span>()
        units <span style="color:#555555">=</span> self.unit_groups[hash_id]
        
        ret, portrait_zone <span style="color:#555555">=</span> id_processor.<span style="color:#cc00ff">find_portrait_zone</span>(
            units.scaled_colour_img_unit,
            units.localized_text_lines_unit,
            units.recognized_text_lines_unit,
            units.detected_quads_unit,
            units.deskewed_image_unit
        )
        
        <span style="color:#006699"><strong>if</strong></span> ret <span style="color:#555555">!=</span> EnumErrorCode.EC_OK:
            <span style="color:#006699"><strong>return</strong></span> <span style="color:#336666">None</span>
        <span style="color:#006699"><strong>return</strong></span> portrait_zone
</code></span></span></span>

IdentityProcessor.find_portrait_zone()方法通过分析 MRZ 文本行和文档边界之间的空间关系来智能地定位人像区域——这是 Dynamsoft SDK 独有的强大功能。

步骤 5:解析 MRZ 结果

创建一个处理器,将 SDK 结果转换为您的数据模型:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>DCPResultProcessor</strong></span>:
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Converts ParsedResultItem into structured MRZ data.</span><span style="color:#cc3300">"""</span>
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">__init__</span>(self, item: ParsedResultItem):
        self.doc_type <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_code_type</span>()
        self.raw_text <span style="color:#555555">=</span> []
        self.doc_id <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.surname <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.given_name <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.nationality <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.issuer <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.gender <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.date_of_birth <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.date_of_expiry <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.is_passport <span style="color:#555555">=</span> <span style="color:#336666">False</span>
        
        <span style="color:#0099ff"><em># Handle passport-specific fields
</em></span>        <span style="color:#006699"><strong>if</strong></span> self.doc_type <span style="color:#555555">==</span> <span style="color:#cc3300">"</span><span style="color:#cc3300">MRTD_TD3_PASSPORT</span><span style="color:#cc3300">"</span>:
            <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">passportNumber</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
                self.doc_id <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">passportNumber</span><span style="color:#cc3300">"</span>)
            <span style="color:#006699"><strong>elif</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">documentNumber</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
                self.doc_id <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">documentNumber</span><span style="color:#cc3300">"</span>)
            self.is_passport <span style="color:#555555">=</span> <span style="color:#336666">True</span>
        
        <span style="color:#0099ff"><em># Extract raw MRZ lines
</em></span>        <span style="color:#006699"><strong>for</strong></span> i <span style="color:#000000"><strong>in</strong></span> <span style="color:#cc00ff">range</span>(<span style="color:#ff6600">1</span>, <span style="color:#ff6600">4</span>):
            line <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(f<span style="color:#cc3300">"</span><span style="color:#cc3300">line</span><span style="color:#aa0000">{</span>i<span style="color:#aa0000">}</span><span style="color:#cc3300">"</span>)
            <span style="color:#006699"><strong>if</strong></span> line <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
                <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_validation_status</span>(f<span style="color:#cc3300">"</span><span style="color:#cc3300">line</span><span style="color:#aa0000">{</span>i<span style="color:#aa0000">}</span><span style="color:#cc3300">"</span>) <span style="color:#555555">==</span> EnumValidationStatus.VS_FAILED:
                    line <span style="color:#555555">+=</span> <span style="color:#cc3300">"</span><span style="color:#cc3300"> [Validation Failed]</span><span style="color:#cc3300">"</span>
                self.raw_text.<span style="color:#cc00ff">append</span>(line)
        
        <span style="color:#0099ff"><em># Extract structured fields with validation checks
</em></span>        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">nationality</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.nationality <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">nationality</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">issuingState</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.issuer <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">issuingState</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">dateOfBirth</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.date_of_birth <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">dateOfBirth</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">dateOfExpiry</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.date_of_expiry <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">dateOfExpiry</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">sex</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.gender <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">sex</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">primaryIdentifier</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.surname <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">primaryIdentifier</span><span style="color:#cc3300">"</span>)
        <span style="color:#006699"><strong>if</strong></span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">secondaryIdentifier</span><span style="color:#cc3300">"</span>) <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
            self.given_name <span style="color:#555555">=</span> item.<span style="color:#cc00ff">get_field_value</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">secondaryIdentifier</span><span style="color:#cc3300">"</span>)
</code></span></span></span>

步骤 6:使用 PySide6 构建 GUI

创建采用双面板布局的主应用程序窗口:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>from</strong></span> PySide6.QtWidgets <span style="color:#006699"><strong>import</strong></span> (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLabel, QFileDialog, QTextEdit, QSplitter,
    QComboBox, QGroupBox, QListWidget, QMessageBox
)
<span style="color:#006699"><strong>from</strong></span> PySide6.QtCore <span style="color:#006699"><strong>import</strong></span> Qt, QThread, Signal
<span style="color:#006699"><strong>from</strong></span> PySide6.QtGui <span style="color:#006699"><strong>import</strong></span> QImage, QPixmap, QPainter, QPen, QColor, QFont

<span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>MRZScannerWindow</strong></span>(QMainWindow):
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Main application window for MRZ scanning.</span><span style="color:#cc3300">"""</span>
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">__init__</span>(self):
        <span style="color:#cc00ff">super</span>().<span style="color:#cc00ff">__init__</span>()
        self.<span style="color:#cc00ff">setWindowTitle</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">MRZ Scanner - Dynamsoft Capture Vision</span><span style="color:#cc3300">"</span>)
        self.<span style="color:#cc00ff">setMinimumSize</span>(<span style="color:#ff6600">1200</span>, <span style="color:#ff6600">800</span>)
        
        <span style="color:#0099ff"><em># Initialize SDK components
</em></span>        self.<span style="color:#cc00ff">_init_sdk</span>()
        
        <span style="color:#0099ff"><em># Setup UI
</em></span>        self.<span style="color:#cc00ff">_setup_ui</span>()
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_init_sdk</span>(self):
        <span style="color:#cc3300">"""</span><span style="color:#cc3300">Initialize Dynamsoft Capture Vision SDK.</span><span style="color:#cc3300">"""</span>
        <span style="color:#0099ff"><em># Initialize license - replace with your license key
</em></span>        error_code, error_message <span style="color:#555555">=</span> LicenseManager.<span style="color:#cc00ff">init_license</span>(
            <span style="color:#cc3300">"</span><span style="color:#cc3300">YOUR_LICENSE_KEY_HERE</span><span style="color:#cc3300">"</span>
        )
        
        <span style="color:#006699"><strong>if</strong></span> error_code <span style="color:#555555">!=</span> EnumErrorCode.EC_OK <span style="color:#000000"><strong>and</strong></span> error_code <span style="color:#555555">!=</span> EnumErrorCode.EC_LICENSE_WARNING:
            QMessageBox.<span style="color:#cc00ff">warning</span>(
                <span style="color:#336666">None</span>, 
                <span style="color:#cc3300">"</span><span style="color:#cc3300">License Error</span><span style="color:#cc3300">"</span>,
                f<span style="color:#cc3300">"</span><span style="color:#cc3300">License initialization failed: </span><span style="color:#aa0000">{</span>error_message<span style="color:#aa0000">}</span><span style="color:#cc3300">"</span>
            )
        
        <span style="color:#0099ff"><em># Create router and intermediate result manager
</em></span>        self.cvr <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>CaptureVisionRouter</strong></span>()
        self.irm <span style="color:#555555">=</span> self.cvr.<span style="color:#cc00ff">get_intermediate_result_manager</span>()
        
        <span style="color:#0099ff"><em># Create and register intermediate result receiver
</em></span>        self.irr <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>MyIntermediateResultReceiver</strong></span>(self.cvr)
        self.irm.<span style="color:#cc00ff">add_result_receiver</span>(self.irr)
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_setup_ui</span>(self):
        <span style="color:#cc3300">"""</span><span style="color:#cc3300">Setup the user interface.</span><span style="color:#cc3300">"""</span>
        central_widget <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QWidget</strong></span>()
        self.<span style="color:#cc00ff">setCentralWidget</span>(central_widget)
        
        main_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QHBoxLayout</strong></span>(central_widget)
        
        <span style="color:#0099ff"><em># Create splitter for resizable panels
</em></span>        splitter <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QSplitter</strong></span>(Qt.Horizontal)
        main_layout.<span style="color:#cc00ff">addWidget</span>(splitter)
        
        <span style="color:#0099ff"><em># Left panel - Image display and controls
</em></span>        left_panel <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QWidget</strong></span>()
        left_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(left_panel)
        
        <span style="color:#0099ff"><em># Input controls
</em></span>        input_group <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QGroupBox</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Input Source</span><span style="color:#cc3300">"</span>)
        input_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(input_group)
        
        self.source_combo <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QComboBox</strong></span>()
        self.source_combo.<span style="color:#cc00ff">addItems</span>([<span style="color:#cc3300">"</span><span style="color:#cc3300">Image File</span><span style="color:#cc3300">"</span>, <span style="color:#cc3300">"</span><span style="color:#cc3300">Image Folder</span><span style="color:#cc3300">"</span>, <span style="color:#cc3300">"</span><span style="color:#cc3300">Camera</span><span style="color:#cc3300">"</span>])
        input_layout.<span style="color:#cc00ff">addWidget</span>(self.source_combo)
        
        button_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QHBoxLayout</strong></span>()
        self.load_btn <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPushButton</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Load File/Folder</span><span style="color:#cc3300">"</span>)
        self.paste_btn <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPushButton</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Paste from Clipboard</span><span style="color:#cc3300">"</span>)
        self.export_btn <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPushButton</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Export Passport</span><span style="color:#cc3300">"</span>)
        button_layout.<span style="color:#cc00ff">addWidget</span>(self.load_btn)
        button_layout.<span style="color:#cc00ff">addWidget</span>(self.paste_btn)
        button_layout.<span style="color:#cc00ff">addWidget</span>(self.export_btn)
        input_layout.<span style="color:#cc00ff">addLayout</span>(button_layout)
        
        left_layout.<span style="color:#cc00ff">addWidget</span>(input_group)
        
        <span style="color:#0099ff"><em># Image display with overlay support
</em></span>        display_group <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QGroupBox</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Image / Camera View</span><span style="color:#cc3300">"</span>)
        display_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(display_group)
        
        self.image_display <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>ImageDisplayWidget</strong></span>()
        display_layout.<span style="color:#cc00ff">addWidget</span>(self.image_display)
        
        left_layout.<span style="color:#cc00ff">addWidget</span>(display_group, <span style="color:#ff6600">1</span>)
        
        <span style="color:#0099ff"><em># Right panel - Results display
</em></span>        right_panel <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QWidget</strong></span>()
        right_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(right_panel)
        
        <span style="color:#0099ff"><em># Raw MRZ text
</em></span>        raw_group <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QGroupBox</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">MRZ Raw Text</span><span style="color:#cc3300">"</span>)
        raw_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(raw_group)
        self.raw_text_edit <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QTextEdit</strong></span>()
        self.raw_text_edit.<span style="color:#cc00ff">setReadOnly</span>(<span style="color:#336666">True</span>)
        self.raw_text_edit.<span style="color:#cc00ff">setFont</span>(<span style="color:#00aa88"><strong>QFont</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Courier New</span><span style="color:#cc3300">"</span>, <span style="color:#ff6600">10</span>))
        raw_layout.<span style="color:#cc00ff">addWidget</span>(self.raw_text_edit)
        right_layout.<span style="color:#cc00ff">addWidget</span>(raw_group)
        
        <span style="color:#0099ff"><em># Parsed results
</em></span>        parsed_group <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QGroupBox</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Parsed Results</span><span style="color:#cc3300">"</span>)
        parsed_layout <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QVBoxLayout</strong></span>(parsed_group)
        self.parsed_text_edit <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QTextEdit</strong></span>()
        self.parsed_text_edit.<span style="color:#cc00ff">setReadOnly</span>(<span style="color:#336666">True</span>)
        parsed_layout.<span style="color:#cc00ff">addWidget</span>(self.parsed_text_edit)
        right_layout.<span style="color:#cc00ff">addWidget</span>(parsed_group, <span style="color:#ff6600">1</span>)
        
        <span style="color:#0099ff"><em># Add panels to splitter
</em></span>        splitter.<span style="color:#cc00ff">addWidget</span>(left_panel)
        splitter.<span style="color:#cc00ff">addWidget</span>(right_panel)
        splitter.<span style="color:#cc00ff">setSizes</span>([<span style="color:#ff6600">800</span>, <span style="color:#ff6600">400</span>])
</code></span></span></span>

步骤 7:处理图像

实现核心图像处理逻辑:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_process_image_file</span>(self, file_path: <span style="color:#336666">str</span>):
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Process a single image file.</span><span style="color:#cc3300">"""</span>
    <span style="color:#006699"><strong>try</strong></span>:
        <span style="color:#0099ff"><em># Read image for display
</em></span>        image <span style="color:#555555">=</span> cv2.<span style="color:#cc00ff">imread</span>(file_path)
        <span style="color:#006699"><strong>if</strong></span> image <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
            self.<span style="color:#cc00ff">statusBar</span>().<span style="color:#cc00ff">showMessage</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Failed to load image</span><span style="color:#cc3300">"</span>)
            <span style="color:#006699"><strong>return</strong></span>
        
        <span style="color:#0099ff"><em># Process with SDK using "ReadPassportAndId" template
</em></span>        result <span style="color:#555555">=</span> self.cvr.<span style="color:#cc00ff">capture</span>(file_path, <span style="color:#cc3300">"</span><span style="color:#cc3300">ReadPassportAndId</span><span style="color:#cc3300">"</span>)
        self.current_captured_result <span style="color:#555555">=</span> result
        
        <span style="color:#006699"><strong>if</strong></span> result <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
            self.image_display.<span style="color:#cc00ff">set_image</span>(image, [])
            self.<span style="color:#cc00ff">_update_results_display</span>([])
            <span style="color:#006699"><strong>return</strong></span>
        
        <span style="color:#0099ff"><em># Extract MRZ results with portrait zones
</em></span>        mrz_results <span style="color:#555555">=</span> self.<span style="color:#cc00ff">_extract_mrz_results</span>(result)
        doc_quad <span style="color:#555555">=</span> self.<span style="color:#cc00ff">_get_document_quad</span>(result)
        
        <span style="color:#0099ff"><em># Update display
</em></span>        self.image_display.<span style="color:#cc00ff">set_image</span>(image, mrz_results, doc_quad)
        self.<span style="color:#cc00ff">_update_results_display</span>(mrz_results)
        
    <span style="color:#006699"><strong>except</strong></span> <span style="color:#336666">Exception</span> <span style="color:#006699"><strong>as</strong></span> e:
        self.<span style="color:#cc00ff">statusBar</span>().<span style="color:#cc00ff">showMessage</span>(f<span style="color:#cc3300">"</span><span style="color:#cc3300">Error: </span><span style="color:#aa0000">{</span><span style="color:#cc00ff">str</span>(e)<span style="color:#aa0000">}</span><span style="color:#cc3300">"</span>)

<span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_extract_mrz_results</span>(self, result: CapturedResult) <span style="color:#555555">-></span> List[MRZResult]:
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Extract MRZ results from CapturedResult.</span><span style="color:#cc3300">"""</span>
    mrz_results <span style="color:#555555">=</span> []
    
    parsed_result <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_parsed_result</span>()
    <span style="color:#006699"><strong>if</strong></span> parsed_result <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
        <span style="color:#006699"><strong>return</strong></span> mrz_results
    
    <span style="color:#0099ff"><em># Get MRZ text line locations
</em></span>    mrz_locations <span style="color:#555555">=</span> []
    line_result <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_recognized_text_lines_result</span>()
    <span style="color:#006699"><strong>if</strong></span> line_result <span style="color:#000000"><strong>is</strong></span> <span style="color:#000000"><strong>not</strong></span> <span style="color:#336666">None</span>:
        <span style="color:#006699"><strong>for</strong></span> item <span style="color:#000000"><strong>in</strong></span> line_result.<span style="color:#cc00ff">get_items</span>():
            mrz_locations.<span style="color:#cc00ff">append</span>(item.<span style="color:#cc00ff">get_location</span>())
    
    hash_id <span style="color:#555555">=</span> result.<span style="color:#cc00ff">get_original_image_hash_id</span>()
    
    <span style="color:#006699"><strong>for</strong></span> item <span style="color:#000000"><strong>in</strong></span> parsed_result.<span style="color:#cc00ff">get_items</span>():
        processor <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>DCPResultProcessor</strong></span>(item)
        
        <span style="color:#0099ff"><em># Get portrait zone for passports
</em></span>        portrait_zone <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        <span style="color:#006699"><strong>if</strong></span> processor.is_passport <span style="color:#000000"><strong>and</strong></span> self.irr:
            portrait_zone <span style="color:#555555">=</span> self.irr.<span style="color:#cc00ff">get_portrait_zone</span>(hash_id)
        
        mrz_result <span style="color:#555555">=</span> processor.<span style="color:#cc00ff">to_mrz_result</span>(portrait_zone, mrz_locations)
        mrz_results.<span style="color:#cc00ff">append</span>(mrz_result)
    
    <span style="color:#006699"><strong>return</strong></span> mrz_results
</code></span></span></span>

步骤 8:添加视觉叠加层

创建一个自定义小部件,用于显示带有 MRZ 和纵向叠加层的图像:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>class</strong></span> <span style="color:#00aa88"><strong>ImageDisplayWidget</strong></span>(QLabel):
    <span style="color:#cc3300">"""</span><span style="color:#cc3300">Custom widget with visual overlays for MRZ and portrait.</span><span style="color:#cc3300">"""</span>
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">__init__</span>(self, parent<span style="color:#555555">=</span><span style="color:#336666">None</span>):
        <span style="color:#cc00ff">super</span>().<span style="color:#cc00ff">__init__</span>(parent)
        self.<span style="color:#cc00ff">setAcceptDrops</span>(<span style="color:#336666">True</span>)
        self.current_image <span style="color:#555555">=</span> <span style="color:#336666">None</span>
        self.mrz_results <span style="color:#555555">=</span> []
        self.doc_quad <span style="color:#555555">=</span> <span style="color:#336666">None</span>
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">set_image</span>(self, image, mrz_results<span style="color:#555555">=</span><span style="color:#336666">None</span>, doc_quad<span style="color:#555555">=</span><span style="color:#336666">None</span>):
        self.current_image <span style="color:#555555">=</span> image
        self.mrz_results <span style="color:#555555">=</span> mrz_results <span style="color:#000000"><strong>or</strong></span> []
        self.doc_quad <span style="color:#555555">=</span> doc_quad
        self.<span style="color:#cc00ff">_update_display</span>()
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_update_display</span>(self):
        <span style="color:#006699"><strong>if</strong></span> self.current_image <span style="color:#000000"><strong>is</strong></span> <span style="color:#336666">None</span>:
            <span style="color:#006699"><strong>return</strong></span>
        
        <span style="color:#0099ff"><em># Convert OpenCV image to QPixmap
</em></span>        rgb_image <span style="color:#555555">=</span> cv2.<span style="color:#cc00ff">cvtColor</span>(self.current_image, cv2.COLOR_BGR2RGB)
        h, w, ch <span style="color:#555555">=</span> rgb_image.shape
        bytes_per_line <span style="color:#555555">=</span> ch <span style="color:#555555">*</span> w
        q_image <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QImage</strong></span>(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        pixmap <span style="color:#555555">=</span> QPixmap.<span style="color:#cc00ff">fromImage</span>(q_image)
        
        <span style="color:#0099ff"><em># Scale to fit widget
</em></span>        scaled_pixmap <span style="color:#555555">=</span> pixmap.<span style="color:#cc00ff">scaled</span>(self.<span style="color:#cc00ff">size</span>(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        
        <span style="color:#0099ff"><em># Calculate scale factor
</em></span>        self.scale_factor <span style="color:#555555">=</span> scaled_pixmap.<span style="color:#cc00ff">width</span>() <span style="color:#555555">/</span> w
        
        <span style="color:#0099ff"><em># Draw overlays
</em></span>        <span style="color:#006699"><strong>if</strong></span> self.mrz_results <span style="color:#000000"><strong>or</strong></span> self.doc_quad:
            painter <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPainter</strong></span>(scaled_pixmap)
            painter.<span style="color:#cc00ff">setRenderHint</span>(QPainter.Antialiasing)
            
            <span style="color:#0099ff"><em># Draw document boundary (blue)
</em></span>            <span style="color:#006699"><strong>if</strong></span> self.doc_quad:
                self.<span style="color:#cc00ff">_draw_quadrilateral</span>(painter, self.doc_quad, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">0</span>, <span style="color:#ff6600">0</span>, <span style="color:#ff6600">255</span>, <span style="color:#ff6600">200</span>), <span style="color:#cc3300">"</span><span style="color:#cc3300">Document</span><span style="color:#cc3300">"</span>)
            
            <span style="color:#006699"><strong>for</strong></span> result <span style="color:#000000"><strong>in</strong></span> self.mrz_results:
                <span style="color:#0099ff"><em># Draw MRZ locations (green)
</em></span>                <span style="color:#006699"><strong>for</strong></span> location <span style="color:#000000"><strong>in</strong></span> result.mrz_locations:
                    self.<span style="color:#cc00ff">_draw_quadrilateral</span>(painter, location, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">0</span>, <span style="color:#ff6600">255</span>, <span style="color:#ff6600">0</span>, <span style="color:#ff6600">200</span>), <span style="color:#cc3300">"</span><span style="color:#cc3300">MRZ</span><span style="color:#cc3300">"</span>)
                
                <span style="color:#0099ff"><em># Draw portrait zone (orange)
</em></span>                <span style="color:#006699"><strong>if</strong></span> result.portrait_zone:
                    self.<span style="color:#cc00ff">_draw_quadrilateral</span>(painter, result.portrait_zone, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">255</span>, <span style="color:#ff6600">165</span>, <span style="color:#ff6600">0</span>, <span style="color:#ff6600">200</span>), <span style="color:#cc3300">"</span><span style="color:#cc3300">Portrait</span><span style="color:#cc3300">"</span>)
            
            painter.<span style="color:#cc00ff">end</span>()
        
        self.<span style="color:#cc00ff">setPixmap</span>(scaled_pixmap)
    
    <span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">_draw_quadrilateral</span>(self, painter: QPainter, quad, color: QColor, label: <span style="color:#336666">str</span>):
        pen <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPen</strong></span>(color, <span style="color:#ff6600">3</span>)
        painter.<span style="color:#cc00ff">setPen</span>(pen)
        
        points <span style="color:#555555">=</span> quad.points
        <span style="color:#006699"><strong>if</strong></span> <span style="color:#cc00ff">len</span>(points) <span style="color:#555555">>=</span> <span style="color:#ff6600">4</span>:
            <span style="color:#0099ff"><em># Scale points to display coordinates
</em></span>            scaled_points <span style="color:#555555">=</span> []
            <span style="color:#006699"><strong>for</strong></span> p <span style="color:#000000"><strong>in</strong></span> points:
                x <span style="color:#555555">=</span> <span style="color:#cc00ff">int</span>(p.x <span style="color:#555555">*</span> self.scale_factor)
                y <span style="color:#555555">=</span> <span style="color:#cc00ff">int</span>(p.y <span style="color:#555555">*</span> self.scale_factor)
                scaled_points.<span style="color:#cc00ff">append</span>((x, y))
            
            <span style="color:#0099ff"><em># Draw quadrilateral
</em></span>            <span style="color:#006699"><strong>for</strong></span> i <span style="color:#000000"><strong>in</strong></span> <span style="color:#cc00ff">range</span>(<span style="color:#ff6600">4</span>):
                x1, y1 <span style="color:#555555">=</span> scaled_points[i]
                x2, y2 <span style="color:#555555">=</span> scaled_points[(i <span style="color:#555555">+</span> <span style="color:#ff6600">1</span>) <span style="color:#555555">%</span> <span style="color:#ff6600">4</span>]
                painter.<span style="color:#cc00ff">drawLine</span>(x1, y1, x2, y2)
            
            <span style="color:#0099ff"><em># Draw label
</em></span>            font <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QFont</strong></span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Arial</span><span style="color:#cc3300">"</span>, <span style="color:#ff6600">12</span>, QFont.Bold)
            painter.<span style="color:#cc00ff">setFont</span>(font)
            painter.<span style="color:#cc00ff">setPen</span>(<span style="color:#00aa88"><strong>QPen</strong></span>(color, <span style="color:#ff6600">2</span>))
            min_y <span style="color:#555555">=</span> <span style="color:#cc00ff">min</span>(p[<span style="color:#ff6600">1</span>] <span style="color:#006699"><strong>for</strong></span> p <span style="color:#000000"><strong>in</strong></span> scaled_points)
            min_x <span style="color:#555555">=</span> <span style="color:#cc00ff">min</span>(p[<span style="color:#ff6600">0</span>] <span style="color:#006699"><strong>for</strong></span> p <span style="color:#000000"><strong>in</strong></span> scaled_points)
            painter.<span style="color:#cc00ff">drawText</span>(min_x, <span style="color:#cc00ff">max</span>(<span style="color:#ff6600">0</span>, min_y <span style="color:#555555">-</span> <span style="color:#ff6600">5</span>), label)
</code></span></span></span>

步骤 9:运行应用程序

使用深色主题和入口点完成应用程序:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>def</strong></span> <span style="color:#cc00ff">main</span>():
    app <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QApplication</strong></span>(sys.argv)
    app.<span style="color:#cc00ff">setStyle</span>(<span style="color:#cc3300">"</span><span style="color:#cc3300">Fusion</span><span style="color:#cc3300">"</span>)
    
    <span style="color:#0099ff"><em># Apply dark theme
</em></span>    palette <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>QPalette</strong></span>()
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.Window, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">53</span>, <span style="color:#ff6600">53</span>, <span style="color:#ff6600">53</span>))
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.WindowText, Qt.white)
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.Base, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">35</span>, <span style="color:#ff6600">35</span>, <span style="color:#ff6600">35</span>))
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.Text, Qt.white)
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.Button, <span style="color:#00aa88"><strong>QColor</strong></span>(<span style="color:#ff6600">53</span>, <span style="color:#ff6600">53</span>, <span style="color:#ff6600">53</span>))
    palette.<span style="color:#cc00ff">setColor</span>(QPalette.ButtonText, Qt.white)
    app.<span style="color:#cc00ff">setPalette</span>(palette)
    
    window <span style="color:#555555">=</span> <span style="color:#00aa88"><strong>MRZScannerWindow</strong></span>()
    window.<span style="color:#cc00ff">show</span>()
    
    sys.<span style="color:#cc00ff">exit</span>(app.<span style="color:#cc00ff">exec</span>())

<span style="color:#006699"><strong>if</strong></span> __name__ <span style="color:#555555">==</span> <span style="color:#cc3300">"</span><span style="color:#cc3300">__main__</span><span style="color:#cc3300">"</span>:
    <span style="color:#cc00ff">main</span>()
</code></span></span></span>

测试应用程序

运行应用程序:

<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code>python mrz_scanner_gui.py
</code></span></span></span>

Python护照扫描仪,支持人像、MRZ和文档检测

源代码

https://github.com/yushulx/python-mrz-scanner-sdk/tree/main/examples/official/gui

更多推荐