I am using this class to record webcam videos successfully. But the problem with this code is, i am unable to show the live video into the picturebox
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;
namespace Video_capture_23_08_2022
{
public class Recorder : IDisposable
{
private readonly VideoCaptureAPIs _videoCaptureApi = VideoCaptureAPIs.DSHOW;
private readonly ManualResetEventSlim _threadStopEvent = new ManualResetEventSlim(false);
private readonly VideoCapture _videoCapture;
private VideoWriter _videoWriter;
private Mat _capturedFrame = new Mat();
private Thread _captureThread;
private Thread _writerThread;
//private OutputArray frame;
private bool IsVideoCaptureValid => _videoCapture is not null && _videoCapture.IsOpened();
public Recorder(int deviceIndex, int frameWidth, int frameHeight, double fps, PictureBox pictureBox)
{
_videoCapture = VideoCapture.FromCamera(deviceIndex, _videoCaptureApi);
_videoCapture.Open(deviceIndex, _videoCaptureApi);
_videoCapture.FrameWidth = frameWidth;
_videoCapture.FrameHeight = frameHeight;
_videoCapture.Fps = fps;
// Custom Function to show the webcam view into picturebox
_videoCapture.Read(_capturedFrame);
if (!(_capturedFrame.Empty()))
{
pictureBox.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(_capturedFrame);
}
}
/// <inheritdoc />
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
~Recorder()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
StopRecording();
_videoCapture?.Release();
_videoCapture?.Dispose();
}
}
public void StartRecording(string path)
{
if (_writerThread is not null)
return;
if (!IsVideoCaptureValid)
ThrowHelper.ThrowVideoCaptureNotReadyException();
_videoWriter = new VideoWriter(path, FourCC.XVID, _videoCapture.Fps, new Size(_videoCapture.FrameWidth, _videoCapture.FrameHeight));
_threadStopEvent.Reset();
_captureThread = new Thread(CaptureFrameLoop);
_captureThread.Start();
_writerThread = new Thread(AddCameraFrameToRecordingThread);
_writerThread.Start();
}
public void StopRecording()
{
_threadStopEvent.Set();
_writerThread?.Join();
_writerThread = null;
_captureThread?.Join();
_captureThread = null;
_threadStopEvent.Reset();
_videoWriter?.Release();
_videoWriter?.Dispose();
_videoWriter = null;
}
private void CaptureFrameLoop()
{
while (!_threadStopEvent.Wait(0))
{
_videoCapture.Read(_capturedFrame);
}
}
private void AddCameraFrameToRecordingThread()
{
var waitTimeBetweenFrames = 1_000 / _videoCapture.Fps;
var lastWrite = DateTime.Now;
while (!_threadStopEvent.Wait(0))
{
if (DateTime.Now.Subtract(lastWrite).TotalMilliseconds < waitTimeBetweenFrames)
continue;
lastWrite = DateTime.Now;
_videoWriter.Write(_capturedFrame);
}
}
public Bitmap GetFrameBitmap()
{
if (!IsVideoCaptureValid)
ThrowHelper.ThrowVideoCaptureNotReadyException();
using (Mat frame = new Mat())
return !_videoCapture.Read(frame) ? null : frame.ToBitmap();
}
}
}
But I want to modify it a bit to show the webcam view into the picturebox but this custom section
// Custom Function to show the webcam view into picturebox
_videoCapture.Read(_capturedFrame);
if (!(_capturedFrame.Empty()))
{
pictureBox.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(_capturedFrame);
}
is only showing a still image instead of a live video. Kindly help me to figure out the issue.
CodePudding user response:
The missing part in your implementation is that you capture the webcam image and display it in pictureBox
in the constructor, only once rather than in CaptureFrameLoop()
, repeatedly. Therefore, you need to move this block to CaptureFrameLoop()
.
_videoCapture.Read(_capturedFrame);
if (!(_capturedFrame.Empty()))
{
pictureBox.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(_capturedFrame);
}
Also, you should use Control.Invoke
to modify the image of the PictureBox
from a non-UI thread.
private void CaptureFrameLoop()
{
while (!_threadStopEvent.Wait(0))
{
_videoCapture.Read(_capturedFrame);
if (!(_capturedFrame.Empty()))
{
pictureBox.Invoke(new Action(() => pictureBox.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(_capturedFrame)));
}
}
}