using Microsoft.Win32; using System.ComponentModel; using MVSDK_Net; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using TeamAAS.Camera.Images; using TeamAAS.Camera.Models; using static MVSDK_Net.IMVDefine; using TeamAAS.Camera.Attributes; using TeamAAS.Camera.Enums; namespace TeamAAS.Camera.Cameras { /// /// 大华/华睿相机(MVSDK_Net SDK) /// [Camera("iRayple", "大华相机")] public class AHuaCamera : CameraBase { #region 字段 MVSDK_Net.MyCamera mvCameraAcq; private bool IsHaveCamera = false; private long m_picSize = 0; IMVDefine.IMV_Frame frame = new IMVDefine.IMV_Frame(); IntPtr pTemp = IntPtr.Zero; IntPtr pConvertDstBuffer = IntPtr.Zero; int nConvertBufSize = 0; private IMVDefine.IMV_PixelConvertParam stPixelConvertParam = new IMVDefine.IMV_PixelConvertParam(); private IMVDefine.IMV_EPixelType type = IMVDefine.IMV_EPixelType.gvspPixelMono8; #endregion [Browsable(false)] public override string CameraBrand => "大华相机"; public AHuaCamera(Guid id, int index, string name, string serialNumber, string cameraIp, bool isFindByIp) : base(id, index, name, serialNumber, cameraIp, isFindByIp) { mvCameraAcq = new MVSDK_Net.MyCamera(); var list = GetDevices(); foreach (var device in list) { if (device.CameraType == CameraType.GIGE) { if (IsFindByIp) { if (device.CameraIp == CameraIp) { IsHaveCamera = true; ManufacturerName = device.ManufacturerName; ModelName = device.Model; SerialNumber = device.SerialNumber; CameraType = device.CameraType; int res = mvCameraAcq.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByCameraKey, 0, serialNumber); break; } } else { if (device.SerialNumber == serialNumber) { IsHaveCamera = true; ManufacturerName = device.ManufacturerName; ModelName = device.Model; SerialNumber = device.SerialNumber; CameraType = device.CameraType; CameraIp = device.CameraIp; int res = mvCameraAcq.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByCameraKey, 0, serialNumber); break; } } } else { if (device.SerialNumber == serialNumber) { IsHaveCamera = true; ManufacturerName = device.ManufacturerName; ModelName = device.Model; SerialNumber = device.SerialNumber; CameraType = device.CameraType; int res = mvCameraAcq.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByCameraKey, 0, serialNumber); break; } } } IsConnected = false; } #region 设备发现 public static CameraInfo[] GetDevices() { List cameras = new List(); if (!CheckSoftwareInstalled().isInstalled) { return cameras.ToArray(); } IMVDefine.IMV_DeviceList deviceList = new IMVDefine.IMV_DeviceList(); IMVDefine.IMV_EInterfaceType interfaceTp = IMVDefine.IMV_EInterfaceType.interfaceTypeAll; int res = MyCamera.IMV_EnumDevices(ref deviceList, (uint)interfaceTp); if (res == IMVDefine.IMV_OK && deviceList.nDevNum > 0) { for (int i = 0; i < deviceList.nDevNum; i++) { IMVDefine.IMV_DeviceInfo deviceInfo = (IMVDefine.IMV_DeviceInfo) Marshal.PtrToStructure( deviceList.pDevInfo + Marshal.SizeOf(typeof(IMVDefine.IMV_DeviceInfo)) * i, typeof(IMVDefine.IMV_DeviceInfo)); if (deviceInfo.nCameraType == IMV_ECameraType.typeGigeCamera) { IMVDefine.IMV_GigEDeviceInfo gigEDeviceInfo = (IMVDefine.IMV_GigEDeviceInfo) ByteToStruct(deviceInfo.deviceSpecificInfo.gigeDeviceInfo, typeof(IMVDefine.IMV_GigEDeviceInfo)); cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = deviceInfo.cameraName, CameraBrand = "大华相机", CameraType = CameraType.GIGE, ManufacturerName = deviceInfo.manufactureInfo, Model = deviceInfo.modelName, SerialNumber = deviceInfo.serialNumber, CameraIp = gigEDeviceInfo.ipAddress, IsFindByIp = true, }); } else if (deviceInfo.nCameraType == IMV_ECameraType.typeU3vCamera) { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = deviceInfo.cameraName, CameraBrand = "大华相机", CameraType = CameraType.USB, ManufacturerName = deviceInfo.cameraName, Model = deviceInfo.modelName, SerialNumber = deviceInfo.serialNumber, CameraIp = "", }); } else if (deviceInfo.nCameraType == IMV_ECameraType.typeCLCamera) { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = deviceInfo.cameraName, CameraBrand = "大华相机", CameraType = CameraType.Link, ManufacturerName = deviceInfo.cameraName, Model = deviceInfo.modelName, SerialNumber = deviceInfo.serialNumber, CameraIp = "", }); } else { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = deviceInfo.cameraName, CameraBrand = "大华相机", CameraType = CameraType.Unknown, ManufacturerName = deviceInfo.cameraName, Model = deviceInfo.modelName, SerialNumber = deviceInfo.serialNumber, CameraIp = "", }); } } } return cameras.ToArray(); } public static (bool isInstalled, string version) CheckSoftwareInstalled() { string softwareName = "MV Viewer"; string softwareVersion = "2.5.1.1"; string[] registryPaths = new[] { @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" }; foreach (var path in registryPaths) { using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path)) { if (key == null) continue; foreach (string subKeyName in key.GetSubKeyNames()) { using (RegistryKey subKey = key.OpenSubKey(subKeyName)) { string displayName = subKey?.GetValue("DisplayName")?.ToString(); if (displayName != null && displayName.Contains(softwareName)) { string version = subKey.GetValue("DisplayVersion")?.ToString(); var match = Regex.Match(version, @"^(\d+\.\d+\.\d+\.\d+)"); if (match.Success) { version = match.Groups[1].Value; } else { continue; } Version v1 = new Version(version); Version v2 = new Version(softwareVersion); if (v1 >= v2) { return (true, version); } else { return (false, version); } } } } } } return (false, ""); } #endregion #region SDK 实现 public override bool OpenDevice() { using (WriteLockScope()) { try { var list = GetDevices(); foreach (var device in list) { if (device.SerialNumber == SerialNumber) { IsHaveCamera = true; ManufacturerName = device.ManufacturerName; ModelName = device.Model; SerialNumber = device.SerialNumber; CameraType = device.CameraType; int res = mvCameraAcq.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByCameraKey, 0, SerialNumber); break; } } if (!IsHaveCamera) { return false; } int res1 = mvCameraAcq.IMV_Open(); if (res1 != IMVDefine.IMV_OK) { return IsConnected; } if (!IsConnected) { OnConnectChanged(true); } long width = 0; long height = 0; res1 = mvCameraAcq.IMV_GetIntFeatureValue("Width", ref width); res1 = mvCameraAcq.IMV_GetIntFeatureValue("Height", ref height); ImageWidth = (uint)width; ImageHeight = (uint)height; m_picSize = width * height; // 按当前触发模式下发(默认连续采集 = TriggerMode Off,与历史硬编码一致) ApplyTriggerToHardware(TriggerMode); res1 = mvCameraAcq.IMV_StartGrabbingEx(3, IMV_EGrabStrategy.grabStrartegyLatestImage); IsConnected = mvCameraAcq.IMV_IsOpen(); return IsConnected; } catch (Exception ex) { ErrorMessage = ex.Message; IsConnected = false; return false; } } } public override void CloseDevice() { using (WriteLockScope()) { try { if (mvCameraAcq != null) { if (mvCameraAcq.IMV_IsOpen()) { mvCameraAcq.IMV_StopGrabbing(); mvCameraAcq.IMV_Close(); } mvCameraAcq.IMV_DestroyHandle(); } } catch { } IsConnected = false; OnConnectChanged(false); } } #region 触发模式 /// 大华(iRayple/IMV)支持连续/软触发/硬触发(Line0) public override IReadOnlyList SupportedTriggerModes => FullTriggerModes; /// 历史行为:OpenDevice 硬编码 TriggerMode=Off,故默认连续采集 protected override CameraTriggerMode DefaultTriggerMode => CameraTriggerMode.连续采集; protected override bool ApplyTriggerMode(CameraTriggerMode mode) { using (WriteLockScope()) return ApplyTriggerToHardware(mode); } /// /// 把触发模式下发到大华 IMV SDK(GenICam 字符串接口,返回 IMV_OK = 成功)。 /// 调用方需已持有写锁(OpenDevice/Grab 内部已在锁内;锁可重入)。 /// private bool ApplyTriggerToHardware(CameraTriggerMode mode) { if (mvCameraAcq == null || !mvCameraAcq.IMV_IsOpen()) return false; mvCameraAcq.IMV_SetEnumFeatureSymbol("AcquisitionMode", "Continuous"); switch (mode) { case CameraTriggerMode.连续采集: return mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerMode", "Off") == IMVDefine.IMV_OK; case CameraTriggerMode.软触发: mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerSource", "Software"); return mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerMode", "On") == IMVDefine.IMV_OK; case CameraTriggerMode.硬触发上升沿: case CameraTriggerMode.硬触发下降沿: mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerSource", "Line0"); mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerActivation", mode == CameraTriggerMode.硬触发上升沿 ? "Rising" : "Falling"); return mvCameraAcq.IMV_SetEnumFeatureSymbol("TriggerMode", "On") == IMVDefine.IMV_OK; default: // 跟随相机:不下发,沿用相机自身配置 return true; } } #endregion public override GenImage Grab() { using (WriteLockScope()) { var sw = Stopwatch.StartNew(); try { if (mvCameraAcq == null || !mvCameraAcq.IMV_IsOpen()) { ErrorMessage = "相机未连接"; return null; } mvCameraAcq.IMV_ClearFrameBuffer(); // 仅软触发模式需主动发触发命令;连续采集自由出图,硬触发等外部信号 if (IsSoftwareTrigger) mvCameraAcq.IMV_ExecuteCommandFeature("TriggerSoftware"); int res = mvCameraAcq.IMV_GetFrame(ref frame, 1000); if (res == IMVDefine.IMV_OK) { try { // Mono图像处理 if (IsMonoPixelFormat(frame.frameInfo.pixelFormat)) { type = IMVDefine.IMV_EPixelType.gvspPixelMono8; if (frame.frameInfo.pixelFormat == IMVDefine.IMV_EPixelType.gvspPixelMono8) { pTemp = frame.pData; } else { if (pConvertDstBuffer == IntPtr.Zero || nConvertBufSize < (frame.frameInfo.width * frame.frameInfo.height)) { if (pConvertDstBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(pConvertDstBuffer); pConvertDstBuffer = IntPtr.Zero; } pConvertDstBuffer = Marshal.AllocHGlobal((int)(frame.frameInfo.width * frame.frameInfo.height)); if (pConvertDstBuffer == IntPtr.Zero) { return null; } nConvertBufSize = (int)(frame.frameInfo.width * frame.frameInfo.height); } stPixelConvertParam.nWidth = frame.frameInfo.width; stPixelConvertParam.nHeight = frame.frameInfo.height; stPixelConvertParam.ePixelFormat = frame.frameInfo.pixelFormat; stPixelConvertParam.pSrcData = frame.pData; stPixelConvertParam.nSrcDataLen = frame.frameInfo.size; stPixelConvertParam.nPaddingX = frame.frameInfo.paddingX; stPixelConvertParam.nPaddingY = frame.frameInfo.paddingY; stPixelConvertParam.eBayerDemosaic = IMVDefine.IMV_EBayerDemosaic.demosaicNearestNeighbor; stPixelConvertParam.eDstPixelFormat = IMVDefine.IMV_EPixelType.gvspPixelMono8; stPixelConvertParam.pDstBuf = pConvertDstBuffer; stPixelConvertParam.nDstBufSize = frame.frameInfo.width * frame.frameInfo.height; res = mvCameraAcq.IMV_PixelConvert(ref stPixelConvertParam); if (res != IMVDefine.IMV_OK) { return null; } pTemp = pConvertDstBuffer; } } // 彩色图像处理 else if (IsColorPixelFormat(frame.frameInfo.pixelFormat)) { type = IMVDefine.IMV_EPixelType.gvspPixelRGB8; if (frame.frameInfo.pixelFormat == IMVDefine.IMV_EPixelType.gvspPixelRGB8) { pTemp = frame.pData; } else { if (pConvertDstBuffer == IntPtr.Zero || nConvertBufSize < (frame.frameInfo.width * frame.frameInfo.height * 3)) { if (pConvertDstBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(pConvertDstBuffer); pConvertDstBuffer = IntPtr.Zero; } pConvertDstBuffer = Marshal.AllocHGlobal((int)(frame.frameInfo.width * frame.frameInfo.height * 3)); if (pConvertDstBuffer == IntPtr.Zero) { return null; } nConvertBufSize = (int)(frame.frameInfo.width * frame.frameInfo.height * 3); } stPixelConvertParam.nWidth = frame.frameInfo.width; stPixelConvertParam.nHeight = frame.frameInfo.height; stPixelConvertParam.ePixelFormat = frame.frameInfo.pixelFormat; stPixelConvertParam.pSrcData = frame.pData; stPixelConvertParam.nSrcDataLen = frame.frameInfo.size; stPixelConvertParam.nPaddingX = frame.frameInfo.paddingX; stPixelConvertParam.nPaddingY = frame.frameInfo.paddingY; stPixelConvertParam.eBayerDemosaic = IMVDefine.IMV_EBayerDemosaic.demosaicNearestNeighbor; stPixelConvertParam.eDstPixelFormat = IMVDefine.IMV_EPixelType.gvspPixelRGB8; stPixelConvertParam.pDstBuf = pConvertDstBuffer; stPixelConvertParam.nDstBufSize = frame.frameInfo.width * frame.frameInfo.height * 3; res = mvCameraAcq.IMV_PixelConvert(ref stPixelConvertParam); if (res != IMVDefine.IMV_OK) { return null; } pTemp = pConvertDstBuffer; } } else { return null; } Image = ConvertImage(frame.frameInfo.width, frame.frameInfo.height, pTemp, type); if (Image != null) { ImageWidth = (uint)Image.Width; ImageHeight = (uint)Image.Height; ApplyGammaToImage(Image); ErrorMessage = ""; TotalTime = sw.Elapsed; return Image; } } catch (Exception) { } finally { mvCameraAcq.IMV_ReleaseFrame(ref frame); } } else { ErrorMessage = res.ToString(); CloseDevice(); OpenDevice(); } } catch (Exception ex) { CloseDevice(); OpenDevice(); ErrorMessage = ex.Message; } return null; } } public override bool SetExposureTime(float exposureTime) { using (WriteLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = (long)exposureTime; mvCameraAcq.IMV_SetIntFeatureValue("ExposureTime", val); } return true; } catch { return false; } } } public override float GetExposureTime() { using (ReadLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = 0; mvCameraAcq.IMV_GetIntFeatureValue("ExposureTime", ref val); return (float)val; } } catch { } return 0; } } public override bool SetGain(float gain) { using (WriteLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = (long)gain; mvCameraAcq.IMV_SetIntFeatureValue("Gain", val); } return true; } catch { return false; } } } public override float GetGain() { using (ReadLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = 0; mvCameraAcq.IMV_GetIntFeatureValue("Gain", ref val); return (float)val; } } catch { } return 0; } } public override bool SetGamma(float gamma) { using (WriteLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = (long)gamma; mvCameraAcq.IMV_SetIntFeatureValue("Gamma", val); return true; } } catch { } UpdateGammaSnapshot(gamma); return true; } } public override float GetGamma() { using (ReadLockScope()) { try { if (mvCameraAcq != null && mvCameraAcq.IMV_IsOpen()) { long val = 0; mvCameraAcq.IMV_GetIntFeatureValue("Gamma", ref val); return (float)val; } } catch { } return Gamma; } } #endregion #region 辅助方法 private bool IsMonoPixelFormat(IMVDefine.IMV_EPixelType enType) { switch (enType) { case IMVDefine.IMV_EPixelType.gvspPixelMono8: case IMVDefine.IMV_EPixelType.gvspPixelMono10: case IMVDefine.IMV_EPixelType.gvspPixelMono10Packed: case IMVDefine.IMV_EPixelType.gvspPixelMono12: case IMVDefine.IMV_EPixelType.gvspPixelMono12Packed: return true; default: return false; } } private bool IsColorPixelFormat(IMVDefine.IMV_EPixelType enType) { switch (enType) { case IMVDefine.IMV_EPixelType.gvspPixelRGB8: case IMVDefine.IMV_EPixelType.gvspPixelBGR8: case IMVDefine.IMV_EPixelType.gvspPixelRGBA8: case IMVDefine.IMV_EPixelType.gvspPixelBGRA8: case IMVDefine.IMV_EPixelType.gvspPixelYUV422_8: case IMVDefine.IMV_EPixelType.gvspPixelYUV422_8_UYVY: case IMVDefine.IMV_EPixelType.gvspPixelBayGR8: case IMVDefine.IMV_EPixelType.gvspPixelBayRG8: case IMVDefine.IMV_EPixelType.gvspPixelBayGB8: case IMVDefine.IMV_EPixelType.gvspPixelBayBG8: case IMVDefine.IMV_EPixelType.gvspPixelBayGB10: case IMVDefine.IMV_EPixelType.gvspPixelBayGB10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayBG10: case IMVDefine.IMV_EPixelType.gvspPixelBayBG10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayRG10: case IMVDefine.IMV_EPixelType.gvspPixelBayRG10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGR10: case IMVDefine.IMV_EPixelType.gvspPixelBayGR10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGB12: case IMVDefine.IMV_EPixelType.gvspPixelBayGB12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayBG12: case IMVDefine.IMV_EPixelType.gvspPixelBayBG12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayRG12: case IMVDefine.IMV_EPixelType.gvspPixelBayRG12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGR12: case IMVDefine.IMV_EPixelType.gvspPixelBayGR12Packed: return true; default: return false; } } public GenImage ConvertImage(UInt32 nWidth, UInt32 nHeight, IntPtr pImageBuf, IMVDefine.IMV_EPixelType enPixelType) { try { if (enPixelType == IMVDefine.IMV_EPixelType.gvspPixelMono8) { return GenImage.FromIntPtr((int)nWidth, (int)nHeight, PixelType.Grey8, pImageBuf, (int)nWidth); } else if (enPixelType == IMVDefine.IMV_EPixelType.gvspPixelRGB8) { return GenImage.FromIntPtr((int)nWidth, (int)nHeight, PixelType.RGB24, pImageBuf, (int)nWidth * 3); } else if (enPixelType == IMVDefine.IMV_EPixelType.gvspPixelBGR8) { return GenImage.FromIntPtr((int)nWidth, (int)nHeight, PixelType.BGR24, pImageBuf, (int)nWidth * 3); } return null; } catch (System.Exception) { return null; } } public static object ByteToStruct(Byte[] bytes, Type type) { int size = Marshal.SizeOf(type); if (size > bytes.Length) { return null; } IntPtr structPtr = Marshal.AllocHGlobal(size); Marshal.Copy(bytes, 0, structPtr, size); object obj = Marshal.PtrToStructure(structPtr, type); Marshal.FreeHGlobal(structPtr); return obj; } #endregion } }