2. VisionPro核心对象模型
VisionPro 的二次开发之所以能保持较高的工程复用率,根本原因在于它把“视觉处理”抽象成了一套职责清晰、生命周期明确的对象模型。理解这套模型,比记住某个 API 的调用顺序更重要:只要知道每个对象“负责什么、由谁创建、何时释放、在哪个线程上运行”,就能在写 C# 或 VB.NET 程序时快速定位问题,也能设计出可复用的工具与作业结构。
一、四类核心对象的职责边界
VisionPro 中最常打交道的对象可以按“采集—组织—执行—调度”四个层次来理解:
- CogFrameGrabber:图像采集对象,负责与相机、图像文件或图像序列对接,把外部图像源转换为 VisionPro 内部的
ICogImage。它不参与算法运算,只负责“把图拿进来”。 - CogTool:最小可执行视觉单元,例如
CogPMAlignTool、CogBlobTool、CogCaliperTool。每个 Tool 封装一个算法、一组输入参数和一组输出结果,是复用粒度最细的对象。 - CogToolGroup:工具容器,把若干 Tool 按处理顺序组织在一起,形成一条可独立运行的视觉流程。它本身不实现算法,但负责工具的执行顺序、输入输出传递和整体运行状态。
- CogJob:作业对象,是运行时调度的顶层单元。一个 Job 通常包含一个或多个 ToolGroup,并绑定图像源、触发方式和结果输出,是“一次完整检测任务”的载体。
这四者的关系可以概括为:FrameGrabber 提供图像,Tool 完成算法,ToolGroup 组织流程,Job 驱动整条链路。下面这张图展示了它们之间的持有关系与数据流向。
二、生命周期:从创建到释放
VisionPro 对象的生命周期可以分成四个阶段:创建、配置、运行、释放。这四个阶段在代码中对应不同的操作,也对应不同的资源管理责任。
创建阶段。Tool 和 ToolGroup 通常有两种创建方式:一种是在代码中直接 new,另一种是从已保存的 .vpp 文件加载。前者适合动态生成流程,后者适合复用已经调好的参数。FrameGrabber 的创建则依赖具体采集卡或相机 SDK,例如 new CogFrameGrabberGigE() 或通过 CogFrameGrabbers 集合枚举可用设备。Job 一般由 CogJob 类实例化,也可以从作业文件加载。
配置阶段。这是最容易被忽视但最影响稳定性的阶段。Tool 需要设置输入图像、区域、阈值、模板等参数;ToolGroup 需要确定工具顺序和输入输出映射;Job 需要绑定 FrameGrabber、设置触发模式(软触发或硬触发)以及结果输出方式。配置完成后,通常调用一次 Run() 做验证,确认工具能正常输出结果。
运行阶段。Job 的 Run() 会依次驱动 ToolGroup 和其中的 Tool。每个 Tool 执行后会产生 Results 集合,后续 Tool 可以通过 Inputs 引用前序 Tool 的输出。例如 PMAlign 工具输出位姿,Blob 工具可以用这个位姿做坐标变换后再检测。运行阶段的关键是“顺序”和“数据传递”,而不是单个工具的参数。
释放阶段。VisionPro 中不少对象持有非托管资源,尤其是 FrameGrabber 和图像对象。工程上应遵循“谁创建、谁释放”的原则,在 Dispose() 或 using 块中释放。如果 Job 长期运行,建议在停止采集后显式释放 FrameGrabber,避免相机被占用导致下次启动失败。
三、一个可参考的代码骨架
下面这段 C# 示例展示了四类对象在一次检测任务中的典型协作方式。参数为教学示例,实际使用时需按相机和工件调整。
// 1. 创建采集对象(示例:GigE 相机)
CogFrameGrabberGigE grabber = new CogFrameGrabberGigE();
grabber.Connect("192.168.1.10");
// 2. 创建工具并配置
CogPMAlignTool pmAlign = new CogPMAlignTool();
pmAlign.Pattern.TrainImage = grabber.Acquire(); // 示例:取一帧训练模板
pmAlign.RunParams.AcceptThreshold = 0.7; // 示例阈值
CogBlobTool blob = new CogBlobTool();
blob.InputImage = pmAlign.OutputImage; // 引用前序输出
// 3. 组织工具组
CogToolGroup toolGroup = new CogToolGroup();
toolGroup.Tools.Add(pmAlign);
toolGroup.Tools.Add(blob);
// 4. 创建作业并绑定
CogJob job = new CogJob();
job.VisionTool = toolGroup;
job.ImageSource = grabber;
// 5. 运行并读取结果
job.Run();
double score = pmAlign.Results[0].Score;
int blobCount = blob.Results.GetBlobs().Count;
// 6. 释放
grabber.Disconnect();
grabber.Dispose();
这段骨架里最值得注意的是第 2 步和第 3 步的分工:Tool 负责“算”,ToolGroup 负责“排”,Job 负责“跑”。如果把所有逻辑都塞进一个 Tool 里,复用性会迅速下降;反过来,如果 ToolGroup 里工具顺序混乱,结果也会不可预期。
四、常见误区与判断依据
实际开发中,几个高频问题值得提前规避:
- 把 FrameGrabber 当成算法对象:它只负责取图,不要在里面做预处理。预处理应交给独立的 Tool。
- 在 ToolGroup 外部直接调用 Tool.Run():这会绕过工具组的顺序管理,导致输入输出引用失效。正确做法是让 Job 或 ToolGroup 统一驱动。
- 忘记释放采集对象:表现为第二次运行时报“相机被占用”。判断依据是异常信息中是否出现设备句柄相关提示。
- Job 与 ToolGroup 职责混淆:Job 管调度和图像源,ToolGroup 管流程。把图像源绑到 ToolGroup 上,会让复用变得困难。