C#, Saving frames from webcam continuously every 5s - c#

When ran the code which is GUI based and it actually opens a webcamera feed and saves frame every 5s, after a random running time(it could be 5 min or 20 min) there is an exception occuring ''Object in use by another process'' possibly related to the saving of the current frame. Any ideas what causes the problem and what code modifications should be done?
public partial class Form1 : Form
{
private FilterInfoCollection CaptureDevices;
private VideoCaptureDevice videoSource;
bool blncapturing;
public Form1()
{
InitializeComponent();
timer1.Enabled = false;
timer1.Interval = 5000;
blncapturing = false;
}
private void button1_Click(object sender, EventArgs e)
{
videoSource = new VideoCaptureDevice(CaptureDevices[comboBox1.SelectedIndex].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(VideoSource_NewFrame);
videoSource.Start();
timer1.Enabled = true;
}
private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
if (pictureBox1.Image != null)
((IDisposable)pictureBox1.Image).Dispose();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void Form1_Load_1(object sender, EventArgs e)
{
CaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo Device in CaptureDevices)
{
comboBox1.Items.Add(Device.Name);
}
comboBox1.SelectedIndex = 0;
videoSource = new VideoCaptureDevice();
}
private void button2_Click(object sender, EventArgs e)
{
videoSource.Stop();
pictureBox1.Image = null;
pictureBox1.Invalidate();
pictureBox2.Image = null;
pictureBox2.Invalidate();
}
private void button3_Click(object sender, EventArgs e)
{
Capturing();
}
private void button4_Click(object sender, EventArgs e)
{
if (videoSource.IsRunning == true)
{
videoSource.Stop();
}
Application.Exit(null);
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
Capturing();
timer1.Start();
}
private void Capturing()
{
try
{
if (blncapturing == false)
{
blncapturing = true;
pictureBox2.Image = (Bitmap)pictureBox1.Image.Clone();
string strGrabFileName = String.Format("C:\\Users\\echristo\\Desktop\\trial\\Snapshot_{0:yyyyMMdd_hhmmss.fff}.bmp", DateTime.Now);
pictureBox2.Image.Save(strGrabFileName, System.Drawing.Imaging.ImageFormat.Jpeg) ;
blncapturing = false;
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}

If you access objects from different threads you need a lock.
Also use invoke when you access a control from a different thread.
private readonly object myLock = new object();
private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
lock (myLock) {
this.Invoke((Action)(() => {
if (pictureBox1.Image != null)
((IDisposable)pictureBox1.Image).Dispose();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}));
}
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
// you can try with timer1 inside the lock
lock (myLock) {
// you don't need invoke here, because the winforms timer runs on the main thread.
Capturing();
}
timer1.Start();
}
Also as #TheGeneral said, dispose pictureBox2.Image after saving.

Related

Problems with Image Tag in Picturebox

So, i try to learn how to Drag and Drop an Image from one PictureBox to another and that works well. But how can i drag and drop the Image TAG from pictureox1 to picturebox2?
i currently have 3 source images and 3 drop boxes.
the dropbox6 is locked with a countdown after a buttonclick (see button2)
(later i will lock all dropboxes)
after i hit this button a countdown starts and if the countdown is 0 (ZERO) only then i can drag one of the 3 images to this box.
however, i have given each of these 3 images in the souceboxes a TAG name.
how can i drop this tagname also to the dropbox?
here is what i have so far:
(the Label labeled "Counter" is actually Label4 in 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;
namespace Game
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
// pictureBox1.DoDragDrop(pictureBox1.Image, DragDropEffects.Copy);
((PictureBox)sender).DoDragDrop(((PictureBox)sender).Image, DragDropEffects.Copy);
}
private void Form1_Load(object sender, EventArgs e)
{
pictureBox4.AllowDrop = true;
pictureBox5.AllowDrop = true;
pictureBox6.AllowDrop = true;
pictureBox6.Enabled = false;
}
private void pictureBox4_DragEnter(object sender, DragEventArgs e)
{
if(e.Data.GetDataPresent(DataFormats.Bitmap))
{
e.Effect = DragDropEffects.Copy;
}
}
private void pictureBox4_DragLeave(object sender, EventArgs e)
{
}
private void pictureBox4_DragDrop(object sender, DragEventArgs e)
{
Image getPicture = (Bitmap) e.Data.GetData(DataFormats.Bitmap);
pictureBox4.Image = getPicture;
}
private void pictureBox5_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Bitmap))
{
e.Effect = DragDropEffects.Copy;
}
}
private void pictureBox5_DragLeave(object sender, EventArgs e)
{
}
private void pictureBox5_DragDrop(object sender, DragEventArgs e)
{
Image getPicture = (Bitmap)e.Data.GetData(DataFormats.Bitmap);
pictureBox5.Image = getPicture;
}
private void pictureBox6_DragDrop(object sender, DragEventArgs e)
{
Image getPicture = (Bitmap)e.Data.GetData(DataFormats.Bitmap);
pictureBox6.Image = getPicture;
timer1.Enabled = false;
}
private void pictureBox6_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Bitmap))
{
e.Effect = DragDropEffects.Copy;
timer1.Enabled = false;
}
}
private void pictureBox6_DragLeave(object sender, EventArgs e)
{
timer1.Enabled = false;
}
private void pictureBox4_Click(object sender, EventArgs e)
{
MessageBox.Show("test");
}
private void timer1_Tick(object sender, EventArgs e)
{
counter--;
if (counter == 0)
timer1.Stop();
label4.Text = counter.ToString();
if(counter == 0)
{
pictureBox6.Enabled = true;
label4.Enabled = false;
timer1.Stop();
}
}
private void button1_Click(object sender, EventArgs e)
{
System.Windows.Forms.Application.Exit();
}
private void pictureBox6_Click(object sender, EventArgs e)
{
MessageBox.Show(pictureBox6.Tag.ToString());
}
private int counter = 3;
private void button2_Click(object sender, EventArgs e)
{
int counter = 3;
timer1 = new Timer();
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Interval = 1000; // 1 second
timer1.Start();
label4.Text = counter.ToString();
if(label4.Text == "0")
{
timer1.Stop();
}
pictureBox6.Image = Properties.Resources.shoreSite_d_1_l_x_x;
button2.Visible=false;
}
}
internal class bild1
{
private Bitmap shoreSite_d_1_l_x_x;
public bild1(Bitmap shoreSite_d_1_l_x_x)
{
this.shoreSite_d_1_l_x_x = shoreSite_d_1_l_x_x;
}
}
}
In MouseDown you are saying what you want to transmit in the DoDragDrop call. In your current case you are transmitting the image ((PictureBox)sender).Image but you can chose to transmit the tag as well...
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
((PictureBox)sender).DoDragDrop(((PictureBox)sender).Image, DragDropEffects.Copy);
((PictureBox)sender).DoDragDrop(((PictureBox)sender).Tag, DragDropEffects.Copy);
}
...Then make sure to parse for each possible input type in DragDrop
private void pictureBox6_DragDrop(object sender, DragEventArgs e)
{
var image = e.Data.GetData(DataFormats.Bitmap) as Bitmap;
var tag = e.Data.GetData(DataFormats.Text) as string;
if (image != null)
{
pictureBox4.Image = image;
}
if (tag != null)
{
pictureBox4.Tag = tag;
}
timer1.Enabled = false;
}
Beware, that you will need to update all the code that checks that the data is Bitmap before it arrives at DragDrop ie in DragEnter and write code that handles both Bitmap and Text, otherwise the Drag functionality will break.
AWESOME!!
It works now. I will continue this Project.

