# Vision 模块使用文档

## 1. 文档范围

本文档说明当前 [`MainShell/Vision`](MainShell/Vision) 模块在“一个算法一个文件夹”的结构下的职责划分和推荐使用方式，重点覆盖：

- [`IImageCaptureService`](MainShell/Vision/ImageCapture/IImageCaptureService.cs) / [`ImageCaptureService`](MainShell/Vision/ImageCapture/ImageCaptureService.cs)：负责按当前已准备好的相机模式执行采图
- [`IFindTemplateService`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:10) / [`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14)：负责模板匹配算法的采图与处理
- [`VisionParController`](MainShell/Models/VisionParController.cs)：负责视觉前置硬件参数准备，包括相机参数、触发模式和抓流状态
- [`FindTemplateRequest`](MainShell/Vision/FindTemplate/FindTemplateRequest.cs:8) / [`FindTemplateImageRequest`](MainShell/Vision/FindTemplate/FindTemplateImageRequest.cs:6)：负责模板匹配请求建模
- [`VisionProcessResult`](MainShell/Vision/Common/VisionProcessResult.cs:8) / [`VisionProcessResult<TData>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9)：负责承载通用处理状态与强类型算法结果
- [`ImageCaptureResult`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:25) / [`ImageCaptureStatus`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:11)：负责承载采图状态、消息与图像对象

当前目录划分如下：

- `Common/`：共享结果模型与算法参数基类
- `ImageCapture/`：采图接口、实现与采图相关模型
- `FindTemplate/`：模板匹配算法接口、实现、请求、参数、结果
- `FindMark/`：Mark 识别参数与后续算法扩展入口
- `FindDie/`：Die 定位参数与后续算法扩展入口
- `FindCenter/`：中心识别草稿算法骨架

## 2. 当前模块职责边界

### 2.1 [`VisionParController`](MainShell/Models/VisionParController.cs)

负责视觉前置硬件准备：

- 设置曝光和增益
- 设置触发模式
- 设置触发源
- 设置触发延时
- 启动或停止抓流
- 设置光源参数

### 2.2 [`ImageCaptureService`](MainShell/Vision/ImageCapture/ImageCaptureService.cs)

只负责执行采图动作：

- 不再自动切换触发模式
- 不再自动切换触发源
- 不再自动设置触发延时
- 不再自动启停抓流
- 同一相机的采图请求在服务内部按相机粒度串行执行
- 通过 [`ImageCaptureResult`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:25) 返回成功、取消、无帧或设备状态异常等结果

调用方必须在采图前通过 [`VisionParController`](MainShell/Models/VisionParController.cs) 或其他上层硬件控制流程准备好相机状态。

### 2.3 [`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14)

负责：

- 模板匹配参数校验
- 模板匹配采图流程组织
- 单个模板匹配结果构建
- 多个模板匹配结果集合构建
- 返回 [`VisionProcessResult<FindTemplateResult>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9) 或 [`VisionProcessResult<FindTemplatesResult>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9)

当前模板匹配算法本体仍为占位实现，尚未接入真实视觉算法库。当前占位实现会明确返回失败，避免上层流程误判为识别成功。

## 3. 当前推荐结构

当前推荐使用“一个算法一个实现”的结构，即：

