Convert a System.Drawing.Bitmap to Windows.Graphics.Imaging.SoftwareBitmap - c#

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();

Related

How to convert UWP SoftwareBitmap to WPF Bitmap?

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());
}

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?

Exception creating BitmapDecoder from WriteableBitmap for Windows Phone UWP

In my UWP Windows 10 Mobile app, I am attempting to access and manipulate the transparency of individual pixels in the PixelBuffer for a given WriteableBitmap. The issue I'm having is that BitmapDecoder.CreateAsync() is throwing
"The component cannot be found. (Exception from HRESULT:
0x88982F50)".
I have spent WAY too much time searching, refactoring and debugging this to no avail; any hints, direction or help of any kind would be much appreciated.
// img is a WriteableBitmap that contains an image
var stream = img.PixelBuffer.AsStream().AsRandomAccessStream();
BitmapDecoder decoder = null;
try
{
decoder = await BitmapDecoder.CreateAsync(stream);
}
catch(Exception e)
{
// BOOM: The component cannot be found. (Exception from HRESULT: 0x88982F50)
}
// Scale image to appropriate size
BitmapTransform transform = new BitmapTransform()
{
ScaledWidth = Convert.ToUInt32(img.PixelWidth),
ScaledHeight = Convert.ToUInt32(img.PixelHeight)
};
PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8, // WriteableBitmap uses BGRA format
BitmapAlphaMode.Straight,
transform,
ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
ColorManagementMode.DoNotColorManage
);
// An array containing the decoded image data, which could be modified before being displayed
byte[] pixels = pixelData.DetachPixelData();
UPDATE: In case this helps spark some ideas, I found that if I use the overloaded CreateAsync constructor that supplies a Codec with the stream, it throws a different exception:
Specified cast is not valid.
Guid BitmapEncoderGuid = BitmapEncoder.PngEncoderId;
decoder = await BitmapDecoder.CreateAsync(BitmapEncoderGuid, stream);
It gives the same exception no matter which codec I supply (e.g. Png, Jpeg, GIF, Tiff, Bmp, JpegXR )
I don't understand, why you want to use a BitmapDecoder at all. The pixel data in WriteableBitmap is not encoded in any way. You would need a BitmapDecoder if you were loading the image from a file stream in a particular compression format - this would require you to use the correct codec.
You can just read the pixel data directly from the stream:
byte[] pixels;
using (var stream = img.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
This will give you a byte array containing 4 bytes for each pixel, corresponding to their R, G, B and A components.

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

BitmapImage METRO re-use WPF code

I have written a little game using IronPython and WPF for didactic purpose and now I want to translate the project to Metro APP for test Shared Projects.
The guilty code is:
def LoadImage(name, sourceRect):
bmp = BitmapImage()
bmp.BeginInit()
bmp.UriSource = Uri("./data/images/" + name, UriKind.Relative)
bmp.SourceRect = sourceRect
bmp.EndInit()
image = Image()
image.Source = bmp
return image
How on the earth I can obtain the same result in a Metro app (using C#)? There must be a way to do this in a simple manner like old BitmapImage. I need this because I have tiled images and I want a portion of it to display. WriteableBitmap work but ignore transparency of the image, so it's useless.
I've been using this code to load an image scaled:
public static async Task SetSourceAsync(
this WriteableBitmap writeableBitmap,
IRandomAccessStream streamSource,
uint decodePixelWidth,
uint decodePixelHeight)
{
var decoder = await BitmapDecoder.CreateAsync(streamSource);
using (var inMemoryStream = new InMemoryRandomAccessStream())
{
var encoder = await BitmapEncoder.CreateForTranscodingAsync(inMemoryStream, decoder);
encoder.BitmapTransform.ScaledWidth = decodePixelWidth;
encoder.BitmapTransform.ScaledHeight = decodePixelHeight;
await encoder.FlushAsync();
inMemoryStream.Seek(0);
await writeableBitmap.SetSourceAsync(inMemoryStream);
}
}
You might use something similar, but you'd specify encoder.BitmapTransform.Bounds instead of ScaledWidth/Height to do the cropping. If you have more specific questions - please clarify.

Categories

Resources