Getting frames from WebCam - c#

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();
}

Related

C#, Saving frames from webcam continuously every 5s

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.

How i am cleaning the label after one displaying of an incrementation by port using arduino?

I am trying to increment an value in Arduino and send it on port and after that to display it in a label in real-time.
even that I put and delay(200) and Thread.sleep(200);
namespace Receiver
{
public partial class Form1 : Form
{
SerialPort port;
public Form1()
{
InitializeComponent();
this.FormClosed += new FormClosedEventHandler(Form1_FormClosed);
if (port == null)
{
port = new SerialPort("COM9", 9600);//Set your board COM
port.Open();
}
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (port != null && port.IsOpen)
{
port.Close();
}
}
private void Afisare_Click(object sender, EventArgs e)
{
while (true)
{
string a = port.ReadExisting();
afisare.Text = a;
Thread.Sleep(200);
}
}
}
}
in change I got all the values, one by after one,down the screen some of them.
You are running an endless loop in your Afisare_Click handler that runs in the same thread as you UI. This means that the UI will not be able to render the control changes.
Thread.Sleep code switches the context to other threads, but not the UI thread.
Your approach should be using Timer.
public partial class Form1 : Form
{
private Timer _timer;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_timer = new Timer();
_timer.Interval = 200;
_timer.Tick += _timer_Tick;
_timer.Enabled = true;
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
// This function will be called every 200 ms.
// Read the information from port, update the UI.
string a = port.ReadExisting();
afisare.Text = a;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
_timer.Stop();
if (port != null && port.IsOpen)
{
port.Close();
}
}
}

Is in C# WPF apps a possibility to do something as long as a button is pressed?

I'm pretty new to C# and WPF, so please forgive me if my question maybe stupid. I come from the C++ world
I have an application that has a button, as long as I press the button I want to record a sounde from the microphone and when I release the button then the recording should stop. Just like the voice message in WhatsApps. I added the events PreviewMouseDown="ButtonLiveDown" and PreviewMouseUp="ButtonLiveUp" to my button and I can see that they are fired:
My main class:
m_MyLive = new AudioLive();
m_MyLive.Init(this);
private void ButtonLiveDown(object sender, MouseButtonEventArgs e)
{
m_MyLive.StartLive();
}
private void ButtonLiveUp(object sender, MouseButtonEventArgs e)
{
m_MyLive.EndLive();
}
and my Live Class:
class AudioLive
{
private MainWindow m_mainWindow;
private WaveIn m_Recorder;
private BufferedWaveProvider m_BufferedWaveProvider;
private SavingWaveProvider m_SavingWaveProvider;
private WaveOut m_Player;
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (m_Recorder != null)
{
m_Recorder.Dispose();
}
m_Recorder = null;
if (m_SavingWaveProvider != null)
{
m_SavingWaveProvider.Dispose();
}
m_SavingWaveProvider = null;
}
private void RecorderOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
{
m_BufferedWaveProvider.AddSamples(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
}
public bool Init(MainWindow mainWindow)
{
m_mainWindow = mainWindow;
m_Recorder = new WaveIn();
m_Recorder.DataAvailable += RecorderOnDataAvailable;
// set up our signal chain
m_BufferedWaveProvider = new BufferedWaveProvider(m_Recorder.WaveFormat);
m_SavingWaveProvider = new SavingWaveProvider(m_BufferedWaveProvider, "live.wav");
// set up playback
m_Player = new WaveOut();
m_Player.Init(m_SavingWaveProvider);
return true;
}
public void SetMicVolume(int nVol)
{
....
}
public void StartLive()
{
SetMicVolume(100);
// begin playback & record
m_Player.Play();
m_Recorder.StartRecording();
}
public void EndLive()
{
// stop recording
m_Recorder.StopRecording();
// stop playback
m_Player.Stop();
}
}
But this doesn't work, as long as I press the button down it seems that it stops working till I release the button. From C++ I know this, as long as I press the button the system is busy with the pressed Event and can't continue to work. Is it the same with C# & WPF? If yes, is there any other way to handle my feature wish?
If I understand your question, then yes, you will have to deal with UI blocking in this case. Use a background worker to kick the event on mouse down and background worker cancellation on mouse up. Example below shows with mouseup and mouse down event handelers as oppossed to the previewmouse up and down
Updated Example to help with understanding
XAML BUTTON:
<Button x:Name="RecordBtn" Content="Button" HorizontalAlignment="Left" Margin="363,199,0,0" VerticalAlignment="Top" Width="75" MouseDown="Button_MouseDown" MouseUp="Button_MouseUp"/>
xaml code behind showing background worker kicking and holding the process:
public partial class MainWindow : Window
{
private readonly BackgroundWorker worker = new BackgroundWorker();
AudioLive m_MyLive = new AudioLive();
Stopwatch stopWatch = new Stopwatch();
public MainWindow()
{
InitializeComponent();
AddHandler(FrameworkElement.MouseDownEvent, new MouseButtonEventHandler(Button_MouseDown), true);
AddHandler(FrameworkElement.MouseUpEvent, new MouseButtonEventHandler(Button_MouseUp), true);
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.WorkerSupportsCancellation = true;
m_MyLive.Init(this);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
if (!m_MyLive.IsRecordinginProgress() && !worker.CancellationPending)
{
m_MyLive.StartLive();
stopWatch.Reset();
stopWatch.Start();
}
while (m_MyLive.IsRecordinginProgress() && !worker.CancellationPending)
{
this.Dispatcher.Invoke(() =>
{
updateLabel(String.Format("{0:0.#}", TimeSpan.FromMilliseconds(stopWatch.ElapsedMilliseconds).TotalSeconds) + " seconds");
});
}
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
m_MyLive.EndLive();
stopWatch.Stop();
updateLabel(String.Format("{0:0.#}", TimeSpan.FromMilliseconds(stopWatch.ElapsedMilliseconds).TotalSeconds) + " seconds");
}
private void updateLabel(string text)
{
RecordBtn.Content = text;
}
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
worker.RunWorkerAsync();
}
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
worker.CancelAsync();
}
}
Updated AudioLive to add property isRecording to use in our background worker:
public class AudioLive
{
private MainWindow m_mainWindow;
private WaveIn m_Recorder;
private BufferedWaveProvider m_BufferedWaveProvider;
private SavingWaveProvider m_SavingWaveProvider;
private WaveOut m_Player;
private bool isRecording { get; set; }
public bool IsRecordinginProgress()
{
return isRecording;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (m_Recorder != null)
{
m_Recorder.Dispose();
}
m_Recorder = null;
if (m_SavingWaveProvider != null)
{
m_SavingWaveProvider.Dispose();
}
m_SavingWaveProvider = null;
}
private void RecorderOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
{
m_BufferedWaveProvider.AddSamples(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
}
public bool Init(MainWindow mainWindow)
{
m_mainWindow = mainWindow;
m_Recorder = new WaveIn();
m_Recorder.DataAvailable += RecorderOnDataAvailable;
// set up our signal chain
m_BufferedWaveProvider = new BufferedWaveProvider(m_Recorder.WaveFormat);
m_SavingWaveProvider = new SavingWaveProvider(m_BufferedWaveProvider, "live.wav");
// set up playback
m_Player = new WaveOut();
m_Player.Init(m_SavingWaveProvider);
return true;
}
public void SetMicVolume(int nVol)
{
}
public void StartLive()
{
SetMicVolume(100);
// begin playback & record
m_Player.Play();
m_Recorder.StartRecording();
isRecording = true;
}
public void EndLive()
{
// stop recording
m_Recorder.StopRecording();
// stop playback
m_Player.Stop();
isRecording = false;
}
}

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;
}

