I have written C# code of live streaming of IP camera (JPEG) in windows form application using AForge library. It is working but it's lagging too much.
Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge.Video;
namespace CCTV_software
{
public partial class Form1 : Form
{
JPEGStream stream;
JPEGStream stream1;
public Form1()
{
InitializeComponent();
stream = new JPEGStream("ip");
stream1 = new JPEGStream("ip1");
stream.Login = "username";
stream.Password = "password";
stream1.Login = "username1";
stream1.Password = "password1";
stream.NewFrame += stream_NewFrame;
stream1.NewFrame += stream1_NewFrame1;
stream.Start();
stream1.Start();
}
void stream_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap bmp = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = bmp;
}
void stream1_NewFrame1(object sender, NewFrameEventArgs eventArgs)
{
Bitmap bp = (Bitmap)eventArgs.Frame.Clone();
pictureBox2.Image = bp;
}
}
Tried adding:
stream.FrameInterval = 0;
But it didn’t made any difference.
Kindly help me out with this issue.
Edit:
Edit 2:
#mjwills and my suggestions combined:
void stream_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
var oldImage = pictureBox1.Image
pictureBox1.Image = eventArgs.Frame; // set to new image without making a copy
oldImage?.Dispose(); // = Dispose previous image, if not null
}
This spares you a clone which is expensive and allows the gc to clean up the images timely.
Those measures combined should make it possible to improve throughput (i.e. lessen "lag"). Further countermeasures could be to make the image size smaller or reduce quality ...
NOTE: Above measures should at least improve lag, yet it is not guaranteed to make it disappear completely, since lag may be caused at other places, too.
Related
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
namespace WebcamTest
{
public partial class Form1 : Form
{
private Capture c;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
c = new Capture();
c.ImageGrabbed += ProcessFrame;
c.Start();
}
public void ProcessFrame(object sender, EventArgs e)
{
Image<Bgr, byte> image = c.QueryFrame();
c.ImageGrabbed -= ProcessFrame;
c.Stop();
c.Dispose();
}
}
}
So I have this really simple code to grab a webcam image and store it, just one time. It does nothing else currently, but I'm having a weird issue. In my file, one line seems to cause the issue:
Image<Bgr, byte> image = c.QueryFrame();
EDIT: I have tried changing this line to this as the wiki suggests:
Image<Bgr, byte> image = new Image<Bgr, byte>(c.RetreiveBgrFrame().ToBitmap());
This one always gives a null reference exception the first time; afterwards it acts the same as the other line. Every pixel's combined values are 0's after the first run.
Then I go a bit deeper into the problem,
I found that I keep getting a StackOverflowException in the Emgu.CV.dll itself. It continously says it is line 240 in this file: Capture.cs
The line is this:
bool grabbed = CvInvoke.cvGrabFrame(_ptr);
I have ran through their examples before and actually had them working before. I have never really had this issue before since it's coming from inside their dll. Why would this line keep causing this error? Is the pointer way off point for some unknown reason? The only thing I could think was that it was trying to access a memory location outside the actual bounds. It's always the very first run it does this. Then when it crashes, the camera stays on and so next time it runs. Though the image pixels are all 0's every time after that.
I have a small image editing program that opens an image and does fancy stuff to it. In this case, I'm trying to adjust the brightness of the pixels. The problem isn't getting the pixels right, its disposing the Bitmap/BitmapSources being created in the ValueChanged event for my slider.
So basically the user will click a button and it will open up a new window with a slider. This new window makes a copy of the original bitmap for later use.
When the slider's value changes, it will create a new Bitmap from the original with the corresponding brightness increase, create a new BitmapSource from this, and update the image control's source. Even though I'm using a 'using' statement, I still get an "out of memory" exception after sliding for so long.
Any idea why this is? Is there a better workaround that can achieve the same thing? I have included the code below:
using Imagin.Slideshow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Imagin
{
/// <summary>
/// Interaction logic for BrightnessWindow.xaml
/// </summary>
public partial class BrightnessWindow : Window
{
public int Increase;
private System.Drawing.Bitmap Bitmap;
private System.Drawing.Bitmap NewBitmap;
private MainWindow ParentWindow;
public BrightnessWindow(MainWindow m)
{
InitializeComponent();
this.ParentWindow = m;
this.Bitmap = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone();
}
private void slider1_ValueChanged(object sender, RoutedEventArgs e)
{
using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
{
this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
this.ParentWindow.image1.Source = m;
this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
}
}
private void applyButton_Click(object sender, RoutedEventArgs e)
{
if (this.NewBitmap != null)
{
this.ParentWindow.Document.Bitmap = this.NewBitmap;
}
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
this.ParentWindow.Document.Refresh();
this.Close();
}
}
}
Another thing I was wondering is if there is a better way to alter the MainWindow's controls without having to pass it as a parameter, or is this the preferred approach for that kind of thing? Thanks!
http://msdn.microsoft.com/en-us/library/1dz311e4%28v=vs.110%29.aspx
Every time you use bitmap.GetHbitmap you create a new instance of a gdibitmap. Per the msdn link, "You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation."
I'd pull that guy out to a variable and call deleteobject on it when you're done with it, otherwise you're just creating n gdibitmaps where n = number of times you move the slider and never disposing of them.
Edit: Highlighting the line:
//Somewhere in the class
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private void slider1_ValueChanged(object sender, RoutedEventArgs e)
{
using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
{
this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
IntPtr hbitmap = b.GetHbitmap(); //create variable so we don't orphan the object
BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); //use variable
this.ParentWindow.image1.Source = m;
this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
DeleteObject(hbitmap); //delete gdi object
}
}
everyone. I have been stuck here dealing with this bugs for days, but I still couldn't figure it out.
My guess: I think my code has some problem as I did not dispose the object properly after using it, (I'm not very familiar with these concepts of releasing resources, threading) .
I got these code by taking reference of what people did on youtube, but despite me doing exactly the same thing, my code didn't work out nicely.
SITUATION:
I have two picture boxes, left one can take video of me, right one take the snapshot, if you press button1 , you will start the video, clone_button will copy a image i.e. take a snapshot, and save_image should save it to the path reference, however, i get a generic error occured in GDI+ again and again while I'm trying to save it. Also, my debugger seemed to get crazy (i.e. failed to terminate the vshost.exe ) once I ran this program, I have to restart the computer to get my code running again, which is bleak and frustrating.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
//AForge.Video dll
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge;
namespace WebCameraCapture
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private FilterInfoCollection CaptureDevice; // list of webcam
private VideoCaptureDevice FinalFrame;
private void Form1_Load(object sender, EventArgs e)
{
CaptureDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);//constructor
foreach (FilterInfo Device in CaptureDevice)
{
comboBox1.Items.Add(Device.Name);
}
comboBox1.SelectedIndex = 0; // default
FinalFrame = new VideoCaptureDevice();
}
private void button1_Click(object sender, EventArgs e)
{
FinalFrame = new VideoCaptureDevice(CaptureDevice[comboBox1.SelectedIndex].MonikerString);// specified web cam and its filter moniker string
FinalFrame.NewFrame += new NewFrameEventHandler(FinalFrame_NewFrame);// click button event is fired,
FinalFrame.Start();
}
void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs) // must be void so that it can be accessed everywhere.
// New Frame Event Args is an constructor of a class
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();// clone the bitmap
}
private void From1_CLosing(object sender, EventArgs e)
{
if (FinalFrame.IsRunning==true) FinalFrame.Stop();
}
private void save_Click(object sender, EventArgs e)
{
if (pictureBox2.Image != null)
{
Bitmap varBmp = new Bitmap(pictureBox2.Image);
Bitmap newBitmap = new Bitmap(varBmp);
varBmp.Dispose();
varBmp = null;
varBmp.Save(#"C:\a.png", ImageFormat.Png);
}
else
{ MessageBox.Show("null exception"); }
}
private void clone_Click(object sender, EventArgs e)
{
pictureBox2.Image = (Bitmap)pictureBox1.Image.Clone();
}
}
}
Any AForge.net user can just PRESS the LINK below and try it out. Thanks!
SAMPLE
After having a look at your code, to me it appears that you are disposing of your image right before you save it. Meaning that your program can't save the image, because it doesn't exist anymore. Actually it reads that you've essentially removed the captured image twice, once on dispose, the second when you set it as null.
So if you move the two code segments after the save, it should be working. Granted without using a dialog box to change the name of the file, you'll surely receive an error unless you remove that file after each time it has been created.
private void save_Click(object sender, EventArgs e)
{
if (pictureBox2.Image != null)
{
//Save First
Bitmap varBmp = new Bitmap(pictureBox2.Image);
Bitmap newBitmap = new Bitmap(varBmp);
varBmp.Save(#"C:\a.png", ImageFormat.Png);
//Now Dispose to free the memory
varBmp.Dispose();
varBmp = null;
}
else
{ MessageBox.Show("null exception"); }
}
If you open the task manager, you can watch how much memory your program is soaking up.
Disposing of the memory after you're done using it, gives it back to the system.
You don't have a dispose inside your FinalFrame_NewFrame thread, so when the camera is reading images,
you should see the memory usage continue to climb until you stop the program.
I've added dispose to my thread, putting the memory usage under control, but now I'm debugging my image saves. Because I'm disposing, I can't save the image lol. My program ends up trying to save a null image file and throws the appropriate error.
I'm using a 2nd picurebox just as you are, but using for example, pbox2.image = pbox1.image, doesn't copy the data, it copies the memory location with the image data, so when I dispose pbox1 to free memory, the image data disappears with the memory location.
So, I had the same issue, and it was resolved one, by moving the dispose method, and two, I had to change the path, it didnt want to save to C:, so I put it on my desktop, you may not have had this issue if you were running as admin but I did so for anyone else who sees this, dont save to the root of C:.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Web;
using System.IO;
namespace WpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(Stream stream)
{
InitializeComponent();
String path = #"C:\Users\Public\Pictures\Sample Pictures\Jellyfish.jpg";
var image = new BitmapImage();
try
{
image.BeginInit();
image.StreamSource = stream;
image.EndInit();
using (Stream bmpStream = System.IO.File.Open(path,System.IO.FileMode.Open))
{
Image im = Image.FromStream(bmpStream);
//Bitmap img = (Bitmap)Image.FromFile("aa.gif", true);
//var im = ImageFromStream(bmpStream);
grid1.Background = new ImageBrush(new BitmapImage(new Uri(#"im")));
}
// return image;
}
catch (Exception a)
{
// return image;
}
}
public void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
I am using the above code to stream the jpg image as bitmap, and set it into a grid using c#, but Image im = Image.FromStream(bmpStream); shows an error. Can someone please guide me in how to stream the jpg image as bitmap and set into grid?
The way WPF handles and displays images is a bit different from the way WinForms did this.
Most image-displaying "things" need a ImageSource and you have to search for the right one to use.
In this case you should be able to use the BitmapImage - but the last time I checked this one needs some more care to display the content of a byte-stream correctly.
This snippet shows how you should be able to get this working right BitmapImage:
private static BitmapImage LoadImage(Stream stream)
{
// assumes that the streams position is at the beginning
// for example if you use a memory stream you might need to point it to 0 first
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
return image;
}
As you can see you need to tell the image to use the stream as soon as it loads (or you can get nasty things like ObjectDisposedExceptions.
You can use the returned object as your Image's source property.
So I think what you are looking for is something like this:
public MainWindow(Stream stream)
{
InitializeComponent();
grid1.Background = new ImageBrush(LoadImage(stream));
}
I'm a newbie in emgu cv and for a major project I'm trying to capture an image from a webcam and show it in an image box, but it's showing a black image.
What is wrong with the following code?
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Emgu.Util;
namespace WindowsFormsApplication7
{
public partial class Form1 : Form
{
private Capture capture;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (capture == null)
capture = new Capture();
Image<Bgr,Byte> img=capture.QueryFrame();
imageBox1.Image = img;
}
}
}
I think there is a minor mistake.
Use this instead:
Declare as global variable:
Capture capture = default(Capture);
Put this in load:
capture = new Capture(0);
Control.CheckForIllegalCrossThreadCalls = false;
System.Threading.Thread t = new System.Threading.Thread(grab);
t.Start();
Create a sub grab and put in,
do {
ImageBox1.Image = capture.QueryFrame();
} while (true);
Cheers
Shreyas
Call QueryFrame() twice, it works for me:
if (capture == null)
capture = new Capture();
capture.QueryFrame();
Image<Bgr,Byte> img=capture.QueryFrame().ToImage<Bgr, Byte>();
imageBox1.Image = img.Bitmap;
In my case Kaspersky AntiVirus was blocking the access to my webcam. After changing some security settings I was able to get emgu capture back working.