- 每个算法拥有独立接口，例如 [`IFindTemplateService`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:10)
- 每个算法拥有独立实现，例如 [`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14)
- 每个算法拥有独立请求、参数、结果模型
- 不再使用统一算法总入口服务

推荐未来新增算法继续按相同方式组织，例如：

- `FindMark/IFindMarkService.cs`
- `FindMark/FindMarkService.cs`
- `FindMark/FindMarkRequest.cs`
- `FindMark/FindMarkResult.cs`

如果算法需要“已有图像直接处理”的场景，可按需再增加 `XXXImageRequest.cs`。

## 4. 推荐调用顺序

推荐顺序如下：

1. 先通过 [`VisionParController`](MainShell/Models/VisionParController.cs) 准备相机与光源参数
2. 再通过 [`IImageCaptureService`](MainShell/Vision/ImageCapture/IImageCaptureService.cs) 执行采图，或由具体算法服务内部采图
3. 最后通过具体算法服务，例如 [`IFindTemplateService`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:10)，执行算法

## 5. 采图前提说明

### 5.1 连续流采图

当 [`CameraCaptureMode.Stream`](MainShell/Vision/ImageCapture/CameraCaptureMode.cs) 用于采图时，调用前应确保：

- 相机已打开
- 相机已处于 grabbing 状态
- 相机已被配置为适合连续流取图的工作模式

### 5.2 软触发采图

当 [`CameraCaptureMode.SoftTrigger`](MainShell/Vision/ImageCapture/CameraCaptureMode.cs) 用于采图时，调用前应确保：

- 相机已打开
- 相机已处于 grabbing 状态
- 相机已被配置为软触发模式
- 触发源已设置为软件触发

如果上述前提不满足，[`ImageCaptureService`](MainShell/Vision/ImageCapture/ImageCaptureService.cs) 不会代替调用方修改硬件状态，而是直接失败并记录日志。

## 6. 请求模型

模板匹配当前使用独立请求模型：

- [`FindTemplateRequest`](MainShell/Vision/FindTemplate/FindTemplateRequest.cs:8)：用于“采图 + 算法”流程
- [`FindTemplateImageRequest`](MainShell/Vision/FindTemplate/FindTemplateImageRequest.cs:6)：用于“已有图像 + 算法”流程

推荐新算法默认最小模型集为：

- `XXXRequest`
- `XXXResult`

只有在算法确实需要时，再增加：

- `XXXParameters`
- `XXXImageRequest`

## 7. 结果模型

当前结果模型已调整为四层：

- [`VisionProcessResult`](MainShell/Vision/Common/VisionProcessResult.cs:8)：仅承载成功/失败/异常/消息
- [`VisionProcessResult<TData>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9)：承载强类型算法结果数据
- [`ImageCaptureResult`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:25)：承载采图动作结果、状态、消息和图像对象
- [`VisionMatchResult`](MainShell/Vision/Common/VisionMatchResult.cs:6)：承载扩展匹配信息

模板匹配当前返回：

- [`VisionProcessResult<FindTemplateResult>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9)
- [`VisionProcessResult<FindTemplatesResult>`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9)

[`FindTemplateResult`](MainShell/Vision/FindTemplate/FindTemplateResult.cs:6) 当前包含：

- `CenterX`
- `CenterY`
- `Score`
- `Match`

[`FindTemplatesResult`](MainShell/Vision/FindTemplate/FindTemplatesResult.cs:6) 当前包含：

- `Items`

## 8. 当前算法实现状态

当前状态如下：

- 采图服务已按新职责边界实现
- 模板匹配保持独立接口与独立服务实现
- 采图结果已区分成功、取消、设备状态错误、无帧和驱动异常
- 已不再使用统一视觉算法入口服务
- [`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14) 当前仍为占位实现，尚未接入真实算法
- 已预留单个模板匹配与多个模板匹配两套接口
- 多个模板匹配当前已明确约定输出规则：保留底层原始顺序，不做得分筛选、不做去重、不做聚类合并

因此本模块当前适合作为：

- 视觉接入骨架
- 采图与参数联调骨架
- 按算法拆服务的长期结构基础

## 9. 取消与超时说明

[`ImageCaptureService`](MainShell/Vision/ImageCapture/ImageCaptureService.cs) 当前采图仍基于底层同步驱动接口。

这意味着：

- `CancellationToken` 主要控制外层任务流程
- 取消仅能在进入驱动前或驱动返回后生效
- 不一定能真正中断驱动内部阻塞调用
- 软触发底层超时仍受驱动实现限制

