Files
XplorePlane/XP.Camera/README.md
T
2026-04-13 13:41:40 +08:00

243 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 即可。