关于海康威视+WPF+MVVM实时取图代码整理

依赖项:MvCameraControl.Net.dll 版本4.4.0.2

相机:MV-CH250-90YM

背景:使用自定义WPF控件显示图片,通过实现INotifyPropertyChanged接口,实现PropertyChanged事件,修改图像控件的显示值。海康自带Demo虽然有WPF的使用示例,底层还是使用Winform的WindowsFormHost。我的代码彻底抛弃Winform,通过多线程+队列+回调取图+Dispatcher.Invoke实现不卡顿,实时显示海康相机的结果图。

代码说明:

这段代码是一个WPF应用程序的一部分,主要用于处理和显示从相机捕获的图像。它使用了Hikvision的MvCameraControl库来控制和获取图像。以下是代码的主要逻辑:

  1. 在构造函数中,它初始化了一些变量,启动了一个接收线程用于处理和显示图像,并调用了InitService函数来初始化相机。

  2. InitService函数中,首先初始化了SDK,枚举设备并打开第一个设备。然后设置触发模式为off,设置了图像节点数量,注册了回调函数,并开始抓图。

  3. FrameGrabedEventHandler函数是图像抓取的回调函数,每当新的图像被抓取时,它会被调用,并将新的图像帧添加到m_frameList队列中。

  4. ShowThread函数是在一个单独的线程中运行的,用于处理和显示m_frameList队列中的图像帧。它会将图像帧转换为Bitmap,然后使用Dispatcher.Invoke将Bitmap显示在UI上。

  5. btnStopGrab_ClickbtnStartGrab_Click函数分别用于停止和开始图像抓取。

  6. GetTriggerModebtnGetSetting_Click函数用于获取相机的设置,btnSetSetting_Click函数用于设置相机的参数。

代码如下:

cs 复制代码
using MvCameraControl;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using WpfApp1.ViewModel;

namespace WpfApp1
{
    /// <summary>
    /// PolishinLargeScreen.xaml 的交互逻辑
    /// </summary>
    public partial class PolishinLargeScreen : UserControl
    {
        const DeviceTLayerType devLayerType = DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvGenTLCameraLinkDevice
             | DeviceTLayerType.MvGenTLCXPDevice | DeviceTLayerType.MvGenTLXoFDevice;
        IDevice device = null;
        Thread receiveThread = null;    // ch:接收图像线程 | en: Receive image thread
        bool m_bShowLoop = true;            // 线程控制变量 | thread looping flag 
        bool isGrabbing = false;        // ch:是否正在取图 | en: Grabbing flag
        List<IFrameOut> m_frameList = new List<IFrameOut>();        // 图像缓存列表 | frame data list 
        Mutex m_mutex = new Mutex();        // 锁,保证多线程安全 | mutex 

        PolishinLargeViewModel viewModel;
        public PolishinLargeScreen()
        {
            InitializeComponent();
            viewModel = new PolishinLargeViewModel();
            this.DataContext = viewModel;
            receiveThread = new Thread(ShowThread);
            receiveThread.Start();
            InitService();
        }

        void FrameGrabedEventHandler(object sender, FrameGrabbedEventArgs e)
        {
            m_mutex.WaitOne();
            m_frameList.Add((IFrameOut)e.FrameOut.Clone());
            m_mutex.ReleaseMutex();
        }

        // display thread routine 
        private void ShowThread()
        {
            while (m_bShowLoop)
            {
                if (m_frameList.Count == 0)
                {
                    Thread.Sleep(10);
                    continue;
                }

                // 图像队列取最新帧 
                // always get the latest frame in list 
                m_mutex.WaitOne();
                IFrameOut frame = m_frameList.ElementAt(m_frameList.Count - 1);
                m_frameList.Clear();
                m_mutex.ReleaseMutex();

                // 主动调用回收垃圾 
                // call garbage collection 
                GC.Collect();

                // 控制显示最高帧率为25FPS 
                // control frame display rate to be 25 FPS 
                if (false == isTimeToDisplay())
                {
                    continue;
                }

                try
                {
                    // 图像转码成bitmap图像 ......................................................................................................................................................................................................................................................................................................................................................................................
                    // raw frame data converted to bitmap 

                    Dispatcher.Invoke(() =>
                    {

                        Bitmap bitmap = frame.Image.ToBitmap();
                        using (MemoryStream memoryStream = new MemoryStream())
                        {
                            // 将 Bitmap 保存到内存流
                            bitmap.Save(memoryStream, ImageFormat.Png);
                            // 重置流的位置
                            memoryStream.Position = 0;
                            // 创建 BitmapImage 并从内存流加载
                            BitmapImage bitmapImage = new BitmapImage();
                            bitmapImage.BeginInit();
                            bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // 设置缓存选项
                            bitmapImage.StreamSource = memoryStream;
                            bitmapImage.EndInit();
                            bitmapImage.Freeze(); // 冻结对象,使其不可修改
                            viewModel.BitmapSource = bitmapImage;
                        }
                        bitmap.Dispose();
                    }, DispatcherPriority.Background);


                }
                catch (Exception exception)
                {
                    //Catcher.Show(exception);
                }
            }
        }

