Files

XP.Camera 使用说明

基于 .NET 8 WPF 的工业相机控制类库,采用工厂模式 + 统一接口设计,支持多品牌相机扩展。当前已实现 Basler pylon SDK 驱动。

环境要求

  • .NET 8 SDK
  • Windows 操作系统
  • Basler pylon 8 SDK(已安装并配置环境变量)

项目结构

XP.Camera/
├── ICameraController.cs       # 控制器接口 + 工厂接口
├── CameraFactory.cs           # 统一工厂(根据品牌创建控制器)
├── BaslerCameraController.cs  # Basler 实现
├── CameraModels.cs            # CameraInfo、ImageGrabbedEventArgs、GrabErrorEventArgs
├── CameraExceptions.cs        # CameraException、ConnectionLostException、DeviceNotFoundException
├── PixelConverter.cs           # 像素数据 → WPF BitmapSource 转换工具
└── XP.Camera.csproj

所有类型统一在 XP.Camera 命名空间下。

项目引用

<ProjectReference Include="..\XP.Camera\XP.Camera.csproj" />

快速开始

1. 通过工厂创建控制器

using XP.Camera;

ICameraFactory factory = new CameraFactory();
using ICameraController camera = factory.CreateController("Basler");

CameraInfo info = camera.Open();
Console.WriteLine($"已连接: {info.ModelName} (SN: {info.SerialNumber})");

2. 依赖注入方式(推荐)

在 Prism / DI 容器中注册:

// App.xaml.cs
var config = AppConfig.Load();
containerRegistry.RegisterSingleton<ICameraFactory, CameraFactory>();
containerRegistry.RegisterSingleton<ICameraController>(() =>
    new CameraFactory().CreateController(config.CameraType));

ViewModel 中注入使用:

public class MyViewModel
{
    private readonly ICameraController _camera;

    public MyViewModel(ICameraController camera)
    {
        _camera = camera;
    }
}

相机品牌通过配置文件 config.json 指定:

{
  "CameraType": "Basler"
}

3. 实时图像显示(WPF 绑定)

_camera.ImageGrabbed += (s, e) =>
{
    // PixelConverter 返回已 Freeze 的 BitmapSource,可跨线程传递
    var bitmap = PixelConverter.ToBitmapSource(
        e.PixelData, e.Width, e.Height, e.PixelFormat);

    Application.Current.Dispatcher.Invoke(() =>
    {
        CameraImageSource = bitmap;
    });
};

XAML 绑定:

<Image Source="{Binding CameraImageSource}" Stretch="Uniform" />

4. 软件触发采集流程

camera.Open();
camera.SetExposureTime(10000);  // 10ms
camera.StartGrabbing();

// 每次需要采集时调用(结果通过 ImageGrabbed 事件返回)
camera.ExecuteSoftwareTrigger();

camera.StopGrabbing();
camera.Close();

5. 实时连续采集(链式触发)

收到上一帧后立即触发下一帧,自动适配任何帧率:

private volatile bool _liveViewRunning;

_camera.ImageGrabbed += (s, e) =>
{
    var bitmap = PixelConverter.ToBitmapSource(e.PixelData, e.Width, e.Height, e.PixelFormat);
    Application.Current.Dispatcher.Invoke(() => CameraImageSource = bitmap);

    if (_liveViewRunning)
        _camera.ExecuteSoftwareTrigger();  // 链式触发下一帧
};

// 启动实时
_camera.StartGrabbing();
_liveViewRunning = true;
_camera.ExecuteSoftwareTrigger();  // 触发第一帧

// 停止实时
_liveViewRunning = false;

核心接口

ICameraController

方法 说明
Open() 打开连接,返回 CameraInfo
Close() 关闭连接(自动停止采集)
StartGrabbing() 以软件触发模式启动采集
ExecuteSoftwareTrigger() 触发一帧采集
StopGrabbing() 停止采集

参数读写

方法 说明
Get/SetExposureTime(double) 曝光时间(微秒)
Get/SetGain(double) 增益值
Get/SetWidth(int) 图像宽度(自动校正到有效值)
Get/SetHeight(int) 图像高度(自动校正到有效值)
Get/SetPixelFormat(string) 像素格式(Mono8 / BGR8 / BGRA8

ICameraFactory

方法 说明
CreateController(string cameraType) 根据品牌名创建控制器

当前支持的 cameraType 值:"Basler"

事件

事件 说明 触发线程
ImageGrabbed 成功采集一帧图像 StreamGrabber 回调线程
GrabError 图像采集失败 StreamGrabber 回调线程
ConnectionLost 相机连接意外断开 pylon SDK 事件线程

所有事件均在非 UI 线程触发。更新 WPF 界面时需通过 Dispatcher.Invoke 调度。 PixelConverter.ToBitmapSource() 返回的 BitmapSource 已调用 Freeze(),可直接跨线程传递。

异常处理

try
{
    camera.Open();
}
catch (DeviceNotFoundException)
{
    // 无可用相机设备
}
catch (CameraException ex)
{
    // 其他相机错误,ex.InnerException 包含原始 SDK 异常
}
异常类型 场景
DeviceNotFoundException 无可用相机
ConnectionLostException 相机物理断开
CameraException SDK 操作失败(基类)
InvalidOperationException 未连接时访问参数,未采集时触发
TimeoutException 软件触发等待超时

扩展其他品牌相机

  1. 实现 ICameraController 接口:
public class HikvisionCameraController : ICameraController
{
    // 实现所有接口方法...
}
  1. CameraFactory.cs 中注册:
public ICameraController CreateController(string cameraType)
{
    return cameraType switch
    {
        "Basler" => new BaslerCameraController(),
        "Hikvision" => new HikvisionCameraController(),
        _ => throw new NotSupportedException($"不支持的相机品牌: {cameraType}")
    };
}
  1. 配置文件切换品牌即可,业务代码无需修改。

线程安全

  • 所有公共方法(Open / Close / StartGrabbing / StopGrabbing / ExecuteSoftwareTrigger / 参数读写)均线程安全
  • 事件回调不持有内部锁,不会导致死锁
  • Open() / Close() 幂等,重复调用安全

日志

使用 Serilog 静态 APILog.ForContext<T>()),与宿主应用共享同一个日志管道。宿主应用只需在启动时配置 Log.Logger 即可。