Skip to content

Commit

Permalink
Add Dynamsoft Capture Vision example for MRZ detection
Browse files Browse the repository at this point in the history
  • Loading branch information
yushulx committed Oct 17, 2024
1 parent 458b3d6 commit c2a6f2e
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 0 deletions.
26 changes: 26 additions & 0 deletions examples/official/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Dynamsoft Capture Vision SDK for MRZ Detection
This repository contains example code for using the Dynamsoft Capture Vision SDK to detect **Machine Readable Zone (MRZ)** in passport, ID card and Visa images.

## Prerequisites
- [Dynamsoft Capture Vision Trial License](https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform)

```python
errorCode, errorMsg = LicenseManager.init_license(
"LICENSE-KEY")
```

- SDK Installation

```bash
pip install -r requirements.txt
```

## Supported Platforms
- Windows
- Linux
- macOS


## Examples
- [camera.py](./camera.py): Detect MRZ from a camera video stream.
- [file.py](./file.py): Detect MRZ from an image file and display the results in a window.
100 changes: 100 additions & 0 deletions examples/official/camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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(
"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
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()
74 changes: 74 additions & 0 deletions examples/official/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import sys
from dynamsoft_capture_vision_bundle import *
import os
import cv2
import numpy as np
from utils import *

if __name__ == '__main__':

print("**********************************************************")
print("Welcome to Dynamsoft Capture Vision - MRZ Sample")
print("**********************************************************")

error_code, error_message = LicenseManager.init_license(
"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==")
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/1.png"

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:
cv_image = cv2.imread(image_path)

# Get the recognized text lines result
line_result = result.get_recognized_text_lines_result()

# Get the parsed result
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)

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()

input("Press Enter to quit...")
2 changes: 2 additions & 0 deletions examples/official/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dynamsoft-capture-vision-bundle
opencv-python
138 changes: 138 additions & 0 deletions examples/official/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from dynamsoft_capture_vision_bundle import *
import numpy as np


def convertImageData2Mat(normalized_image):
ba = bytearray(normalized_image.get_bytes())
width = normalized_image.get_width()
height = normalized_image.get_height()

channels = 3
if normalized_image.get_image_pixel_format() == EnumImagePixelFormat.IPF_BINARY:
channels = 1
all = []
skip = normalized_image.stride * 8 - width

index = 0
n = 1
for byte in ba:

byteCount = 7
while byteCount >= 0:
b = (byte & (1 << byteCount)) >> byteCount

if index < normalized_image.stride * 8 * n - skip:
if b == 1:
all.append(255)
else:
all.append(0)

byteCount -= 1
index += 1

if index == normalized_image.stride * 8 * n:
n += 1

mat = np.array(all, dtype=np.uint8).reshape(height, width, channels)
return mat

elif normalized_image.get_image_pixel_format() == EnumImagePixelFormat.IPF_GRAYSCALED:
channels = 1

mat = np.array(ba, dtype=np.uint8).reshape(height, width, channels)

return mat


def convertMat2ImageData(mat):
if len(mat.shape) == 3:
height, width, channels = mat.shape
pixel_format = EnumImagePixelFormat.IPF_RGB_888
else:
height, width = mat.shape
channels = 1
pixel_format = EnumImagePixelFormat.IPF_GRAYSCALED

stride = width * channels
imagedata = ImageData(mat.tobytes(), width, height, stride, pixel_format)
return imagedata


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())

0 comments on commit c2a6f2e

Please sign in to comment.