# 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`](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`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9) 或 [`VisionProcessResult`](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`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9):承载强类型算法结果数据 - [`ImageCaptureResult`](MainShell/Vision/ImageCapture/ImageCaptureResult.cs:25):承载采图动作结果、状态、消息和图像对象 - [`VisionMatchResult`](MainShell/Vision/Common/VisionMatchResult.cs:6):承载扩展匹配信息 模板匹配当前返回: - [`VisionProcessResult`](MainShell/Vision/Common/VisionProcessResultOfT.cs:9) - [`VisionProcessResult`](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 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 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()` 负责避免共享可变输入对象 这样可以保证不同算法之间结构一致,便于维护和扩展。