WPF 二维码扫描仪中出现内存不足错误

问题描述 投票:0回答:1

我尝试使用

AForge.Video.DirectShow
包制作 WPF QR 代码扫描仪页面来实现视频捕获,该视频捕获使用 XAML 端的图像来显示它。

应用程序的内存使用量迅速增加,并在 15 秒内达到 3.6GB+,然后在这些代码行崩溃并抛出内存不足异常:

private ImageSource BitmapToImageSource(Bitmap bitmap)
{
    return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        bitmap.GetHbitmap(),
        IntPtr.Zero,
        Int32Rect.Empty,
        BitmapSizeOptions.FromEmptyOptions());
}

WPF扫描仪页面的完整代码为:

using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ZXing;
using System.IO;

namespace QRCodeApp
{
    /// <summary>
    /// Interaction logic for Scanner.xaml
    /// </summary>
    public partial class Scanner : Page
    {
        private FilterInfoCollection videoDevices;
        private VideoCaptureDevice videoSource;
        public Scanner()
        {
            InitializeComponent();
            InitializeCamera();
        }
        private void InitializeCamera()
        {
            videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            if (videoDevices.Count > 0)
            {
                videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
                videoSource.NewFrame += VideoSource_NewFrame;
                videoSource.Start();
            }
            else
            {
                MessageBox.Show("No video capture devices found.","No Camera");
            }
        }

        private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            try
            {
                using (Bitmap bitmap = (Bitmap)eventArgs.Frame.Clone())
                {
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        cameraImage.Source = BitmapToImageSource(bitmap);
                    });

                    BarcodeReader barcodeReader = new BarcodeReader();
                    Result result = barcodeReader.Decode(bitmap);

                    if (result != null)
                    {
                        string downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                        string initialDirectory = Path.Combine(downloadsPath, "QR Codes");

                        if (!Directory.Exists(initialDirectory))
                        {
                            Directory.CreateDirectory(initialDirectory);
                        }

                        DateTime currentDateTime = DateTime.Now;
                        string fdt = currentDateTime.ToString("yyyy-MM-dd HH_mm_ss");
                        string fileName = "QRCode " + fdt + ".png";

                        fileName = CleanFileName(fileName);

                        string path = Path.Combine(initialDirectory, fileName);
                        bitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);
                        videoSource.NewFrame -= VideoSource_NewFrame;
                        videoSource.SignalToStop();

                        System.Threading.Tasks.Task.Run(() =>
                        {
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                cameraImage.Source = null;
                                videoSource = null;
                                videoDevices = null;
                                myframe.frame.Content = new Scanned(bitmap, path, false, false);
                            });
                        });
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error: {ex.Message}", "An Exception occured");
            }
        }

        private ImageSource BitmapToImageSource(Bitmap bitmap)
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                bitmap.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (videoSource != null && videoSource.IsRunning)
            {
                videoSource.SignalToStop();
                videoSource.WaitForStop();
            }
        }

        private string CleanFileName(string fileName)
        {
            foreach (char c in System.IO.Path.GetInvalidFileNameChars())
            {
                fileName = fileName.Replace(c, '_');
            }
            return fileName;
        }



        private void Back(object sender, RoutedEventArgs e)
        {
            myframe.frame.Content = new ScanSelection();
        }
    }
}

扫描完成后我尝试了

bitmap.Dispose()
,但没有效果。同时停止摄像头并将UI图像源、视频源和视频设备设置为空。请记住,当 QR 码在应用程序崩溃之前完美扫描时,内存使用量将停止在扫描完成之前的水平,因此,如果在扫描 QR 码之前内存使用量为 1.5GB,则应用程序期间内存使用量将停止增加并保持在 1.5GB移动到我设置的页面。

c# .net wpf aforge zxing.net
1个回答
0
投票

内存使用量增加得如此之多且如此之快,只是因为旧的位图没有被处理,并且每次都从 BitmapToImageSource() 中删除 hBitmap 完全解决了问题。

上一个 BitmapToImageSource() 函数:

private ImageSource BitmapToImageSource(Bitmap bitmap)
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                bitmap.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }

更新的 BitmapToImageSource() 函数以及用于删除位图的外部函数解决了问题:

private ImageSource BitmapToImageSource(Bitmap bitmap)
        {
            IntPtr hBitmap = bitmap.GetHbitmap();
            try
            {
                return Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(hBitmap);
            }
        }

        [DllImport("gdi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool DeleteObject(IntPtr hObject);
© www.soinside.com 2019 - 2024. All rights reserved.