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)}`).
""";
}
}