我一直在研究WPF,该WPF可以解码用户持有的网络摄像头的QR码。该应用程序运行良好,可以在我的开发机上正常运行(Core i7 3770 CPU,NVidia Quadro K4200 GPU上最多可使用23%CPU和4%GPU),但是当我在计算机上安装并运行它时,它将在(一个Intel NUC NUC7CJYH)该应用程序的CPU使用率> 94%,导致该计算机上的使用率达到100%。
当前,该应用程序扫描了用户的QR码两次-第一个是员工的QR码,第二个是他们从事的项目编号的QR码。该应用程序使用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总时间(3745ms)
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事件处理程序。该方法每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总时间(1895ms)的5.34%
我不确定如何优化代码。我已经看过为两个DispatcherTimer中的每个设置DispactherPriority,但是在测试中这对应用程序的CPU使用率没有影响,并且会干扰每个DispatcherTimer的间隔。我还尝试将我在qrTimer_Tick方法中配置的3个BarcodeReader选项更改为false,这带来了大约1%的改进。但是,我认为这可以忽略不计,因为它取决于多种因素,例如用户将QR码放置在相机前面的速度以及qrTimer_Tick是否已经触发。
我缺少什么吗?难道仅仅是该应用程序正在运行它的NUC机器上压倒一切?
编辑 在遵循kennyzx和lerthe61的建议之后,我设法使NUC NUC7CJYH上的应用程序的CPU使用率高峰降低至〜58%。最大的好处是删除了DispatcherTimer对象,该对象每秒调用一次timer_Tick事件处理程序。进行此更改后不久运行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方法中使用了锁定功能,以锁定来自网络摄像头的当前捕获图像:
if (capturedImage != null)
{
lock (_qrTimerLock)
{
result = reader.Decode(capturedImage);
}
}
尽管我使用了锁,但该应用程序偶尔会抛出错误“对象在其他地方当前正在使用”。在删除timer_Tick之前,我的代码重构对应用程序的CPU使用效率影响不大,因此我可以保持原样,但是效率显然较低。
答案 0 :(得分:0)
我决定决定删除每秒运行的DispatcherTimer,剩下第二个DispatcherTimer对象,该对象每3秒运行一次。仅使用此修复程序,应用程序的性能就会令人满意。
感谢大家的帮助!