【问题标题】:How to get byte array from SoftwareBitmap如何从 SoftwareBitmap 获取字节数组
【发布时间】:2019-11-13 15:50:45
【问题描述】:

您好,我需要有关如何从 C# UWP 中的 SoftwareBitmap 获取字节数组的帮助,以便我可以通过 TCP 套接字发送它。

我还可以访问“VideoFrame previewFrame”对象,这是我从中获取SoftwareBitmap 的地方。

我在网上看到过类似下面的操作,但是 UWP 不支持wb.SaveJpeg(...)。除非我错过了什么?

MemoryStream ms = new MemoryStream();
WriteableBitmap wb = new WriteableBitmap(myimage);
wb.SaveJpeg(ms, myimage.PixelWidth, myimage.PixelHeight, 0, 100);
byte [] imageBytes = ms.ToArray();

任何帮助或正确方向的指示,将不胜感激。

谢谢,安迪

【问题讨论】:

  • 这个问题与 TCP 无关,专注于你真正的问题“我如何从 uwp 中的SoftwareBitmapSource 获取字节数组”,暂时不要使用 TCP 的东西。
  • 好的,你能告诉我如何从 uwp 中的 SoftwareBitmapSource 获取字节数组吗?
  • 我的意思是 edit your question 并用更集中的问题更新它(我也不做 uwp 所以我不知道答案)。

标签: c# win-universal-app uwp


【解决方案1】:

当然,您可以从SoftwareBitmap 访问编码 byte[] 数组。

看这个例子,提取一个jpeg编码的byte[]

// includes BitmapEncoder, which defines some static encoder IDs
using Windows.Graphics.Imaging;


private async void PlayWithData(SoftwareBitmap softwareBitmap)
{
    // get encoded jpeg bytes
    var data = await EncodedBytes(softwareBitmap, BitmapEncoder.JpegEncoderId);

    // todo: save the bytes to a DB, etc
}

private async Task<byte[]> EncodedBytes(SoftwareBitmap soft, Guid encoderId)
{
    byte[] array = null;

    // First: Use an encoder to copy from SoftwareBitmap to an in-mem stream (FlushAsync)
    // Next:  Use ReadAsync on the in-mem stream to get byte[] array

    using (var ms = new InMemoryRandomAccessStream())
    {
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(encoderId, ms);
        encoder.SetSoftwareBitmap(soft);

        try
        {
            await encoder.FlushAsync();
        }
        catch ( Exception ex ){ return new byte[0]; }

        array = new byte[ms.Size];
        await ms.ReadAsync(array.AsBuffer(), (uint)ms.Size, InputStreamOptions.None);
    }
    return array;
}

【讨论】:

  • 哦!为什么应该是正确的答案(这个)最后被埋没了,而一个完全不相关的代码目前被最高投票!
【解决方案2】:

要从 SoftwareBitmap 获取字节数组,您可以使用“SoftwareBitmap.CopyToBuffer”

但是,首先你需要:

使用 System.Runtime.InteropServices.WindowsRuntime;

因为方法 AsBuffer() 到 byte[]

...

StorageFile file = await StorageFile.GetFileFromPathAsync(ImageFilePath);
using (IRandomAccessStream fileStream = await File.OpenAsync(FileAccessMode.Read),
                                           memStream = new InMemoryRandomAccessStream())
  {
  // Open a Stream and decode a JPG image
  BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
  var softwareBitmap = await decoder.GetSoftwareBitmapAsync();

  byte [] imageBytes = new byte[4*decoder.PixelWidth*decoder.PixelHeight];
  softwareBitmap.CopyToBuffer(imageBytes.AsBuffer());
  //...  now you can use the imageBytes[]
}

【讨论】:

  • 欢迎来到 SO。请考虑在您的代码中添加上下文。另外,请仔细检查您的代码格式。
【解决方案3】:

据我所知,你做不到。但您可以使用 SoftwareBitmap。看例子:https://msdn.microsoft.com/en-us/library/windows/apps/mt244351.aspx(SoftwareBitmap 是 SoftwareBitmapSource 的私有字段..只是通过反射阅读它......也许这是完全错误的建议)

private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
    using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        // Create an encoder with the desired format
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);

        // Set the software bitmap
        encoder.SetSoftwareBitmap(softwareBitmap);

        // Set additional encoding parameters, if needed
        encoder.BitmapTransform.ScaledWidth = 320;
        encoder.BitmapTransform.ScaledHeight = 240;
        encoder.BitmapTransform.Rotation = Windows.Graphics.Imaging.BitmapRotation.Clockwise90Degrees;
        encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
        encoder.IsThumbnailGenerated = true;

        try
        {
            await encoder.FlushAsync();
        }
        catch (Exception err)
        {
            switch (err.HResult)
            {
                case unchecked((int)0x88982F81): //WINCODEC_ERR_UNSUPPORTEDOPERATION
                                                 // If the encoder does not support writing a thumbnail, then try again
                                                 // but disable thumbnail generation.
                    encoder.IsThumbnailGenerated = false;
                    break;
                default:
                    throw;
            }
        }

        if (encoder.IsThumbnailGenerated == false)
        {
            await encoder.FlushAsync();
        }


    }
}

