How to convert UWP SoftwareBitmap to WPF Bitmap? - c#

I'm trying to get a SoftwareBitmap to be usable by stuff in WPF as a Bitmap.
Approaches like this question shows look promising, but they seem to be using objects from different namespaces that don't quite work in my case. For context, I'm using MediaCapture from UWP land for webcam streaming in my app which is WPF.
public static WriteableBitmap WriteableBitmapFromSoftwareBitmap(SoftwareBitmap soft)
{
WriteableBitmap writeable = new
WriteableBitmap(soft.PixelWidth, soft.PixelHeight);
soft.CopyToBuffer(writeable.PixelBuffer);
return writeable;
}
public static Bitmap BitmapFromWriteableBitmap(WriteableBitmap writeBmp)
{
Bitmap bmp;
using (MemoryStream outStream = new MemoryStream())
{
System.Windows.Media.Imaging.BitmapEncoder enc = new
BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(writeBmp)); // Error here
enc.Save(outStream);
bmp = new Bitmap(outStream);
}
return bmp;
}
Unfortunately, in BitmapFromWriteableBitmap, the call to BitmapFrame.Create is complaining that it can't create a URI from that kind of WriteableBitmap.
Note that the WriteableBitmap is a Windows.UI.Xaml.Media.Imaging.WriteableBitmap,
the SoftwareBitmap is a Windows.Graphics.Imaging.SoftwareBitmap,
and the desired Bitmap is a System.Drawing.Bitmap.

There is no direct conversion. You'll need to extract the image data from the SoftwareBitmap and then create the new Bitmap from that data.
This is essentially what the linked question does: it encodes the data from the WriteableBitmap into a .BMP stream and then loads the System.Drawing.Bitmap from that stream.
You can do the same from the SoftwareBitmap by using Windows.Graphics.Imaging.BitmapEncoder.SetSoftwareBitmap to convert its contents to an image stream and then create a new System.Drawing.Bitmap from that stream.
See Save a SoftwareBitmap to a file with BitmapEncoder for sample code. You can render to an InMemoryRandomAccessStream instead of a StorageFile's stream to avoid saving to disk, and can use AsStream to convert it to a .Net System.IO.Stream to read into the System.Drawing.Bitmap something like the following (untested) snippet:
using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream())
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
bmp = new System.Drawing.Bitmap(stream.AsStream());
}

Related

Convert a System.Drawing.Bitmap to Windows.Graphics.Imaging.SoftwareBitmap

I have a WPF project and captured an image from a usb camera to a System.Drawing.Bitmap (I also can capture System.Windows.Media.Imaging.BitmapSource) and need to convert it to a Windows.Graphics.Imaging.SoftwareBitmap to make a "VideoFrame" to compare to an Onnx model.
The camera driver is a .net assembly and will not bind to a uwp project. I've tried creating a .net standard assembly to bridge the gap with no success. I simply need a bitmap converted to a SoftwareBitmap. Please help!
I'm using this code for the basis of the compassion of the Bitmap image from the camera - https://github.com/Azure-Samples/cognitive-services-onnx12-customvision-sample
There is no direct conversion. You'll need to extract the image data from the System.Drawing.Bitmap and then create the new SoftwareBitmap from that data.
For example, you could use Save(Stream, ImageFormat) method to save this image to the specified stream in the specified format.
Then, you could try to call BitmapDecoder.CreateAsync method to create the decoder from the stream.
After that you could call GetSoftwareBitmapAsync to get a SoftwareBitmap object.
The following is a simple code sample:
Bitmap bitmap = getyourbitmap();
using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream())
{
bitmap.Save(stream.AsStream(),ImageFormat.Jpeg);//choose the specific image format by your own bitmap source
Windows.Graphics.Imaging.BitmapDecoder decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
}
I found that you can use Windows.Security.Cryptography to create a IBuffer from the image array bytes. Then you can copy the IBuffer to the SoftwareBitmap.
using Windows.Security.Cryptography;
IBuffer buffer = CryptographicBuffer.CreateFromByteArray(ImageByteArray);
SoftwareBitmap softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Gray8, 800, 600);
softwareBitmap.CopyFromBuffer(buffer);
VideoFrame inputImage = VideoFrame.CreateWithSoftwareBitmap(softwareBitmap);
This worked for me:
Bitmap bitmap = ...;
var memoryStream = new MemoryStream();
using (var graphics = Graphics.FromImage(bitmap))
{
bitmap.Save(memoryStream, ImageFormat.Bmp);
}
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
var bitmap = await decoder.GetSoftwareBitmapAsync();

Load Image from Stream

