243 lines
6.1 KiB
Markdown
243 lines
6.1 KiB
Markdown
# 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` 命名空间下。
|
||
|
||
## 项目引用
|
||
|
||
```xml
|
||
<ProjectReference Include="..\XP.Camera\XP.Camera.csproj" />
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
### 1. 通过工厂创建控制器
|
||
|
||
```csharp
|
||
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 容器中注册:
|
||
|
||
```csharp
|
||
// App.xaml.cs
|
||
var config = AppConfig.Load();
|
||
containerRegistry.RegisterSingleton<ICameraFactory, CameraFactory>();
|
||
containerRegistry.RegisterSingleton<ICameraController>(() =>
|
||
new CameraFactory().CreateController(config.CameraType));
|
||
```
|
||
|
||
ViewModel 中注入使用:
|
||
|
||
```csharp
|
||
public class MyViewModel
|
||
{
|
||
private readonly ICameraController _camera;
|
||
|
||
public MyViewModel(ICameraController camera)
|
||
{
|
||
_camera = camera;
|
||
}
|
||
}
|
||
```
|
||
|
||
相机品牌通过配置文件 `config.json` 指定:
|
||
|
||
```json
|
||
{
|
||
"CameraType": "Basler"
|
||
}
|
||
```
|
||
|
||
### 3. 实时图像显示(WPF 绑定)
|
||
|
||
```csharp
|
||
_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 绑定:
|
||
|
||
```xml
|
||
<Image Source="{Binding CameraImageSource}" Stretch="Uniform" />
|
||
```
|
||
|
||
### 4. 软件触发采集流程
|
||
|
||
```csharp
|
||
camera.Open();
|
||
camera.SetExposureTime(10000); // 10ms
|
||
camera.StartGrabbing();
|
||
|
||
// 每次需要采集时调用(结果通过 ImageGrabbed 事件返回)
|
||
camera.ExecuteSoftwareTrigger();
|
||
|
||
camera.StopGrabbing();
|
||
camera.Close();
|
||
```
|
||
|
||
### 5. 实时连续采集(链式触发)
|
||
|
||
收到上一帧后立即触发下一帧,自动适配任何帧率:
|
||
|
||
```csharp
|
||
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()`,可直接跨线程传递。
|
||
|
||
## 异常处理
|
||
|
||
```csharp
|
||
try
|
||
{
|
||
camera.Open();
|
||
}
|
||
catch (DeviceNotFoundException)
|
||
{
|
||
// 无可用相机设备
|
||
}
|
||
catch (CameraException ex)
|
||
{
|
||
// 其他相机错误,ex.InnerException 包含原始 SDK 异常
|
||
}
|
||
```
|
||
|
||
| 异常类型 | 场景 |
|
||
|---------|------|
|
||
| `DeviceNotFoundException` | 无可用相机 |
|
||
| `ConnectionLostException` | 相机物理断开 |
|
||
| `CameraException` | SDK 操作失败(基类) |
|
||
| `InvalidOperationException` | 未连接时访问参数,未采集时触发 |
|
||
| `TimeoutException` | 软件触发等待超时 |
|
||
|
||
## 扩展其他品牌相机
|
||
|
||
1. 实现 `ICameraController` 接口:
|
||
|
||
```csharp
|
||
public class HikvisionCameraController : ICameraController
|
||
{
|
||
// 实现所有接口方法...
|
||
}
|
||
```
|
||
|
||
2. 在 `CameraFactory.cs` 中注册:
|
||
|
||
```csharp
|
||
public ICameraController CreateController(string cameraType)
|
||
{
|
||
return cameraType switch
|
||
{
|
||
"Basler" => new BaslerCameraController(),
|
||
"Hikvision" => new HikvisionCameraController(),
|
||
_ => throw new NotSupportedException($"不支持的相机品牌: {cameraType}")
|
||
};
|
||
}
|
||
```
|
||
|
||
3. 配置文件切换品牌即可,业务代码无需修改。
|
||
|
||
## 线程安全
|
||
|
||
- 所有公共方法(Open / Close / StartGrabbing / StopGrabbing / ExecuteSoftwareTrigger / 参数读写)均线程安全
|
||
- 事件回调不持有内部锁,不会导致死锁
|
||
- `Open()` / `Close()` 幂等,重复调用安全
|
||
|
||
## 日志
|
||
|
||
使用 Serilog 静态 API(`Log.ForContext<T>()`),与宿主应用共享同一个日志管道。宿主应用只需在启动时配置 `Log.Logger` 即可。
|