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;
}
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'm tying together two libraries. One only gives output of type System.Windows.Media.Imaging.BitmapSource, the other only accepts input of type System.Drawing.Image.
How can this conversion be performed?
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;
}
This is an alternate technique that does the same thing. The accepted answer works, but I ran into problems with images that had alpha channels (even after switching to PngBitmapEncoder). This technique may also be faster since it just does a raw copy of pixels after converting to compatible pixel format.
public Bitmap BitmapFromSource(System.Windows.Media.Imaging.BitmapSource bitmapsource)
{
//convert image format
var src = new System.Windows.Media.Imaging.FormatConvertedBitmap();
src.BeginInit();
src.Source = bitmapsource;
src.DestinationFormat = System.Windows.Media.PixelFormats.Bgra32;
src.EndInit();
//copy to bitmap
Bitmap bitmap = new Bitmap(src.PixelWidth, src.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
src.CopyPixels(System.Windows.Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
bitmap.UnlockBits(data);
return bitmap;
}
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;
}
I have a WPF control in my PowerPoint add-in that hosts an image that I want to be able to drag & drop onto the active slide. I can get the image to appear on the slide, but the transparent areas are rendered in black.
My code to initialize the drag from my attached behavior:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var dataObject = new DataObject(
DataFormats.Bitmap,
targetBitmap);
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
Thinking that maybe I needed to pass a System.Drawing.Image, I attempted this modification, which only resulted in the transparent areas being rendered in gray:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
var ms = new MemoryStream();
encoder.Save(ms);
var dataObject = new DataObject(DataFormats.Bitmap, Image.FromStream(ms, true))
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
I did a test where I replaced the memory stream with a file stream, and the image that was written did indeed have the correct transparency.
So what am I missing here? How can I maintain transparency?
I was able to resolve this by following the instructions in this blog post. The solution was to use the EnhancedMetafile DataFormat in my DataObject.
Edit:
Here’s the code that initiates the drag operation.
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Bitmap bitmap = ImageToBitmap(e.Source as System.Windows.Controls.Image);
DataObject data = new DataObject(DataFormats.EnhancedMetafile, MakeMetafileStream(bitmap));
DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy);
}
This makes use of a utility function to convert the Image to a Bitmap:
private Bitmap ImageToBitmap(System.Windows.Controls.Image image)
{
RenderTargetBitmap rtBmp = new RenderTargetBitmap((int)image.ActualWidth, (int)image.ActualHeight,
96.0, 96.0, PixelFormats.Pbgra32);
image.Measure(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight));
image.Arrange(new Rect(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight)));
rtBmp.Render(image);
PngBitmapEncoder encoder = new PngBitmapEncoder();
MemoryStream stream = new MemoryStream();
encoder.Frames.Add(BitmapFrame.Create(rtBmp));
// Save to memory stream and create Bitamp from stream
encoder.Save(stream);
return new System.Drawing.Bitmap(stream);
}
This also requires a utility function that converts a Bitmap to a stream containing a Metafile, taken from Stack Overflow.
// From Convert an image into WMF with .NET?
private MemoryStream MakeMetafileStream(Bitmap image)
{
Graphics graphics = null;
Metafile metafile = null;
var stream = new MemoryStream();
try
{
using (graphics = Graphics.FromImage(image))
{
var hdc = graphics.GetHdc();
metafile = new Metafile(stream, hdc);
graphics.ReleaseHdc(hdc);
}
using (graphics = Graphics.FromImage(metafile))
{ graphics.DrawImage(image, 0, 0); }
}
finally
{
if (graphics != null)
{ graphics.Dispose(); }
if (metafile != null)
{ metafile.Dispose(); }
}
return stream;
}
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;
}