Is there any way of converting Stream to Image?
I tried Bitmap, but it states that I don't have System.Drawing... so I tried this:
var bitMap = new BitmapImage();
bitMap.SetSourceAsync(stream);
image.Source = bitMap;
EDIT:
I am trying to build UWP app + using VS 2015.
2 - It just states that System.Drawing does not exist in the namespace.
EDIT2:
Ok, I might have explained it wrong. The idea is: I have an Image, and I want to change its source to something different and then for it to reload, so I can see the image.
The image is effectively a "Stream", so I assume I need to convert it to Bitmap and then load somehow.
EDIT3:
Ok, so I think it will be easier to describe and then use the code above:
There is a picture box and I am using:
var stream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);
Now I would like this Captured Photo to be displayed as an Image. ( Image "box" is created at the start, so the idea is to change source).
Right, so I managed to fix it:
var bitMap = new BitmapImage();
stream.seek(0); // LINE ADDED
bitMap.SetSourceAsync(stream);
image.Source = bitMap;
I turned out that the error that was being produced was : "The component cannot be found.", so I managed to fix it by using this trick.
I am not sure if this is what you are looking for but if you would like to use stream with BitMapImage you should use:
var image = new BitmapImage();
await image.SetSourceAsync(stream);
For instance when you have your photo stored as a byte[] array you can use the stream to convert it to image:
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0))
writer.WriteBytes(<<here your byte[] array>>);
await writer.StoreAsync();
var image = new BitmapImage();
await image.SetSourceAsync(stream);
}
Is that what you need?

Convert BitmapImage to byte[] array in Windows Phone 8.1 Runtime

There are a few samples to do this but they are for Windows Phone 8.0 or 8.1 Silverlight.
But how can you do this for Windows Phone 8.1 Runtime?
You cannot extract the pixels from a Windows.UI.Xaml.Media.Imaging.BitmapImage.
The most general solution is to use a WriteableBitmap instead of a BitmapImage. These classes are both BitmapSources and can be used almost interchangeably. The WriteableBitmap provides access to its pixel data via its PixelBuffer property:
byte[] pixelArray = myWriteableBitmap.PixelBuffer.ToArray(); // convert to Array
Stream pixelStream = wb.PixelBuffer.AsStream(); // convert to stream
Otherwise you will need to acquire the pixels from wherever the BitmapImage got them from. Depending on how the BitmapImage was initialized you may be able to find its origin from its UriSource property. The WinRT Xaml Toolkit has an extension method FromBitmapImage to create a WriteableBitmap from an BitmapImage based on its UriSource.
An ugly option would be to render the BitmapImage into an Image, create a RenderTargetBitmap based on the Image and then get its Pixels with RenderTargetBitmap.CopyPixelsAsync()
Tried this?
private byte[] ConvertToBytes(BitmapImage bitmapImage)
{
byte[] data = null;
using (MemoryStream stream = new MemoryStream())
{
WriteableBitmap wBitmap = new WriteableBitmap(bitmapImage);
wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
data = stream.GetBuffer();
}
return data;
}

Optimizing conversions between System.Windows.Drawing.Bitmap and System.Windows.Media.Imaging.BitmapImage

I'm working on a project that uses the following function to convert between System.Drawing.Bitmap images, which are needed to work with the System.Drawing.Printing API, and System.Windows.Media.Imaging.BitmapImage class, used to display the image in WPF:
public System.Windows.Media.Imaging.BitmapImage getDisplayImage(System.Drawing.Bitmap bm)
{
MemoryStream ms = new MemoryStream();
bm.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
System.Windows.Media.Imaging.BitmapImage wpfImg = new System.Windows.Media.Imaging.BitmapImage();
ms.Seek(0, SeekOrigin.Begin);
wpfImg.BeginInit();
wpfImg.StreamSource = ms;
wpfImg.EndInit();
return wpfImg;
}
It certainly works for the task, but it's dog slow for the data I use it for (images with dimensions of about 5400 by 3600). Is there a more efficient method to convert between these formats?

Convert System.Drawing.Image to System.Windows.Controls.Image?

Is there a way in C# to do this conversion and back?
I have a WPF app which has a Image control. I'm trying to save the image in that control to a SQL Database.
In my Entity Model, the datatype of the picture column in my database is a byte[]. So I found a method to convert a System.Drawing.Image to a byte[] and back. But I haven't found a method to convert from System.Windows.Controls.Image to a byte[].
So that's why I now need to do the above conversion.
If you have a byte array that represents a file that WPF can decode (bmp, jpg, gif, png, tif, ico), you can do the following:
BitmapSource LoadImage(Byte[] imageData)
{
using (MemoryStream ms = new MemoryStream(imageData))
{
var decoder = BitmapDecoder.Create(ms,
BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
return decoder.Frames[0];
}
}
Likewise, to convert it back, you can do the following:
byte[] SaveImage(BitmapSource bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(ms);
return ms.GetBuffer();
}
}
Well, one is an image and one is a control that shows an image, so I don't think that there's a conversion between the two. But, you could set the Source of the ...Controls.Image to be your ...Drawing.Image.
Edit based on update
Does this do what you need - http://msdn.microsoft.com/en-us/library/ms233764%28VS.100%29.aspx

Categories

Resources