⟨/⟩ 55_Backend/Handlers/Recognition/ClassifyFrameHandler.cs

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

using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
using Ban.Sdaid.Icd.Api.Endpoints.Recognition.ClassifyFrame;
using Ban.Sdaid.Icd.Domain.Cards.Template;
using Ban.Sdaid.Icd.Arch.Adr;

namespace Ban.Sdaid.Icd.Backend.Handlers.Recognition
{
    /// <summary>Handler опознания формы и страницы кадра геометрическим детектором.</summary>
    public class ClassifyFrameHandler : IHandlerDocument, IFolder<BackendFolder>, IHasStructuralLinks
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Ban.Sdaid.Icd.Domain.BC_Icd>(),
            Rel.Uses<PageDetectionProfileSpec>(),
            Rel.Uses<DocumentFormSpec>(),
            Rel.Uses<FormPageSpec>(),
            Rel.Uses<DOC_ClassifyFrame>(),
            Rel.Uses<ADR_015_FormPageDetection>(),
            Rel.Uses<ADR_014_PacketProcessingQueue>(),
        };

        public static string _refName = "Handler ClassifyFrame";

        public string Name => "Handler: ClassifyFrame";

        public string Description =>
            @"Что произойдёт при опознании кадра: выровнять, посчитать признаки раскладки, сравнить с эталонными профилями форм и проставить на кадре опознанную форму и страницу с уверенностью — или пометить «вид не опознан».";

        public string Version => "0.1";
        public string Status => "draft";
        public string[] Comments => new string[0];

        public static string Purpose = $"""
            ## Назначение

            Опознать, к какой форме и странице относится кадр, **до** извлечения полей — механизм
            {SdaidAnchors.RefTo<ADR_015_FormPageDetection>()}: геометрический детектор сравнивает признаки
            раскладки кадра с эталонными профилями {SdaidAnchors.RefTo<PageDetectionProfileSpec>()}.
            Штатно вызывается воркером фоновой очереди {SdaidAnchors.RefTo<ADR_014_PacketProcessingQueue>()}
            как шаг «опознание вида»; тот же сценарий — при ручном повторе.
            """;

        public static string Scenario = $"""
            ## Сценарий вызова

            1. Найти пакет по `{nameof(ClassifyFrameCommand.PacketId)}` (не удалённый) с кадрами; нет — `404`.
            2. Отобрать кадры: если задан `{nameof(ClassifyFrameCommand.FrameOrder)}` — один кадр, иначе все.
            3. По каждому кадру:
               - взять изображение из хранилища (обработанное, если есть, иначе оригинал);
               - выровнять по вертикали (deskew) и детектировать рамку-якорь таблицы;
               - посчитать признаки: число линий сетки по всей рамке (грубый — сторона листа)
                 и по зонам-различиям профилей (тонкий — какая форма);
               - сравнить с профилями страниц активных форм, посчитать скоринг с отрывом
                 ({SdaidAnchors.RefTo<ADR_015_FormPageDetection>()}); лучший кандидат и отрыв от второго — уверенность.
            4. Если уверенность выше порога — проставить на кадре опознанную форму (`{nameof(ClassifiedFrameInfo.FormId)}`),
               страницу (`{nameof(ClassifiedFrameInfo.PageNumber)}`) и `{nameof(ClassifiedFrameInfo.Confidence)}`;
               иначе кадр помечается «вид не опознан» (`{nameof(ClassifiedFrameInfo.Error)}`), с ближайшими альтернативами.
            5. Опознание — шаг фоновой очереди: если хотя бы кадр не опознан, пакет уходит в состояние
               «ошибка» ({SdaidAnchors.RefTo<ADR_014_PacketProcessingQueue>()}); оператор разбирается командой —
               ручной выбор формы **авторитетнее** детектора ({SdaidAnchors.RefTo<ADR_015_FormPageDetection>()}).
            6. Вернуть `{nameof(ClassifyFrameResult)}` со сводкой (`{nameof(ClassifiedPacketPayload.Recognized)}` /
               `{nameof(ClassifiedPacketPayload.Unrecognized)}`).
            """;
    }
}