WinForm之相机的单例模式

在这里插入图片描述

在这里插入图片描述

一 定义海康相机类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HalconDotNet;
using MvCamCtrl.NET;

namespace WinForm之AOI外观检测.Camera
{

    /// <summary>
    /// 触发模式
    /// </summary>
    public enum TriggerMode
    {
        /// <summary>
        /// 连续模式
        /// </summary>
        Continues,

        /// <summary>
        /// 触发模式
        /// </summary>
        Trigger
    }

    /// <summary>
    /// 触发源
    /// </summary>
    public enum TriggerSource
    {
        /// <summary>
        /// 外部触发
        /// </summary>
        Hardware,

        /// <summary>
        /// 软触发
        /// </summary>
        Software
    }


    public delegate void GrabHImage(HImage hImg);
    public class HikCamera
    {
        // 抓取图像事件
        public event GrabHImage grabHImage;
        public HikCamera()
        {
            m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
            m_MyCamera = new MyCamera();
        }

        #region 共有属性
        /// <summary>
        /// 相机名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 相机序列号
        /// </summary>
        public string SerialNumber { get; set; }

        /// <summary>
        /// 相机是否已连接
        /// </summary>
        public bool IsConnect { get; private set; }

        /// <summary>
        /// 是否为触发模式
        /// </summary>
        public bool isTriggerMode { get; private set; }

        /// <summary>
        /// 是否开始采集
        /// </summary>
        public bool isGrabbing { get; private set; }

        #endregion

        #region 内部变量

        // 设备列表
        private MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList;

        // 相机对象
        private MyCamera m_MyCamera = null;

        // 取像线程
        Thread m_hReceiveThread = null;

        // 帧信息
        MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();

        // 用于从驱动获取图像的缓存
        IntPtr m_BufForDriver;
        UInt32 m_nBufSizeForDriver = 0;
        byte[] m_pDataForRed = null;
        byte[] m_pDataForGreen = null;
        byte[] m_pDataForBlue = null;
        private Object BufForDriverLock = new Object();
        private Object BufForImageLock = new Object();  // 读写图像时锁定
        #endregion

