Họ tên là trường khó đoán nhất trong vùng đọc máy (MRZ). Ngày tháng là sáu chữ số, số giấy tờ là chín ký tự kèm một chữ số kiểm tra, còn các mã lấy từ một danh sách cố định. Họ tên thì là bất cứ tên gọi nào của một người, bị nén vào một số chữ in hoa cố định, không có khoảng trắng, không có dấu, không có dấu câu và không có chữ số kiểm tra nào báo cho bạn biết khi một ký tự bị đọc sai.

Bài viết này trình bày cách vùng MRZ ghi họ tên, cách tách nó trở lại thành họ và tên, chuyển tự làm gì với họ tên, cách nhận biết họ tên có thể đã bị cắt ngắn, và cách so sánh nó với họ tên in trên trang. Mọi họ tên dưới đây đều là hư cấu hoặc là mẫu của chính ICAO, Anna Maria Eriksson; không có người thật nào xuất hiện.

Đây là blog của doc.cheap, một API OCR hộ chiếu và giấy tờ tùy thân. Không phần nào dưới đây cần đến nó; đoạn mã không có phụ thuộc nào.

Họ tên nằm ở đâu

ICAO Doc 9303, tiêu chuẩn cho giấy tờ đi lại đọc được bằng máy, dành cho họ tên một trường có độ rộng cố định trong cả ba bố cục. Vị trí ở đây được đếm từ 1, theo cách tiêu chuẩn đếm:

Định dạng Vị trí của họ tên Độ rộng
TD3 (hộ chiếu), 2 × 44 dòng 1, vị trí 6–44 39 ký tự
TD2, 2 × 36 dòng 1, vị trí 6–36 31 ký tự
TD1 (thẻ căn cước), 3 × 30 dòng 3, cả dòng 30 ký tự

Năm ký tự đầu của dòng 1 trong TD3 hoặc TD2 là mã giấy tờ và quốc gia cấp; họ tên chiếm toàn bộ phần sau đó. TD1 dành riêng một dòng cho họ tên, nhưng đó vẫn là trường hẹp nhất trong ba định dạng. Trang định dạng MRZ giải thích từng bố cục theo từng trường.

Trên mẫu TD3, dòng 1 là P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<. Bỏ P<UTO đi, trường họ tên là ERIKSSON<<ANNA<MARIA, theo sau là các ký tự đệm cho đủ 39 ký tự.

Ngữ pháp: <<, < và phần đệm

Bảng chữ cái của vùng MRZ gồm A–Z, 0–9 và ký tự đệm <. Họ tên dùng thêm ba quy ước trên nền đó:

  • Định danh chính đứng trước. Thường đó là họ.
  • Hai ký tự đệm << kết thúc định danh chính. Phần sau là định danh phụ, thường là tên.
  • Một ký tự đệm < ngăn cách các từ bên trong mỗi phần. ANNA<MARIA là hai tên; VAN<DER<BERG là một họ gồm ba từ.

Phần còn lại của trường sau họ tên được lấp bằng <. Vì vậy đọc lại họ tên gồm ba bước: bỏ các ký tự đệm ở cuối, tách tại << đầu tiên, và đổi các < đơn còn lại thành khoảng trắng.

Có hai dạng trông lạ nhưng không phải lỗi. Người chỉ có một tên thì chỉ có định danh chính, nên trường là tên rồi đến ký tự đệm, không có << đứng trước: OKONKWO<<<<…. Một trường ghi dấu phân cách mà không có gì phía sau, NAKAMURA<<<<…, cũng được đọc y hệt sau khi bỏ ký tự đệm ở cuối. Dù theo cách nào, định danh phụ là rỗng, chứ không phải bị thiếu.

Thứ không giữ lại được là dấu câu và chính khoảng trắng. Dấu gạch nối, dấu nháy đơn hay khoảng trắng đều không thể ghi vào vùng MRZ, nên Smith-Jones in trên trang sẽ thành SMITH<JONES, không phân biệt được với một họ gồm hai từ. Nếu bạn cần dấu câu, nó nằm trên trang in, không nằm trong vùng MRZ.

Chuyển tự: từ mọi hệ chữ sang 26 chữ cái

