0

我创建了一个表单,C#其中有一个正在转换为布尔数组的图像。我正在尝试使用计时器生成一个线程,其中图像每秒转换 4 次。

当我调试它时,它可以工作,我可以跟踪计时器的滴答声。但是当表单正在运行时,应用程序会退出而不会出现错误。

这是初始化脚本:

form = new LoadForm();
form.Show();
form.BringToFront();

timer = new System.Threading.Timer(new TimerCallback(camTick), null, 0, 250);

这是有效的刻度:

private void camTick(Object myObject) 
{
    if (form.isRunning) 
    {
        bool[,] ar = form.getBoolBitmap(100);
    }
}

这是加载和保存位图的函数。在form1.cs

public bool[,] getBoolBitmap(uint threshold) {
        unsafe {
            Bitmap b = getBitmap();

            BitmapData originalData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            bool[,] ar = new bool[b.Width, b.Height];

            for (int y = 0; y < b.Height; y++) {
                byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);

                for (int x = 0; x < b.Width; x++) {
                    if ((byte)Row[x * 3] < threshold) {
                        ar[x, y] = false;
                    } else {
                        ar[x, y] = true;
                    }
                }
            }

            b.Dispose();

            return ar;
        }
    }

public Bitmap getBitmap() {
        if (!panelVideoPreview.IsDisposed) {
            Bitmap b = new Bitmap(panelVideoPreview.Width, panelVideoPreview.Height, PixelFormat.Format24bppRgb);
            using (Graphics g = Graphics.FromImage(b)) {
                Rectangle rectanglePanelVideoPreview = panelVideoPreview.Bounds;
                Point sourcePoints = panelVideoPreview.PointToScreen(new Point(panelVideoPreview.ClientRectangle.X, panelVideoPreview.ClientRectangle.Y));
                g.CopyFromScreen(sourcePoints, Point.Empty, rectanglePanelVideoPreview.Size);
            }

            return b;
        } else {
            Bitmap b = new Bitmap(panelVideoPreview.Width, panelVideoPreview.Height);
            return b;
        }
    }
4

3 回答 3

2

该图像是否存储在像 PictureBox 这样的控件中?如果是这样,请确保您仅在控件的线程上访问它。查看Control.Invoke()

于 2012-05-03T16:31:53.507 回答
1

来自的回调在线程System.Threading.Timer上运行。ThreadPool在该回调中,您正在访问一个Form实例。你根本无法做到这一点。好吧,你可以,但它不能正常工作。它会以不可预知的方式失败,有时甚至会非常壮观。

改为使用System.Windows.Forms.Timer。让它每 4 秒打勾。如果Tick从表单中获取您需要的任何数据,然后将其传递给另一个线程进行进一步处理。

在下面的代码中,我Bitmap通过调用DrawToBitmapUI 线程来获取对象。然后我将它传递Bitmap给a Task,它可以bool[]在后台转换为a。或者,如果需要,您可以bool[]Taskand 然后调用ContinueWith将其返回到 UI 线程(听起来您可能不需要这样做)。

private void YourWindowsFormsTimer_Tick(object sender, EventArgs args)
{
  // Get the bitmap object while still on the UI thread.
  var bm = new Bitmap(panelVideoPreview.ClientSize.Width, panelVideoPreview.ClientSize.Height, PixelFormat.Format24bppRgb);
  panelVideoPreview.DrawToBitmap(bm, panelVideoPreview.ClientRectangle);

  Task.Factory
    .StartNew(() =>
    {
      // It is not safe to access the UI here.

      bool[,] ar = ConvertBitmapToByteArray(bm);

      DoSomethingWithTheArrayHereIfNecessary(ar);

      // Optionally return the byte array if you need to transfer it to the UI thread.
      return ar;
    })
    .ContinueWith((task) =>
    {
      // It is safe to access the UI here.

      // Get the returned byte array.
      bool[,] ar = task.Result;

      UpdateSomeControlIfNecessaryHere(ar);

    }, TaskScheduler.FromCurrentSynchronizationContext());
}

ConvertBitmapToByteArray现在看起来像这样。

public unsafe bool[,] ConvertBitmapToBoolArray(Bitmap b, uint threshold) 
{
  BitmapData originalData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

  bool[,] ar = new bool[b.Width, b.Height];

  for (int y = 0; y < b.Height; y++) 
  {
    byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);
    for (int x = 0; x < b.Width; x++) 
    {
      if ((byte)Row[x * 3] < treshold) 
      {
        ar[x, y] = false;
      } else 
      {
        ar[x, y] = true;
      }
    }
  }
  b.Dispose();
  return ar;
}
于 2012-05-03T19:19:42.273 回答
0

在网上搜索了一番后,我发现了这一点,我需要制作另一个函数来返回bool[,],这就是我想出的:

public bool[,] invokeBitmap(uint treshold) {
        if (this.InvokeRequired) {
            return (bool[,])this.Invoke((Func<bool[,]>)delegate {
                return getBoolBitmap(treshold);
            });
        } else {
            return getBoolBitmap(treshold);
        }
    }
于 2012-05-04T14:32:56.437 回答