How to adjust the opacity of an object(Cloned) in C#

This is my code and my goal is to make the cloned images(Bitmap) opacity adjustable using the color matrix however, the tutorial on YouTube is insufficient for me hence, I need help on my project. Thank you very much in advance for helping me! If there are questions regarding on my project, just comment in and I will try to reply as soon as possible guys.
This is my code:
private FilterInfoCollection CaptureDevices;
private VideoCaptureDevice videoSource;
private void Form1_Load(object sender, EventArgs e)
{
{
CaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo Device in CaptureDevices)
{
comboBox1.Items.Add(Device.Name);
comboBox2.Items.Add(Device.Name);
}
comboBox1.SelectedIndex = 0;
videoSource = new VideoCaptureDevice();
comboBox2.SelectedIndex = 0;
videoSource = new VideoCaptureDevice();
}
}
private void s1_Click(object sender, EventArgs e)
{
videoSource = new VideoCaptureDevice(CaptureDevices[comboBox1.SelectedIndex].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(VideoSource_NewFrame);
videoSource.Start();
}
private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void s2_Click(object sender, EventArgs e)
{
videoSource = new VideoCaptureDevice(CaptureDevices[comboBox2.SelectedIndex].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(VideoSource_NewFrames);
videoSource.Start();
}
private void VideoSource_NewFrames(object sender, NewFrameEventArgs eventArgs)
{
pictureBox2.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void r1_Click(object sender, EventArgs e)
{
videoSource.Stop();
pictureBox1.Image = null;
pictureBox1.Invalidate();
pictureBox3.Image = null;
pictureBox3.Invalidate();
}
private void r2_Click(object sender, EventArgs e)
{
videoSource.Stop();
pictureBox2.Image = null;
pictureBox2.Invalidate();
pictureBox4.Image = null;
pictureBox4.Invalidate();
}
private void c1_Click(object sender, EventArgs e)
{
pictureBox3.Image = (Bitmap)pictureBox1.Image.Clone();
}
private void c2_Click(object sender, EventArgs e)
{
pictureBox4.Image = (Bitmap)pictureBox2.Image.Clone();
}

Customization censored in textBox

I have problem, which consists in aesthetic sense, correctly - There is textBox to which i apply true condition of UseSystemPasswordChar.. It's work! But i get bold points. Try to change font size - decreases textbox's field. Below is the code (although why is it here?). Can anyone help, thank you in advance)
public partial class frmRegistr : Form
{
public frmRegistr()
{
InitializeComponent();
}
int counter = 0;
int a = 0;
string b;
private void frmRegistr_Load(object sender, EventArgs e)
{
b = label1.Text;
a = b.Length;
label1.Text = "";
timer1.Start();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void label2_Click(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
if (counter < a)
{
counter++;
label1.Text = b.Substring(0, counter);
}
else
{
timer1.Stop();
}
}
private void label4_Click(object sender, EventArgs e)
{
timer3.Start();
}
private void label4_MouseHover(object sender, EventArgs e)
{
//if this.MouseLeave
label4.BackColor = Color.FromArgb(((int)(((byte)(154)))), ((int)(((byte)(181)))), ((int)(((byte)(101)))));
}
private void timer2_Tick(object sender, EventArgs e)
{
if (Opacity == 1)
{
timer2.Stop();
}
Opacity += .2;
}
private void timer3_Tick(object sender, EventArgs e)
{
if (Opacity <= 0)
{
this.Close();
}
Opacity -= .2;
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
textBox2.UseSystemPasswordChar = true;
}
}
}
If you want to define your own password character, use property TextBox.PasswordChar. If you want this in a certain font, use Control.Font
As you only have to do this once, do this in the constructor:
public MyForm : Form
{
InitializeComponents(),
this.textBox1.PasswordChar = '_';
this.textBox11.Font = new Font(...)
};
You can also decide to do this using the visual studio designer.
You can setup this in VisualStudio designer, but this is code:
textBox1.PasswordChar = '*';
//* = password character

An exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll but was not handled in user code

I am making a simple camera with a 3,2,1 countdown in C# using a Windows Form that opens a new Form to display the captured image. When the first Form is left running idle, it will eventually error out with: "An exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll but was not handled in user code".
The program appears to indicate the error is that pictureBox1.Image is being used elsewhere:
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
I'm not sure what is causing this. Here is all of the code relating to the camera:
private static object locker = new Object();
private FilterInfoCollection CaptureDevice;
private VideoCaptureDevice FinalFrame;
public Image File { get; private set; }
public void Form1_Load(object sender, EventArgs e)
{
FormBorderStyle = FormBorderStyle.None;
if (CaptureDevice == null)
{
lock (locker)
{
CaptureDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
}
}
foreach (FilterInfo Device in CaptureDevice)
{
lock(locker)
{
comboBox1.Items.Add(Device.Name);
}
}
lock (locker)
{
comboBox1.SelectedIndex = 0;
FinalFrame = new VideoCaptureDevice();
button1.PerformClick();
button2.BringToFront();
}
}
private void button1_Click(object sender, EventArgs e)
{
lock (locker)
{
button1.SendToBack();
FinalFrame = new VideoCaptureDevice(CaptureDevice[comboBox1.SelectedIndex].MonikerString);
FinalFrame.NewFrame += new NewFrameEventHandler(FinalFrame_NewFrame);
FinalFrame.Start();
}
}
private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
lock (locker)
{
try
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone(); // <- Here is where the program indicates there is an error
}
catch (Exception exec)
{
Console.Write(exec);
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (FinalFrame.IsRunning == true)
{
FinalFrame.Stop();
}
}
private async void button2_Click(object sender, EventArgs e)
{
button2.SendToBack();
button2.Hide();
customLabel1.Show();
await Task.Delay(200);
customLabel1.BringToFront();
customLabel1.Refresh();
await Task.Delay(800);
customLabel1.Refresh();
customLabel1.Text = "2";
await Task.Delay(800);
customLabel1.Refresh();
customLabel1.Text = "1";
await Task.Delay(800);
customLabel1.Refresh();
customLabel1.Text = "3";
customLabel1.Hide();
button2.Show();
button2.BringToFront();
button2.Text = "CAPTURE";
Form2 myPic = new Form2();
myPic.pictureBox1.Image = (Bitmap)pictureBox1.Image.Clone() as Image;
myPic.ShowDialog();
}
You must dispose all previously used images.
//if (pictureBox1.Image != null)
pictureBox1.Image.Dispose();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
I've encountered similar problem. Solution is to set Image in UI thread only.
So optimal method would look something like this:
void SetImageThreadSafe(PictureBox pb, Image img)
{
if (pb.InvokeRequired)
{
BeginInvoke((Action) delegate
{
SetImageThreadSafe(pb, img);
});
return;
}
pb.Image?.Dispose();
pb.Image = img;
}

Getting frames from WebCam

I am using class WebCam from MetriCam library, and I need to periodically get frames from my WebCam, anyone knows how to do that?
Here is code I already have:
namespace MetriCam_Coding_Example
{
public partial class Form1 : Form
{
#region Private Fields
private WebCam camera;
#endregion
#region Constructor
public Form1()
{
InitializeComponent();
camera = new WebCam();
}
#endregion
#region Private Methods
private void buttonConnect_Click(object sender, EventArgs e)
{
if (!camera.IsConnected())
{
camera.Connect();
buttonConnect.Text = "&Disconnect";
backgroundWorker1.RunWorkerAsync();
}
else
{
backgroundWorker1.CancelAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
camera.Update();
pictureBox1.Image = camera.CalcBitmap();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
camera.Disconnect();
buttonConnect.Text = "&Connect";
}
#endregion
}
}
You probably run into issues when trying to draw the Bitmaps too fast. The 200ms delay just makes these problems less likely but doesn't really solve it. You could try this code to skip frames that cannot be drawn:
namespace TestGUI
{
public partial class TestGUIForm : Form
{
private IAsyncResult setBMPResult;
private Webcam;
private delegate void SetBmpDelegate(Bitmap b);
/// <summary>
/// Standard constructor.
/// </summary>
public TestGUIForm()
{
InitializeComponent();
this.FormClosing += TestGUIForm_FormClosing;
cam = new WebCam();
}
private void TestGUIForm_FormClosing(object sender, FormClosingEventArgs e)
{
backgroundWorkerGetFrames.CancelAsync();
}
private void buttonConnect_Click(object sender, EventArgs e)
{
if (cam.IsConnected())
{
// if we are already connected, just disable the button and cancel the display thread, the actual disconnection takes place in the *_RunWorkerCompleted method.
buttonConnect.Enabled = false;
backgroundWorkerGetFrames.CancelAsync();
}
else
{
// connect the camera and start the display background worker.
buttonConnect.Enabled = false;
try
{
cam.Connect();
}
catch (Exception ex)
{
MessageBox.Show("Connection error: " + ex.Message);
buttonConnect.Enabled = true;
return;
}
buttonConnect.Text = "Disconnect";
backgroundWorkerGetFrames.RunWorkerAsync();
buttonConnect.Enabled = true;
}
}
private void backgroundWorkerGetFrames_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorkerGetFrames.CancellationPending)
{
// capture a new frame
cam.Update();
// get the current frame
Bitmap bitmap = cam.CalcBitmap();
// set the picturebox-bitmap in the main thread to avoid concurrency issues (a few helper methods required, easier/nicer solutions welcome).
this.InvokeSetBmp(bitmap);
}
}
private void InvokeSetBmp(Bitmap bmp)
{
if (setBMPResult == null || setBMPResult.IsCompleted)
{
setBMPResult = this.BeginInvoke(new SetBmpDelegate(this.SetImage), bmp);
}
}
private void SetImage(Bitmap bitmap)
{
Bitmap oldBitmap = (Bitmap)pictureBoxImageStream.Image;
pictureBoxImageStream.Image = bitmap;
if (oldBitmap != null && oldBitmap != bitmap)
oldBitmap.Dispose();
}
private void backgroundWorkerGetFrames_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// disconnect camera and re-enable button.
cam.Disconnect();
buttonConnect.Text = "Connect";
buttonConnect.Enabled = true;
}
}
}
You can create a DispatcherTimer with the time you whant. Its a timer interruption.
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 20); // 20ms
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
Then when the timer tick it will reach in the event, and you get the image from you camera.
void timer_Tick(object sender, EventArgs e)
{
camera.Update();
pictureBox1.Image = camera.CalcBitmap();
}

Categories