C# 封装工业相机SDK:海康Halcon无缝对接
用 C# 封装一套工业相机通用 SDK:从海康到 Halcon 的无缝对接
在实际机器视觉项目中,我们往往需要同时对接多品牌工业相机(海康、Basler、大恒等),又要让采集到的图像能同时供给 WinForms/WPF 界面显示和 Halcon 算法流水线使用。如果每个项目都直接调用厂商原生 SDK,代码耦合严重、重复劳动多。本文分享我最近为
Halcon测量软件封装的一套通用相机库CameraLib的设计思路与核心实现。
资源地址点这里
一、项目定位与核心痛点
CameraLib 的定位非常明确:做一层轻量级的抽象,屏蔽厂商差异,统一设备生命周期管理,并原生支持双图像格式输出。
在项目初期,我们遇到了三个典型痛点:
- 厂商 SDK 差异大:海康用
MvCameraControl.Net,Basler 用Pylon.NET,接口命名、参数类型完全不同。 - 图像格式割裂:UI 层习惯用
System.Drawing.Bitmap,而 Halcon 视觉算法只认HObject,来回转换既繁琐又容易出错。 - 触发模式混乱:软触发、硬触发、连续采集的切换逻辑如果散落在业务代码里,维护起来非常痛苦。
于是,我决定基于 .NET 8 编写一套自己的封装,从根源上解决这些问题。
二、整体架构:接口 + 抽象基类 + 品牌实现
项目结构非常清晰,只有三层:
CameraLib
├── Abstractions
│ ├── ICamera.cs // 通用接口契约
│ ├── BaseCamera.cs // 模板方法 + 同步取图封装
│ └── BaseCameraConfig.cs // 配置实体
└── Devices
└── Hik
└── HiKCamera.cs // 海康具体实现
2.1 接口层:ICamera
ICamera 定义了所有品牌相机必须具备的能力,分为两大类:
- 设备操作:枚举、连接、开始/停止采集、软触发、同步取图
- 参数配置:曝光、增益、触发模式/源/极性/延时/滤波、IO 控制、白平衡
特别值得一提的是,接口中设计了两个回调,分别输出不同格式:
/// Bitmap 图像采集回调,供 WinForms/WPF 显示使用
Action<string, Bitmap> ActionGetImage { get; set; }
/// Halcon HObject 图像采集回调,直接送入算法流程
Action<string, HObject> ActionGetHImage { get; set; }
这样上层模块可以按需订阅,不需要自己做格式转换,做到了职责分离。
2.2 抽象基类:BaseCamera
BaseCamera 是项目的核心,它用 模板方法模式 封装了通用业务逻辑,让子类只关注与 SDK 交互的“最小必要实现”。
亮点 1:同步阻塞取图
工业场景中经常需要这样的调用方式:“给我拍一张图,我等着用”。基类内部用 AutoResetEvent 实现了阻塞等待:
public virtual bool GetImage(out Bitmap bitmap, int timeout = 3000)
{
bitmap = null;
lock (_imageLock) { _callbackBitmap = null; }
_grabEvent.Reset();
bool signaled = _grabEvent.WaitOne(timeout);
if (signaled)
{
lock (_imageLock)
{
if (_callbackBitmap != null)
bitmap = new Bitmap(_callbackBitmap); // 深拷贝,避免外部释放影响缓存
}
}
return bitmap != null;
}
子类在 SDK 图像回调里只需调用 OnImageGrabbed(bitmap),基类会自动唤醒阻塞线程并触发外部事件。
亮点 2:一键软触发取图
很多视觉检测场景是:“平时硬触发连续跑,偶尔人工点一下按钮单张取图”。基类封装了 GetImageWithSoftTrigger,内部自动完成:
- 保存当前触发模式
- 切换到软触发 → 发送触发命令 → 阻塞等待图像
- 停止采集并恢复原来的触发模式
业务层一行代码搞定,完全不用关心状态切换的细节。
亮点 3:批量参数读写
public virtual void SetCamConfig(CamConfig config)
{
if (config == null) return;
SetExpouseTime(config.ExposureTime);
SetTriggerMode(config.TriggerMode, config.TriggerSource);
SetGain(config.Gain);
SetTriggerDelay(config.TriggerDelay);
}
子类只需要实现单个参数的 Set/Get,组合逻辑由基类统一完成,减少了大量重复代码。
2.3 品牌实现:HiKCamera
目前先对接了海康机器人(Hikrobot),引用官方 MvCameraControl.Net SDK。实现类只需 Override 约十几个抽象方法,例如:
public override bool SetExpouseTime(float value)
{
return _device?.Parameters.SetFloatValue("ExposureTime", value) == MvError.MV_OK;
}
图像回调双输出是这里的重点。海康 SDK 的 FrameGrabedEvent 注册了两个 handler:
OnFrameGrabbed:将原始帧转为Bitmap,设置灰度调色板,调用基类OnImageGrabbedOnFrameGrabbedForHalcon:直接将非托管指针通过HOperatorSet.GenImageInterleaved/GenImage1生成HObject
两者各自管理缓冲区释放,互不干扰。
// Halcon 回调核心代码
if (pixelType == MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
HOperatorSet.GenImageInterleaved(out hImage, bufferPtr, "rgb",
width, height, "", "byte", width, height, 0, 0, -1, 0);
}
else if (pixelType == MvGvspPixelType.PixelType_Gvsp_Mono8)
{
HOperatorSet.GenImage1(out hImage, "byte", width, height, bufferPtr);
}
三、使用示例
接入海康相机只需要几行代码:
// 1. 实例化
var cam = new HiKCamera();
// 2. 枚举设备
var snList = cam.GetListEnum();
Console.WriteLine($"发现相机: {string.Join(", ", snList)}");
// 3. 连接并初始化(内部已注册回调、设置缓存、启动采集)
cam.InitDevice(snList[0]);
// 4. 订阅图像(同时拿到 Bitmap 和 Halcon 对象)
cam.ActionGetImage = (sn, bmp) => pictureBox1.Image = bmp;
cam.ActionGetHImage = (sn, ho) => halconWindow.DispObj(ho);
// 5. 参数调整
cam.SetExpouseTime(5000); // 5ms 曝光
cam.SetGain(2.5f);
// 6. 同步取一张图
if (cam.GetImageWithSoftTrigger(out Bitmap bmp))
{
bmp.Save("snap.jpg");
}
// 7. 释放
cam.Dispose();
简洁、直观,业务代码完全不需要感知底层 SDK 的差异。
四、后续规划
目前 CameraLib 已经完成了海康相机的完整对接,架构上预留了良好的扩展性:
- 接入 Basler(Pylon.NET)
- 接入 大恒图像(Galaxy SDK)
- 补充相机掉线重连机制
- 增加 ROI(感兴趣区域)参数配置
欢迎感兴趣的朋友一起探讨或贡献代码。
五、总结
这套库的核心设计思想就是 “把通用的留下来,把变化的交出去”:
ICamera保证多品牌替换时业务代码零改动BaseCamera把触发模式管理、同步取图、参数批量读写等脏活累活做完- 具体品牌子类只写最纯粹的 SDK 调用
如果你也在做 .NET + Halcon 的工业视觉项目,希望这篇文章能给你一些启发。好的封装不是追求大而全,而是让上层开发更省心,让底层适配更规范。
项目环境:
.NET 8-windows|MVTec.HalconDotNet-Windows|MvCameraControl.Net
以上就是本次分享的全部内容,感谢阅读!如果有任何疑问或建议,欢迎在评论区留言交流。
更多推荐
所有评论(0)