⟨/⟩ 45_Api/Endpoints/Recognition/ClassifyFrame.cs

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

using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
using Ban.Sdaid.Icd.Api.Based;

namespace Ban.Sdaid.Icd.Api.Endpoints.Recognition.ClassifyFrame
{
    /// <summary>Документ-обёртка над endpoint опознания формы и страницы кадра (геометрический детектор).</summary>
    public class DOC_ClassifyFrame : IApiContractDocument, IFolder<ApiFolder>, IHasStructuralLinks
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Ban.Sdaid.Icd.Backend.Handlers.Recognition.ClassifyFrameHandler>(),
        };

        public static string _refName = "Endpoint ClassifyFrame";

        public string Name => "Endpoint: ClassifyFrame";

        public string Description =>
            @"Опознание формы и страницы кадра: по эталонным профилям детектор относит кадр к форме/странице и возвращает уверенность и ближайшие альтернативы. Основной запуск — автоматический в фоновой очереди (ADR-014); endpoint — ручной повтор/инспекция. Поведение — ClassifyFrameHandler.";

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

        public static string Transport = """
            ## Транспорт

            - `POST /api/InputPacket/{id}/frames/{order}/classify` — опознать **один** кадр.
            - `POST /api/InputPacket/{id}/classify` — опознать **все** кадры пакета.

            Параметры — в пути, тела запроса нет. Ответ — единая обёртка со сводкой по кадрам
            (форма, страница, уверенность, альтернативы).

            Штатно опознание выполняет фоновая очередь автоматически (ADR-014): результат уже готов
            к моменту, когда пакет берёт оператор. Этот endpoint — ручной повтор (после пересъёмки/правки
            состава кадров) и инспекция результата; окончательное слово за оператором (выбор формы вручную
            авторитетнее детектора, ADR-015).
            """;

        public static string Body = $"""
            ## Command

            {SdaidCode.OfClass<ClassifyFrameCommand>()}

            ## Result

            {SdaidCode.OfClass<ClassifyFrameResult>()}

            ## Payload

            {SdaidCode.OfClass<ClassifiedPacketPayload>()}

            ## FrameInfo

            {SdaidCode.OfClass<ClassifiedFrameInfo>()}

            ## Candidate

            {SdaidCode.OfClass<FormCandidate>()}
            """;
    }

    /// <summary>Команда опознания: `FrameOrder` задан — один кадр; null — все кадры пакета.</summary>
    public class ClassifyFrameCommand : BasedCommand
    {
        public Guid PacketId { get; init; }

        /// <summary>Порядковый номер кадра; null — опознать все кадры.</summary>
        public int? FrameOrder { get; init; }
    }

    /// <summary>Результат опознания (обёртка + сводка по кадрам).</summary>
    public class ClassifyFrameResult : BasedResult
    {
        public ClassifiedPacketPayload? Payload { get; init; }
    }

    /// <summary>Итог: сколько кадров опознано/не опознано и результат по каждому.</summary>
    public class ClassifiedPacketPayload
    {
        public Guid PacketId { get; init; }
        public int Recognized { get; init; }
        public int Unrecognized { get; init; }
        public ClassifiedFrameInfo[] Frames { get; init; } = new ClassifiedFrameInfo[0];
    }

    /// <summary>Результат опознания одного кадра.</summary>
    public class ClassifiedFrameInfo
    {
        public int Order { get; init; }

        /// <summary>Опознанная форма; null — вид не опознан.</summary>
        public Guid? FormId { get; init; }

        /// <summary>Наименование опознанной формы; null — вид не опознан.</summary>
        public string? FormName { get; init; }

        /// <summary>Номер страницы опознанной формы; null — вид не опознан.</summary>
        public int? PageNumber { get; init; }

        /// <summary>Уверенность — отрыв лучшего кандидата от второго места (больше = увереннее).</summary>
        public double Confidence { get; init; }

        /// <summary>Ближайшие альтернативы (для ручного выбора оператором при низкой уверенности).</summary>
        public FormCandidate[] Alternatives { get; init; } = new FormCandidate[0];

        /// <summary>Причина, если вид не опознан (нет подходящей формы / уверенность ниже порога); null — успех.</summary>
        public string? Error { get; init; }
    }

    /// <summary>Кандидат опознания: форма, страница и её оценка соответствия (меньше — ближе).</summary>
    public class FormCandidate
    {
        public Guid FormId { get; init; }
        public string FormName { get; init; } = "";
        public int PageNumber { get; init; }
        public double Score { get; init; }
    }
}