如何在 Python 中实现机器可读区域 (MRZ) 识别?
阅读:44
点赞:0
机器可读区域 (MRZ)是现代护照、签证和身份证采用的一项重要功能。它包含有关证件持有人的基本信息,例如其姓名、性别、国家代码和证件号码。MRZ 识别在边境管制、机场安全和酒店入住过程中起着关键作用。在本教程中,我们将演示如何利用 Dynamsoft Capture Vision SDK在Windows、Linux和macOS平台上实现 MRZ 识别。本指南将提供分步方法来利用 SDK 的强大功能,使跨平台 MRZ 检测无缝且高效。
macOS 上的 Python 机读区识别演示
先决条件
-
Dynamsoft Capture Vision 试用许可证:获取Dynamsoft Capture Vision SDK 的30 天试用许可证密钥。
-
Python 包:使用以下命令安装所需的 Python 包:
pip install dynamsoft-capture-vision-bundle opencv-python
这些包裹是做什么用的?
dynamsoft-capture-vision-bundle
是适用于 Python 的 Dynamsoft Capture Vision SDK。opencv-python
捕获相机帧并显示处理后的图像结果。
开始使用 Dynamsoft Python Capture Vision 示例
官方 MRZ 扫描仪示例演示了如何在短时间内使用 Dynamsoft Capture Vision SDK 创建一个简单的基于 Python 的 MRZ 阅读器。
我们来看一下源代码并分析一下其功能:
import sys
from dynamsoft_capture_vision_bundle import *
import os
class MRZResult:
def __init__(self, item: ParsedResultItem):
self.doc_type = item.get_code_type()
self.raw_text=[]
self.doc_id = None
self.surname = None
self.given_name = None
self.nationality = None
self.issuer = None
self.gender = None
self.date_of_birth = None
self.date_of_expiry = None
if self.doc_type == "MRTD_TD3_PASSPORT":
if item.get_field_value("passportNumber") != None and item.get_field_validation_status("passportNumber") != EnumValidationStatus.VS_FAILED:
self.doc_id = item.get_field_value("passportNumber")
elif item.get_field_value("documentNumber") != None and item.get_field_validation_status("documentNumber") != EnumValidationStatus.VS_FAILED:
self.doc_id = item.get_field_value("documentNumber")
line = item.get_field_value("line1")
if line is not None:
if item.get_field_validation_status("line1") == EnumValidationStatus.VS_FAILED:
line += ", Validation Failed"
self.raw_text.append(line)
line = item.get_field_value("line2")
if line is not None:
if item.get_field_validation_status("line2") == EnumValidationStatus.VS_FAILED:
line += ", Validation Failed"
self.raw_text.append(line)
line = item.get_field_value("line3")
if line is not None:
if item.get_field_validation_status("line3") == EnumValidationStatus.VS_FAILED:
line += ", Validation Failed"
self.raw_text.append(line)
if item.get_field_value("nationality") != None and item.get_field_validation_status("nationality") != EnumValidationStatus.VS_FAILED:
self.nationality = item.get_field_value("nationality")
if item.get_field_value("issuingState") != None and item.get_field_validation_status("issuingState") != EnumValidationStatus.VS_FAILED:
self.issuer = item.get_field_value("issuingState")
if item.get_field_value("dateOfBirth") != None and item.get_field_validation_status("dateOfBirth") != EnumValidationStatus.VS_FAILED:
self.date_of_birth = item.get_field_value("dateOfBirth")
if item.get_field_value("dateOfExpiry") != None and item.get_field_validation_status("dateOfExpiry") != EnumValidationStatus.VS_FAILED:
self.date_of_expiry = item.get_field_value("dateOfExpiry")
if item.get_field_value("sex") != None and item.get_field_validation_status("sex") != EnumValidationStatus.VS_FAILED:
self.gender = item.get_field_value("sex")
if item.get_field_value("primaryIdentifier") != None and item.get_field_validation_status("primaryIdentifier") != EnumValidationStatus.VS_FAILED:
self.surname = item.get_field_value("primaryIdentifier")
if item.get_field_value("secondaryIdentifier") != None and item.get_field_validation_status("secondaryIdentifier") != EnumValidationStatus.VS_FAILED:
self.given_name = item.get_field_value("secondaryIdentifier")
def to_string(self):
msg = (f"Raw Text:\n")
for index, line in enumerate(self.raw_text):
msg += (f"\tLine {index + 1}: {line}\n")
msg+=(f"Parsed Information:\n"
f"\tDocumentType: {self.doc_type or ''}\n"
f"\tDocumentID: {self.doc_id or ''}\n"
f"\tSurname: {self.surname or ''}\n"
f"\tGivenName: {self.given_name or ''}\n"
f"\tNationality: {self.nationality or ''}\n"
f"\tIssuingCountryorOrganization: {self.issuer or ''}\n"
f"\tGender: {self.gender or ''}\n"
f"\tDateofBirth(YYMMDD): {self.date_of_birth or ''}\n"
f"\tExpirationDate(YYMMDD): {self.date_of_expiry or ''}\n")
return msg
def print_results(result: ParsedResult) -> None:
tag = result.get_original_image_tag()
if isinstance(tag, FileImageTag):
print("File:", tag.get_file_path())
if result.get_error_code() != EnumErrorCode.EC_OK:
print("Error:", result.get_error_string())
else:
items = result.get_items()
print("Parsed", len(items), "MRZ Zones.")
for item in items:
mrz_result = MRZResult(item)
print(mrz_result.to_string())
if __name__ == '__main__':
print("**********************************************************")
print("Welcome to Dynamsoft Capture Vision - MRZ Sample")
print("**********************************************************")
error_code, error_message = LicenseManager.init_license("LICENSE-KEY")
if error_code != EnumErrorCode.EC_OK and error_code != EnumErrorCode.EC_LICENSE_CACHE_USED:
print("License initialization failed: ErrorCode:", error_code, ", ErrorString:", error_message)
else:
cvr_instance = CaptureVisionRouter()
while (True):
image_path = input(
">> Input your image full path:\n"
">> 'Enter' for sample image or 'Q'/'q' to quit\n"
).strip('\'"')
if image_path.lower() == "q":
sys.exit(0)
if image_path == "":
image_path = "../Images/passport-sample.jpg"
if not os.path.exists(image_path):
print("The image path does not exist.")
continue
result = cvr_instance.capture(image_path, "ReadPassportAndId")
if result.get_error_code() != EnumErrorCode.EC_OK:
print("Error:", result.get_error_code(), result.get_error_string())
else:
parsed_result = result.get_parsed_result()
if parsed_result is None or len(parsed_result.get_items()) == 0:
print("No parsed results.")
else:
print_results(parsed_result)
input("Press Enter to quit...")
解释
- 该
LicenseManager.init_license
方法使用有效的许可证密钥初始化 Dynamsoft Capture Vision SDK。 - 该类
CaptureVisionRouter
管理图像处理任务并协调各个图像处理模块。其capture
方法处理输入图像并返回结果。 - 是
ReadPassportAndId
指定处理模式的内置模板。SDK 支持各种处理模式,例如MRZ 识别、文档边缘检测和条形码检测。 - 该
get_parsed_result
方法以字典形式检索 MRZ 识别结果。该类MRZResult
提取并包装相关的 MRZ 信息。由于此类可以在不同的应用程序中重复使用,因此建议将其移动到utils.py
文件中。
护照图像中机器可读区域位置的可视化
在上面的代码中,是CapturedResultresult
类的一个实例。调用其方法将检索TextLineResultItem对象的列表。每个对象都包含检测到的文本行的坐标。使用以下代码片段提取坐标并在护照图像上绘制轮廓:get_recognized_text_lines_result()
TextLineResultItem
cv_image = cv2.imread(image_path)
line_result = result.get_recognized_text_lines_result()
items = line_result.get_items()
for item in items:
location = item.get_location()
x1 = location.points[0].x
y1 = location.points[0].y
x2 = location.points[1].x
y2 = location.points[1].y
x3 = location.points[2].x
y3 = location.points[2].y
x4 = location.points[3].x
y4 = location.points[3].y
del location
cv2.drawContours(
cv_image, [np.intp([(x1, y1), (x2, y2), (x3, y3), (x4, y4)])], 0, (0, 255, 0), 2)
cv2.imshow(
"Original Image with Detected MRZ Zone", cv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
通过网络摄像头实时扫描和识别 MRZ
通过网络摄像头实时扫描和识别 MRZ 需要捕获连续的图像流。我们可以使用 OpenCV 库从网络摄像头捕获帧并使用 Dynamsoft Capture Vision SDK 对其进行处理。以下代码片段演示了如何使用网络摄像头实现实时 MRZ 识别:
from dynamsoft_capture_vision_bundle import *
import cv2
import numpy as np
import queue
from utils import *
class FrameFetcher(ImageSourceAdapter):
def has_next_image_to_fetch(self) -> bool:
return True
def add_frame(self, imageData):
self.add_image_to_buffer(imageData)
class MyCapturedResultReceiver(CapturedResultReceiver):
def __init__(self, result_queue):
super().__init__()
self.result_queue = result_queue
def on_captured_result_received(self, captured_result):
self.result_queue.put(captured_result)
if __name__ == '__main__':
errorCode, errorMsg = LicenseManager.init_license(
"LICENSE-KEY")
if errorCode != EnumErrorCode.EC_OK and errorCode != EnumErrorCode.EC_LICENSE_CACHE_USED:
print("License initialization failed: ErrorCode:",
errorCode, ", ErrorString:", errorMsg)
else:
vc = cv2.VideoCapture(0)
if not vc.isOpened():
print("Error: Camera is not opened!")
exit(1)
cvr = CaptureVisionRouter()
fetcher = FrameFetcher()
cvr.set_input(fetcher)
# Create a thread-safe queue to store captured items
result_queue = queue.Queue()
receiver = MyCapturedResultReceiver(result_queue)
cvr.add_result_receiver(receiver)
errorCode, errorMsg = cvr.start_capturing("ReadPassportAndId")
if errorCode != EnumErrorCode.EC_OK:
print("error:", errorMsg)
while True:
ret, frame = vc.read()
if not ret:
print("Error: Cannot read frame!")
break
fetcher.add_frame(convertMat2ImageData(frame))
if not result_queue.empty():
captured_result = result_queue.get_nowait()
items = captured_result.get_items()
for item in items:
if item.get_type() == EnumCapturedResultItemType.CRIT_TEXT_LINE:
text = item.get_text()
line_results = text.split('\n')
location = item.get_location()
x1 = location.points[0].x
y1 = location.points[0].y
x2 = location.points[1].x
y2 = location.points[1].y
x3 = location.points[2].x
y3 = location.points[2].y
x4 = location.points[3].x
y4 = location.points[3].y
cv2.drawContours(
frame, [np.intp([(x1, y1), (x2, y2), (x3, y3), (x4, y4)])], 0, (0, 255, 0), 2)
delta = y3 - y1
for line_result in line_results:
cv2.putText(
frame, line_result, (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
y1 += delta
del location
elif item.get_type() == EnumCapturedResultItemType.CRIT_PARSED_RESULT:
mrz_result = MRZResult(item)
print(mrz_result.to_string())
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.imshow('frame', frame)
cvr.stop_capturing()
vc.release()
cv2.destroyAllWindows()
解释
- 该类
FrameFetcher
实现ImageSourceAdapter
将帧数据输入到内置缓冲区的接口。 - 该类
MyCapturedResultReceiver
实现CapturedResultReceiver
接口。该on_captured_result_received
方法在本机 C++ 工作线程上运行,将CapturedResult
对象发送到主线程,并将它们存储在线程安全队列中以供进一步使用。 - A
CapturedResult
包含多个CapturedResultItem
对象。CRIT_TEXT_LINE
类型表示已识别的文本行,类型CRIT_PARSED_RESULT
表示已解析的 MRZ 数据。