⟨/⟩ 40_Arch/ADR/ADR_015_FormPageDetection.cs

168 строк · в начало

using System;
using Ban.Sdaid.Notation.Documents;

namespace Ban.Sdaid.Icd.Arch.Adr
{
    /// <summary>
    /// Решение: как Сервис опознаёт форму и страницу кадра — детерминированным геометрическим
    /// детектором по пикселям (выравнивание → рамка-якорь → счёт линий по зонам-различиям → скоринг
    /// с отрывом), а не моделью. Заполняет шаг «опознание вида» фоновой очереди (ADR-014).
    /// </summary>
    public class ADR_015_FormPageDetection : IAdrDocument
    {
        public static string _refName = "ADR-015 «Опознание формы и страницы кадра»";

        public string Name => "ADR-015. Опознание формы и страницы кадра";

        public string Description =>
            @"Как Сервис относит кадр к форме и её странице до извлечения полей: геометрический детектор по пикселям (deskew, рамка-якорь, счёт линий сетки в зонах-различиях, скоринг с отрывом-уверенностью), эталонные данные — справочник при форме, низкая уверенность уводит пакет в «ошибка». Механизм отработан на стенде распознавания.";

        public string Version => "0.1";
        public string Status => "proposed";

        public string[] Comments => new[]
        {
            "2026-08-27. Заведён по итогам обкатки детектора на стенде (src-tools/card-extraction-stand): deskew + рамка-якорь (frameYbyLines) + счёт линий по зонам + скоринг; закрывает OQ-RS-1 стенда и слот «опознание вида», который ADR-014 держал открытым.",
        };

        public Type? Supersedes => null;

        public static string S1_Context = $"""
            ## Контекст

            {nameof(Ban.Sdaid.Icd.Requirements.FR_003_AttributeExtraction)} требует: прежде чем читать
            поля, Сервис **определяет форму бланка и распределяет кадры по её страницам** — пока форма
            не опознана, состав страниц и полей неизвестен, а кадр, не отнесённый ни к одной странице,
            считается лишним.

            {nameof(ADR_014_PacketProcessingQueue)} поставил **опознание вида** шагом фоновой очереди
            (воркер прогоняет нормализацию и опознание вида; неуспех уводит пакет в «ошибка»), но
            **механизм не задал** и сослался на {nameof(ADR_009_RecognitionModelIntegration)}. А тот
            в разделе «За рамками» опознание формы/страницы явно **исключает** — считает, что
            к моменту вызова модели форма уже известна, кадр отнесён к странице, каталог полей собран.

            Отсюда пробел, который закрывает это решение: **чем именно кадр относится к форме и странице**.

            Что уже есть. Механизм отработан на стенде — {nameof(Ban.Sdaid.Icd.Epics.RecognitionStand.EPIC_RecognitionStand)}:
            выравнивание кадра, детекция рамки-якоря таблицы, счёт линий сетки, разведение вариантов
            бланка по зонам-различиям, скоринг с отрывом. Геометрия полей для последующего чтения
            уже смоделирована — область поля {nameof(Ban.Sdaid.Icd.Domain.Cards.Template.FormField)}
            в процентах страницы.

            ### За рамками

            Чтение значений полей известной формы ({nameof(ADR_009_RecognitionModelIntegration)}),
            нормализация кадра ({nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_005_ImageNormalizationOrthocover)})
            и семантический вид карты (`card_kind` модель возвращает **в ходе** извлечения,
            {nameof(Ban.Sdaid.Icd.Requirements.FR_003_AttributeExtraction)}) — не здесь. Это решение
            про геометрическое опознание раскладки, дающее конкретную форму и страницу.
            """;

