用 C# 封装一套工业相机通用 SDK:从海康到 Halcon 的无缝对接

在实际机器视觉项目中,我们往往需要同时对接多品牌工业相机(海康、Basler、大恒等),又要让采集到的图像能同时供给 WinForms/WPF 界面显示和 Halcon 算法流水线使用。如果每个项目都直接调用厂商原生 SDK,代码耦合严重、重复劳动多。本文分享我最近为 Halcon测量软件 封装的一套通用相机库 CameraLib 的设计思路与核心实现。
资源地址点这里


一、项目定位与核心痛点

CameraLib 的定位非常明确:做一层轻量级的抽象,屏蔽厂商差异,统一设备生命周期管理,并原生支持双图像格式输出

在项目初期,我们遇到了三个典型痛点:

  1. 厂商 SDK 差异大:海康用 MvCameraControl.Net,Basler 用 Pylon.NET,接口命名、参数类型完全不同。
  2. 图像格式割裂:UI 层习惯用 System.Drawing.Bitmap,而 Halcon 视觉算法只认 HObject,来回转换既繁琐又容易出错。
  3. 触发模式混乱:软触发、硬触发、连续采集的切换逻辑如果散落在业务代码里,维护起来非常痛苦。

于是,我决定基于 .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,内部自动完成:

  1. 保存当前触发模式
  2. 切换到软触发 → 发送触发命令 → 阻塞等待图像
  3. 停止采集并恢复原来的触发模式

业务层一行代码搞定,完全不用关心状态切换的细节。

亮点 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,设置灰度调色板,调用基类 OnImageGrabbed
  • OnFrameGrabbedForHalcon:直接将非托管指针通过 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


以上就是本次分享的全部内容,感谢阅读!如果有任何疑问或建议,欢迎在评论区留言交流。

更多推荐