4. pylon SDK核心对象模型
pylon SDK 的 C++ 接口围绕一组职责清晰的核心类构建。理解这些类之间的生命周期依赖、所有权关系和异常传播路径,是写出稳定工业视觉程序的前提。本节从初始化与终止的边界条件出发,依次梳理设备枚举、相机控制、格式转换、抓取结果持有以及异常处理的对象模型。
PylonInitialize / PylonTerminate:进程级生命周期边界
pylon 的运行时环境由 PylonInitialize() 和 PylonTerminate() 成对管理。前者加载传输层(如 GigE、USB3 Vision)、初始化底层驱动与资源池;后者释放这些全局资源。二者必须在同一进程内严格配对,且 PylonTerminate() 之后不应再创建任何 pylon 对象。
典型用法是在 main() 入口调用 PylonInitialize(),在退出前调用 PylonTerminate()。若使用 RAII 风格,可借助 PylonAutoInitTerm 对象,在构造时自动初始化、析构时自动终止,避免异常路径下遗漏终止调用。需要注意:PylonTerminate() 会阻塞直到所有相机对象、抓取结果和转换器被释放,因此若仍有 CInstantCamera 实例存活,终止可能挂起或返回错误。
DllMain 中调用 PylonInitialize(),也不要在多个线程中重复初始化。pylon 的初始化是进程级、非线程安全的,重复调用虽可能返回成功,但终止次数必须与初始化次数匹配。CDeviceInfo:设备发现与信息载体
CDeviceInfo 是设备枚举的结果对象,由 CTlFactory::EnumerateDevices() 填充。它不直接控制相机,而是承载设备的静态描述信息,常用属性包括:
GetFriendlyName():用户可读的设备名,如 "Basler acA1920-40gc";GetDeviceClass():设备类别,如 "BaslerGigE"、"BaslerUsb";GetSerialNumber():序列号,用于唯一标识;GetUserDefinedName():用户自定义名称,便于多相机场景区分;GetModelName()、GetInterfaceID():型号与接口标识。
在多相机系统中,通常先枚举所有 CDeviceInfo,再按序列号或用户自定义名筛选目标设备,最后通过 CInstantCamera::Attach() 或构造函数绑定。设备信息对象本身不持有硬件资源,可安全复制和缓存。
CInstantCamera:相机控制与抓取入口
CInstantCamera 是应用层最核心的类,封装了设备连接、参数配置、抓取循环和事件回调。它继承自 CPylonImageEventHandler 等基类,但日常使用主要关注以下接口:
Open()/Close():打开或关闭与设备的会话;IsOpen():判断当前连接状态;GetNodeMap():获取 GenICam 节点映射,用于读写曝光、增益、触发模式等参数;StartGrabbing()/StopGrabbing():启动或停止抓取引擎;RetrieveResult():从抓取队列取出一个结果,返回CGrabResultPtr;RegisterImageEventHandler():注册回调,实现事件驱动抓取。
抓取模式分为连续抓取(Continuous)和单帧抓取(OneByOne)。连续模式下相机持续采集,应用按需取帧;单帧模式下每次 RetrieveResult() 后需重新触发。选择哪种模式取决于触发源和实时性要求。
CImageFormatConverter:像素格式转换
相机输出的原始像素格式(如 BayerRG8、Mono12、YUV422)往往不能直接用于显示或算法处理。CImageFormatConverter 负责将抓取结果转换为目标格式,典型目标包括 BGR8、RGB8、Mono8。
使用流程为:创建转换器实例,调用 SetOutputPixelFormat() 指定目标格式,再调用 Convert() 将 CGrabResultPtr 转换为 CPylonImage。转换器可复用,不必每帧重建。对于 Bayer 格式,转换器会自动执行去马赛克;对于高位深格式,会按比例缩放到 8 位。若目标格式与源格式相同,转换器会直接拷贝,此时应避免不必要的转换开销。
CGrabResultPtr 与智能指针资源管理
CGrabResultPtr 是 CGrabResultData 的智能指针封装,采用引用计数管理底层缓冲区。当最后一个 CGrabResultPtr 析构时,缓冲区自动归还给 pylon 的抓取池。这一机制使得多线程传递抓取结果时无需手动加锁释放,但必须注意:
- 不要长期持有
CGrabResultPtr,否则会耗尽抓取池导致RetrieveResult()超时; - 在回调中处理完图像后应及时释放引用,或将其转换为
CPylonImage深拷贝; CGrabResultPtr可安全复制,复制操作仅增加引用计数,不拷贝像素数据。
pylon 的智能指针体系还包括 CPylonImage、CPylonImageWindow 等,均遵循相同的引用计数语义。理解这一点,就能避免“图像突然变黑”或“抓取卡死”这类由缓冲区生命周期引发的典型问题。
异常类层次结构
pylon 的异常体系以 GenericException 为基类,派生出的常用异常包括:
LogicalException:逻辑错误,如参数越界、状态非法;RuntimeException:运行时错误,如设备断开、超时;TimeoutException:RetrieveResult()超时;InvalidArgumentException:传入参数不合法;AccessException:访问被拒绝,如设备被占用。
所有异常均提供 GetDescription() 和 GetErrorCode(),便于日志记录和错误分类。推荐在抓取循环外层捕获 GenericException,根据具体类型决定重试、重连或终止。例如,TimeoutException 通常可忽略并继续下一帧,而 RuntimeException 往往意味着设备已离线,需要重新枚举和连接。
对象协作与资源释放顺序
一个完整的抓取周期涉及多个对象的协作:PylonInitialize() 建立运行时环境后,CDeviceInfo 描述可用设备,CInstantCamera 绑定并打开设备,StartGrabbing() 启动采集,RetrieveResult() 返回 CGrabResultPtr,再经 CImageFormatConverter 转换为 CPylonImage 供后续处理。释放顺序与创建顺序相反:先停止抓取、关闭相机,再释放转换器和图像,最后调用 PylonTerminate()。
若在抓取循环中发生异常,应确保 StopGrabbing() 和 Close() 被调用。推荐使用 RAII 包装类或在 catch 块中显式清理。对于长时间运行的应用,还应定期检查 IsOpen() 和抓取统计信息,及时发现设备掉线或缓冲区泄漏。
PylonTerminate() 前打印所有存活对象的引用计数,若计数不为零,说明存在未释放的 CGrabResultPtr 或 CInstantCamera,这是定位资源泄漏最直接的手段。