【问题标题】:SoftwareBitmap 16 Gray pixels to ushort[]SoftwareBitmap 16 灰度像素到 ushort[]
【发布时间】:2021-01-16 09:41:17
【问题描述】:

我正在使用 mediaframes (Kinect) 在 UWP 上实时获取颜色、深度/红外帧。这是为了将帧数据存储在磁盘上,然后再进行处理。

对于颜色,我使用 Memorystream 获取以字节为单位的像素。

                // Get the Individual color Frame
            var vidFrame = clrFrame?.VideoMediaFrame;
            {
                if (vidFrame == null) return;

                // create a UWP SoftwareBitmap and copy Color Frame into Bitmap
                SoftwareBitmap sbt = new SoftwareBitmap(vidFrame.SoftwareBitmap.BitmapPixelFormat, vidFrame.SoftwareBitmap.PixelWidth, vidFrame.SoftwareBitmap.PixelHeight);
                vidFrame.SoftwareBitmap.CopyTo(sbt);

                // PixelFormat needs to be in 8bit for Colour only
                if (sbt.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
                    sbt = SoftwareBitmap.Convert(vidFrame.SoftwareBitmap, BitmapPixelFormat.Bgra8);

                if (source != null)
                {
                    var ignore = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
                    {
                        
                        extBitmap = new WriteableBitmap(sbt.PixelWidth, sbt.PixelHeight);
                        sbt.CopyToBuffer(extBitmap.PixelBuffer);
                        byte[] pixels = PixelBufferToWriteableBitmap(extBitmap);                       

                        extBitmap.Invalidate();
                        await SavePixelsToFile(pixels);
                    });
                }
            }


public async Task<byte[]> PixelBufferToWriteableBitmap(WriteableBitmap wb)
    {
        using (Stream stream = wb.PixelBuffer.AsStream())
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                await stream.CopyToAsync(memoryStream);
                byte[] pixels = memoryStream.ToArray();
                return pixels;
            }
        }
    }

红外像素格式为 Gray16(在 SoftwareBitmap 中);我想保留原始像素数据(因此帧中不会丢失任何数据)并将其写入 ushort[] 数组中的本地文件夹。

以下是链接,我遇到了如何从软件位图中获取设置像素。但是,它是bgra到字节,我想将软件位图转换为ushort。

How to set/get pixel from Softwarebitmap

https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/imaging

我是新手,不知道如何继续。

有人可以帮忙吗?

编辑

我认为可以通过执行以下操作将缓冲区媒体帧转换为字节数组:

公共异步任务 BufferStreamTobyte(BufferMediaFrame buffFrame) {

        using (Stream stream = buffFrame.Buffer.AsStream())
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                await stream.CopyToAsync(memoryStream);
                byte[] pixels = memoryStream.ToArray();
                return pixels;
            }
        }
    }

但我不确定这样做是否会丢失红外帧的信息。由于红外和深度是每像素 16 位,并且当前的字节转换保持 8 bpp。对于这个 ushort[] 将能够保持 16bpp。 我对此很陌生,不确定所以我希望我做对了吗?

编辑 2:

我在 byte[] 中得到了像素数据。 我知道 byte 是 8 位,short 是 16 位,所以我更改了数组的长度:

int width = softwareBitmap.PixelWidth;
int height = softwareBitmap.PixelHeight;
int length = width * height * 2;

byte[] irbyteData = new byte[length]; // *2 to get 16 bit

var irshortData = new ushort[width * height]; // 16 bit ushort 

IntPtr ptr = (IntPtr)pixelBytesAddress;
Marshal.Copy(ptr, irbyteData, 0, length); //seems to successfully get pixels from memory but not sure if this is lossless

【问题讨论】:

  • 您的问题不清楚。您声明“像素格式为 Gray16” 并且您“想要保留原始像素数据”,但您发布的代码假定像素格式为 32 位RGBA。而且它甚至不读取缓冲区;它只是根据位图中像素的 X 值生成灰度渐变。您发布的代码以什么方式相关?与您想要发生的事情类似的实际代码在哪里?
  • 帧到达时像素格式为 Gray16 但如何获取 ushort[] (16bit) 中的像素以将这些信息存储在磁盘上?
  • 那么,你只是想将相同的 Gray16 帧作为图像存入磁盘吗?
  • 是的,我想在不丢失任何信息的情况下将 Gray16 帧保存到磁盘。

标签: c# image image-processing uwp bitmap


【解决方案1】:

我通过以下链接找到了将 IntPtr 数据复制到 ushort 数组的解决方案:

Copy from IntPtr (16 bit) array to managed ushort

我通过

获得地址 IntPtr
using (var input = softwareBitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
using (var inputReference = input.CreateReference())    
((IMemoryBufferByteAccess)inputReference).GetBuffer(out inputBytes, out inputCapacity);

IntPtr ptr = (IntPtr)inputBytes;
Marshal.Copy(ptr, infraredbyteData, 0, length);

我得到长度(宽度*高度*2)的字节来保存16位数据。

后来我把它转换成ushort by

var size = infraredbyteData.Length / 2; 
ushort[] output = new ushort[size]; // every ushort is 2 bytes
Buffer.BlockCopy(infraredbyteData, 0, output, 0, infraredbyteData.Length);

这似乎行得通!

【讨论】:

    【解决方案2】:

    我会尝试使用 PNG

    例如:

        using(var fs=new FileStream("sample.png"))
    {
            BitmapSource bmpSource=BitmapSource(Width,Height,96,96,PixelFormats.Gray16,
            BitmapPalettes.Gray16,buffer,ImageWidth*sizeof(ushort));
        PngBitmapEncoder enc = new PngBitmapEncoder();
        end.Frames.Add(BitmapFrame.Create(bmpSource));
        enc.Save(fs)
    }
    
    1. 抱歉,如果我在没有 IDE 的计算机上从内存中编写的代码中出现拼写错误
    2. 将 System.Windows.media.Imaging 添加到您的使用中
    3. buffer是一个带有像素值的ushort数组(总长度应该是:width*height*2)

    【讨论】:

    • 感谢 Felix 提供的解决方案。我会试一试。这是否能确保数据不会丢失并被存储以供进一步处理?
    • 当我使用 MediaInfo (v18.05) 检查输出文件时,它报告:每像素 16 位,无损 压缩,所以看起来如此,但它可能在不同的操作系统上有所不同
    • BitmapSource 在 UWP 中的实现完全不同。 docs.microsoft.com/en-us/uwp/api/…。该代码似乎不起作用。我已经尝试了其他方法,但似乎不起作用。
    猜你喜欢
    • 2020-05-15
    • 1970-01-01
    • 2015-06-29
    • 1970-01-01
    • 2017-07-31
    • 2012-06-13
    • 2012-11-18
    • 1970-01-01
    • 2013-02-08
    相关资源
    最近更新 更多