        public List<string> GetDeviceList()
        {
            List<string> deviceList = new List<string>();

            EnumDevices();

            for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usb3Info = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    deviceList.Add($"{usb3Info.chSerialNumber}");
                }
                else if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                    deviceList.Add($"{gigeInfo.chSerialNumber}");
                }
            }

            return deviceList;
        }

        public bool OpenDevice()
        {
            // 枚举并搜索指定ID的相机是否存在
            EnumDevices();
            int camIdx = GetDeviceIndex(SerialNumber);
            if (camIdx == -1)
            {
                MessageBox.Show("找不到该ID的相机!");
                return false;
            }
            MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[camIdx],
                                                              typeof(MyCamera.MV_CC_DEVICE_INFO));

            // 建立设备对象
            if (null == m_MyCamera)
            {
                m_MyCamera = new MyCamera();
                if (null == m_MyCamera)
                {
                    MessageBox.Show("初始化相机对象失败");

                    return false;
                }
            }

            // 创建设备
            int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"创建设备失败,失败代码:{nRet}");
                return false;
            }

            // 尝试打开设备
            nRet = m_MyCamera.MV_CC_OpenDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                m_MyCamera.MV_CC_DestroyDevice_NET();

                MessageBox.Show($"设备打开失败,失败代码:{nRet}");
                return false;
            }

            // 探测网络最佳包大小(只对GigE相机有效)
            if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
            {
                int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET();
                if (nPacketSize > 0)
                {
                    nRet = m_MyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                    if (nRet != MyCamera.MV_OK)
                    {
                        MessageBox.Show($"设置包大小失败,失败代码:{nRet}");
                    }
                }
                else
                {
                    MessageBox.Show($"获取包大小失败,返回的包大小为:{nPacketSize}");
                }
            }

            // 设置采集连续模式
            m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
            m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);

            // 注册异常回调
            m_MyCamera.MV_CC_RegisterExceptionCallBack_NET(cbException, IntPtr.Zero);

            IsConnect = true;
            return true;
        }

        private void cbException(uint nMsgType, IntPtr pUser)
        {
            IsConnect = false;

            if (nMsgType == MyCamera.MV_EXCEPTION_DEV_DISCONNECT)
            {
                // 先关闭设备
                CloseDevice();

                // 在尝试重新打开设备
                if (OpenDevice())
                {
                    MessageBox.Show("尝试重新连接设备失败!");
                }
            }
        }

        public void CloseDevice()
        {
            // 取流标志位清零
            if (isGrabbing == true)
            {
                isGrabbing = false;
                m_hReceiveThread.Join();
            }

            if (m_BufForDriver != IntPtr.Zero)
            {
                Marshal.Release(m_BufForDriver);
            }

            // 关闭设备
            m_MyCamera.MV_CC_CloseDevice_NET();
            m_MyCamera.MV_CC_DestroyDevice_NET();

            IsConnect = false;
        }

        public bool StartGrab()
        {
            // 标志位置位true
            isGrabbing = true;

            m_hReceiveThread = new Thread(ReceiveThreadProcess);
            m_hReceiveThread.Start();

            // 取流之前先清除帧长度
            m_stFrameInfo.nFrameLen = 0;
            m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8;

            // 开始采集
            int nRet = m_MyCamera.MV_CC_StartGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                isGrabbing = false;
                m_hReceiveThread.Join();

                MessageBox.Show($"连续采集失败,失败代码:{nRet}");
                return false;
            }

            return true;
        }

        public bool StopGrab()
        {
            // 标志位设为false
            isGrabbing = false;
            m_hReceiveThread.Join();

            // 停止采集
            int nRet = m_MyCamera.MV_CC_StopGrabbing_NET();
            if (nRet != MyCamera.MV_OK)
            {
                MessageBox.Show($"停止采集失败,失败代码:{nRet}");
                return false;
            }

            return true;
        }

        public bool SetTriggerMode(TriggerMode triggerMode, TriggerSource triggerSource)
        {
            if (triggerMode == TriggerMode.Continues)
            {
                int nRet = m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);
                if (nRet == MyCamera.MV_OK)
                    isTriggerMode = false;
            }
            else if (triggerMode == TriggerMode.Trigger)
            {
                int nRet = m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
                if (nRet == MyCamera.MV_OK)
                    isTriggerMode = true;

                if (triggerSource == TriggerSource.Hardware)
                {
                    // Line0触发
                    m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0);

                    // 采用上升沿触发
                    m_MyCamera.MV_CC_SetEnumValue_NET("TriggerActivation", 0);

                    // 线路防抖 250us
                    m_MyCamera.MV_CC_SetIntValue_NET("LineDebouncerTime", 250);
                }
                else if (triggerSource == TriggerSource.Software)
                {
                    m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
                }
                else
                {
                    MessageBox.Show("不支持的触发源");
                    return false;
                }
            }
            else
            {
                MessageBox.Show("不支持的触发模式");
                return false;
            }

            return true;
        }

        public bool SnapImage()
        {
            // 触发命令
            int nRet = m_MyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"单张采集触发失败,失败代码:{nRet}");
                return false;
            }

            return true;
        }

        public void SetExposure(float exp)
        {
            // 关闭自动曝光
            m_MyCamera.MV_CC_SetEnumValue_NET("ExposureAuto", 0);
            int nRet = m_MyCamera.MV_CC_SetFloatValue_NET("ExposureTime", exp);
            if (nRet != MyCamera.MV_OK)
            {
                MessageBox.Show($"设置曝光失败,失败代码:{nRet}");
            }
        }

        public float GetExposure()
        {
            // 曝光时间
            MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE();
            int nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ExposureTime", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                return stParam.fCurValue;
            }

            return 0;
        }

        public void SetFrameRate(float fr)
        {
            int nRet = m_MyCamera.MV_CC_SetFloatValue_NET("AcquisitionFrameRate", fr);
            if (nRet != MyCamera.MV_OK)
            {
                MessageBox.Show($"设置帧率失败,失败代码:{nRet}");
            }
        }

        public float GetFrameRate()
        {
            // 帧率
            MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE();
            int nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ResultingFrameRate", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                return stParam.fCurValue;
            }

            return 0;
        }

        public void SetGain(float gain)
        {
            m_MyCamera.MV_CC_SetEnumValue_NET("GainAuto", 0);
            int nRet = m_MyCamera.MV_CC_SetFloatValue_NET("Gain", gain);
            if (nRet != MyCamera.MV_OK)
            {
                MessageBox.Show($"设置增益失败,失败代码:{nRet}");
            }
        }

        public float GetGain()
        {
            // 增益
            MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE();
            int nRet = m_MyCamera.MV_CC_GetFloatValue_NET("Gain", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                return stParam.fCurValue;
            }

            return 0;
        }

        public void SetGammaEnable(bool bEnable)
        {
            int nRet = m_MyCamera.MV_CC_SetBoolValue_NET("GammaEnable", bEnable);
            if (nRet != MyCamera.MV_OK)
            {
                MessageBox.Show($"设置Gamma使能失败,失败代码:{nRet}");
            }
        }

        public bool GetGammaEnable()
        {
            // 增益使能
            bool bGamma = false;
            int nRet = m_MyCamera.MV_CC_GetBoolValue_NET("GammaEnable", ref bGamma);
            if (MyCamera.MV_OK == nRet)
            {
                return bGamma;
            }

            return false;
        }

        public void SetGammaValue(float gamma)
        {
            bool bEnable = false;
            int nRet = m_MyCamera.MV_CC_GetBoolValue_NET("GammaEnable", ref bEnable);
            if (bEnable)
            {
                nRet = m_MyCamera.MV_CC_SetFloatValue_NET("Gamma", gamma);
                if (nRet != MyCamera.MV_OK)
                {
                    //MessageBox.Show($"设置Gamma失败,失败代码:{nRet}");
                }
            }
        }

        public float GetGammaValue()
        {
            // Gamma值
            bool bGamma = false;
            int nRet = m_MyCamera.MV_CC_GetBoolValue_NET("GammaEnable", ref bGamma);

            MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE();
            if (MyCamera.MV_OK == nRet && bGamma)
            {
                m_MyCamera.MV_CC_GetFloatValue_NET("Gamma", ref stParam);
                return stParam.fCurValue;
            }

            return 0;
        }

        public override string ToString()
        {
            return $"{Name}_{SerialNumber}";
        }

        #region 私有方法

        // 取像线程
        private void ReceiveThreadProcess()
        {
            MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE();
            int nRet = m_MyCamera.MV_CC_GetIntValue_NET("PayloadSize", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"读取PayloadSize失败,失败代码:{nRet}");
                return;
            }
            UInt32 nPayloadSize = stParam.nCurValue;

            // 获取图像高
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Height", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像高失败,失败代码:{nRet}");
                return;
            }
            uint nHeight = stParam.nCurValue;

            // 获取图像宽
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Width", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像宽失败,失败代码:{nRet}");
                return;
            }
            uint nWidth = stParam.nCurValue;

            // 根据图像大小设置图像缓存
            m_pDataForRed = new byte[nWidth * nHeight];
            m_pDataForGreen = new byte[nWidth * nHeight];
            m_pDataForBlue = new byte[nWidth * nHeight];
            if (3 * nPayloadSize > m_nBufSizeForDriver)
            {
                if (m_BufForDriver != IntPtr.Zero)
                {
                    Marshal.Release(m_BufForDriver);
                }
                m_nBufSizeForDriver = 3 * nPayloadSize;
                m_BufForDriver = Marshal.AllocHGlobal((Int32)m_nBufSizeForDriver);
            }
            if (m_BufForDriver == IntPtr.Zero)
            {
                return;
            }

            IntPtr pImageBuffer = Marshal.AllocHGlobal((int)nPayloadSize * 3);
            if (pImageBuffer == IntPtr.Zero)
            {
                MessageBox.Show($"申请图像缓存区失败!");
                return;
            }

            MyCamera.MV_FRAME_OUT_INFO_EX stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
            IntPtr RedPtr = IntPtr.Zero;
            IntPtr GreenPtr = IntPtr.Zero;
            IntPtr BluePtr = IntPtr.Zero;
            IntPtr pTemp = IntPtr.Zero;
            DateTime ProStartTime = DateTime.MinValue;
            while (isGrabbing)
            {
                lock (BufForDriverLock)
                {
                    nRet = m_MyCamera.MV_CC_GetOneFrameTimeout_NET(m_BufForDriver, m_nBufSizeForDriver, ref stFrameInfo, 1000);
                    if (nRet == MyCamera.MV_OK)
                    {
                        ProStartTime = DateTime.Now;
                        m_stFrameInfo = stFrameInfo;

                    }
                }
                HImage hImage = new HImage();

                if (nRet == MyCamera.MV_OK)
                {
                    // 彩色相机
                    if (IsColorData(stFrameInfo.enPixelType))
                    {
                        if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                        {
                            pTemp = m_BufForDriver;
                        }
                        else
                        {
                            nRet = ConvertToRGB(m_MyCamera, m_BufForDriver, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType, pImageBuffer);
                            if (MyCamera.MV_OK != nRet)
                            {
                                return;
                            }
                            pTemp = pImageBuffer;
                        }
                        
                        unsafe
                        {
                            byte* pBufForSaveImage = (byte*)pTemp;

                            UInt32 nSupWidth = (stFrameInfo.nWidth + (UInt32)3) & 0xfffffffc;//5120

                            for (int nRow = 0; nRow < stFrameInfo.nHeight; nRow++)
                            {
                                for (int col = 0; col < stFrameInfo.nWidth; col++)
                                {
                                    m_pDataForRed[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col)];
                                    m_pDataForGreen[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 1)];
                                    m_pDataForBlue[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 2)];
                                }
                            }
                        }

                        RedPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForRed, 0);
                        GreenPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForGreen, 0);
                        BluePtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForBlue, 0);
                 
                        lock (BufForImageLock)
                        {
                            hImage.GenImage3Extern("byte", stFrameInfo.nWidth, stFrameInfo.nHeight, RedPtr, GreenPtr, BluePtr, IntPtr.Zero);
                            if (grabHImage != null)
                                grabHImage(hImage);
                        }
                    }
                    else if (IsMonoData(stFrameInfo.enPixelType))                     // 黑白图像
                    {
                        if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
                        {
                            pTemp = m_BufForDriver;
                        }
                        else
                        {
                            nRet = ConvertToMono8(m_MyCamera, m_BufForDriver, pImageBuffer, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType);
                            if (MyCamera.MV_OK != nRet)
                            {
                                return;
                            }
                            pTemp = pImageBuffer;
                        }

                        // 通过事件传递图像数据
                        lock (BufForImageLock)
                        {
                            hImage.GenImage1("byte", stFrameInfo.nWidth, stFrameInfo.nHeight, pTemp);
                            grabHImage(hImage);
                        }
               
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    if (isTriggerMode)
                    {
                        Thread.Sleep(5);
                    }
                }
            }
        }

        // 枚举海康相机(GIGE,USB3)
        public void EnumDevices()
        {
            // 枚举设备列表
            m_stDeviceList.nDeviceNum = 0;
            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
            if (0 != nRet)
            {
                MessageBox.Show("枚举HIK相机设备失败!");
                return;
            }
        }

        // 获取相机对应的枚举索引
        private int GetDeviceIndex(string CameraID)
        {
            for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                    if (gigeInfo.chSerialNumber == CameraID)
                        return i;
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usb3Info = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    if (usb3Info.chSerialNumber == CameraID)
                        return i;
                }
            }

            return -1;
        }

        // 判断是否为黑白图像
        private Boolean IsMonoData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                    return true;

                default:
                    return false;
            }
        }

        // 判断是否为彩色图像
        private Boolean IsColorData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YCBCR411_8_CBYYCRYY:
                    return true;
                default:
                    return false;
            }
        }

        // 转换为RGB格式
        private Int32 ConvertToRGB(object obj, IntPtr pSrc, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType, IntPtr pDst)
        {
            if (IntPtr.Zero == pSrc || IntPtr.Zero == pDst)
            {
                return MyCamera.MV_E_PARAMETER;
            }

            int nRet = MyCamera.MV_OK;
            MyCamera device = obj as MyCamera;
            MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

            stPixelConvertParam.pSrcData = pSrc;//源数据
            if (IntPtr.Zero == stPixelConvertParam.pSrcData)
            {
                return -1;
            }

            stPixelConvertParam.nWidth = nWidth;//图像宽度
            stPixelConvertParam.nHeight = nHeight;//图像高度
            stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
            stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
            stPixelConvertParam.pDstBuffer = pDst;//转换后的数据
            stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
            stPixelConvertParam.nDstBufferSize = (uint)nWidth * nHeight * 3;

            nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
            if (MyCamera.MV_OK != nRet)
            {
                return -1;
            }

            return MyCamera.MV_OK;
        }

        // 转换为Mono8格式
        private Int32 ConvertToMono8(object obj, IntPtr pInData, IntPtr pOutData, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType)
        {
            if (IntPtr.Zero == pInData || IntPtr.Zero == pOutData)
            {
                return MyCamera.MV_E_PARAMETER;
            }

            int nRet = MyCamera.MV_OK;
            MyCamera device = obj as MyCamera;
            MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

            stPixelConvertParam.pSrcData = pInData;//源数据
            if (IntPtr.Zero == stPixelConvertParam.pSrcData)
            {
                return -1;
            }

            stPixelConvertParam.nWidth = nWidth;//图像宽度
            stPixelConvertParam.nHeight = nHeight;//图像高度
            stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
            stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
            stPixelConvertParam.pDstBuffer = pOutData;//转换后的数据
            stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * 3);

            nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
            if (MyCamera.MV_OK != nRet)
            {
                return -1;
            }

            return nRet;
        }


        #endregion
    }
}