        const int DEFAULT_INTERVAL = 40;
        Stopwatch m_stopWatch = new Stopwatch();

        // 判断是否应该做显示操作 
        // calculate interval to determine if it's show time now 
        private bool isTimeToDisplay()
        {
            m_stopWatch.Stop();
            long m_lDisplayInterval = m_stopWatch.ElapsedMilliseconds;
            if (m_lDisplayInterval <= DEFAULT_INTERVAL)
            {
                m_stopWatch.Start();
                return false;
            }
            else
            {
                m_stopWatch.Reset();
                m_stopWatch.Start();
                return true;
            }
        }



        // ch:显示错误信息 | en:Show error message
        private void ShowErrorMsg(string message, int errorCode)
        {
            string errorMsg;
            if (errorCode == 0)
            {
                errorMsg = message;
            }
            else
            {
                errorMsg = message + ": Error =" + String.Format("{0:X}", errorCode);
            }

            switch (errorCode)
            {
                case MvError.MV_E_HANDLE: errorMsg += " Error or invalid handle "; break;
                case MvError.MV_E_SUPPORT: errorMsg += " Not supported function "; break;
                case MvError.MV_E_BUFOVER: errorMsg += " Cache is full "; break;
                case MvError.MV_E_CALLORDER: errorMsg += " Function calling order error "; break;
                case MvError.MV_E_PARAMETER: errorMsg += " Incorrect parameter "; break;
                case MvError.MV_E_RESOURCE: errorMsg += " Applying resource failed "; break;
                case MvError.MV_E_NODATA: errorMsg += " No data "; break;
                case MvError.MV_E_PRECONDITION: errorMsg += " Precondition error, or running environment changed "; break;
                case MvError.MV_E_VERSION: errorMsg += " Version mismatches "; break;
                case MvError.MV_E_NOENOUGH_BUF: errorMsg += " Insufficient memory "; break;
                case MvError.MV_E_UNKNOW: errorMsg += " Unknown error "; break;
                case MvError.MV_E_GC_GENERIC: errorMsg += " General error "; break;
                case MvError.MV_E_GC_ACCESS: errorMsg += " Node accessing condition error "; break;
                case MvError.MV_E_ACCESS_DENIED: errorMsg += " No permission "; break;
                case MvError.MV_E_BUSY: errorMsg += " Device is busy, or network disconnected "; break;
                case MvError.MV_E_NETER: errorMsg += " Network error "; break;
            }

            MessageBox.Show(errorMsg, "PROMPT");
        }

        public async void InitService()
        {
            // ch: 初始化 SDK | en: Initialize SDK
            SDKSystem.Initialize();

            try
            {
                List<IDeviceInfo> devInfoList;
                // ch:枚举设备 | en:Enum device
                int ret = DeviceEnumerator.EnumDevices(devLayerType, out devInfoList);
                if (ret != MvError.MV_OK)
                {
                    Console.WriteLine("Enum device failed:{0:x8}", ret);
                    return;
                }
                // ch:创建设备 | en:Create device
                device = DeviceFactory.CreateDevice(devInfoList[0]);
                ret = device.Open();
                if (ret != MvError.MV_OK)
                {
                    Console.WriteLine("Open device failed:{0:x8}", ret);
                    return;
                }

                // ch:设置触发模式为off || en:set trigger mode as off
                ret = device.Parameters.SetEnumValue("TriggerMode", 0);
                if (ret != MvError.MV_OK)
                {
                    Console.WriteLine("Set TriggerMode failed:{0:x8}", ret);
                    return;
                }

                //ch: 设置合适的缓存节点数量 | en: Setting the appropriate number of image nodes
                device.StreamGrabber.SetImageNodeNum(5);



                // ch:注册回调函数 | en:Register image callback
                device.StreamGrabber.FrameGrabedEvent += FrameGrabedEventHandler;



                // ch:开启抓图 || en: start grab image
                ret = device.StreamGrabber.StartGrabbing();
                if (ret != MvError.MV_OK)
                {
                    m_bShowLoop = false;
                    return;
                }


            }
            catch (Exception e)
            {
                Console.Write("Exception: " + e.Message);
            }
            finally
            {
                 ch:销毁设备 | en:Destroy device
                //if (device != null)
                //{
                //    device.Dispose();
                //    device = null;
                //}

                 ch: 反初始化SDK | en: Finalize SDK
                //SDKSystem.Finalize();

            }
        }