Mọi họ tên cuối cùng đều phải nằm trong A–Z. Với họ tên in bằng chữ Latin có dấu phụ, điều đó có nghĩa là ánh xạ mỗi chữ có dấu sang chữ không dấu. Với họ tên bằng chữ Hy Lạp, Ả Rập, Kirin, chữ Hán hay bất kỳ hệ chữ nào khác, điều đó có nghĩa là viết toàn bộ họ tên bằng chữ Latin.

Part 3 của Doc 9303, phần chung cho mọi định dạng, đưa ra các cách chuyển tự được khuyến nghị cho cả hai trường hợp. Tuy vậy, quyết định thuộc về quốc gia cấp, và không phải quốc gia nào cũng chọn giống nhau. Có ba hệ quả quan trọng đối với mã nguồn:

  1. Một họ tên in có thể có nhiều cách viết trong vùng MRZ. MÜLLER in trên trang có thể xuất hiện trong vùng MRZ là MUELLER hoặc MULLER, tùy quy tắc của cơ quan cấp. Mã nguồn chờ đợi một ánh xạ cố định duy nhất sẽ coi một vùng MRZ đúng là sai.
  2. Ánh xạ không đảo ngược được. MULLER trong vùng MRZ có thể đến từ MULLER, MÜLLER hoặc MÚLLER trên trang. Vùng MRZ cho bạn biết những chữ cái nó chứa, không phải họ tên mà người đó viết.
  3. Họ tên Latin trên trang có thể khác vùng MRZ. Giấy tờ in bằng chữ quốc gia thường in kèm một phiên bản Latin, và cách viết Latin in trên trang đó không phải lúc nào cũng trùng từng chữ với cách viết trong vùng MRZ. Hãy coi sự khác biệt là điều cần xem xét, không phải bằng chứng của việc đọc sai.

Quy tắc thực tế: so sánh họ tên trong vùng MRZ với họ tên in sau khi rút cả hai về chữ cái thuần, chấp nhận các cách viết mà cơ quan cấp bạn làm việc thực sự dùng, và giữ cách viết in trên trang cho mọi thứ con người đọc.

Cắt ngắn: khi họ tên không vừa

Họ tên dài không xuống dòng. Nó được rút ngắn cho vừa trường, còn họ tên đầy đủ vẫn ở trên trang in. Tiêu chuẩn đưa ra quy tắc rút ngắn họ tên, và họ tên đã rút ngắn thường kéo dài đến đúng ký tự cuối cùng của trường.

Điều đó cho trình phân tích tín hiệu duy nhất mà nó có: nếu ký tự cuối cùng của trường họ tên là một chữ cái chứ không phải <, họ tên có thể đã bị cắt ngắn. Là "có thể", không phải "đã": một họ tên dài đúng bằng trường cũng kết thúc bằng chữ cái. Nhưng một trường kết thúc bằng ký tự đệm thì chắc chắn không bị cắt.

Độ rộng rất quan trọng. Lấy một người mang giấy tờ hư cấu, Konstantin Friedrich Hartmann Schoenebeck. Trong trường TD3 rộng 39 ký tự:

HARTMANN<SCHOENEBECK<<KONSTANTIN<FRIEDR

Ký tự cuối là R, một chữ cái, nên họ tên có thể bị cắt, và thực tế đã bị cắt: FRIEDRICH mất ba chữ cái. Cùng họ tên đó cắt ở độ rộng TD1 là 30 chỉ còn HARTMANN<SCHOENEBECK<<KONSTANT. Một hệ thống lưu họ tên trong vùng MRZ làm họ tên của người mang giấy tờ sẽ lưu KONSTANT làm tên.

Từ đó có hai thói quen. Không bao giờ dùng họ tên trong vùng MRZ làm tên hiển thị khi trường đã đầy; hãy dùng họ tên in. Và khi đối chiếu họ tên in đầy đủ với họ tên trong vùng MRZ, một họ tên in bắt đầu bằng họ tên trong vùng MRZ được coi là khớp nếu trường đã đầy.

Cơ quan cấp cũng có thể rút ngắn họ tên theo cách khác ngoài việc cắt phần cuối, ví dụ rút tên thứ hai thành chữ cái đầu. Phép kiểm tra tiền tố sẽ báo một họ tên như vậy là không khớp. Hãy chuyển trường hợp đó cho người xem xét thay vì từ chối giấy tờ.

Không có chữ số kiểm tra nào bao phủ họ tên

