WPF 使用内存映射显示相机传回的图像数据

1.相机数据封装

这里以大恒的水星相机为例,下图是相机回调委托定义:

1).定义相机数据实体

cs 复制代码
public class ImageData
{
    public IntPtr GrayPixel { get; set; }
    
    public int Width { get; set; }

    public int Height { get; set; }
}

其中GrayPixel 表示相机图像数据,是指向字节数组的指针;Width和Height分别表示图像的宽度和高度;

2).填充相机数据实体

cs 复制代码
 //IFrameData data  
//data是大恒相机回调方法的参数
var w = data.GetWidth();
var h = data.GetHeight();
//定义字节数组接受相机数据
byte[]  imgbuffer = new byte[w * h];
//赋值
Marshal.Copy(data.GetBuffer(), imgbuffer, 0, (int)(imgw * imgh));
var imageData = new ImageData()
{
    //获得一个数组的第某个元素的内存地址
    GrayPixel = Marshal.UnsafeAddrOfPinnedArrayElement(imgbuffer, 0),
    Width = (int)w ,
    Height =(int)h 
};

2.利用映射内存生成位图数据

cs 复制代码
 /// <summary>
 /// 图像source
 /// </summary>
 private InteropBitmap iBitmap;

 /// <summary>
 /// 图像数据
 /// </summary>
 private IntPtr iBitmapData;

 /// <summary>
 /// 映射文件句柄
 /// </summary>
 private IntPtr bitmapSection;

//Alpha通道数据指针
private IntPtr oneChannelAlpha;
/// <summary>
/// 控件对象
/// </summary>
internal Image imageHost;

