1

我一直在研究一个 WPF,它解码用户持有网络摄像头的 QR 码。该应用程序运行良好,在我的开发机器上运行良好(Core i7 3770 CPU,NVidia Quadro K4200 GPU 上最多 23% 的 CPU 和 4% 的 GPU 使用率)但是当我在机器上安装并运行它时,它将被使用(英特尔 NUC NUC7CJYH)应用程序的 CPU 使用率 > 94%,导致机器上的使用率为 100%。

目前,该应用程序会扫描用户的二维码两次——第一次是员工的二维码,第二次是他们从事的项目编号的二维码。该应用程序使用 ZXing.Net 解码 QR 码,并使用 AForge.Net 访问网络摄像头。

我已经运行了 VS Profiler,这是结果的屏幕截图: VS Profiler Output

根据 VS Profiler 的结果,有 4 个方法调用使用了最多的 CPU 时间,大概我应该关注:

  • videoSource_NewFrame:获取在 VideoCaptureDevice(AForge.Video.DirectShow 类的一部分)中呈现的每一帧,并将其显示在名为 imgSource 的 Image 控件中。这使用了约 10.56% 的总 CPU 时间(3745 毫秒)

    void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        try
        {
            BitmapImage bi;
            using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
            {
                bi = new BitmapImage();
                bi.BeginInit();
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
            }
            bi.Freeze();
            Dispatcher.BeginInvoke(new ThreadStart(delegate { imgSource.Source = bi; }));
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error with attaching video frame.\n " + ex.Message);
            MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            return;
        }
    
    }        
    
  • timer_Tick:该方法是一个DispatcherTimer Tick EventHandler,每隔1秒调用一次。这用于在 lblTime 控件中显示当前时间,它使用了约 9.56% 的 CPU 时间(3389 毫秒)

    private void timer_Tick(object sender, EventArgs e)
    {
        currentTime = new DateTime();
        ts = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
        currentTime = currentTime.Date + ts;
    
        lblTime.Content = $"{currentTime.ToString("HH:mm")}"; 
    }  
    
  • qrTimer_Tick:另一个 DispatcherTimer Tick EventHandler。此方法每 3 秒调用一次,并在每个间隔开始扫描 QR 码。它正在使用 ~5.52% (1959ms)

    try
    {
        MemoryStream memoryStream = new MemoryStream();
        var encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
        encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(imgSource.Source as System.Windows.Media.Imaging.BitmapSource));
        encoder.Save(memoryStream);
        memoryStream.Flush();
    
        capturedImage = (Bitmap)System.Drawing.Image.FromStream(memoryStream);
    
        BarcodeReader reader = new BarcodeReader();
    
        reader.AutoRotate = true;
        reader.TryInverted = true;
        reader.Options = new DecodingOptions { TryHarder = true };
    
        if (capturedImage != null)
            result = reader.Decode(capturedImage);
    
        if (result != null)
        {
            if (isStep1 == true)
            {
                QRScanUserID();                        
            }
            else if (isStep1 == false & isStep2 == true)
            {
                QRScanProjectID();                        
            }
        }                
    
    }
    catch (Exception)
    {                
        MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        return;
    }
    
  • 对 ZXing.Net 的 Decode 方法的外部调用。这使用了总 CPU 时间的 5.34%(1895 毫秒)

我不确定如何优化代码。我已经查看了为两个 DispatcherTimer 中的每一个设置 DispactherPriority,但在测试中这对应用程序的 CPU 使用率没有影响,并且会干扰每个 DispatcherTimer 的间隔。我还尝试将我在 qrTimer_Tick 方法中配置的 3 个 BarcodeReader 选项更改为 false,这产生了大约 1% 的改进。但是,我认为这可以忽略不计,因为它取决于用户将 QR 码放在相机前的速度以及 qrTimer_Tick 是否已经触发等因素。

有什么我想念的吗?难道仅仅是该应用程序压倒了它运行的 NUC 机器吗?

编辑 在遵循 kennyzx 和 lerthe61 的建议后,我设法将 NUC NUC7CJYH 上的应用程序 CPU 使用率降低到峰值时的约 58%。最大的好处是删除了每秒调用 timer_Tick 事件处理程序的 DispatcherTimer 对象。在我进行此更改后不久运行 VS Profiler 显示我的开发机器上的峰值 CPU 使用率为 18%,这是在应用程序启动后不久,必须自行运行。

在 lerthe61 的输入之后,我查看了我对 MemoryStream 和 Bitmap 对象的使用。videoSource_NewFrame 方法现在如下所示:

void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            try
            {
                BitmapImage bi;

                capturedImage = (Bitmap)eventArgs.Frame.Clone();
                bi = new BitmapImage();
                bi.BeginInit();
                ms = new MemoryStream();
                capturedImage.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                bi.Freeze();
                Dispatcher.BeginInvoke(new ThreadStart(delegate { imgSource.Source = bi; }));
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error with attaching video frame.\n " + ex.Message);
                MessageBox.Show("An error occurred. \nPlease contact the Systems Development team for assistance.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

        }          

我现在在 qrTimer_Tick 方法中使用了 lock 来锁定来自网络摄像头的当前捕获的图像:

if (capturedImage != null)
{
   lock (_qrTimerLock)
   {
      result = reader.Decode(capturedImage);
   }
}

尽管我使用了锁,但该应用程序偶尔会抛出错误“对象当前正在其他地方使用”。我对代码的重构在删除 timer_Tick 之上对应用程序的 CPU 使用效率影响不大,所以我可以保持原样,但这显然效率较低。

4

1 回答 1

0

我决定移除每秒运行的 DispatcherTimer,留下每 3 秒运行的第二个 DispatcherTimer 对象。仅使用此修复程序的应用程序的性能是有利的。

感谢大家的帮助!

于 2018-10-02T07:37:48.853 回答