Các chữ số kiểm tra bao phủ số giấy tờ, hai ngày tháng, số cá nhân tùy chọn trên TD3, và một chữ số tổng hợp cho phần lớn dòng dưới. Không chữ số nào bao phủ họ tên. Trên TD3 và TD2, họ tên nằm ở dòng 1, nơi không chữ số kiểm tra nào chạm tới; trên TD1, họ tên ở dòng 3, còn chữ số tổng hợp dừng ở dòng 2.

Vì vậy, lỗi đọc sai trong họ tên diễn ra âm thầm. Một chữ M bị đọc thành N, hay một ký tự đệm bị đọc thành chữ cái, vẫn cho ra một vùng MRZ có phép tính khớp. Cách đối chiếu duy nhất cho họ tên là trang in: đọc cả hai và so sánh, và đó là việc hàm so sánh bên dưới làm. Bài chữ số kiểm tra MRZ hoạt động như thế nào trình bày phép tính cho các trường được bao phủ.

Tách trường bằng Python

Không có phụ thuộc. Hàm chỉ nhận riêng trường họ tên, đã được cắt ra khỏi dòng của nó (line1[5:44] trên TD3, line1[5:36] trên TD2, line3 trên TD1).

import re

def words(part):
    return " ".join(word for word in part.split("<") if word)

def read_mrz_name(field):
    """Split an MRZ name field into (primary, secondary, may_be_truncated)."""
    if not re.fullmatch(r"[A-Z<]+", field):
        raise ValueError("a name field holds only A-Z and the filler <")
    may_be_truncated = not field.endswith("<")
    body = field.rstrip("<")
    primary, _separator, secondary = body.partition("<<")
    return words(primary), words(secondary), may_be_truncated

partition chỉ tách tại << đầu tiên và trả về phần phụ rỗng khi không có, đó chính là trường hợp chỉ có một tên. Mẫu biểu thức cố ý từ chối chữ số: một 0 trong trường họ tên là O bị đọc sai, và nó nên được chuyển cho người xem xét thay vì được in ra như một họ tên.

Cùng phép tách bằng JavaScript

export function readMrzName(field) {
  if (!/^[A-Z<]+$/.test(field)) throw new Error("a name field holds only A-Z and the filler <");
  const mayBeTruncated = !field.endsWith("<");
  const body = field.replace(/<+$/, "");
  const at = body.indexOf("<<");
  const words = (part) => part.split("<").filter(Boolean).join(" ");
  if (at < 0) return { primary: words(body), secondary: "", mayBeTruncated };
  return { primary: words(body.slice(0, at)), secondary: words(body.slice(at + 2)), mayBeTruncated };
}

So sánh vùng MRZ với họ tên in

Họ tên in lấy từ vùng quan sát bằng mắt, phần trang mà con người đọc, với dấu và dấu câu của nó. Hàm dưới đây rút nó về chữ in hoa thuần và so sánh với một phần của họ tên trong vùng MRZ. Hàm trả về một trong ba từ: match, mismatch, hoặc unknown khi họ tên in chứa một chữ cái không tự rút về A–Z được, chẳng hạn Ø, Æ hay Ł.

import re
import unicodedata

# The spellings to try besides plain removal of the accent. These three are
# an example; put in the ones your issuers use.
EXPANSIONS = {"Ä": "AE", "Ö": "OE", "Ü": "UE"}

def compare_name(zone_part, printed_part, truncated=False, expansions=EXPANSIONS):
    """'match', 'mismatch' or 'unknown' for one name part: zone against printed."""
    zone = zone_part.replace(" ", "")
    upper = unicodedata.normalize("NFC", printed_part).upper()
    candidates = {upper, "".join(expansions.get(c, c) for c in upper)}
    verdict = "unknown"
    for candidate in candidates:
        letters = "".join(
            c for c in unicodedata.normalize("NFKD", candidate)
            if c.isalpha() and not unicodedata.combining(c)
        )
        if not re.fullmatch(r"[A-Z]*", letters):
            continue  # a letter with no plain A-Z form: leave it to a person
        if letters == zone or (truncated and letters.startswith(zone)):
            return "match"
        verdict = "mismatch"
    return verdict

