How can I playback and save selected raw video frames from raw video data (in .rgb or .raw).
Raw uncompressed video data is 320 x 240 in grayscale format with 1 byte/pixel and just 30 fps.
But how can I view multiple the frames in 30 FPS?
Can I use DirectShow or a similar API? And what is the best resource to get started? I looked at FFPMEG but I want to avoid showing compressed frames as this it is for scientific application.
So far I have been able to view one frame from a raw video data (in .rgb) using this code in C# .NET for Win Forms using a Picture Box control.
But not sure how to do multiple frames and perhaps have a seeking control.
byte[] imageData = File.ReadAllBytes("output.rgb");
Console.WriteLine("imageDataLen=" + imageData.Length);
int width = 320;
int height = 240;
var bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
//bmp = (Bitmap) ConvertToGrayScale(bmp);
ColorPalette pal = bmp.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
bmp.Palette = pal;
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(imageData, 0, bmpData.Scan0, width * height);
bmp.UnlockBits(bmpData);
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
pictureBox1.Image = bmp;
Related
I'm currently loading and saving Texture2Ds used as mapped images into a database so they can be preloaded later. Each color channel needs to be the precise color it was when saved after loading. The problem is Texture2D.FromStream sometimes returns incorrect color channels that are sometimes off by 1 or so. This is unacceptable as the mapped colors are useless if they are incorrect.
The example below provides a situation where an RGB of 255 is changed to an RGB of 254 when the alpha is set at 100. When setting the alpha to 1 or 255 they return as 255 correctly, other alpha values cause the same issue as 100.
Texture2D tex = new Texture2D(GraphicsDevice, 20, 20, false, SurfaceFormat.Color);
Color[] data = new Color[tex.Width * tex.Height];
for (int i = 0; i < data.Length; i++) {
data[i] = new Color(255, 255, 255, 100);
}
tex.SetData<Color>(data);
using (Stream stream = File.Open("TestAlpha.png", FileMode.OpenOrCreate)) {
tex.SaveAsPng(stream, tex.Width, tex.Height);
stream.Position = 0;
tex = Texture2D.FromStream(GraphicsDevice, stream);
tex.GetData<Color>(data);
Console.WriteLine(data[0]); // Returns (R:254 G:254 B:254 A:100)
}
I have confirmed the png has the correct RGB of 255 when looking at the saved image in Paint.NET so it can only be something caused during Texture2D.FromStream.
Well, I did not find the cause of Texture2D.FromStream's issue but I found an all-around better way to load Texture2Ds. In this case I load GDI Bitmap from the stream instead, take its data, and transfer it over to a newly created Texture2D.
I plan on using this whether or not Texture2D.FromSteam is fixable but I'd still love to know if anyone knows what's going on with it.
For those of you who are new, make sure to include System.Drawing as a reference as its not present in XNA projects by default.
public static unsafe Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream) {
// Load through GDI Bitmap because it doesn't cause issues with alpha
using (Bitmap bitmap = (Bitmap) Bitmap.FromStream(stream)) {
// Create a texture and array to output the bitmap to
Texture2D texture = new Texture2D(graphicsDevice,
bitmap.Width, bitmap.Height, false, SurfaceFormat.Color);
Color[] data = new Color[bitmap.Width * bitmap.Height];
// Get the pixels from the bitmap
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
// Write the pixels to the data buffer
byte* ptr = (byte*) bmpData.Scan0;
for (int i = 0; i < data.Length; i++) {
// Go through every color and reverse red and blue channels
data[i] = new Color(ptr[2], ptr[1], ptr[0], ptr[3]);
ptr += 4;
}
bitmap.UnlockBits(bmpData);
// Assign the data to the texture
texture.SetData<Color>(data);
// Fun fact: All this extra work is actually 50% faster than
// Texture2D.FromStream! It's not only broken, but slow as well.
return texture;
}
}
I have an .rgb file that I attached. It is a valid rgb file that I verified through other software. As far as I understand this is a raw RGB data file.
I need to load the file into byte[] and display it in PictureBox.
From all my searches here I came to this solution that seems correct and that works for others. Unfortunately it displays nothing for me:
string imgPath = Path.Combine(mInstallPath, "squirrel-720x576x50.rgb\\squirrel-720x576x50.rgb");
byte[] imageData = File.ReadAllBytes(imgPath);
using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(imageData, 0, bmpData.Scan0, width * height * 3);//imageData.Length);
//bmpSource.CopyPixels((Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride);
bmp.UnlockBits(bmpData);
pictureBox.Image = bmp;
}
Any ideas why nothing is displayed in my pictureBox ?
hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.
I've been working for a few weeks on grabbing a webcam image and rendering it on a windows form, but have been struggeling with speed issues the entire time. I need at least a frame rate of 10 Hz to be able to update my background process.
I started of using a pictureBox but the solution I've ended up with is to create a XNA-panel inside my Form, and then rendering the image as a background-sprite by converting the bitmap into a Texture2D using a script I found here.
The problem I've encountered now and have not been able to solve is; when I load a Bitmap in the code by calling the bitmap constructor like below, everything runs smoothly and I can get high fps. This is what I did during testing and was very happy with the results.
Bitmap image = new Bitmap(320, 240);
But as soon as I send the bitmap that I grab from the webcam it takes a lot longer to render for some reason I can not fathom. To my knowledge of bitmaps the images are the same format, it's just the colour of the pixels that are different. What I checked for format is size (320*240), resolution (96) and pixel format (Format32bppArgb). Am I missing anything?
This is how I grab the image from the webcam:
VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
FinalVideoSource = new VideoCaptureDevice(VideoCaptureDevices[0].MonikerString);
FinalVideoSource.DesiredFrameSize = new Size(320, 240);
FinalVideoSource.DesiredFrameRate = fps;
FinalVideoSource.NewFrame += new NewFrameEventHandler(FinalVideoSource_NewFrame);
void FinalVideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
// create bitmap from frame
image = eventArgs.Frame.Clone(new Rectangle(0, 0, 320, 240), PixelFormat.Format32bppArgb);
...
This is my draw function in XNA:
protected override void Draw()
{
backTexture = GetTexture(GraphicsDevice, image);
GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
// TODO: Add your drawing code here
sprites.Begin();
Vector2 pos = new Vector2(0, 0);
sprites.Draw(backTexture, pos, Microsoft.Xna.Framework.Color.White);
sprites.End();
}
private Texture2D GetTexture(GraphicsDevice dev, System.Drawing.Bitmap bmp)
{
int[] imgData = new int[bmp.Width * bmp.Height];
Texture2D texture = new Texture2D(dev, bmp.Width, bmp.Height);
unsafe
{
// lock bitmap
System.Drawing.Imaging.BitmapData origdata =
bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
uint* byteData = (uint*)origdata.Scan0;
// Switch bgra -> rgba
for (int i = 0; i < imgData.Length; i++)
{
byteData[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);
}
// copy data
System.Runtime.InteropServices.Marshal.Copy(origdata.Scan0, imgData, 0, bmp.Width * bmp.Height);
byteData = null;
// unlock bitmap
bmp.UnlockBits(origdata);
}
texture.SetData(imgData);
return texture;
}
I would be very grateful if someone could help me with this since I'm stuck now. The community here has been great and I managed to get this far without asking before, which is amazing since I have no prior experience with C# or XNA. With that in mind I realize I might be missing something simple or just approaching this the wrong way.
I've narrowed it down to the bitmap image loading. The only thing I change when using the newly constructed bitmap is to simply overwrite the one from the webcam before processing it in XNA.
My question right now is really, am I missing something with how the bitmaps are constructed which can explain the big difference in rendering speed? Is the conversion to Texture2D the problem here? But I don't see how a different image could affect the speed of that conversion.
Ok. I don't know what is the problem exactly. But I can give you some comments.
-First, set the frame rate of the XNA game equal or less that your webcam fps.
By default XNA run at 60fps, so you are calling the GetTexture() method twice for each frame of your webcam if you are using 30fps.
In the Initialize code:
TargetElapsedTime = TimeSpan.FromSeconds(1f/webcam fps)
If that don't work...
you can try this code to convert from bitmap to texture.
protected override void Draw()
{
//Unset the texture from the GraphicsDevice
for (int i = 0; i < 16; i++)
{
if (Game.GraphicsDevice.Textures[i] == backTexture)
{
Game.GraphicsDevice.Textures[i] = null;
break;
}
}
backTexture.SetData<byte>(image.GetBytes());
GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
// TODO: Add your drawing code here
sprites.Begin();
Vector2 pos = new Vector2(0, 0);
sprites.Draw(backTexture, pos, Microsoft.Xna.Framework.Color.White);
sprites.End();
}
public static byte[] GetBytes(this Bitmap bitmap)
{
var data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
// calculate the byte size: for PixelFormat.Format32bppArgb (standard for GDI bitmaps) it's the hight * stride
int bufferSize = data.Height * data.Stride; // stride already incorporates 4 bytes per pixel
// create buffer
byte[] bytes = new byte[bufferSize];
// copy bitmap data into buffer
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
// unlock the bitmap data
bitmap.UnlockBits(data);
return bytes;
}
I was testing this code and works fine. Hope this help.
I am opening different types of images like (8 and 16 bit) and they are (Monocrome, RGB ,palette color).
I have raw pixel data of these images.
I create bitmap like this for 8 bit images.
//for monocrome images i am passing PixelFormat.Format8bppIndexed.
//for RGB images i am passing PixelFormat.Format24bppRgb
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(Img_Width, Img_Height,format);
Rectangle rect = new Rectangle(0, 0, Img_Width, Img_Height);
//locking the bitmap on memory
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
// copy the managed byte array to the bitmap's image data
Marshal.Copy(rawPixel, 0, bmpData.Scan0, rawPixel.Length);
bmp.UnlockBits(bmpData);
The problem is that when i draw that bmp image then it differs in color than original.
So is there any way to apply lut (lookup table) on that colored images.
i want any unsafe code because i tried getixel and setPixel and they are very slow.
I also dont want Image.fromSource() methods.
Take a look at the bitmap's Image.Palette property.
As far as I know, .NET does not support ICC color profiles, so if you for instance open an image that is using the AdobeRGB color profile, the colours will appear a bit duller and more "greyish" than if you open the same image file in, say, Photoshop or another color-profile-aware software.
This post discusses some color profile issues; you may find something of interest there.
GetPixel and SetPixel are indeed very slow. If you want to do operations on single pixels, consider using a FastBitmap. It allows to quickly color pixels. Using this unsafe bitmap will greatly improve your speed.
i solved this problem see how.
Somewhere i read that GDI+ return BGR value not the RGB.
So i reverse the order and amazing everything fine.
But it is little bit slow.
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(Img_Width, Img_Height,format);
Rectangle rect = new Rectangle(0, 0, Img_Width, Img_Height);
//locking the bitmap on memory
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
Marshal.Copy(rawPixel, 0, bmpData.Scan0, rawPixel.Length);
int stride = bmpData.Stride;
System.IntPtr Scan0 = bmpData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - bmp.Width * SAMPLES_PER_PIXEL ;
byte red, green, blue;
for (int y = 0; y < bmp.Height; ++y)
{
for (int x = 0; x < bmp.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
p[0] = red;
p[1] = green;
p[2] = blue;
p += 3;
}
p += nOffset;
}
}
////unlockimg the bitmap
bmp.UnlockBits(bmpData);
thanks
Can anybody is having some faster code than that.