二 相机单例模式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WinForm之AOI外观检测.Camera
{
    public class CameraService
    {
        //1. 私有静态变量(在第四步供外界使用),创建类的实例
        //2. 私有构造函数,确保外部无法直接实例化(确保是单个实例)
        //3. 确定供外界调用的代码资源
        //4. 公开静态属性,供外界使用(把第一步类的实例,开放出去)
        //5. 外界使用



    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WinForm之AOI外观检测.Camera
{
    public class CameraService
    {
        //1. 私有静态变量(在第四步供外界使用),创建类的实例
        //2. 私有构造函数,确保外部无法直接实例化(确保是单个实例)
        //3. 确定供外界调用的代码资源
        //4. 公开静态属性,供外界使用(把第一步类的实例,开放出去)
        //5. 外界使用


        //1. 私有静态变量(在第四步供外界使用),创建类的实例
        private static CameraService intance = new CameraService();

        //2. 私有构造函数,确保外部无法直接实例化(确保是单个实例)
        private CameraService()
        {

        }

        //4. 公开静态属性,供外界使用(把第一步类的实例,开放出去)
        public static CameraService Instance
        {
            get { return intance; }
        }


        //3. 确定供外界调用的代码资源

        #region 定义相机
        public HikCamera Camera1 = null;

        #endregion

        #region 初始化相机连接

        #endregion

        #region 加载相机参数

        #endregion

        #region 设置相机参数

        #endregion

        #region 启动相机取像
        #endregion


        #region 关闭相机
        #endregion

        #region 相机软触发
        #endregion

        #region 图像回调方法

        #endregion

    }
}

三 设置相机配置页面

在这里插入图片描述

cbb_DeviceList
rbn_ContinuesMode
rbn_TriggerMode
rbn_SoftTrigger
rbn_HardTrigger
txt_Gain
txt_Exposure
txt_FrameRate

三 显示相机列表

CameraService

        #region 定义相机
        public HikCamera Camera1 = new HikCamera();

        #endregion

        #region 获取相机列表
        public List<string> GetDeviceList()
        {
           return  Camera1.GetDeviceList();
        }

        #endregion

界面-构造器里,显示所有相机

// 获取所有相机
List<string> cameraList = CameraService.Instance.GetDeviceList();

// 判定是否有设备
if(cameraList.Count>0)
{
   // 默认显示第一个相机
   cbb_DeviceList.Text = cameraList[0];

   foreach (string item in cameraList)
   {
       cbb_DeviceList.Items.Add(item);

   }
}

在这里插入图片描述

四 打开设备

        #region 打开相机
        public void OpenDevice(string CameraSeriesNum)
        {
            Camera1.SerialNumber = CameraSeriesNum;
            Camera1.OpenDevice();
        }

        #endregion



        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Open_Click(object sender, EventArgs e)
        {
            //打开设备
            CameraService.Instance.OpenDevice(cbb_DeviceList.Text.Trim());
        }

五 关闭相机

        #region 关闭相机
        public void CloseDevice()
        {
            Camera1.CloseDevice();  

        }

        #endregion
      /// <summary>
      /// 关闭设备
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void btn_Close_Click(object sender, EventArgs e)
      {
          CameraService.Instance.CloseDevice();
      }

六 开始采集

把取图委托,放到打开相机函数中,更安全

        #region 开始采集
        public void StartGrab()
        {
            Camera1.grabHImage += CameraGrabHImage1;
            Camera1.StartGrab();

        }

        #endregion
        #region 图像回调方法
        private void CameraGrabHImage1(HImage hImg)
        {
            // 获取主窗体
            var mainForm = Application.OpenForms.OfType<MainForm>().FirstOrDefault();

            // 使用扩展方法更新UI
            mainForm.Invoke(new Action(() =>
            {
                HWindow_Final hWindow_Final2 = mainForm.Get_HWindow_Final2();
                hWindow_Final2.HobjectToHimage(hImg);
            }));

        }


        #endregion

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartGrab_Click(object sender, EventArgs e)
        {
            CameraService.Instance.StartGrab();
        }

七 停止采集

        #region 停止采集
        public void StopGrab()
        {
    
            Camera1.StopGrab();

        }

        #endregion
    /// <summary>
    /// 停止采集
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btn_StopGrab_Click(object sender, EventArgs e)
    {
        CameraService.Instance.StopGrab();
    }

八 设置采集模式

        #region 设置采集模式
        public void SetTrigeModel(TriggerMode triggerMode,TriggerSource triggerSource)
        {
            Camera1.SetTriggerMode(triggerMode,triggerSource);
        }
        #endregion
        /// <summary>
        /// 设置采集模式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rbn_ContinuesMode_CheckedChanged(object sender, EventArgs e)
        {
            if(rbn_ContinuesMode.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Continues,TriggerSource.Software);
            }else if(rbn_TriggerMode.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Software);

            }
        }

        /// <summary>
        /// 设置采集模式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rbn_TriggerMode_CheckedChanged(object sender, EventArgs e)
        {
            if (rbn_ContinuesMode.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Continues, TriggerSource.Software);
            }
            else if (rbn_TriggerMode.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Software);

            }
        }