/// <summary>
/// 生成映射文件然后转换为WPF支持的位图
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
private void CreateInteropBitmap(int width, int height)
{
    if (iBitmapData != IntPtr.Zero)
    {
        UnmapViewOfFile(iBitmapData);
        iBitmapData = IntPtr.Zero;
    }
    if (bitmapSection != IntPtr.Zero)
    {
        CloseHandle(bitmapSection);
        bitmapSection = IntPtr.Zero;
    }
    //目前仅仅是灰度图像,这里把容量*4,为待会儿转换为bgra做准备
    var imageSize = (uint)(width * height * 4);
    // 获取文件映像对象句柄
    bitmapSection = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, imageSize, null);
    //非托管资源gc获取不到数据
    //GCHandle handle = GCHandle.FromIntPtr(bitmapSection);
    // 将内存映射文件映射到进程的虚拟地址中,iBitmapData 现在指向可读写的内存区域
    iBitmapData = MapViewOfFile(bitmapSection, 0xF001F, 0, 0, imageSize);
    //从非托管内存获取位图,把共享内存中的数组转换为图片
    iBitmap = Imaging.CreateBitmapSourceFromMemorySection(bitmapSection, width, height, PixelFormats.Pbgra32, width * 4, 0) as InteropBitmap;
}



 #region DllImport
 [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
 internal static extern unsafe IntPtr memcpy(IntPtr dst, IntPtr src, int count);

 [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
 internal static extern void memset(IntPtr dst, int filler, int count);

 [DllImport("Simd.dll", EntryPoint = "SimdInterleaveBgra", PreserveSig = false)]
 internal static extern void SimdInterleaveBgra(IntPtr b, uint bStride, IntPtr g, uint gStride, IntPtr r, uint rStride, IntPtr a, uint aStride, uint width, uint height, IntPtr bgra, uint bgraStride);

 [DllImport("Simd.dll", EntryPoint = "SimdBayerToBgra", PreserveSig = false)]
 internal static extern void SimdBayerToBgra(IntPtr bayer, uint width, uint height, uint bayerStride, SimdPixelFormatType format, IntPtr bgra, uint bgraStride, byte alpha);

 [DllImport("Simd.dll", EntryPoint = "SimdDeinterleaveBgr", PreserveSig = false)]
 internal static extern void SimdDeinterleaveBgr(IntPtr bgr, uint bgrStride, uint width, uint height, IntPtr b, uint bStride, IntPtr g, uint gStride, IntPtr r, uint rStride);

 [DllImport("Simd.dll")]
 private static extern IntPtr SimdAllocate(uint size, uint align);

 [DllImport("Simd.dll")]
 private static extern void SimdFree(IntPtr ptr);

 // some ideas/code borowed from CL NUI sample CLNUIImage.cs
 [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
 private static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
 [DllImport("kernel32.dll")]
 private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
 [DllImport("kernel32.dll")]
 private static extern bool UnmapViewOfFile(IntPtr hMap);
 [DllImport("kernel32.dll")]
 private static extern bool CloseHandle(IntPtr hHandle);
cs 复制代码
 //映射文件和位图处理
 CreateInteropBitmap(width, height);
 if (oneChannelAlpha != IntPtr.Zero)
 {
     Marshal.FreeHGlobal(oneChannelAlpha);
 }
 //申请内存
 oneChannelAlpha = Marshal.AllocHGlobal(width * height);
 //给每个字节赋值255
 memset(oneChannelAlpha, 255, width * height);
 //iBitmapData 在CreateInteropBitmap方法中已经指向了bgra图像数据
 var pixelData = iBitmapData;
 //将 8 位蓝色、绿色、红色和 Alpha 平面图像交错合并为一个 32 位 BGRA 交错图像。
 //imageData是图像数据实体里面的指针:GrayPixel 
 SimdInterleaveBgra(imageData, (uint)width, imageData, (uint)width, imageData, (uint)width, oneChannelAlpha, (uint)width, (uint)width, (uint)height, pixelData , (uint)width * 4);

 if (imageHost.Source != iBitmap)
 {
     imageHost.Source = iBitmap;
 }
 //触发图像绘制
 iBitmap.Invalidate();

tips:

调用dll的小知识:

以CreateFileMapping为例,原型:

因为调用的时候指定了Unicode,所以在C#中才可以使用CreateFileMapping而不是CreateFileMappingW。

C#引用时方法参数也是根据原型中定义的枚举设置的:

感觉图像处理的话C++要比C#方便一点。

引用:

InteropBitmap指定内存,绑定WPF的Imag控件时刷新问题。 - 亲福 - 博客园

WPF 通过共享内存播放视频 - 昨夜飘风 - 博客园

相关推荐
拿我格子衫来4 小时前
LaserWeb4:矢量图与位图如何变成 G-code
图像处理·图形渲染
Dfreedom.7 小时前
目标检测后处理核心:NMS非极大值抑制详解
图像处理·人工智能·深度学习·目标检测·目标跟踪
牧羊人.33312 小时前
动手学深度学习 02 | 手写数字识别
图像处理·人工智能·深度学习·算法
AndrewHZ12 小时前
图像处理入门023 | 自适应直方图均衡化:CLAHE——把 HE 的“全局“变“局部“,治好光照不均与噪声放大
图像处理·双线性插值·自适应均衡化·clahe·对比度限制·分块处理·美颜pipeline
hahaha60162 天前
HLS高层次综合设计技巧--C++类和模板
图像处理·人工智能·算法·计算机视觉
AndrewHZ2 天前
图像处理入门021 | 直方图:图像像素分布的可视化 —— cv2.calcHist 与 4 类典型分布
图像处理·opencv·直方图·直方图均衡化·cdf·灰度直方图·多通道直方图
AndrewHZ3 天前
图像处理入门020 | 图像二值化:阈值分割基础 —— cv2.threshold 五种类型与自适应阈值铺垫
图像处理·opencv·文档扫描·阈值分割·图像二值化·trackbar·自适应阈值
笑霸final4 天前
不用上传服务器!用 Vue3 + ONNX Runtime Web 在浏览器本地实现智能抠图
运维·前端·图像处理·ai·onnx·canva可画·web性能优化
A hao4 天前
户外LED广告牌需要什么防护等级IP级别?
大数据·网络·图像处理·人工智能·网络协议·tcp/ip·广告
一只嵌入式爱好者5 天前
深入理解ISP
图像处理·isp