关于海康威视+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);
            }
        }
    }



}
相关推荐
小白18 小时前
WPF自定义Dialog模板,内容用不同的Page填充
wpf
Crazy Struggle1 天前
C# + WPF 音频播放器 界面优雅,体验良好
c#·wpf·音频播放器·本地播放器
James.TCG2 天前
WPF动画
wpf
He BianGu2 天前
笔记:简要介绍WPF中FormattedText是什么,主要有什么功能
笔记·c#·wpf
脚步的影子2 天前
.NET 6.0 + WPF 使用 Prism 框架实现导航
.net·wpf
jyl_sh2 天前
Ribbon (WPF)
ribbon·wpf·client·桌面程序开发·c/s客户端
wo63704312 天前
[Visual Stuidio 2022使用技巧]2.配置及常用快捷键
c#·.net·wpf
小黄人软件3 天前
wpf 字符串 与 变量名或函数名 相互转化:反射
wpf
界面开发小八哥3 天前
DevExpress WPF中文教程:如何解决排序、过滤遇到的常见问题?(二)
.net·wpf·界面控件·devexpress·ui开发
Vae_Mars3 天前
WPF中图片的宫格显示
wpf