在 WPF 界面上高效地刷新来自 C++ 处理的超大分辨率相机图像

下面给出两种常用方案,均能让你在 WPF 界面上高效地刷新来自 C++ 处理的超大分辨率相机图像:


方案一:WriteableBitmap + C++/CLI 桥接

1. 在 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;
};

2. 用 C++/CLI 封装成托管接口

// 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;
    };
}

把这个项目编译成 Bridge.dll,在你的 WPF (.NET) 项目中直接引用。

3. 在 WPF 中创建并不断更新 WriteableBitmap

<!-- MainWindow.xaml -->
<Window x:Class="WpfApp.MainWindow"
        …>
    <Image x:Name="imgDisplay"/>
</Window>
// 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();
            });
        }
    }
}

优点

  • 完全托管式调用,代码清晰;

  • WriteableBitmap.WritePixels 在大块内存拷贝时性能较好;

  • 易于调试、跨平台 .NET Core/Framework 均支持。


方案二:D3DImage + Direct3D 资源共享

当分辨率更高(8K+)、帧率更大(60+ FPS)时,CPU 拷贝已经成为瓶颈,这时可考虑 GPU 直通渲染:

  1. C++ 侧

    • 使用 Direct3D11/9Ex 创建一个 ID3D11Texture2DIDirect3DSurface9,把相机数据上传到 GPU 资源。

    • 导出这个 D3D 资源的共享句柄(shared handle 或 DXGI SharedResource)。

  2. WPF 侧

    • 在 C# 中通过 P/Invoke 拿到共享资源句柄,创建一个 D3DImage

      D3DImage d3dImg = new D3DImage();
      imgDisplay.Source = d3dImg;
      // 通过 D3D9Ex 接口调用 Lock()、SetBackBuffer()、Unlock() 来绑定你的 Direct3D surface
      
    • 每次 C++ 侧更新 GPU 纹理后,只需在 UI 线程:

      d3dImg.Lock();
      d3dImg.AddDirtyRect(new Int32Rect(0,0,width,height));
      d3dImg.Unlock();
      
    • WPF 会直接使用 GPU 纹理渲染,大幅减轻 CPU 负担。

优点

  • 零拷贝、最低延迟;

  • GPU 加速,适合实时性极强的场景。


小结

  • 简单场景(<4K,<30FPS):建议用 WriteableBitmap + C++/CLI,开发成本最低。

  • 极限场景(>4K,>60FPS):推荐使用 D3DImage + 资源共享,最大化利用 GPU。

根据你的相机分辨率和实时性能要求,选择最合适的方案即可。

posted @ 2025-05-26 09:26  阿坦  阅读(120)  评论(1)    收藏  举报