#define MVCAMERA using MvCameraControl; using LB_VisionProcesses; using System.Diagnostics; using System.Runtime.InteropServices; using LB_VisionProcesses.Cameras; namespace LB_VisionProcesses.Cameras.HikCameras { public class HikCamera : BaseCamera { readonly DeviceTLayerType enumTLayerType = DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvGenTLCXPDevice | DeviceTLayerType.MvGenTLCameraLinkDevice | DeviceTLayerType.MvGenTLXoFDevice; public HikCamera() : base() { Brand = CameraBrand.HikCamera; } #region param [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); List deviceInfoList = new List(); List deviceSNList = new List(); IDevice device = null; IInterface _ifInstance; IntPtr m_BufForDriver = IntPtr.Zero; UInt32 _bufferSize = 3072 * 2048 * 3; private byte[] _buffer; private uint _buffSizeForSaveImage = 3072 * 2048 * (16 * 3 + 4) + 2048; private byte[] _bufForSaveImage; private Object m_BufForSaveImageLock = new Object(); public IntPtr m_pSaveImageBuf = IntPtr.Zero; #endregion #region operate public override List GetListEnum() { List deviceList = new List(); foreach (var item in GetListInfoEnum()) { IDeviceInfo deviceInfo = item; if (deviceInfo.UserDefinedName != "") { //deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")"); deviceList.Add(deviceInfo.SerialNumber); } else { //deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")"); deviceList.Add(deviceInfo.SerialNumber); } } return deviceList; } public override bool InitDevice(string CamSN, object handle = null) { try { int nRet; if (string.IsNullOrEmpty(CamSN)) return false; var infolist = GetListInfoEnum(); if (infolist.Count < 1) return false; IDeviceInfo deviceInfo = infolist[0]; bool selectSNflag = false; foreach (var item in infolist) { if (item.SerialNumber.Equals(CamSN)) { deviceInfo = item; selectSNflag = true; break; } } if (!selectSNflag) return false; try { // ch:打开设备 | en:Open device device = DeviceFactory.CreateDevice(deviceInfo); } catch (Exception ex) { MessageBox.Show("Create Device fail!" + ex.Message, "异常"); return false; } nRet = device.Open(); if (nRet != MvError.MV_OK) { ShowErrorMsg("Open Device fail!", nRet); return false; } // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) if (device is IGigEDevice) { int packetSize; nRet = (device as IGigEDevice).GetOptimalPacketSize(out packetSize); if (packetSize > 0) { nRet = device.Parameters.SetIntValue("GevSCPSPacketSize", packetSize); if (nRet != MvError.MV_OK) { Debug.WriteLine("Warning: Set Packet Size failed {0:x8}", nRet); } else { Debug.WriteLine("Set PacketSize to {0}", packetSize); } } else { Debug.WriteLine("Warning: Get Packet Size failed {0:x8}", nRet); } } // ch:设置触发模式为On || en:set trigger mode as On // 设置为Off会一直触发回调 SetTriggerMode(TriggerMode.On, TriggerSource.Software); device.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous"); // ch:注册回调函数 | en:Register image callback // 注册回调函数操作应放在打开采集流操作之前 device.StreamGrabber.FrameGrabedEvent += CallBackEventHandler; if (nRet != MvError.MV_OK) { Debug.WriteLine("Set TriggerMode failed:{0:x8}", nRet); return false; } if (!StartGrabbing()) { Debug.WriteLine("开始采集失败"); return false; } //ch: 设置合适的缓存节点数量 | en: Setting the appropriate number of image nodes device.StreamGrabber.SetImageNodeNum(5); SN = CamSN; Brand = CameraBrand.HikCamera; return true; } catch { return false; } } public override bool CloseDevice() { if (device == null) { return false; } try { isGrabbing = false; if (m_BufForDriver != IntPtr.Zero) { Marshal.Release(m_BufForDriver); } // ch:关闭设备 | en:Close device var nRet = device.Close(); if (MvError.MV_OK == nRet) { // ch:销毁设备 | en:Destroy device device.Dispose(); return true; } nRet = device.Close(); if (MvError.MV_OK != nRet) { Debug.WriteLine("Close device failed:{0:x8}", nRet); return false; } // ch:销毁设备 | en:Destroy device device.Dispose(); device = null; return true; } catch { device = null; return false; } } /// /// ch:软触发执行一次 | en:Trigger once by software /// public override bool SoftTrigger() { int nRet; // ch:触发命令 | en:Trigger command nRet = device.Parameters.SetCommandValue("TriggerSoftware"); return (MvError.MV_OK == nRet); } #endregion #region SettingConfig public override bool SetTriggerMode(TriggerMode mode = TriggerMode.On, TriggerSource triggerEnum = TriggerSource.None) { try { int nRet; switch (mode) { case TriggerMode.Off: nRet = device.Parameters.SetEnumValueByString("TriggerMode", "Off"); break; case TriggerMode.On: nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On"); break; default: nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On"); break; } bool flag1 = (MvError.MV_OK == nRet); switch (triggerEnum) { case TriggerSource.Software: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Software"); break; case TriggerSource.Line0: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line0"); break; case TriggerSource.Line1: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line1"); break; case TriggerSource.Line2: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line2"); break; case TriggerSource.Line3: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line3"); break; case TriggerSource.Line4: nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line4"); break; default: break; } bool flag2 = (MvError.MV_OK == nRet); return flag1 && flag2; } catch { return false; } } public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source) { int nRet; mode = TriggerMode.On; source = TriggerSource.Software; IEnumValue enumValue; nRet = device.Parameters.GetEnumValue("TriggerMode", out enumValue); bool flag1 = (MvError.MV_OK == nRet); if (flag1) { switch (enumValue.CurEnumEntry.Symbolic) { case "On": mode = TriggerMode.On; break; case "Off": mode = TriggerMode.Off; break; default: mode = TriggerMode.On; break; } } nRet = device.Parameters.GetEnumValue("TriggerSource", out enumValue); bool flag2 = (MvError.MV_OK == nRet); if (flag2) { switch (enumValue.CurEnumEntry.Symbolic) { case "TriggerSoftware": case "Software": source = TriggerSource.Software; break; case "Line0": source = TriggerSource.Line0; break; case "Line1": source = TriggerSource.Line1; break; case "Line2": source = TriggerSource.Line2; break; default: source = TriggerSource.Line0; break; } } return flag1 && flag2; } public override bool SetExpouseTime(double value) { try { int nRet = device.Parameters.SetFloatValue("ExposureTime", Convert.ToSingle(value)); return (MvError.MV_OK == nRet); } catch { return false; } } public override bool GetExpouseTime(out double value) { IFloatValue floatValue; int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue); value = Convert.ToDouble(floatValue.CurValue); return (MvError.MV_OK == nRet); } public override bool SetTriggerPolarity(TriggerPolarity polarity) { //return true; IEnumValue enumValue; int nRet = device.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue); if (nRet != MvError.MV_OK) return false; for (int i = 0; i < enumValue.SupportedNum; ++i) { if (polarity.ToString().Equals(enumValue.SupportEnumEntries[i].Symbolic, StringComparison.OrdinalIgnoreCase)) { nRet = device.Parameters.SetEnumValue("TimerTriggerActivation", enumValue.SupportEnumEntries[i].Value); if (nRet != MvError.MV_OK) return false; break; } } return (MvError.MV_OK == nRet); } public override bool GetTriggerPolarity(out TriggerPolarity polarity) { polarity = TriggerPolarity.RisingEdge; //return true; IEnumValue enumValue; int nRet = device.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue); if (nRet != MvError.MV_OK) { return false; } string activate = enumValue.CurEnumEntry.Symbolic; //1下降沿 0上升沿 //上升沿 if (activate == "RisingEdge") polarity = TriggerPolarity.RisingEdge; //下降沿 else if (activate == "FallingEdge") polarity = TriggerPolarity.FallingEdge; return (MvError.MV_OK == nRet); } public override bool SetTriggerFliter(double flitertime) { try { int nRet = device.Parameters.SetIntValue("LineDebouncerTimeNs", (uint)flitertime); //nRet = device.Parameters.SetIntValue("LineDebouncerTime", (uint)flitertime); return (MvError.MV_OK == nRet); } catch { return false; } } public override bool GetTriggerFliter(out double flitertime) { flitertime = 1000; IFloatValue floatValue; int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue); flitertime = Convert.ToDouble(floatValue.CurValue); return (MvError.MV_OK == nRet); } public override bool SetTriggerDelay(double delay) { try { int nRet = device.Parameters.SetFloatValue("TriggerDelay", Convert.ToSingle(delay)); return (MvError.MV_OK == nRet); } catch { return false; } } public override bool GetTriggerDelay(out double delay) { delay = 0; IFloatValue floatValue; int nRet = device.Parameters.GetFloatValue("TriggerDelay", out floatValue); delay = Convert.ToDouble(floatValue.CurValue); return (MvError.MV_OK == nRet); } public override bool SetGain(double gain) { try { int nRet = device.Parameters.SetFloatValue("Gain", Convert.ToSingle(gain)); return (MvError.MV_OK == nRet); } catch { return false; } } public override bool GetGain(out double gain) { gain = 0; IFloatValue floatValue; int nRet = device.Parameters.GetFloatValue("Gain", out floatValue); gain = Convert.ToDouble(floatValue.CurValue); return (MvError.MV_OK == nRet); } public override bool SetLineMode(IOLines line, LineMode mode) { int nRet = device.Parameters.SetEnumValueByString(line.ToString(), mode.ToString()); return (MvError.MV_OK == nRet); } public override bool SetLineStatus(IOLines line, LineStatus linestatus) { int nRet = device.Parameters.SetBoolValue(line.ToString(), linestatus.Equals(LineStatus.Hight)); return (MvError.MV_OK == nRet); } public override bool GetLineStatus(IOLines line, out LineStatus linestatus) { bool resultsignal = false; int nRet = device.Parameters.GetBoolValue(line.ToString(), out resultsignal); linestatus = resultsignal ? LineStatus.Hight : LineStatus.Low; return (MvError.MV_OK == nRet); } public override bool AutoBalanceWhite() { int nRet = device.Parameters.SetEnumValueByString("BalanceWhiteAuto", "Once"); return (MvError.MV_OK == nRet); } #endregion #region helper // 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, "异常"); } public override bool StartGrabbing() { try { // Set default state after grabbing starts // Turn off real-time mode which is default // 0: real-time // 1: trigger CallBackImg = null; int nRet = device.StreamGrabber.StartGrabbing(); if (MvError.MV_OK != nRet) Debug.WriteLine("Grab start failed"); isGrabbing = true; return (MvError.MV_OK == nRet); } catch (Exception ex) { Debug.WriteLine("StartGrabbing failed: " + ex.Message); return false; } } public override bool StopGrabbing() { int nRet = device.StreamGrabber.StopGrabbing(); if (MvError.MV_OK != nRet) Debug.WriteLine("Grab stop failed"); isGrabbing = false; CallBackImg = null; return (MvError.MV_OK == nRet); } private List GetListInfoEnum() { System.GC.Collect(); deviceInfoList.Clear(); // ch:创建设备列表 | en:Create Device List int nRet = DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList); if (nRet != MvError.MV_OK) { ShowErrorMsg("Enumerate devices fail!", nRet); return new List(); } // ch:在窗体列表中显示设备名 | en:Display device name in the form list for (int i = 0; i < deviceInfoList.Count; i++) { IDeviceInfo deviceInfo = deviceInfoList[i]; if (deviceInfo.UserDefinedName != "") deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")"); else deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")"); } // ch:选择第一项 | en:Select the first item if (deviceInfoList.Count != 0) { deviceSNList[0] = deviceSNList[0]; } return deviceInfoList; } static bool IsMonoPixelFormat(MvGvspPixelType enType) { switch (enType) { case MvGvspPixelType.PixelType_Gvsp_Mono10: case MvGvspPixelType.PixelType_Gvsp_Mono10_Packed: case MvGvspPixelType.PixelType_Gvsp_Mono12: case MvGvspPixelType.PixelType_Gvsp_Mono12_Packed: return true; default: return false; } } static bool IsColorPixelFormat(MvGvspPixelType enType) { switch (enType) { case MvGvspPixelType.PixelType_Gvsp_BGR8_Packed: case MvGvspPixelType.PixelType_Gvsp_YUV422_Packed: case MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerGR8: case MvGvspPixelType.PixelType_Gvsp_BayerRG8: case MvGvspPixelType.PixelType_Gvsp_BayerGB8: case MvGvspPixelType.PixelType_Gvsp_BayerBG8: case MvGvspPixelType.PixelType_Gvsp_BayerGB10: case MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerBG10: case MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerRG10: case MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerGR10: case MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerGB12: case MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerBG12: case MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerRG12: case MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed: case MvGvspPixelType.PixelType_Gvsp_BayerGR12: case MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed: return true; default: return false; } } /// /// 回调函数(不要在内部增加耗时操作) /// /// /// private void CallBackEventHandler(object sender, FrameGrabbedEventArgs e) { try { int nRet; //Debug.WriteLine("Get Image Buffer: Width[{0}] , Height[{1}] , FrameNum[{2}]" //, e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum); IImage inputImage = e.FrameOut.Image; IImage outImage = inputImage; MvGvspPixelType dstPixelType = MvGvspPixelType.PixelType_Gvsp_Undefined; if (IsColorPixelFormat(e.FrameOut.Image.PixelType)) dstPixelType = MvGvspPixelType.PixelType_Gvsp_RGB8_Packed; else if (IsMonoPixelFormat(e.FrameOut.Image.PixelType)) dstPixelType = MvGvspPixelType.PixelType_Gvsp_Mono8; else Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】Don't need to convert!"); if (dstPixelType != MvGvspPixelType.PixelType_Gvsp_Undefined) { // ch:像素格式转换 | en:Pixel type convert nRet = device.PixelTypeConverter.ConvertPixelType(inputImage, out outImage, dstPixelType); if (nRet != MvError.MV_OK) { Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】Image Convert failed:{0:x8}", nRet); return; } //Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】Image Convert success!"); } //ch: 释放图像缓存 | en: Release image buffer device.StreamGrabber.FreeImageBuffer(e.FrameOut); if (this.ImageGrabbed != null) this.ImageGrabbed(this, new CameraEventArgs(SN, outImage.ToBitmap())); try { CallBackImg = outImage.ToBitmap(); if (CallBackImg == null) return; if (GetTriggerMode(out TriggerMode mode, out TriggerSource source)) { if (mode == TriggerMode.On && source != TriggerSource.Software) TriggerRunMessageReceived?.Invoke(SN, source.ToString()); // 触发运行事件 } } catch { } //ActionGetImage?.Invoke(SN, outImage.ToBitmap()); } catch { } } public override bool GetImage(out Bitmap bitmap, int outtime = 3000) { bitmap = null; try { // 设置超时时间 DateTime lastTime = DateTime.Now.AddMilliseconds(outtime); // 判断是否超时 while (lastTime > DateTime.Now)// 设置超时时间为 3 秒 { try { if (CallBackImg != null) { // 保存旧 Bitmap 并释放 bitmap = CallBackImg.Clone() as Bitmap; // 创建副本 // 释放旧资源 CallBackImg.Dispose(); CallBackImg = null; return true; } } catch { } Thread.Sleep(10); } return false; } catch { return bitmap == null ? false : true; } } public override bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000) { if (!isGrabbing) { StartGrabbing(); } GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); if (triggerMode != TriggerMode.On && triggerSource != TriggerSource.Software) { SetTriggerMode(TriggerMode.On, TriggerSource.Software); } bitmap = null; CallBackImg = null; if (!SoftTrigger()) { return false; } // 开始时间 DateTime startTime = DateTime.Now; // 当前时间 // 判断是否超时 while (DateTime.Now < startTime.AddMilliseconds(outtime))// 设置超时时间为 3 秒 { GetImage(out bitmap, 50); if (bitmap != null) { break; } Thread.Sleep(10); } if (triggerMode != TriggerMode.On) { SetTriggerMode(TriggerMode.On, triggerSource); } return (bitmap != null); } public override void SetCamConfig(CameraConfig config) { if (Enum.TryParse(config.Params.Inputs["触发模式"].ToString(), out TriggerMode TriggerMode) && Enum.TryParse(config.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource) && Enum.TryParse(config.Params.Inputs["触发极性"].ToString(), out TriggerPolarity TriggerPolarity) ) { SetTriggerMode(TriggerMode, TriggerSource); SetTriggerPolarity(TriggerPolarity); //SetTriggerFliter(Convert.ToDouble(config.Params.Inputs["触发消抖"].ToString())); //SetTriggerDelay(Convert.ToDouble(config.Params.Inputs["触发延时"].ToString())); SetExpouseTime(Convert.ToDouble(config.Params.Inputs["曝光时间"].ToString())); SetGain(Convert.ToDouble(config.Params.Inputs["增益"].ToString())); } } public override void GetCamConfig(out CameraConfig config) { GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); GetTriggerPolarity(out TriggerPolarity triggerPolarity); //GetTriggerFliter(out double triggerfilter); //GetTriggerDelay(out double triggerdelay); GetExpouseTime(out double expouseTime); GetGain(out double gain); config = new CameraConfig(null); config.Params.Inputs.Add("触发模式", triggerMode); config.Params.Inputs.Add("触发方式", triggerSource); config.Params.Inputs.Add("触发极性", triggerPolarity); //config.Params.Inputs.Add("触发消抖", triggerfilter); //config.Params.Inputs.Add("触发延时", triggerdelay); config.Params.Inputs.Add("曝光时间", expouseTime); config.Params.Inputs.Add("增益", gain); } public override bool StartWith_SoftTriggerModel() { SetTriggerMode(TriggerMode.Off, TriggerSource.Software); return StartGrabbing(); } public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0) { if (hardtriggeritem == TriggerSource.Software) hardtriggeritem = TriggerSource.Line0; SetTriggerMode(TriggerMode.On, hardtriggeritem); return StartGrabbing(); } public override bool StartContinuousGrab() { throw new NotImplementedException(); } #endregion } }