I am using kinect!!I get a frame and then I convert it to bitmap in order to use Emgucv to convert frame in grayscale then convert bitmap to bitmpa source in order to show in window!I am usign C# visual studio WPF!But my program consume much CPU usage and in case the video is frozen for seconds!!I guess that is the conversion bitmpa source to bitmap and viceverse
byte[] colorData = null;
WriteableBitmap colorImageBitmap = null;
void myKinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame == null) return;
if (colorData == null)
colorData = new byte[colorFrame.PixelDataLength];
colorFrame.CopyPixelDataTo(colorData);
if (colorImageBitmap == null)
{
this.colorImageBitmap = new WriteableBitmap(
colorFrame.Width,
colorFrame.Height,
96, // DpiX
96, // DpiY
PixelFormats.Bgr32,
null);
}
this.colorImageBitmap.WritePixels(
new Int32Rect(0, 0, colorFrame.Width, colorFrame.Height),
colorData, // video data
colorFrame.Width * colorFrame.BytesPerPixel, // stride,
0 // offset into the array - start at 0
);
Image<Gray, Byte> My_Image = new Image<Gray, byte>(BitmapFromSource(colorImageBitmap));
kinectVideo.Source = ToBitmapSource(My_Image);
}
}
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;
}
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource ToBitmapSource(IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
}
Instead of using Emgucv to do the greyscaling...which means you have to create a GDI+ Bitmap (System.Drawing.Bitmap) pass it to Emgucv, then convert it back from the GDI+ Bitmap to a BitmapSource, you could use FormatConvertedBitmap to keep it in the WPF world.
void myKinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame == null) return;
if (colorData == null)
colorData = new byte[colorFrame.PixelDataLength];
colorFrame.CopyPixelDataTo(colorData);
if (colorImageBitmap == null)
{
this.colorImageBitmap = new WriteableBitmap(
colorFrame.Width,
colorFrame.Height,
96, // DpiX
96, // DpiY
PixelFormats.Bgr32,
null);
}
this.colorImageBitmap.WritePixels(
new Int32Rect(0, 0, colorFrame.Width, colorFrame.Height),
colorData, // video data
colorFrame.Width * colorFrame.BytesPerPixel, // stride,
0 // offset into the array - start at 0
);
kinectVideo.Source = new FormatConvertedBitmap(colorImageBitmap, PixelFormats.Gray32Float, null, 0);
}
}
http://www.shujaat.net/2010/08/wpf-image-format-conversion-including.html
Another option is to use a pixel shader that applies a greyscale effect on your "kinectVideo" element (which is presumably an Image element to display the frame).
http://bursjootech.blogspot.co.uk/2008/06/grayscale-effect-pixel-shader-effect-in.html
Related
As far as I can tell the only way to convert from BitmapSource to Bitmap is through unsafe code... Like this (from Lesters WPF blog):
myBitmapSource.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pBits = bits)
{
IntPtr ptr = new IntPtr(pBits);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
width,
height,
stride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb,ptr);
return bitmap;
}
}
To do the reverse:
System.Windows.Media.Imaging.BitmapSource bitmapSource =
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
Is there an easier way in the framework? And what is the reason it isn't in there (if it's not)? I would think it's fairly usable.
The reason I need it is because I use AForge to do certain image operations in an WPF app. WPF wants to show BitmapSource/ImageSource but AForge works on Bitmaps.
It is possible to do without using unsafe code by using Bitmap.LockBits and copy the pixels from the BitmapSource straight to the Bitmap
Bitmap GetBitmap(BitmapSource source) {
Bitmap bmp = new Bitmap(
source.PixelWidth,
source.PixelHeight,
PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(
new Rectangle(Point.Empty, bmp.Size),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppPArgb);
source.CopyPixels(
Int32Rect.Empty,
data.Scan0,
data.Height * data.Stride,
data.Stride);
bmp.UnlockBits(data);
return bmp;
}
You can just use these two methods:
public static BitmapSource ConvertBitmap(Bitmap source)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
source.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
It works perfectly for me.
Is this what your looking for?
Bitmap bmp = System.Drawing.Image.FromHbitmap(pBits);
Here a code to set transparent background to any bitmap resource within a Resource Dictionary (not Resources.resx often used in Windows.Forms age). I call this methode before InitializeComponent() - methode. The methodes 'ConvertBitmap(Bitmap source)' and BitmapFromSource(BitmapSource bitmapsource) are mentioned in post from melvas above.
private void SetBitmapResourcesTransparent()
{
Image img;
BitmapSource bmpSource;
System.Drawing.Bitmap bmp;
foreach (ResourceDictionary resdict in Application.Current.Resources.MergedDictionaries)
{
foreach (DictionaryEntry dictEntry in resdict)
{
// search for bitmap resource
if ((img = dictEntry.Value as Image) is Image
&& (bmpSource = img.Source as BitmapSource) is BitmapSource
&& (bmp = BitmapFromSource(bmpSource)) != null)
{
// make bitmap transparent and assign it back to ressource
bmp.MakeTransparent(System.Drawing.Color.Magenta);
bmpSource = ConvertBitmap(bmp);
img.Source = bmpSource;
}
}
}
}
This is neat and faster than light:
return Imaging.CreateBitmapSourceFromHBitmap( bitmap.GetHbitmap(), IntPtr.Zero,
Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() );
You can share the pixeldata between both namespaces. You don't have to convert.
Use the SharedBitmapSource. https://stackoverflow.com/a/32841840/690656
I have a program that captures a frame from webcam on a click. The capture works ok and I save it as a bitmap ok, but I have a weird problem that the bitmap is offset - the size is correct, but it cuts from the bottom a part away (like 25% of the picture) and the top is all black.
Any ideas what causes this?
The flow goes like:
public static string TempPicLocation = #"%USERPROFILE%\AppData\Local\temppic.bmp";
private void StartButton_Click(object sender, RoutedEventArgs e)
{
int capturedeviceindex = cboDevices.SelectedIndex;
FilterInfo cd = CaptureDevice[cboDevices.SelectedIndex];
string cdms = cd.MonikerString;
FinalFrame = new VideoCaptureDevice(cdms);
FinalFrame.NewFrame += FinalFrame_NewFrame;
FinalFrame.Start();
}
private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
var imageSource = ImageSourceForBitmap(eventArgs.Frame);
imageSource.Freeze();
this.Dispatcher.Invoke(
new Action(
() =>
{
pboLive.Source = imageSource;
return;
}
)
);
}
//If you get 'dllimport unknown'-, then add 'using System.Runtime.InteropServices;'
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject([In] IntPtr hObject);
public ImageSource ImageSourceForBitmap(Bitmap bmp)
{
var handle = bmp.GetHbitmap();
try
{
return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
finally { DeleteObject(handle); }
}
private void CaptureButton_Click(object sender, RoutedEventArgs e)
{
ImageSource Captured = pboLive.Source;
pboSnap.Source = Captured.Clone();
capturedpictures.Add(pboLive.Source);
var filePath = Environment.ExpandEnvironmentVariables(TempPicLocation);
ImageHandlers.SaveToBmp(pboLive, filePath);
}
internal static void SaveToBmp(FrameworkElement visual, string fileName)
{
var encoder = new BmpBitmapEncoder(); //In System.Windows.Media.Imaging
SaveUsingEncoder(visual, fileName, encoder);
}
internal static void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
//Here the commented part is the right size, but with 5k x 5k is used to check that the entire picture actually is there. And yes, it indeed is.
//RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32); //In System.Windows.Media.Imaging
RenderTargetBitmap bitmap = new RenderTargetBitmap(5000, 5000, 96, 96, PixelFormats.Pbgra32); //In System.Windows.Media.Imaging
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap); //In System.Windows.Media.Imaging
encoder.Frames.Add(frame);
string filePath = fileName.Replace(Path.GetFileName(fileName), string.Empty);
System.IO.Directory.CreateDirectory(filePath);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
https://social.msdn.microsoft.com/Forums/vstudio/en-US/984da366-33d3-4fd3-b4bd-4782971785f8/questions-about-rendertargetbitmap?forum=wpf
public static BitmapSource CreateBitmapFromVisual(Visual target, Double dpiX, Double dpiY)
{
if (target == null)
{
return null;
}
Rect bounds = VisualTreeHelper.GetContentBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((Int32)(bounds.Width * dpiX / 96.0),
(Int32)(bounds.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(target);
dc.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}
this piece of code is posted by some one from microsoft, and then you can do this:
using (FileStream outStream = new FileStream(#"C:\mycanvas.png", FileMode.Create))
{
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(CreateBitmapFromVisual(myCanvas, 96, 96)));
enc.Save(outStream);
}
I have a System.Windows.Media.Drawing object that I am wanting to convert into a Bitmap object, and then from there extract the bytes that represent the image. I've looked about the internet and I can't seem to find how to do what I need, so any help would be appreciated.
So I finally found a way to convert a System.Windows.Media.Drawing object to a System.Drawing.Bitmap object, and from that get a byte[] object representing the image data. The following is not pretty but does actually work.
public static byte[] DrawingToBytes(Drawing drawing)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext context = visual.RenderOpen())
{
// If using the BitmapEncoder uncomment the following line to get a white background.
// context.DrawRectangle(Brushes.White, null, drawing.bounds);
context.DrawDrawing(drawing);
}
int width = (int)(drawing.Bounds.Width)
int height = (int)(drawing.Bounds.Height)
Bitmap bmp = new Bitmap(width, height);
Bitmap bmpOut;
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(System.Drawing.Color.White);
RenderTargetBitmap rtBmp = new RenderTargetBitmap(width, height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
PixelFormats.Pbgra32);
rtBmp.Render(visual);
// Alternative using BmpBitmapEncoder, use in place of what comes after if you wish.
// MemoryStream stream = new MemoryStream();
// BitmapEncoder encoder = new BmpBitmapEncoder();
// encoder.Frames.Add(BitmapFrame.Create(rtBmp));
// encoder.save(stream);
int stride = width * ((rtBmp.Format.BitsPerPixel + 7) / 8);
byte[] bits = new byte[height * stride];
bitmapSource.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pBits = bits)
{
IntPtr ptr = new IntPtr(pBits);
bmpOut = new Bitmap(width, height, stride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb, ptr);
}
}
g.DrawImage(bmpOut, 0, 0, bmp.Width, bmp.Height);
}
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.bmp);
data = ms.ToArray();
}
return bytes;
}
So yeah, it's horrible but it actually works.
You can try that:
byte[] ImageToByte(Image image)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
This work also for Bitmap.
I use the following code for resizing a gif file.
public void Resize(int newWidth, int newHeight)
{
var dimension = new FrameDimension(_image.FrameDimensionsList[0]);
var frameCount = _image.GetFrameCount(dimension);
// load frames
var encoder = new GifBitmapEncoder();
for (var i = 0; i < frameCount; ++i)
{
_image.SelectActiveFrame(dimension, i);
encoder.Frames.Add(BitmapFrame.Create(
new Bitmap(_image, newWidth, newHeight).CreateBitmapSource()));
}
using (var ms = new MemoryStream())
{
encoder.Save(ms);
ms.Position = 0;
_image = new Bitmap(ms);
}
}
public static BitmapSource CreateBitmapSource(this Bitmap bitmap)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
lock (bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
If I output it to a filestream, the gif is animated (altough the frames seem a little slower), but if i load it into an image, it doesn't matter wether from the memorystream or loading it from the "working" gif that came from the filestream, it only contains 1 frame.
What am I doing wrong?
I´m using Kinect (Microsoft SDK) with XNA. I want to use GRATF for marker-recognition
How to convert the data of a Kinect ColorImageFrame to a System.Drawing.Bitmap or AForge.Imaging.UnmanagedImage that I can process them with GRATF?
void kinectSensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
Bitmap bitmap = null;
ColorImageFrame frame = e.OpenColorImageFrame();
byte[] buffer = new byte[frame.PixelDataLength];
frame.CopyPixelData(buffer);
// how to convert the data in buffer to a bitmap?
var glyphs = recognizer.FindGlyphs(bitmap);
...
}
You can find the answer in this article.
To summarize it, this method should do the trick:
Bitmap ImageToBitmap(ColorImageFrame img)
{
byte[] pixeldata = new byte[img.PixelDataLength];
img.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, img.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}