NFKD tách một chữ có dấu thành chữ không dấu và dấu của nó, rồi dấu bị bỏ đi. Khoảng trắng, dấu gạch nối và dấu nháy đơn cũng bị bỏ, vì phép so sánh chỉ xét chữ cái: Smith-Jones và SMITH JONES đều thành SMITHJONES. Với họ tên chỉ in bằng hệ chữ không phải Latin, hãy so sánh vùng MRZ với phiên bản Latin in trên trang, không phải với bản chữ quốc gia; bản chữ quốc gia sẽ luôn trả về unknown ở đây.

Vector kiểm thử

Mọi họ tên đều là hư cấu, trừ mẫu của ICAO. Trong bảng đầu tiên, mỗi trường được đệm bằng < cho đủ độ rộng TD3 là 39 (trong Python là name.ljust(39, "<")), trừ hàng cuối, vốn đã dài đúng 39 ký tự.

Trường trước khi đệm Định danh chính Định danh phụ Có thể bị cắt
ERIKSSON<<ANNA<MARIA ERIKSSON ANNA MARIA không
VAN<DER<BERG<<PIETER VAN DER BERG PIETER không
SMITH<JONES<<MARY SMITH JONES MARY không
OKONKWO OKONKWO (rỗng) không
NAKAMURA<< NAKAMURA (rỗng) không
HARTMANN<SCHOENEBECK<<KONSTANTIN<FRIEDR HARTMANN SCHOENEBECK KONSTANTIN FRIEDR có

Và cho compare_name:

Phần trong vùng MRZ Phần in trên trang Bị cắt Kết quả mong đợi
ANNA MARIA Anna Maria không match
SMITH JONES Smith-Jones không match
MULLER Müller không match
MUELLER Müller không match (qua bảng mở rộng)
MILLER Müller không mismatch
KONSTANTIN FRIEDR Konstantin Friedrich có match
KONSTANTIN FRIEDR Konstantin Friedrich không mismatch
SORENSEN Sørensen không unknown

Hàng cuối là có chủ ý. Ø trở thành O hay OE là lựa chọn của cơ quan cấp, và một hàm đoán mò sẽ sai với ai đó; thay vào đó, hãy thêm dạng mà các cơ quan cấp của bạn dùng vào bảng mở rộng.

Trình phân tích MRZ của chúng tôi làm gì

Trình phân tích MRZ trên trang này đọc họ tên theo cùng cách: bỏ các ký tự đệm ở cuối, tách tại << đầu tiên, và đổi các ký tự đệm đơn thành khoảng trắng; một trường không có << được đọc là chỉ có họ. Khi họ tên kéo dài đến ký tự cuối cùng của trường, nó hiển thị cảnh báo rằng họ tên có thể đã bị cắt cho vừa, kèm các vị trí mà họ tên chiếm. Nó chạy trong trình duyệt, nên bạn có thể dán một vùng MRZ tổng hợp và so sánh kết quả đọc với mã của chính mình.

Vị trí trong quy trình xử lý

Một dịch vụ nhận dạng được lưu trữ đọc cả vùng MRZ lẫn trang in, nên câu hỏi là nó trao cho bạn họ tên nào. Trong phản hồi của doc.cheap, holder.surname, holder.given_names và holder.full_name chứa kết quả đọc Latin đã hợp nhất. Họ tên in bằng chữ quốc gia là một mục riêng của fields: mỗi kết quả đọc có mục riêng, mục Latin mặc định có id kết thúc bằng @0, còn mục chữ quốc gia nêu rõ language của nó, ví dụ Greek. Bản thân vùng MRZ nằm trong mrz.lines, đúng như khi được đọc, nên bạn có thể cắt trường họ tên ra và chạy hai hàm ở trên với nó. Hướng dẫn về hệ chữ không phải Latin (bằng tiếng Anh) chỉ cách nhóm các kết quả đọc và lưu cả hai cách viết. Nếu bạn vẫn đang chọn dịch vụ, bảng so sánh API OCR hộ chiếu đặt các mức giá đã công bố cạnh nhau.

Nếu bạn tìm thấy một họ tên mà đoạn mã trên đọc sai, hãy viết cho chúng tôi tại admin@doc.cheap.

Một câu hỏi dành cho bạn: khi so sánh vùng MRZ với họ tên in, cách chuyển tự nào khiến bạn bất ngờ nhất? Hãy cho chúng tôi biết trong phần bình luận bên dưới.