[`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14) 当前会区分：

- 调用方主动取消
- 参数校验失败
- 算法未实现
- 采图阶段失败

## 10. 图像对象生命周期说明

- 通过 [`FindTemplateService.ProcessAsync()`](MainShell/Vision/FindTemplate/FindTemplateService.cs:22) 内部采集到的图像，由服务内部释放
- 通过 [`IFindTemplateService.ProcessImageAsync()`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:22) 或 [`IFindTemplateService.ProcessMultipleImageAsync()`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:36) 传入的已有图像，由调用方负责释放
- 调用方不应在算法执行期间提前释放正在使用的图像对象

## 11. 多模板匹配规则

当前多个模板匹配算法接口约定如下：

1. 输入一张图像和一组模板匹配参数
2. 调用底层模板匹配能力，获取原始匹配结果集合
3. 不按 `MinScore` 做二次筛选
4. 不做最佳值选优
5. 不做结果去重
6. 不做聚类合并
7. 保持底层算法返回顺序
8. 将原始匹配结果逐个映射为 [`FindTemplateResult`](MainShell/Vision/FindTemplate/FindTemplateResult.cs:6) 并输出到 [`FindTemplatesResult`](MainShell/Vision/FindTemplate/FindTemplatesResult.cs:6)

## 12. 新增算法模板建议

推荐新增一个普通算法时，默认最小文件集合为：

- `IXXXService.cs`
- `XXXService.cs`
- `XXXRequest.cs`
- `XXXResult.cs`

复杂算法再按需增加：

- `XXXParameters.cs`
- `XXXImageRequest.cs`

这样可以兼顾：

- 一算法一实现的清晰边界
- 文件数量不过度膨胀
- 后续维护和问题定位的可读性

## 13. 代码使用示例

### 13.1 示例一：由算法服务内部采图后执行单个模板匹配

这是推荐的业务调用方式。先通过 [`VisionParController`](MainShell/Models/VisionParController.cs) 准备相机状态，再调用 [`IFindTemplateService.ProcessAsync()`](MainShell/Vision/FindTemplate/IFindTemplateService.cs:14)。

```csharp
using MainShell.Common;
using MainShell.Vision;
using System.Threading;
using System.Threading.Tasks;

public class VisionUsageExample
{
    private readonly IFindTemplateService _findTemplateService;

    public VisionUsageExample(IFindTemplateService findTemplateService)
    {
        _findTemplateService = findTemplateService;
    }

    public async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        FindTemplateRequest request = new FindTemplateRequest();
        request.CameraSource = CameraType.UpCamera;
        request.TimeoutMilliseconds = 3000;
        request.CaptureOptions.TimeoutMilliseconds = 2000;
        request.CaptureOptions.CaptureMode = CameraCaptureMode.Stream;
        request.Parameters.UseRoi = true;
        request.Parameters.RoiName = "DieCenterRoi";
        request.Parameters.TemplatePath = "Template/DieCenter";
        request.Parameters.MinScore = 0.8d;

        VisionProcessResult<FindTemplateResult> result = await _findTemplateService.ProcessAsync(
            request,
            cancellationToken).ConfigureAwait(false);

        if (!result.Succeeded)
        {
            string failureMessage = result.Message;
            return;
        }

        FindTemplateResult data = result.Data;
        double centerX = data.CenterX;
        double centerY = data.CenterY;
        double score = data.Score;
    }
}
```

### 13.2 示例二：对已有图像执行多个模板匹配

这个方式适合调试、联调或者需要复用同一帧图像的场景。

```csharp
using MainShell.Common;
using MainShell.Models;
using MainShell.Vision;
using System.Threading;
using System.Threading.Tasks;

public class VisionImageUsageExample
{
    private readonly IImageCaptureService _imageCaptureService;
    private readonly IFindTemplateService _findTemplateService;