九 设置软硬触发模式



        /// <summary>
        /// 设置触发模式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rbn_SoftTrigger_CheckedChanged(object sender, EventArgs e)
        {
            if(rbn_SoftTrigger.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Software);


            }
            else if (rbn_HardTrigger.Checked)

            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Hardware);

            }
        }
        /// <summary>
        /// 设置触发模式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rbn_HardTrigger_CheckedChanged(object sender, EventArgs e)
        {
            if (rbn_SoftTrigger.Checked)
            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Software);

            }
            else if (rbn_HardTrigger.Checked)

            {
                CameraService.Instance.SetTrigeModel(TriggerMode.Trigger, TriggerSource.Hardware);


            }
        }

软触发一次

        #region 相机软触发
        public void SnapImage()
        {
            Camera1.SnapImage();
        }
        #endregion
  /// <summary>
  /// 软触发一次
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void btn_TriggerExec_Click(object sender, EventArgs e)
  {
      CameraService.Instance.SnapImage();
  }

十 获取参数

定义相机参数类

    public class CameraParamModel
    {

        /// <summary>
        /// 相机序列号
        /// </summary>
        public string SerialNumber {  get; set; }
        /// <summary>
        /// 曝光
        /// </summary>
        public float ExposureTime {  get; set; }

        /// <summary>
        /// 增益
        /// </summary>
        public float Gain {  get; set; }

    }
       #region 获取参数
       public CameraParamModel GetCameraParams()
       {
           CameraParamModel cameraParam = new CameraParamModel();
           cameraParam.ExposureTime = Camera1.GetExposure();
           cameraParam.Gain = Camera1.GetGain();

           return cameraParam;
          
       }
       #endregion
        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_GetParam_Click(object sender, EventArgs e)
        {

            CameraParamModel cameraParam = CameraService.Instance.GetCameraParams();

            txt_Exposure.Text = cameraParam.ExposureTime.ToString();
            txt_Gain.Text = cameraParam.Gain.ToString();    

        }