【讨论】:

  • 好的,我已经确定我可以访问 SoftwareBitmap,但是我仍然看不到如何从中获取字节数组?在您上面显示的示例中,我看不到字节数组可以从哪里获得?
  • 使用 (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite)) 示例将字节流写入文件。您应该使用 mem 流作为缓冲区或直接输出到 socket/wcf
  • 但是我没有从文件“StorageFile outputFile”打开图像。我从 VideoFrame 获取 SoftwareBitmap。那么我怎样才能从 VideoFrame 打开一个流呢?
  • 此代码将 SoftwareBitmap 保存到文件中。使用编码器转换为流的软件位图:msdn.microsoft.com/en-us/library/windows/apps/br226212.aspx。您可以使用任何 IRandomAccessStream 流。在这里查看如何转换为 io 流:stackoverflow.com/questions/7669311/…
  • 您不应该使用throw err; 重新抛出错误,因为它会覆盖堆栈跟踪。请改用throw;
【解决方案4】:

Get Preview Frame UWP 摄像头样本以 SoftwareBitmap 的形式获取摄像头帧,并通过数组对其上的像素(就地)进行操作,之后可以将其保存为 JPEG。如果我正确理解了您的问题,那么您需要的所有代码都应该在那里。

基本上,这应该是大部分(包括一些相机代码):

private async Task GetPreviewFrameAsSoftwareBitmapAsync()
{
    // Get information about the preview
    var previewProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;

    // Create the video frame to request a SoftwareBitmap preview frame
    var videoFrame = new VideoFrame(BitmapPixelFormat.Bgra8, (int)previewProperties.Width, (int)previewProperties.Height);

    // Capture the preview frame
    using (var currentFrame = await _mediaCapture.GetPreviewFrameAsync(videoFrame))
    {
        // Collect the resulting frame
        SoftwareBitmap previewFrame = currentFrame.SoftwareBitmap;

        // Add a simple green filter effect to the SoftwareBitmap
        EditPixels(previewFrame);
    }
}

private unsafe void EditPixels(SoftwareBitmap bitmap)
{
    // Effect is hard-coded to operate on BGRA8 format only
    if (bitmap.BitmapPixelFormat == BitmapPixelFormat.Bgra8)
    {
        // In BGRA8 format, each pixel is defined by 4 bytes
        const int BYTES_PER_PIXEL = 4;

        using (var buffer = bitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
        using (var reference = buffer.CreateReference())
        {
            // Get a pointer to the pixel buffer
            byte* data;
            uint capacity;
            ((IMemoryBufferByteAccess)reference).GetBuffer(out data, out capacity);

            // Get information about the BitmapBuffer
            var desc = buffer.GetPlaneDescription(0);

            // Iterate over all pixels
            for (uint row = 0; row < desc.Height; row++)
            {
                for (uint col = 0; col < desc.Width; col++)
                {
                    // Index of the current pixel in the buffer (defined by the next 4 bytes, BGRA8)
                    var currPixel = desc.StartIndex + desc.Stride * row + BYTES_PER_PIXEL * col;

                    // Read the current pixel information into b,g,r channels (leave out alpha channel)
                    var b = data[currPixel + 0]; // Blue
                    var g = data[currPixel + 1]; // Green
                    var r = data[currPixel + 2]; // Red

                    // Boost the green channel, leave the other two untouched
                    data[currPixel + 0] = b;
                    data[currPixel + 1] = (byte)Math.Min(g + 80, 255);
                    data[currPixel + 2] = r;
                }
            }
        }
    }
}

并在你的类之外声明它以启用GetBuffer 方法:

[ComImport]
[Guid("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

当然,您的项目必须允许不安全的代码才能使所有这些工作。

最后,您可以通过以下方式将SoftwareBitmap 保存为 JPEG 文件:

private static async Task SaveSoftwareBitmapAsync(SoftwareBitmap bitmap, StorageFile file)
{
    using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, outputStream);

        // Grab the data from the SoftwareBitmap
        encoder.SetSoftwareBitmap(bitmap);
        await encoder.FlushAsync();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2021-05-02
    • 2019-07-23
    相关资源
    最近更新 更多