Move shape while key is down in WPF

I want to move a shape in a window while user holds a key down.
I'm new to this so i tried this approach, but i can't change circle.RederTransform from my new thread, because it belongs to a different thread. How should it be done?
public partial class MainWindow : Window
{
private Matrix transformationMatrix = Matrix.Identity;
private Thread myThread = null;
private bool isGoing = false;
public MainWindow()
{
InitializeComponent();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
isGoing = true;
myThread = new Thread(HandleMyThread);
myThread.Start();
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
isGoing = false;
}
private void HandleMyThread(object obj)
{
while(isGoing)
{
transformationMatrix.Translate(10, 0);
circle.RenderTransform = new MatrixTransform(transformationMatrix);
Thread.Sleep(50);
}
}
}
In WPF you want to use the System.Windows.Threading.DispatcherTimer class for UI-thread safe timers.
Here is an example:
public partial class MainWindow : Window
{
private DispatcherTimer _timer;
public MainWindow()
{
InitializeComponent();
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(50);
_timer.Tick += timer_Tick;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
_timer.Start();
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
_timer.Stop();
}
private void timer_Tick(object sender, EventArgs e)
{
transform.X += 2;
transform.Y += 2;
}
}
You might get away with doing it by just using an Invoke...but there are better ways to do what you want to do.
Firstly, creating a thread each time you handle the KeyDown is inefficient...and could lead to problems...because it may take a while to create and schedule the Thread...thus a delay in your "thing" moving.
In addition it's possible for you to get in a mess with having multiple threads staying in existence. For instance, say someone was repeatedly pressing and releasing the key....in that case it's possible for "old" threads to still keep running...because isGoing flag is true....in other words, there is poor synchronization.
private void HandleMyThread(object obj)
{
Action action = new Action( ()=>
{
transformationMatrix.Translate(10, 0);
circle.RenderTransform = new MatrixTransform(transformationMatrix);
};
while(isGoing)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, action);
Thread.Sleep(50);
}
}

Categories