I am currently working with AForge, and have an on new frame event that posts the frame, as a bitmap, into a picturebox. 90% of the time it works great... UNLESS I fiddle with something on the winform. Changing a combo box, moving the window, or anything like that risks causing the Picturebox to switch from the video to a big red X. Code sample below:
我目前正在与AForge合作,并有一个新的帧事件,将帧作为位图发布到图片框中。 90%的时间它很有效......除非我在winform上摆弄一些东西。更改组合框,移动窗口或任何类似的风险会导致Picturebox从视频切换到大红色X.下面的代码示例:
private void connectButton_Click(object sender, EventArgs e)
{
try
{
cam = new VideoCaptureDevice(captureDevices[CameraSelectComboBox.SelectedIndex].MonikerString);
cam.NewFrame -= Handle_New_Frame; //Just to avoid the possibility of a second event handler being put on
cam.NewFrame += new AForge.Video.NewFrameEventHandler(Handle_New_Frame);
cam.Start();
}
catch
{
MessageBox.Show("An error has occured with connecting to the specified webcam. The application will now close!");
Application.Exit();
}
}
private void Handle_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (bitmap != null)
bitmap.Dispose(); //Without this, memory goes nuts
bitmap = new Bitmap(eventArgs.Frame);
}
catch { }
//Draw some stuff on the images
bitmap = AdjustBrightness(bitmap, brightnessMeter);
bitmap = ApplyContrast(contrastMeter, bitmap);
bitmap = Draw_Top_Line(bitmap);
bitmap = Draw_Bottom_Line(bitmap);
//Set the image into the picturebox
this.Invoke((MethodInvoker)delegate
{
videoPictureBox1.Image = bitmap;
frameRate++; //Keep track of the frame rate
});
GC.Collect(); //Without this, memory goes nuts
this.Invoke((MethodInvoker)delegate {
videoPictureBox1.Refresh(); //NOT NECESSARY. JUST TRYING TO FIX THE BIG RED X!
});
if (videoPictureBox1.Image == videoPictureBox1.ErrorImage)
{
cam.Stop(); //ALSO NOT NECESSARY> AGAIN, JUST TRYING TO FIX THE BIG RED X!
cam.Start();
}
}
I put a break on the if (videoPictureBox1.Image == videoPictureBox1.ErrorImage) and it is evaluating to false, even when the big red X is up, because the image is actually being set to the bitmap. So cam.Stop() and cam.Start() never run (not sure if that would even help, but I figured I would give it a try).
我对if(videoPictureBox1.Image == videoPictureBox1.ErrorImage)进行了中断,即使大红色X上升,它也会评估为false,因为图像实际上是设置为位图。所以cam.Stop()和cam.Start()永远不会运行(不确定这是否会有所帮助,但我想我会尝试一下)。
videoPictureBox1.Refresh() is running every time, but again- it's not making a difference. Still have the big red X.
videoPictureBox1.Refresh()每次都在运行,但同样 - 它没有任何区别。还有大红X.
As I said before: if I start the video and touch nothing, the big red X will never happen. But the moment I start changing combo boxes, or dragging the form itself around, the chance of the big red X goes up exponentially. Sometimes I can flip through the combo box 10-12 times before it happens, other times it happens the second I click the combobox. :-\
正如我之前所说:如果我开始播放视频并且什么都不触摸,那么大红色的X将永远不会发生。但是当我开始改变组合框或者拖动表单本身时,大红色X的几率呈指数上升。有时候我可以在它发生之前翻阅组合框10-12次,有时候它会在第二次点击组合框时发生。 : - \
Can anyone explain what is happening here and perhaps offer a suggestion on the best method to go about fixing it? I'm still very new to threading, so I've been struggling to wrap my head around exactly what is happening here and the best way to fix the issue! Any nudges in the right direction would be a huge help!
任何人都可以解释这里发生的事情,也许可以提出一个关于修复它的最佳方法的建议吗?我仍然很新线程,所以我一直在努力解决这里发生的事情以及解决问题的最佳方法!任何朝着正确方向的推动都将是一个巨大的帮助!
4 个解决方案
#1
6
In the end, I wrapped EVERYTHING in in the Handle_New_Frame in an invoke. It completely removed the big red X issue, permanently. >_>
最后,我在调用Handle_New_Frame中包装了一切。它永久地完全消除了大红色X问题。 > _>
private void Handle_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
this.Invoke((MethodInvoker)delegate
{
try
{
if (bitmap != null)
{
bitmap.Dispose(); //Without this, memory goes nuts
}
bitmap = new Bitmap(eventArgs.Frame);
}
catch { }
//Draw some stuff on the images
bitmap = AdjustBrightness(bitmap, brightnessMeter);
bitmap = ApplyContrast(contrastMeter, bitmap);
bitmap = Draw_Top_Line(bitmap);
bitmap = Draw_Bottom_Line(bitmap);
//Set the image into the picturebox
this.Invoke((MethodInvoker)delegate
{
videoPictureBox1.Image = bitmap;
frameRate++; //Keep track of the frame rate
});
GC.Collect(); //Without this, memory goes nuts
});
}
#2
3
Shawn Hargreaves has an excellent, concise writeup of the "big red X of doom". I found it very helpful in the general case of dealing with WinForm components suddenly showing the red "X".
肖恩哈格里夫斯(Shawn Hargreaves)对“厄运的大红色X”进行了精彩而简洁的写作。我发现在处理WinForm组件突然显示红色“X”的一般情况下,它非常有用。
In summary:
综上所述:
- This is caused by a control throwing an exception out of the
OnPaint
event. - 这是由于控件从OnPaint事件中抛出异常引起的。
- Once it is thrown, that control will continue to show the red X and skip firing
OnPaint.
- 一旦抛出,该控件将继续显示红色X并跳过触发OnPaint。
- To debug, set the debugger to catch Common Language Runtime Exceptions, and then do whatever you normally do to get the red X. The debugger will stop right where it is happening, allowing you to investigate and hopefully figure out a way to prevent it.
- 要进行调试,请将调试器设置为捕获公共语言运行时异常,然后执行通常所做的任何操作以获取红色X.调试器将在发生的位置停止,允许您进行调查并希望找到一种方法来阻止它。
#3
1
try using the clone in places where you use the bitmap. Ex:
尝试在使用位图的地方使用克隆。例如:
videoPictureBox1.Image = (Bitmap)bitmap.Clone();
#4
0
Just to sum it up, this is a tested bare minimum that shows no red cross for me, even when resized, started, stoped or resolution changed.
总而言之,这是一个经过测试的最低限度,即使在调整大小,启动,停止或分辨率更改时,也没有显示红十字。
public partial class PictureBoxVideo : Form
{
public PictureBoxVideo()
{
InitializeComponent();
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
var videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.NewFrame += Handle_Very_New_Frame;
videoSource.Start();
}
private void Handle_Very_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
this.Invoke((MethodInvoker)delegate {
pictureBox.Image = new Bitmap(eventArgs.Frame);
});
}
}
Please note, that we call videoSource.Start(); from the GUI-(creation)-thread, but the call back handler (Handle_Very_New_Frame) is called from the video (worker) thread.
请注意,我们称之为videoSource.Start();来自GUI-(创建)-thread,但是从视频(工作者)线程调用回调处理程序(Handle_Very_New_Frame)。
I think that's why we need both, the Invoke and the new Bitmap, so the new bmp will also be generated from the gui-thread. But I'm just guessing here, as I couldn't come up with a proof.
我认为这就是为什么我们需要Invoke和新的Bitmap,所以新的bmp也将从gui-thread生成。但我只是在这里猜测,因为我无法提出证据。
#1
6
In the end, I wrapped EVERYTHING in in the Handle_New_Frame in an invoke. It completely removed the big red X issue, permanently. >_>
最后,我在调用Handle_New_Frame中包装了一切。它永久地完全消除了大红色X问题。 > _>
private void Handle_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
this.Invoke((MethodInvoker)delegate
{
try
{
if (bitmap != null)
{
bitmap.Dispose(); //Without this, memory goes nuts
}
bitmap = new Bitmap(eventArgs.Frame);
}
catch { }
//Draw some stuff on the images
bitmap = AdjustBrightness(bitmap, brightnessMeter);
bitmap = ApplyContrast(contrastMeter, bitmap);
bitmap = Draw_Top_Line(bitmap);
bitmap = Draw_Bottom_Line(bitmap);
//Set the image into the picturebox
this.Invoke((MethodInvoker)delegate
{
videoPictureBox1.Image = bitmap;
frameRate++; //Keep track of the frame rate
});
GC.Collect(); //Without this, memory goes nuts
});
}
#2
3
Shawn Hargreaves has an excellent, concise writeup of the "big red X of doom". I found it very helpful in the general case of dealing with WinForm components suddenly showing the red "X".
肖恩哈格里夫斯(Shawn Hargreaves)对“厄运的大红色X”进行了精彩而简洁的写作。我发现在处理WinForm组件突然显示红色“X”的一般情况下,它非常有用。
In summary:
综上所述:
- This is caused by a control throwing an exception out of the
OnPaint
event. - 这是由于控件从OnPaint事件中抛出异常引起的。
- Once it is thrown, that control will continue to show the red X and skip firing
OnPaint.
- 一旦抛出,该控件将继续显示红色X并跳过触发OnPaint。
- To debug, set the debugger to catch Common Language Runtime Exceptions, and then do whatever you normally do to get the red X. The debugger will stop right where it is happening, allowing you to investigate and hopefully figure out a way to prevent it.
- 要进行调试,请将调试器设置为捕获公共语言运行时异常,然后执行通常所做的任何操作以获取红色X.调试器将在发生的位置停止,允许您进行调查并希望找到一种方法来阻止它。
#3
1
try using the clone in places where you use the bitmap. Ex:
尝试在使用位图的地方使用克隆。例如:
videoPictureBox1.Image = (Bitmap)bitmap.Clone();
#4
0
Just to sum it up, this is a tested bare minimum that shows no red cross for me, even when resized, started, stoped or resolution changed.
总而言之,这是一个经过测试的最低限度,即使在调整大小,启动,停止或分辨率更改时,也没有显示红十字。
public partial class PictureBoxVideo : Form
{
public PictureBoxVideo()
{
InitializeComponent();
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
var videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.NewFrame += Handle_Very_New_Frame;
videoSource.Start();
}
private void Handle_Very_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
this.Invoke((MethodInvoker)delegate {
pictureBox.Image = new Bitmap(eventArgs.Frame);
});
}
}
Please note, that we call videoSource.Start(); from the GUI-(creation)-thread, but the call back handler (Handle_Very_New_Frame) is called from the video (worker) thread.
请注意,我们称之为videoSource.Start();来自GUI-(创建)-thread,但是从视频(工作者)线程调用回调处理程序(Handle_Very_New_Frame)。
I think that's why we need both, the Invoke and the new Bitmap, so the new bmp will also be generated from the gui-thread. But I'm just guessing here, as I couldn't come up with a proof.
我认为这就是为什么我们需要Invoke和新的Bitmap,所以新的bmp也将从gui-thread生成。但我只是在这里猜测,因为我无法提出证据。