        /// <summary>
        /// 停止采集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStopGrab_Click(object sender, RoutedEventArgs e)
        {
            isGrabbing = false;
            //await receiveThread;

            device.StreamGrabber.FrameGrabedEvent -= FrameGrabedEventHandler;
            // ch:停止抓图 | en:Stop grabbing
            int result = device.StreamGrabber.StopGrabbing();
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("Stop Grabbing Fail!", result);
                return;
            }

            // ch:关闭设备 | en:Close device
            result = device.Close();
            if (result != MvError.MV_OK)
            {
                Console.WriteLine("Close device failed:{0:x8}", result);
                return;
            }

        }

        private void btnStartGrab_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                int ret = device.Open();
                if (ret != MvError.MV_OK)
                {
                    Console.WriteLine("Open device failed:{0:x8}", ret);
                    return;
                }
                // ch:标志位置位true | en:Set position bit true
                isGrabbing = true;

            }
            catch (Exception ex)
            {
                MessageBox.Show("Start thread failed!, " + ex.Message);
                throw;
            }
            device.StreamGrabber.FrameGrabedEvent += FrameGrabedEventHandler;
            // ch:开始采集 | en:Start Grabbing
            int result = device.StreamGrabber.StartGrabbing();
            if (result != MvError.MV_OK)
            {
                isGrabbing = false;
                ShowErrorMsg("Start Grabbing Fail!", result);
                return;
            }
        }

        /// <summary>
        /// ch:获取触发模式 | en:Get Trigger Mode
        /// </summary>
        private void GetTriggerMode()
        {
            IEnumValue enumValue;
            int result = device.Parameters.GetEnumValue("TriggerMode", out enumValue);
            if (result == MvError.MV_OK)
            {
                if (enumValue.CurEnumEntry.Symbolic == "On")
                {


                    result = device.Parameters.GetEnumValue("TriggerSource", out enumValue);
                    if (result == MvError.MV_OK)
                    {
                        if (enumValue.CurEnumEntry.Symbolic == "TriggerSoftware")
                        {

                        }
                    }
                }

            }
        }

        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGetSetting_Click(object sender, RoutedEventArgs e)
        {
            GetTriggerMode();

            IFloatValue floatValue;
            int result = device.Parameters.GetFloatValue("ExposureTime", out floatValue);
            if (result == MvError.MV_OK)
            {
                tbExposure.Text = floatValue.CurValue.ToString("F1");
            }


            result = device.Parameters.GetFloatValue("ResultingFrameRate", out floatValue);
            if (result == MvError.MV_OK)
            {
                tbFrameRate.Text = floatValue.CurValue.ToString("F1");
            }
            ///数字增益使能
            bool boolValue;
            result = device.Parameters.GetBoolValue("DigitalShiftEnable", out boolValue);
            chk_Digital.IsChecked = false;
            if (result == MvError.MV_OK)
            {
                if (boolValue)
                {
                    chk_Digital.IsChecked = true;
                }
            }
            ///数字增益
            IFloatValue digitalShift;
            result = device.Parameters.GetFloatValue("DigitalShift", out digitalShift);
             if (result == MvError.MV_OK)
            {
                nud_digitalShift.Value = double.Parse(digitalShift.CurValue.ToString());
            }
        }

        private void btnSetSetting_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                float.Parse(tbExposure.Text);
                float.Parse(tbFrameRate.Text);
            }
            catch
            {
                ShowErrorMsg("Please enter correct type!", 0);
                return;
            }

            device.Parameters.SetEnumValue("ExposureAuto", 0);
            int result = device.Parameters.SetFloatValue("ExposureTime", float.Parse(tbExposure.Text));
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("Set Exposure Time Fail!", result);
            }



            result = device.Parameters.SetFloatValue("AcquisitionFrameRate", float.Parse(tbFrameRate.Text));
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("Set Frame Rate Fail!", result);
            }

            ///数字增益使能
            result = device.Parameters.SetBoolValue("DigitalShiftEnable", (bool)chk_Digital.IsChecked);
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("Set DigitalShiftEnable Fail!", result);
            }

            result = device.Parameters.SetFloatValue("DigitalShift", (float)nud_digitalShift.Value);
            if (result != MvError.MV_OK)
            {
                ShowErrorMsg("Set DigitalShiftEnable Fail!", result);
            }
        }
    }



}
相关推荐
晚安苏州9 小时前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末1 天前
WPF依赖属性详解
wpf
Hat_man_1 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州3 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng3 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·3 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple5 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn5 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm
orangapple5 天前
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
wpf
晚安苏州5 天前
WPF ControlTemplate 控件模板
wpf