🔗 在 WPF 界面上高效地刷新来自 C++ 处理的超大分辨率相机图像
下面给出两种常用方案,均能让你在 WPF 界面上高效地刷新来自 C++ 处理的超大分辨率相机图像:
🔗 方案一:WriteableBitmap + C++/CLI 桥接
🔗 1. 在 C++ 侧准备原始图像缓冲区
c++
// NativeProcessor.h (纯 C++)
class NativeProcessor {
public:
NativeProcessor(int w, int h);
~NativeProcessor();
// 调用后,_buffer 中存放 BGRA32 数据
void GrabFrame();
uint8_t* GetBuffer() const { return _buffer; }
int Width() const { return _w; }
int Height() const { return _h; }
int Stride() const { return _w * 4; }
private:
int _w, _h;
uint8_t* _buffer;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
🔗 2. 用 C++/CLI 封装成托管接口
c++
// ManagedBridge.h (C++/CLI)
#pragma once
#include "NativeProcessor.h"
using namespace System;
namespace Bridge {
public ref class FrameProvider {
public:
FrameProvider(int width, int height)
: _native(new NativeProcessor(width, height)) {}
~FrameProvider() { this->!FrameProvider(); }
!FrameProvider() { delete _native; }
// 抓取一帧
void Update() { _native->GrabFrame(); }
// 获取图像指针给 WPF
IntPtr GetBuffer() {
return IntPtr(_native->GetBuffer());
}
property int Width { int get() { return _native->Width(); }}
property int Height { int get() { return _native->Height(); }}
property int Stride { int get() { return _native->Stride(); }}
private:
NativeProcessor* _native;
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
把这个项目编译成 Bridge.dll,在你的 WPF (.NET) 项目中直接引用。
🔗 3. 在 WPF 中创建并不断更新 WriteableBitmap
xml
<!-- MainWindow.xaml -->
<Window x:Class="WpfApp.MainWindow"
…>
<Image x:Name="imgDisplay"/>
</Window>
1
2
3
4
5
2
3
4
5
cs
// MainWindow.xaml.cs
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Bridge; // 引用 C++/CLI
namespace WpfApp {
public partial class MainWindow : Window {
private FrameProvider _provider;
private WriteableBitmap _wb;
public MainWindow() {
InitializeComponent();
_provider = new FrameProvider(3840, 2160); // 例如 4K
_wb = new WriteableBitmap(
_provider.Width, _provider.Height,
96, 96, PixelFormats.Bgra32, null);
imgDisplay.Source = _wb;
// 启动定时器或后台线程不断拉帧
var timer = new System.Timers.Timer(30); // ~33 FPS
timer.Elapsed += (s, e) => RefreshFrame();
timer.Start();
}
private void RefreshFrame() {
_provider.Update(); // C++ 抓取并处理一帧
var bufferPtr = _provider.GetBuffer();
Dispatcher.Invoke(() => {
_wb.Lock();
// 直接把 native 缓冲区复制到 WriteableBitmap
_wb.WritePixels(
new Int32Rect(0, 0, _wb.PixelWidth, _wb.PixelHeight),
bufferPtr,
_provider.Stride * _provider.Height,
_provider.Stride);
_wb.Unlock();
});
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
优点
- 完全托管式调用,代码清晰;
- WriteableBitmap.WritePixels 在大块内存拷贝时性能较好;
- 易于调试、跨平台 .NET Core/Framework 均支持。
🔗 方案二:D3DImage + Direct3D 资源共享
当分辨率更高(8K+)、帧率更大(60+ FPS)时,CPU 拷贝已经成为瓶颈,这时可考虑 GPU 直通渲染:
🔗 C++ 侧
- 使用 Direct3D11/9Ex 创建一个 ID3D11Texture2D 或 IDirect3DSurface9,把相机数据上传到 GPU 资源。
- 导出这个 D3D 资源的共享句柄(shared handle 或 DXGI SharedResource)。
🔗 WPF 侧
- 在 C# 中通过 P/Invoke 拿到共享资源句柄,创建一个 D3DImage:
cs
D3DImage d3dImg = new D3DImage();
imgDisplay.Source = d3dImg;
// 通过 D3D9Ex 接口调用 Lock()、SetBackBuffer()、Unlock() 来绑定你的 Direct3D surface
1
2
3
2
3
- 每次 C++ 侧更新 GPU 纹理后,只需在 UI 线程:
cs
d3dImg.Lock();
d3dImg.AddDirtyRect(new Int32Rect(0,0,width,height));
d3dImg.Unlock();
1
2
3
2
3
- WPF 会直接使用 GPU 纹理渲染,大幅减轻 CPU 负担。
优点
- 零拷贝、最低延迟;
- GPU 加速,适合实时性极强的场景。
🔗 小结
- 简单场景(<4K,<30FPS):建议用 WriteableBitmap + C++/CLI,开发成本最低。
- 极限场景(>4K,>60FPS):推荐使用 D3DImage + 资源共享,最大化利用 GPU。
根据你的相机分辨率和实时性能要求,选择最合适的方案即可。