十一 设置参数

        #region 设置参数
        public void SetCameraParams(CameraParamModel cameraParam)
        {
            Camera1.SetGain(cameraParam.Gain);
            Camera1.SetExposure(cameraParam.ExposureTime);

        }

        #endregion
       /// <summary>
       /// 设置参数
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
       private void btn_SetParam_Click(object sender, EventArgs e)
       {
           CameraParamModel cameraParam = new CameraParamModel();
           cameraParam.Gain = (float)Convert.ToDouble(txt_Gain.Text.Trim());
           cameraParam.ExposureTime = (float)Convert.ToDouble(txt_Exposure.Text.Trim());

           CameraService.Instance.SetCameraParams(cameraParam);
       }

在这里插入图片描述

十二 设置在线模式

        /// <summary>
        /// 判断是否在线模式
        /// </summary>
        private void IsOnLine_ToggledChanged()
        {
            // 在线模式
            if (IsOnLine.Toggled)
            {
                MessageBox.Show("在线");

            }
            else
            {
                MessageBox.Show("离线");
            }
        }

在这里插入图片描述

十三 在线模式下启动相机

        #region 初始化相机连接
        public bool InitCameras(string CameraSeriesNum)
        {
            Camera1 = new HikCamera();
            Camera1.SerialNumber = CameraSeriesNum;
            Camera1.grabHImage += CameraGrabHImage2;

            return true;
        }
        #endregion
        private void CameraGrabHImage2(HImage hImg)
        {
            // 获取主窗体
            var mainForm = Application.OpenForms.OfType<Form1>().FirstOrDefault();

            // 使用扩展方法更新UI
            mainForm.Invoke(new Action(() =>
            {
                mainForm.hWindow_Final1.HobjectToHimage(hImg);
                //HWindow_Final hWindow_Final = mainForm.GetWH();
                //hWindow_Final
            }));

        }

