Реалізація AI-витягування даних із паспортів і посвідчень

Проектуємо та впроваджуємо системи штучного інтелекту: від прототипу до production-ready рішення. Наша команда поєднує експертизу в машинному навчанні, дата-інжинірингу та MLOps, щоб AI працював не в лабораторії, а в реальному бізнесі.
Показано 1 з 1Усі 1566 послуг
Реалізація AI-витягування даних із паспортів і посвідчень
Середній
~3-5 днів
Часті запитання

Напрямки AI-розробки

Етапи розробки AI-рішення

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1288
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1123
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    590
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    860

AI-видобуток даних з паспортів та посвідчень особистості

Розпізнавання документів, що посвідчують особистість — область з особливими вимогами: точність поля >99.5% для критичних полів (серія/номер, дата народження), обробка зношення документа, робота з документами різних країн, детекція підделок.

MRZ — зона машиносчитываємості

Machine Readable Zone (MRZ) — дві строки внизу паспорта з контрольними сумами по ИКАО 9303. Це надійна точка входу: MRZ містить усі ключові поля та верифікується математично.

import re
from dataclasses import dataclass
from typing import Optional

@dataclass
class MRZData:
    document_type: str
    issuing_country: str
    surname: str
    given_names: str
    document_number: str
    nationality: str
    date_of_birth: str      # YYMMDD
    sex: str
    expiry_date: str        # YYMMDD
    personal_number: str
    check_digits_valid: bool

class MRZParser:
    """
    Парсер MRZ для TD1 (ID-карти, 3 строки × 30 символів)
    та TD3 (паспорти, 2 строки × 44 символи).
    """

    WEIGHTS = [7, 3, 1]

    def _check_digit(self, s: str) -> int:
        """Контрольна цифра ICAO 9303"""
        charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<'
        values  = {c: i for i, c in enumerate(charset)}
        total = sum(
            values.get(c, 0) * self.WEIGHTS[i % 3]
            for i, c in enumerate(s)
        )
        return total % 10

    def parse_td3(self, line1: str, line2: str) -> Optional[MRZData]:
        """TD3 — паспорт, 2 строки по 44 символи"""
        if len(line1) != 44 or len(line2) != 44:
            return None

        # Строка 1
        doc_type   = line1[0:2].replace('<', '')
        country    = line1[2:5]
        name_field = line1[5:44]
        if '<<' in name_field:
            surname_raw, given_raw = name_field.split('<<', 1)
        else:
            surname_raw, given_raw = name_field, ''

        # Строка 2
        doc_num    = line2[0:9].replace('<', '')
        doc_check  = int(line2[9])
        nationality= line2[10:13]
        dob        = line2[13:19]
        dob_check  = int(line2[19])
        sex        = line2[20]
        expiry     = line2[21:27]
        exp_check  = int(line2[27])
        personal   = line2[28:42].replace('<', '')
        composite_check = int(line2[43])

        # Верифікація контрольних сум
        valid = all([
            self._check_digit(line2[0:9])  == doc_check,
            self._check_digit(line2[13:19]) == dob_check,
            self._check_digit(line2[21:27]) == exp_check,
            self._check_digit(line2[0:10] + line2[13:20] + line2[21:43]) == composite_check
        ])

        return MRZData(
            document_type=doc_type,
            issuing_country=country,
            surname=surname_raw.replace('<', ' ').strip(),
            given_names=given_raw.replace('<', ' ').strip(),
            document_number=doc_num,
            nationality=nationality,
            date_of_birth=dob,
            sex=sex,
            expiry_date=expiry,
            personal_number=personal,
            check_digits_valid=valid
        )

OCR зон VIZ (Visual Inspection Zone)

Крім MRZ, потрібно читати візуальну зону: адреса прописки, місце народження (в російському паспорті немає в MRZ). Для цього — регіональний OCR з коригуючим словником населених пунктів:

from paddleocr import PaddleOCR
from rapidfuzz import process, fuzz
import json

class PassportVIZExtractor:
    def __init__(self, region_dict_path: str):
        self.ocr = PaddleOCR(
            use_angle_cls=True, lang='uk',
            det_model_dir='models/det/',
            rec_model_dir='models/rec/'   # fine-tuned на паспортах
        )
        with open(region_dict_path) as f:
            self.regions = json.load(f)   # список регіонів/міст

    def extract_fields(self, page_image) -> dict:
        result = self.ocr.ocr(page_image, cls=True)
        if not result or not result[0]:
            return {}

        # Групуємо рядки за вертикальною позицією
        lines = sorted(
            [(r[0][0][1], r[1][0]) for r in result[0]],
            key=lambda x: x[0]
        )

        fields = {}
        for y_pos, text in lines:
            if 'місце народження' in text.lower():
                fields['birth_place_label_y'] = y_pos
            elif 'місце народження' in fields and \
                 abs(y_pos - fields.get('birth_place_label_y', 0)) < 50:
                fields['birth_place_raw'] = text
                # Нормалізація через fuzzy-matching до справочника
                match, score, _ = process.extractOne(
                    text, self.regions, scorer=fuzz.token_sort_ratio
                )
                fields['birth_place_normalized'] = match if score > 70 else text

        return fields

Детекція підделок (базовий рівень)

import numpy as np
import cv2

def detect_basic_tampering(image: np.ndarray) -> dict:
    """
    Прості ознаки підробки:
    - JPEG-артефакти в різних блоках (copy-paste з іншого фото)
    - Аномальна різкість на окремих полях (вклейка)
    - Невідповідність DPI між зонами
    """
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Error Level Analysis: виявляємо області з іншим стисненням
    import tempfile, os
    with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
        tmp_path = tmp.name
    cv2.imwrite(tmp_path, image, [cv2.IMWRITE_JPEG_QUALITY, 90])
    recompressed = cv2.imread(tmp_path)
    os.unlink(tmp_path)

    ela = cv2.absdiff(image, recompressed)
    ela_gray = cv2.cvtColor(ela, cv2.COLOR_BGR2GRAY)

    # Регіони з високим ELA — потенціальні вклейки
    high_ela_mask = ela_gray > ela_gray.mean() + 3 * ela_gray.std()
    tamper_ratio  = high_ela_mask.mean()

    return {
        'ela_anomaly_ratio': float(tamper_ratio),
        'suspicious':        tamper_ratio > 0.05,  # >5% пікселів аномальних
        'ela_map':           ela_gray
    }

Точність на benchmark MIDV-2020

Поле Точність видобутку Метод
MRZ (усі поля) 99.8% MRZ OCR + check digits
Серія/номер (RF паспорт) 99.3% PaddleOCR fine-tuned
Дата народження 99.1% MRZ + VIZ cross-check
ПІБ 97.8% VIZ + BERT NER
Адреса прописки 94.2% VIZ + справочник ФІАС

Сроки

Завдання Сроки
MRZ + базові поля (паспорти РФ/EU) 2–4 тижні
Мультидокументна система (10+ типів) 6–9 тижнів
Система з детекцією підделок та liveness 10–16 тижнів