I have a problem with Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder.
this is my method:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
/*PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
I'm using BmpBitmapEncoder because this is only way to Save without change size of Image(*.bmp). I want to Save Image with changed table of pixels and the specified format pixel (Bgr24). Using BmpBitmapEncoder forces to set bmp.UriSource and this is a problem. WriteableBitmap doesn't have this Property. Moreover, when I comment line //bmp.UriSource shows me an exception: "System.ArgumentNullException" in bmp.EndInit().
When I change my Method to this:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
/*BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
bmp.BeginInit();
//bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
//bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
everything works fine but the result is the Image increase size and change pixel format to Bgr32 and this is not the result, what I expect. My method which Save Image is fine because I tested it on unchanged pixels and the result is good - Image don't change format and size. Plz help me with this.
For changing pixel formats WPF has the FormatConvertedBitmap class, which is a BitmapSource:
WriteableBitmap wbm;
...
var bm = new FormatConvertedBitmap(wbm, PixelFormats.Bgr24, null, 0);
It is not a BitmapImage, but you do not really need that anywhere. All WPF methods and properties (e.g. Image.Source) use either ImageSource or the derived BitmapSource as their type. There is no API that explicitly needs a BitmapImage, so conversion to BitmapImage is never necessary.
I resolve this problem. This is a code which Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder. Using this method ensure us, that our Image while saving stay unchanged. Unchanged I mean - size don't increase and Format Pixel stay on Bgr24.
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = new MemoryStream(stream.ToArray()); //stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
Related
I'm developing a kind of real-time video chatting and also trying to make a binary image using OpenCV. I'm working on C# WPF and I have to draw images on Canvas. This means I need 'BitmapImage' to draw the screen. But I have to use 'Mat' to make a binary image.
So I tried many solutions found from StackOverflow but never worked for me.
Below is my code.
Mat tempMat = new Mat();
Bitmap tempImage = BitmapImage2Bitmap(tempOriginal);
tempMat = BitmapConverter.ToMat(tempImage);
Converting BitmapImage to Bitmap first, and then convert Bitmap to Mat.
After that, make image binary. Below is the code.
Mat hsv = new Mat();
Mat binary = new Mat();
Cv2.CvtColor(tempMat, hsv, ColorConversionCodes.BGR2HSV);
Cv2.InRange(hsv, new Scalar(minH, minS, minV), new Scalar(maxH, maxS, maxV), binary);
tempMat = binary.Clone();
binary.Release();
hsv.Release();
After this operation, convert the mat back to Bitmap and BitmapImage again.
Below is the code.
tempImage = BitmapConverter.ToBitmap(tempMat);
BitmapImage resultBitmap = Bitmap2BitmapImage(tempImage);
ipWindow.ipView.ImageSource = resultBitmap;
I searched a lot and tried several solutions to change Bitmap to BitmapImage and vice versa.
But never worked. Below codes are solutions I tried.
I'm new to C# and also WPF, so I have no idea how to solve this problem.
Thanks for reading this long question.
Solution1. BitmapImage -> Bitmap
private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage){
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
outStream.Close();
return new Bitmap(bitmap);
}
}
Solution1. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap bitmap){
BitmapSource i = Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return (BitmapImage)i;
}
Solution2. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
Bitmap bitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
return bi;
}
Solution3. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
using (var memory = new MemoryStream())
{
inputBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
Solution4. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
MemoryStream ms = new MemoryStream();
inputBitmap.Save(ms, ImageFormat.Bmp);
BitmapImage image = new BitmapImage();
image.BeginInit();
ms.Seek(0, SeekOrigin.Begin);
image.StreamSource = ms;
image.EndInit();
return image;
}
I solved. The problems were two.
One was OpenCvSharp and another was Bitmap to BitmapImage.
Actually OpenCvSharp problem was severe so I couldn't find proper solutions between above. But after I uninstall and reinstall the openCvSharp, I found out what solution works for me. The code is below.
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap)
{
using (var memory = new MemoryStream())
{
inputBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
I am using MemoryStram for converting Bitmap to BitmapImage and when I checked the CPU usage it is consuming more memory. I wanted to reduce the memory consumption of MemoryStream object. I used it in Using statement also and the result is same as earlier mentioned.
I am pating my code snippet please can anyone help me out to find the solution or any other alternative that can be used.
Code :
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
System.Windows.Media.Imaging.BitmapImage bImg = new System.Windows.Media.Imaging.BitmapImage();
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(ms.ToArray());
bImg.EndInit();
return bImg;
}
}
Or
public static BitmapImage ConvertBitmapImage(this Bitmap bitmap)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
return bi;
}
There is no need for a second MemoryStream.
Just rewind the one that the Bitmap was encoded to, before decoding the BitmapImage, and set BitmapCacheOption.OnLoad to make sure that the stream can be closed after EndInit():
public static BitmapImage ConvertBitmapImage(this System.Drawing.Bitmap bitmap)
{
var bImg = new BitmapImage();
using (var ms = new MemoryStream())
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0; // here, alternatively use ms.Seek(0, SeekOrigin.Begin);
bImg.BeginInit();
bImg.CacheOption = BitmapCacheOption.OnLoad; // and here
bImg.StreamSource = ms;
bImg.EndInit();
}
return bImg;
}
Note that there are also other ways of converting between Bitmap and BitmapImage, e.g. this: fast converting Bitmap to BitmapSource wpf
I was looking for a routine by which I can crop tiff image and I got it but it gives many error. Here is the routine:
Bitmap comments = null;
string input = "somepath";
// Open a Stream and decode a TIFF image
using (Stream imageStreamSource = new FileStream(input, FileMode.Open, FileAccess.Read, FileShare.Read))
{
TiffBitmapDecoder decoder = new TiffBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
using (Bitmap b = BitmapFromSource(bitmapSource))
{
Rectangle cropRect = new Rectangle(169, 1092, 567, 200);
comments = new Bitmap(cropRect.Width, cropRect.Height);
//first cropping
using (Graphics g = Graphics.FromImage(comments))
{
g.DrawImage(b, new Rectangle(0, 0, comments.Width, comments.Height),
cropRect,
GraphicsUnit.Pixel);
}
}
}
When I try to compile it, I get an error. I tried adding references to many assemblies searching google but couldn't resolve it. I got this code from this url:
http://snipplr.com/view/63053/
I am looking for advice.
TiffBitmapDecoder class is from Presentation.Core in another words it is from WPF.
BitmapFromSource isn't method of any .net framework class. You can convert BitmapSource to Bitmap using this code.
private Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapsource));
encoder.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
Using Visual C# 2010, I'm trying to write an .avi file from frames received from a Windows Kinect. The frames can be saved easily enough as .png files with the use of a BitmapEncoder and PngBitmapEncoder (saving to a stream) but I can't add these images at my discretion to a VideoStream provided here:
http://www.codeproject.com/Articles/7388/A-Simple-C-Wrapper-for-the-AviFile-Library
because I need to be able to convert either a RenderTargetBitmap or a DrawingVisual to a System.Drawing.Bitmap.
I've found example codes that do similar things but they all seem to want to instantiate the Image class which Visual Studio tells me is abstract and can't be instantiated.
I'm going round in circles and not getting anywhere.
I just want to do something like this:
...
renderBitmap.Render(dv);
Bitmap bmp=new Bitmap(dv);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bmp);
...
But Bitmap has no useful constructors to get me from dv (DrawingVisual) to bmp. :(
Those 3 lines come from this snippet:
var renderBitmap=new RenderTargetBitmap(colorWidth,colorHeight,96.0,96.0,PixelFormats.Pbgra32);
DrawingVisual dv=new DrawingVisual();
using(DrawingContext dc=dv.RenderOpen())
{
VisualBrush backdropBrush=new VisualBrush(Backdrop);
dc.DrawRectangle(backdropBrush,null,new Rect(0,0,colorWidth,colorHeight));
VisualBrush colorBrush=new VisualBrush(MaskedColor);
dc.DrawRectangle(colorBrush,null,new Rect(0,0,colorWidth,colorHeight));
VisualBrush watermarkBrush=new VisualBrush(Watermark);
dc.DrawRectangle(watermarkBrush,null,new Rect(colorWidth-96,colorHeight-80,64,48));
}
renderBitmap.Render(dv);
Bitmap bmp=new Bitmap(dv);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bmp);
The result of using RenderTargetBitMap is a WPF BitMapSource it doesn't convert the Visual itself to a BitmapSource, it contains the result of the conversion as a BitmapSource. In order to convert a BitmapSource to a System.Drawing.Bitmap try using a modified version of the code from this MSDN Forum Post.
renderBitmap.Render(dv);
BitmapSource bmp = renderBitmap;
using(MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmp));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
VideoStream aviStream=aviManager.AddVideoStream(true,60,bitmap);
}
Created a Method to return your Bitmap
renderBitmap.Render(dv);
BitmapSource bmp =renderBitmap;
VideoStream aviStream = aviManager.AddVideoStream(true, 60, ConvertToBitmap(bmp));
private System.Drawing.Bitmap ConvertToBitmap(BitmapSource target)
{
System.Drawing.Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(target));
enc.Save(outStream);
bitmap = new System.Drawing.Bitmap(outStream);
}
return bitmap;
}
private BitmapSource ToBitmapSource(Visual visual, Brush transparentBackground)
{
var bounds = VisualTreeHelper.GetDescendantBounds(visual);
var scale = VisualTreeHelper.GetDpi(visual);
var bitmapSource = new RenderTargetBitmap(
(int)(bounds.Width * scale.DpiScaleX),
(int)(bounds.Height * scale.DpiScaleY),
scale.PixelsPerInchX,
scale.PixelsPerInchY,
PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(transparentBackground, null, new Rect(bounds.Size));
drawingContext.DrawRectangle(new VisualBrush(visual), null, new Rect(bounds.Size));
}
bitmapSource.Render(drawingVisual);
return bitmapSource;
}
private System.Drawing.Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
System.Drawing.Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new System.Drawing.Bitmap(outStream);
}
return bitmap;
}
I have BitmapImage in C#. I need to do operations on image. For example grayscaling, adding text on image, etc.
I have found function in stackoverflow for grayscaling which accepts Bitmap and returns Bitmap.
So I need to convert BitmapImage to Bitmap, do operation and convert back.
How can I do this? Is this best way?
There is no need to use foreign libraries.
Convert a BitmapImage to Bitmap:
private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
// BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));
using(MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
return new Bitmap(bitmap);
}
}
To convert the Bitmap back to a BitmapImage:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapImage retval;
try
{
retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
Here's an extension method for converting a Bitmap to BitmapImage.
public static BitmapImage ToBitmapImage(this Bitmap bitmap)
{
using (var memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
If you just need to go from BitmapImage to Bitmap it's quite easy,
private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
return new Bitmap(bitmapImage.StreamSource);
}
using System.Windows.Interop;
...
private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
BitmapSource i = Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return (BitmapImage)i;
}
I've just been trying to use the above in my code and I believe that there is a problem with the Bitmap2BitmapImage function (and possibly the other one as well).
using (MemoryStream ms = new MemoryStream())
Does the above line result in the stream being disposed of? Which means that the returned BitmapImage loses its content.
As I'm a WPF newbie I'm not sure that this is the correct technical explanation, but the code didn't work in my application until I removed the using directive.
Here the async version.
public static Task<BitmapSource> ToBitmapSourceAsync(this Bitmap bitmap)
{
return Task.Run(() =>
{
using (System.IO.MemoryStream memory = new System.IO.MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage as BitmapSource;
}
});
}
Thanks Guillermo Hernandez, I created a variation of your code that works. I added the namespaces in this code for reference.
System.Reflection.Assembly theAsm = Assembly.LoadFrom("My.dll");
// Get a stream to the embedded resource
System.IO.Stream stream = theAsm.GetManifestResourceStream(#"picture.png");
// Here is the most important part:
System.Windows.Media.Imaging.BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.StreamSource = stream;
bmi.EndInit();
This converts from System.Drawing.Bitmap to BitmapImage:
MemoryStream ms = new MemoryStream();
YOURBITMAP.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
BitmapImage image = new BitmapImage();
image.BeginInit();
ms.Seek(0, SeekOrigin.Begin);
image.StreamSource = ms;
image.EndInit();