在这里插入图片描述

        #region 启动相机取像
        public void StartCameras1()
        {

            Camera1.GetDeviceList();
            if (Camera1 == null)
            {
                MessageBox.Show($"相机 {Camera1.Name}:{Camera1.SerialNumber} 未配置!");
                return;
            }

            // 打开设备
            if (!Camera1.OpenDevice())
            {
                MessageBox.Show($"打开相机 {Camera1.Name}:{Camera1.SerialNumber} 失败!");
                return;
            }
            else
            {


   
                // 启动相机取像
                Camera1.StartGrab();

         
                // 设置为软触发
                Camera1.SetTriggerMode(TriggerMode.Trigger, TriggerSource.Software);

                //($"相机 {Camera1.Name}:{Camera1.SerialNumber} 已连接!");
            }
        }
        #endregion

初始化相机

         // 初始化相机
         if (txt_CameraSeriesNum.Text.Trim().Length==0)
         {
             rtb_Log.AppendText("初始化相机失败!  无相机序列号 \n");

         }
         else
         {
             CameraService.Instance.InitCameras(txt_CameraSeriesNum.Text.Trim());
             rtb_Log.AppendText($"初始化相机成功!  相机序列号:{txt_CameraSeriesNum.Text.Trim()} \n");

         }

是否在线模式

      /// <summary>
      /// 判断是否在线模式
      /// </summary>
      private void IsOnLine_ToggledChanged()
      {
          // 在线模式
          if (IsOnLine.Toggled)
          {
              CameraService.Instance.StartCameras1();
              rtb_Log.AppendText("相机已打开 \n");

          }
          else
          {

              CameraService.Instance.CloseDevice();
              rtb_Log.AppendText("相机已关闭 \n");

          }
      }

十四 通讯触发相机拍照

    CameraService.Instance.SnapImage();

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十五 启动通讯服务

        // 启动通讯
        if(CommunicateService.Instance.StartCommunicateService())
        {
            lb_Comm_Status.Text = "启动";
            lb_Comm_Status.BackColor = Color.Green;
            rtb_Log.AppendText($"通讯服务端已启动 IP :{txt_Ip.Text.Trim()} 端口:{txt_Port.Text.Trim()}");
        }


Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