        public static string S2_Decision = $"""
            ## Решение

            **Опознание геометрическое и детерминированное, не модельное.** Форму и страницу кадра
            Сервис определяет алгоритмом по пикселям, а не обращением к VLM. Причина в порядке:
            опознание нужно **до** извлечения — выбрать форму, чтобы собрать каталог полей страницы
            и включить офлайн-чтение отметок; на этом шаге модель избыточна, недетерминирована
            и дороже, а признак — чисто геометрический (структура линий бланка).

            **Два признака, грубый и тонкий.** Кадр выравнивается по вертикали (deskew), у таблицы
            детектируется рамка-якорь (границы по краевым линиям сетки). Дальше:
            - **страница** (сторона листа, {nameof(Ban.Sdaid.Icd.Domain.Cards.Template.FormPage)}) —
              по грубому признаку: число горизонтальных линий по всей рамке разводит стороны;
            - **форма** (редакция бланка, {nameof(Ban.Sdaid.Icd.Domain.Cards.Template.DocumentForm)}) —
              по тонкому: в заранее размеченных **зонах-различиях** сравнивается число линий.

            **Скоринг с отрывом.** Кандидату считается взвешенная сумма модулей расхождений
            (грубый признак весомее, зоны уточняют); выбирается минимум. **Отрыв от второго места —
            уверенность**: мал — «вид не опознан».

            **Стыковка с очередью.** Это и есть шаг «опознание вида» воркера {nameof(ADR_014_PacketProcessingQueue)}:
            уверенное опознание даёт пару «форма + страница» каждому кадру, пакет идёт дальше по конвейеру;
            низкая уверенность или отсутствие подходящей формы = «вид не опознан» → пакет в состояние
            **«ошибка»**, оператор разбирается командой (ручной повтор, отклонение вида, выбор формы вручную).
            **Ручной выбор оператора авторитетнее детектора** — детектор снимает рутину, а не решает окончательно.

            **Эталонные данные — справочные, при форме.** Детектору нужен эталон на каждую форму/страницу:
            рамка-якорь, линии сетки, зоны-различия и ожидаемые счётчики линий. Это отдельная
            **reference-сущность профиля детектирования** при форме и странице (вводится следующим
            артефактом). Разметка эталона — работа аналитика на стенде.

            **Координаты — доли выровненного кадра, сводятся к процентам страницы** — той же шкале,
            что область поля {nameof(Ban.Sdaid.Icd.Domain.Cards.Template.FormField)} и источник значения
            ({nameof(ADR_005_FieldValueSourceBinding)}); шкала не зависит от разрешения снимка.

            **Обработка образа — локальная, управляемой библиотекой.** Пиксельные профили и счёт линий
            выполняются в процессе Сервиса CPU-библиотекой обработки изображений (конкретная —
            в пункте реализации); кадры наружу не передаются, как и при обращении к модели.

            ### Чего в первой редакции нет

            Сознательно откладываем до фактов:

            | Не делаем | Почему |
            |---|---|
            | Модельное (VLM) опознание формы | геометрия дешевле, детерминирована и её достаточно на размеченных бланках |
            | Обучаемый классификатор кадров | требует эталонного набора, которого пока нет |
            | Автопостроение эталона формы из скана | разметку ведёт аналитик; автоподсказку добавим по мере надобности |
            | Порог уверенности как настройка калибровки | распределение уверенности на проде неизвестно; пока порог грубый, спорное уводится в «ошибка» |
            """;

        public static string S3_Rationale = """
            ## Обоснование

            Критерий выбора — «сколько до первого воспроизводимого опознания на наших бланках», а не
            «насколько универсально». Структура линий бланка — устойчивый признак: он не зависит от
            рукописного наполнения и берётся арифметикой по пикселям, поэтому опознание бесплатно
            и повторяемо, а его ошибки объяснимы (видно, какая зона не совпала), в отличие от вердикта
            модели.

            Это прямое продолжение принципа стенда «математика вперёд, модель — только на текст»:
            детерминированное опознание раскладки открывает офлайн-чтение отметок и адресную
            сегментацию, а модель остаётся там, где без неё нельзя, — на рукописном тексте.

            Геометрия и модель не конкурируют, а дополняют: детектор даёт конкретную форму/страницу
            (редакцию и сторону), а семантический `card_kind` модель возвращает в ходе извлечения —
            это независимая перекрёстная проверка, а не источник геометрии.
            """;

        public static string S4_Consequences = $"""
            ## Следствия

            **Каждой форме нужен размеченный эталон.** Без профиля детектирования форма не опознаётся —
            появляется работа аналитика по разметке (её и ведёт стенд, {nameof(Ban.Sdaid.Icd.Epics.RecognitionStand.EPIC_RecognitionStand)}),
            и справочник форм versionируется вместе с профилями.

            **В бэкенде появляется обработка изображений.** Раньше Сервис лишь звал внешнюю нормализацию
            ({nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_005_ImageNormalizationOrthocover)}); теперь он сам
            считает пиксельные профили — новая библиотека-зависимость под своим пунктом реализации.

            **Порог уверенности живёт без калибровки.** Пока набора для настройки нет, граница «опознано /
            не опознано» грубая; спорные кадры уходят в «ошибка» {nameof(ADR_014_PacketProcessingQueue)},
            где решает оператор. Это осознанно строгая сторона: лучше спросить человека, чем сесть не на ту форму.

            **Потолок — разрешение снимка.** Счёт линий шумит на мелком фото; на этом упирается и точность
            опознания, и последующее офлайн-чтение (открытый вопрос стенда про разрешение).
            """;

        public static string S5_Alternatives = """
            ## Рассмотренные альтернативы

            **Опознавать форму моделью (VLM).** Отправлять кадр модели и просить назвать вид/форму.
            Отвергнуто как первый шаг: недетерминировано, дороже и позже (сетевой вызов на каждый кадр
            до конвейера), а признак раскладки чисто геометрический — модель тут не нужна. Семантический
            `card_kind` модель и так вернёт при извлечении; это оставляет перекрёстную сверку.

            **Обучаемый классификатор кадров.** Свёрточная сеть на «front_v1 / back_v2 / …». Отвергнуто:
            требует размеченного набора кадров, которого нет и который собирать только после того, как
            заработает базовое опознание; эвристика по линиям даёт результат сразу и объяснимо.

            **Только ручной выбор формы оператором.** Оператор указывает форму каждому кадру. Отвергнуто
            как единственный путь — это рутина, ради устранения которой всё и затеяно; но сохранено как
            **фолбэк и высшая инстанция**: детектор предлагает, при низкой уверенности спрашивает,
            исправление оператора не переспрашивается.
            """;
    }
}