    public VisionImageUsageExample(
        IImageCaptureService imageCaptureService,
        IFindTemplateService findTemplateService)
    {
        _imageCaptureService = imageCaptureService;
        _findTemplateService = findTemplateService;
    }

    public async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        CameraCaptureOptions captureOptions = new CameraCaptureOptions();
        captureOptions.TimeoutMilliseconds = 2000;
        captureOptions.CaptureMode = CameraCaptureMode.Stream;

        ImageCaptureResult captureResult = await _imageCaptureService.CaptureAsync(
            CameraType.UpCamera,
            captureOptions,
            cancellationToken).ConfigureAwait(false);

        if (!captureResult.Succeeded)
        {
            string captureFailureMessage = captureResult.Message;
            return;
        }

        MxImage image = captureResult.Image;
        try
        {
            FindTemplateImageRequest request = new FindTemplateImageRequest();
            request.TimeoutMilliseconds = 3000;
            request.Parameters.UseRoi = false;
            request.Parameters.TemplatePath = "Template/DieCenter";
            request.Parameters.MinScore = 0.8d;

            VisionProcessResult<FindTemplatesResult> result = await _findTemplateService.ProcessMultipleImageAsync(
                image,
                request,
                cancellationToken).ConfigureAwait(false);

            if (!result.Succeeded)
            {
                string failureMessage = result.Message;
                return;
            }

            FindTemplatesResult data = result.Data;
            int count = data.Items.Count;
        }
        finally
        {
            if (image != null)
            {
                image.Dispose();
            }
        }
    }
}
```

### 13.3 示例三：如何判断采图失败原因

[`ImageCaptureResult`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:25) 适合在上层做更细的失败分流。

```csharp
using MainShell.Common;
using MainShell.Vision;
using System.Threading;
using System.Threading.Tasks;

public class CaptureResultExample
{
    private readonly IImageCaptureService _imageCaptureService;

    public CaptureResultExample(IImageCaptureService imageCaptureService)
    {
        _imageCaptureService = imageCaptureService;
    }

    public async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        ImageCaptureResult captureResult = await _imageCaptureService.CaptureAsync(
            CameraType.UpCamera,
            2000,
            CameraCaptureMode.Stream,
            cancellationToken).ConfigureAwait(false);

        if (captureResult.Succeeded)
        {
            return;
        }

        switch (captureResult.Status)
        {
            case ImageCaptureStatus.Cancelled:
                break;

            case ImageCaptureStatus.CameraNotFound:
            case ImageCaptureStatus.CameraNotOpen:
            case ImageCaptureStatus.CameraNotGrabbing:
            case ImageCaptureStatus.NoFrame:
            case ImageCaptureStatus.DriverError:
                string message = captureResult.Message;
                break;

            default:
                break;
        }
    }
}
```

### 13.4 示例四：新增一个新算法时的推荐骨架

以 `FindMark` 为例，推荐至少准备：

- `FindMark/IFindMarkService.cs`
- `FindMark/FindMarkService.cs`
- `FindMark/FindMarkRequest.cs`
- `FindMark/FindMarkResult.cs`

如果需要已有图像处理，再增加：

- `FindMark/FindMarkImageRequest.cs`

如果参数较复杂，再增加：

- `FindMark/FindMarkParameters.cs`

服务实现建议沿用 [`FindTemplateService`](MainShell/Vision/FindTemplate/FindTemplateService.cs:14) 的结构：

- `ProcessAsync()` 负责“采图 + 单个模板匹配”
- `ProcessImageAsync()` 负责“已有图像 + 单个模板匹配”
- `ProcessMultipleAsync()` 负责“采图 + 多个模板匹配”
- `ProcessMultipleImageAsync()` 负责“已有图像 + 多个模板匹配”
- `NormalizeRequest()` 负责标准化
- `ValidateRequest()` 负责参数校验
- `CloneParameters()` 负责避免共享可变输入对象

这样可以保证不同算法之间结构一致，便于维护和扩展。
