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;
}
Related
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 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
I'm using a C# library for reading of QRCodes. A lot of the samples I've found are based off the old version of zxing where the RGBLuminanceSource constructor still takes in bitmap. In the latest version RGBLuminanceSource only takes byte[]. I've tried to convert bitmap to byte[], but the decode result is always null.
Here's the code I used for conversion:
private byte[] GetRGBValues(Bitmap bmp)
{
// Lock the bitmap's bits.
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
return rgbValues;
}
and for decode:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
LuminanceSource source = new RGBLuminanceSource(GetRGBValues(bitmap), bitmap.Width, bitmap.Height);
var binarizer = new HybridBinarizer(source);
var binBitmap = new BinaryBitmap(binarizer);
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(binBitmap);
Somehow the result is always null.
Also, our requirement is that we have to use image taken by camera. I've tried this:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
BarcodeReader reader = new BarcodeReader { AutoRotate = true, TryHarder = true };
Result result = reader.Decode(bitmap);
It only works for the QR image I download online, but if I print out that image and take a picture of that with my phone, then try to process that image, the result is back to null.
Any suggestions would be appreciated.
Here's the image I'm using:
I've attempted two different methods to achieve this, the first being an Android-style method and the second being an OpenGL style method. From my activity, I create a view which contains the OpenGL (1.1) code.
The first method (android):
Bitmap b = gameView.GetDrawingCache (true); // this is always null
And the second method (opengl):
public Bitmap GrabScreenshot()
{
int size = Width * Height * 4;
byte[] bytes = new byte[size];
GL.ReadPixels<byte>(0, 0, Width, Height, All.Rgba, All.UnsignedByte, bytes);
Bitmap bmp = BitmapFactory.DecodeByteArray (bytes, 0, size);
return bmp;
}
I have not tested this code. I was thinking you might be able to use it as a guide.
How about trying something like this (derived from: OpenTK Forums):
public Bitmap GrabScreenshot()
{
Bitmap bmp = new Bitmap(Width, Height);
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(otkViewport.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.Finish();
GL.ReadPixels(0, 0, this.otkViewport.Width, this.otkViewport.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
I believe a problem might occur due to the formatting of the bytes. In the example, they explicitly state the beginning to the array of data with
data.Scan0
However, you just sends in a byte array.
Here a version that works on Xamarin.Android:
private static Bitmap GraphicsContextToBitmap(int width, int height)
{
GL.Flush();
GL.PixelStore (PixelStoreParameter.PackAlignment, 1);
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
var data = bitmap.LockPixels();
GL.ReadPixels(0, 0, width, height, PixelFormat.Rgba, PixelType.UnsignedByte, data);
GL.Finish();
bitmap.UnlockPixels();
return bitmap;
}
The XAML:
<ImageBrush x:Key="Symbol1Brush" ImageSource="Resources\Symbol1.png" Stretch="Uniform" />
The code:
// In some class
_imageProcessor = new ImageProcessor(Resources["Symbol1Image"] as BitmapImage)
and
public class ImageProcessor
{
private readonly Bitmap _primaryMarkerSymbol;
public ImageProcessor(BitmapImage primaryMarkerSymbol)
{
if (primaryMarkerSymbol == null)
throw new ArgumentNullException("primaryMarkerSymbol");
_primaryMarkerSymbol = new Bitmap(primaryMarkerSymbol.StreamSource);
}
public Bitmap ProcessImage()
{
Graphics g = Graphics.FromImage(img);
g.DrawImage(_primaryMarkerSymbol);
g.Flush();
return img;
}
}
_primaryMarkerSymbol = new Bitmap(primaryMarkerSymbol.StreamSource)
throws Exception: Value of 'null' is not valid for 'stream'.
I assume the StreamSource is not populated if BitmapImage is created from Resource.
What alternatives there are?
Thanks.
EDIT:
The point is to use the source object (ex. ImageBrush, BitmapImage) defined in the XAML ResourceDictionary.
You might need to copy the bitmap's pixels somehow like this:
// test image
BitmapImage image = new BitmapImage(new Uri(#"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"));
// copy to byte array
int stride = image.PixelWidth * 4;
byte[] buffer = new byte[stride * image.PixelHeight];
image.CopyPixels(buffer, stride, 0);
// create bitmap
System.Drawing.Bitmap bitmap =
new System.Drawing.Bitmap(
image.PixelWidth,
image.PixelHeight,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// lock bitmap data
System.Drawing.Imaging.BitmapData bitmapData =
bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
bitmap.PixelFormat);
// copy byte array to bitmap data
System.Runtime.InteropServices.Marshal.Copy(
buffer, 0, bitmapData.Scan0, buffer.Length);
// unlock
bitmap.UnlockBits(bitmapData);
This worked for me to set an image source using resource files
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(MyProject.Properties.Resources.myImage.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
MyButton.Background = new ImageBrush(bitmapSource);
In the constructor, I would rather use this:
System.Windows.Resources.StreamResourceInfo imageInfo = System.Windows.Application.GetResourceStream(primaryMarkerSymbol.UriSource);
_primaryMarkerSymbol = Image.FromStream(imageInfo.Stream);
On a clear moment I have come up with a solution that I could saddle with.
I used the BitmapImage.UriSource to get the relative image path and load the Image:
public class ImageProcessor
{
private readonly Image _primaryMarkerSymbol;
public ImageProcessor(BitmapImage primaryMarkerSymbol)
{
_primaryMarkerSymbol = Image.FromFile(primaryMarkerSymbol.UriSource.ToString());
}
public Bitmap ProcessImage(string fileName)
{
var img = new Bitmap(fileName);
Graphics g = Graphics.FromImage(img);
g.DrawImage(_primaryMarkerSymbol);
g.Flush();
return img;
}
}
It would be good if I could use the object itself to draw the image on the graphics not to load by path. So you are welcome to come up with a better idea.