海康相机SDK取图参照:海康相机(HIKROBOT)SDK C# 取图完整代码
以下是基于大恒相机(Galaxy ) SDK(实现的相机搜索、连接、采图、参数设置等功能的完整代码,代码风格、架构,同时融入了资源优化、逻辑严谨性等最佳实践:
using Cognex.VisionPro;using GxIAPINET;using System;using System.Collections.Generic;using System.ComponentModel;using System.Diagnostics;using System.IO;using System.Linq;using System.Runtime.InteropServices;using System.Text;using System.Threading;using System.Threading.Tasks;namespace DAL.Camera{ [Category("大恒相机")] [DisplayName("大恒相机")] [Serializable] public class CamerasDaheng : Camerasbase { #region 私有成员(严格遵循官方文档数据类型定义) [NonSerialized] private IGXDevice _gxDevice = null; // 设备对象(文档6.5节) [NonSerialized] private IGXStream _gxStream = null; // 流对象(文档6.14节) [NonSerialized] private IGXFeatureControl _remoteFeatureCtrl = null; // 远端设备属性控制器(文档6.6节) [NonSerialized] private IGXFeatureControl _streamFeatureCtrl = null; // 流层属性控制器(文档6.14.9节) [NonSerialized] private Stopwatch _stopwatch = new Stopwatch(); [NonSerialized] private List<IGXDeviceInfo> _deviceInfoList = new List<IGXDeviceInfo>(); // 设备信息列表(文档6.4节) private uint _payloadSize = 0; // 有效载荷大小(文档6.14.14节) private uint _rowStep = 0; // 行步长 private bool _isGrabbing = false; // 是否正在抓图 private bool _disposed = false; // 是否已释放资源 private IGXDeviceInfo _curDeviceInfo = null; // 当前设备信息(文档6.4节) // 常量定义(严格对应官方文档枚举值) private const int TRIGGER_SOURCE_SOFTWARE = 1; // 软触发(文档2.11.3节) private const int TRIGGER_SOURCE_LINE0 = 0; // Line0触发(文档2.10.6节) private const int TRIGGER_ACTIVATION_RISING = 0; // 上升沿(文档2.10.6节) private const int TRIGGER_ACTIVATION_FALLING = 1; // 下降沿(文档2.10.6节) private const int TRIGGER_MODE_OFF = 0; // 触发模式关闭(内触发) private const int TRIGGER_MODE_ON = 1; // 触发模式开启(外触发/软触发) private const string ACQUISITION_MODE_ConTINUOUS = "Continuous"; // 连续采集模式(文档2.10.6节) private const int TIMEOUT_MILLISEConDS = 1000; // 超时时间(文档2.11.2节) private const int BUFFER_COUNT = 10; // 采集缓存个数(文档2.11.4节,建议≥3) #endregion #region 构造函数与析构函数 public CamerasDaheng() : base() { try { // 初始化SDK(文档2.3节) IGXFactory.GetInstance().Init(); Log.Addlog(2, "大恒相机SDK初始化成功"); } catch (CGalaxyException ex) { Log.Error($"SDK初始化失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); throw new Exception($"SDK初始化失败:{ex.Message}"); } } ~CamerasDaheng() { Dispose(false); } #endregion #region 核心方法实现(遵循官方文档编程流程) /// <summary> /// 搜索相机(文档2.5节,全网枚举) /// </summary> public override List<CamerasInfo> SearchCameras() { List<CamerasInfo> camInfoList = new List<CamerasInfo>(); try { _deviceInfoList.Clear(); // 枚举所有设备(千兆网+USB3.0,超时200ms) IGXFactory.GetInstance().UpdateAllDeviceList(200, _deviceInfoList); if (_deviceInfoList.Count == 0) { Log.ShowErrorDialog("未找到任何大恒相机设备"); return camInfoList; } // 遍历设备列表(文档6.4节,IGXDeviceInfo接口) foreach (var deviceInfo in _deviceInfoList) { CamerasInfo camInfo = new CamerasInfo(); string serialNo = deviceInfo.GetSN(); string modelName = deviceInfo.GetModelName(); string vendorName = deviceInfo.GetVendorName(); string deviceClass = GetDeviceClassDesc(deviceInfo.GetDeviceClass()); // 构建相机名称(文档6.4.4节) camInfo.m_CamName = $"Daheng({deviceClass}): {vendorName} {modelName} ({serialNo})"; camInfo.m_ExtInfo = deviceInfo; camInfo.m_SerialNO = serialNo; camInfo.m_MaskName = serialNo; camInfoList.Add(camInfo); } } catch (CGalaxyException ex) { Log.Error($"相机搜索失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } catch (Exception ex) { Log.Error($"相机搜索异常:{ex.Message}"); } return camInfoList; } /// <summary> /// 连接相机(文档2.9节,按序列号打开) /// </summary> public override bool ConnectDev() { try { base.ConnectDev(); DisConnectDev(); // 先断开已连接设备 if (string.IsNullOrEmpty(mSerialNo)) { Log.Error("相机序列号不能为空"); return false; } // 查找目标设备 if (!FindCameraBySN(mSerialNo)) { Log.Error($"未找到序列号为{mSerialNo}的大恒相机"); return false; } // 打开设备(独占模式,文档6.1.9节) _gxDevice = IGXFactory.GetInstance().OpenDeviceBySN(mSerialNo, GX_ACCESS_MODE.GX_ACCESS_EXCLUSIVE); if (_gxDevice == null) { Log.Error("打开相机设备失败"); return false; } // 获取属性控制器(文档6.5.4/6.5.5节) _remoteFeatureCtrl = _gxDevice.GetRemoteFeatureControl(); if (_remoteFeatureCtrl == null) { Log.Error("获取远端设备属性控制器失败"); DisConnectDev(); return false; } // 打开流对象(文档6.5.3节,流索引0) if (_gxDevice.GetStreamCount() > 0) { _gxStream = _gxDevice.OpenStream(0); if (_gxStream == null) { Log.Error("创建流对象失败"); DisConnectDev(); return false; } _streamFeatureCtrl = _gxStream.GetFeatureControl(); } else { Log.Error("相机无可用流通道"); DisConnectDev(); return false; } // 千兆网相机优化:设置最优包长(文档2.11.1节) SetGigEOptimalPacketSize(); // 设置采集缓存个数(文档2.11.4节,建议≥3) _gxStream.SetAcqusitionBufferNumber(BUFFER_COUNT); // 获取相机基础参数 GetImageSizeAndPayload(); // 设置默认参数 SetSetting(); // 注册回调函数(文档2.11.3节) _gxStream.RegisterCaptureCallback(this, frameCallbackFunc); // 开始抓图 StartGrab(); mConnected = true; Log.Addlog(2, $"大恒相机({mSerialNo})连接成功"); return true; } catch (CGalaxyException ex) { Log.Error($"相机连接失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); DisConnectDev(); } catch (Exception ex) { Log.Error($"相机连接异常:{ex.Message}"); DisConnectDev(); } return false; } /// <summary> /// 断开相机(文档2.9节,释放资源流程) /// </summary> public override void DisConnectDev() { try { // 停止抓图 if (_isGrabbing) { StopGrab(); } // 注销回调函数(文档6.14.4节) _gxStream?.UnregisterCaptureCallback(); // 关闭流对象(文档6.14.12节) if (_gxStream != null) { _gxStream.Close(); _gxStream = null; } // 关闭设备(文档6.5.19节) if (_gxDevice != null) { _gxDevice.Close(); _gxDevice = null; } // 释放属性控制器 _remoteFeatureCtrl = null; _streamFeatureCtrl = null; mConnected = false; Log.Addlog(2, "大恒相机已断开连接"); } catch (CGalaxyException ex) { Log.Error($"断开连接失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } catch (Exception ex) { Log.Error($"断开连接异常:{ex.Message}"); } } /// <summary> /// 开始抓图(文档2.11.3节,先开启流采集再发送开采命令) /// </summary> public override void StartGrab() { try { if (_gxStream == null || _isGrabbing) { return; } // 开启流层采集(文档6.14.1节) _gxStream.StartGrab(); // 发送开采命令(文档2.10.6.7节) var startCmd = _remoteFeatureCtrl.GetCommandFeature("AcquisitionStart"); startCmd?.Execute(); _isGrabbing = true; Log.Addlog(2, "大恒相机开始抓图"); } catch (CGalaxyException ex) { Log.Error($"开始抓图失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 停止抓图(文档2.11.3节) /// </summary> public override void StopGrab() { try { if (_gxStream == null || !_isGrabbing) { return; } // 发送停采命令(文档2.10.6.7节) var stopCmd = _remoteFeatureCtrl.GetCommandFeature("AcquisitionStop"); stopCmd?.Execute(); // 停止流层采集(文档6.14.2节) _gxStream.StopGrab(); // 清空采集队列(文档6.14.10节) _gxStream.FlushQueue(); _isGrabbing = false; Log.Addlog(2, "大恒相机已停止抓图"); } catch (CGalaxyException ex) { Log.Error($"停止抓图失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 连续采图模式(内触发,文档2.11.3节) /// </summary> public override void ContinuesMode() { try { if (!mConnected) { Log.Error("相机未连接,无法切换连续模式"); return; } SetTriggerMode(TrigMode.内触发); Log.Addlog(2, "大恒相机已切换为连续采图模式"); } catch (Exception ex) { Log.Error($"切换连续模式异常:{ex.Message}"); } } /// <summary> /// 采集图像(软触发,文档2.11.2节) /// </summary> public override async Task<bool> CaptureImageAsync(bool byHand) { bool result = false; try { if (!mConnected) { Log.Error("相机未连接,无法采集图像"); return false; } EventWait.Reset(); // 设置软触发模式 if (!SetTriggerMode(TrigMode.软触发)) { Log.Error("设置软触发模式失败"); return false; } // 发送软触发命令(文档2.10.6.7节) var trigCmd = _remoteFeatureCtrl.GetCommandFeature("TriggerSoftware"); trigCmd?.Execute(); // 异步延迟,避免线程阻塞 await Task.Delay(10); // 等待采集完成 result = EventWait.WaitOne(TIMEOUT_MILLISECONDS); } catch (CGalaxyException ex) { Log.Error($"采集图像失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); result = false; } catch (Exception ex) { Log.Error($"采集图像异常:{ex.Message}"); result = false; } return result; } /// <summary> /// 加载参数配置(文档2.14节) /// </summary> public override void LoadSetting(string filePath) { try { if (!mConnected || !File.Exists(filePath)) { Log.Error("相机未连接或配置文件不存在"); return; } // 导入配置文件(文档6.5.16节) _gxDevice.importConfigFile(filePath); Log.Addlog(2, $"成功加载配置文件:{filePath}"); // 重新获取参数 GetSetting(); } catch (CGalaxyException ex) { Log.Error($"加载配置失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 保存参数配置(文档2.14节) /// </summary> public override void SaveSetting(string filePath) { try { if (!mConnected) { Log.Error("相机未连接,无法保存配置"); return; } // 导出配置文件(文档6.5.15节) _gxDevice.ExportConfigFile(filePath); Log.Addlog(2, $"成功保存配置文件:{filePath}"); } catch (CGalaxyException ex) { Log.Error($"保存配置失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 设置相机参数(文档2.10节) /// </summary> public override void SetSetting() { try { if (!mConnected) { return; } // 设置采集模式为连续(文档2.10.6.5节) var acquisitionMode = _remoteFeatureCtrl.GetEnumFeature("AcquisitionMode"); if (acquisitionMode != null && _remoteFeatureCtrl.IsWritable("AcquisitionMode")) { acquisitionMode.SetValue(ACQUISITION_MODE_CONTINUOUS); } // 设置触发模式 SetTriggerMode((TrigMode)mTrigMode); // 设置曝光时间(文档2.10.6.3节) var exposeFeature = _remoteFeatureCtrl.GetFloatFeature("ExposureTime"); if (exposeFeature != null && _remoteFeatureCtrl.IsWritable("ExposureTime")) { exposeFeature.SetValue(mExposeTime); } // 设置增益(文档2.10.6.3节) var gainFeature = _remoteFeatureCtrl.GetFloatFeature("Gain"); if (gainFeature != null && _remoteFeatureCtrl.IsWritable("Gain")) { gainFeature.SetValue(mGain); } } catch (CGalaxyException ex) { Log.Error($"设置参数失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 获取相机参数(文档2.10节) /// </summary> public override void GetSetting() { try { if (!mConnected) { return; } // 获取曝光时间(文档2.10.6.3节) _remoteFeatureCtrl = _gxDevice.GetRemoteFeatureControl(); // 获取属性控制器 var exposeFeature = _remoteFeatureCtrl.GetFloatFeature("ExposureTime"); if (exposeFeature != null && _remoteFeatureCtrl.IsReadable("ExposureTime")) { mExposeTime = (float)exposeFeature.GetValue(); } // 获取增益(文档2.10.6.3节) var gainFeature = _remoteFeatureCtrl.GetFloatFeature("Gain"); if (gainFeature != null && _remoteFeatureCtrl.IsReadable("Gain")) { mGain = (float)gainFeature.GetValue(); } // 获取帧率(文档2.10.6.3节) var frameRateFeature = _remoteFeatureCtrl.GetFloatFeature("AcquisitionframeRate"); if (frameRateFeature != null && _remoteFeatureCtrl.IsReadable("AcquisitionframeRate")) { mframerate = frameRateFeature.GetValue().ToString("F2"); } // 获取触发模式(文档2.10.6.5节) var trigModeFeature = _remoteFeatureCtrl.GetEnumFeature("TriggerMode"); if (trigModeFeature != null && _remoteFeatureCtrl.IsReadable("TriggerMode")) { mTrigMode = trigModeFeature.GetEnumValue().nCurValue == TRIGGER_MODE_OFF ? TrigMode.内触发 : TrigMode.软触发; } // 获取图像宽高(文档2.10.6.2节) // 正确:通过IGXFeatureControl调用IsWritable()判断属性是否可写 var widthFeature = _remoteFeatureCtrl.GetIntFeature("Width"); if (widthFeature != null && _remoteFeatureCtrl.IsReadable("Width")) { mWidth = (int)widthFeature.GetValue(); mWidthMax = (int)widthFeature.GetMax(); } var heightFeature = _remoteFeatureCtrl.GetIntFeature("Height"); if (heightFeature != null && _remoteFeatureCtrl.IsReadable("Height")) { mHeight = (int)heightFeature.GetValue(); mHeightMax = (int)heightFeature.GetMax(); } } catch (CGalaxyException ex) { Log.Error($"获取参数失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 相机参数变更(文档2.10节) /// </summary> public override void CameraChanged(ChangType changTyp) { try { if (!mConnected) { return; } switch (changTyp) { case ChangType.增益: SetGain(mGain); break; case ChangType.曝光: SetExposureTime(mExposeTime); break; case ChangType.宽度: SetImageWidth(mWidth); break; case ChangType.高度: SetImageHeight(mHeight); break; case ChangType.触发: SetTriggerMode((TrigMode)mTrigMode); break; } } catch (Exception ex) { Log.Error($"参数变更异常:{ex.Message}"); } } /// <summary> /// 设置触发模式(文档2.10.6.5节) /// </summary> public override bool SetTriggerMode(TrigMode mode) { try { if (!mConnected) { return false; } // 设置触发模式开关 var trigModeFeature = _remoteFeatureCtrl.GetEnumFeature("TriggerMode"); if (trigModeFeature == null || !_remoteFeatureCtrl.IsWritable("TriggerMode")) { Log.Error("触发模式功能不支持"); return false; } trigModeFeature.SetEnumValue(mode == TrigMode.内触发 ? TRIGGER_MODE_OFF : TRIGGER_MODE_ON); // 设置触发源和触发方式 var trigSourceFeature = _remoteFeatureCtrl.GetEnumFeature("TriggerSource"); var trigActivationFeature = _remoteFeatureCtrl.GetEnumFeature("TriggerActivation"); if (trigSourceFeature == null || !_remoteFeatureCtrl.IsWritable("TriggerSource")) { Log.Error("触发源功能不支持"); return false; } switch (mode) { case TrigMode.内触发: break; case TrigMode.软触发: trigSourceFeature.SetEnumValue(TRIGGER_SOURCE_SOFTWARE); break; case TrigMode.上升沿: trigSourceFeature.SetEnumValue(TRIGGER_SOURCE_LINE0); if (trigActivationFeature != null && _remoteFeatureCtrl.IsWritable("TriggerActivation")) { trigActivationFeature.SetEnumValue(TRIGGER_ACTIVATION_RISING); } break; case TrigMode.下降沿: trigSourceFeature.SetEnumValue(TRIGGER_SOURCE_LINE0); if (trigActivationFeature != null && _remoteFeatureCtrl.IsWritable("TriggerActivation")) { trigActivationFeature.SetEnumValue(TRIGGER_ACTIVATION_FALLING); } break; } return true; } catch (CGalaxyException ex) { Log.Error($"设置触发模式失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); return false; } } #endregion #region 辅助方法(严格遵循官方文档API使用规范) /// <summary> /// 根据序列号查找相机(文档6.4节) /// </summary> private bool FindCameraBySN(string serialNo) { try { _deviceInfoList.Clear(); IGXFactory.GetInstance().UpdateAllDeviceList(200, _deviceInfoList); foreach (var deviceInfo in _deviceInfoList) { if (deviceInfo.GetSN().Equals(serialNo, StringComparison.OrdinalIgnoreCase)) { _curDeviceInfo = deviceInfo; return true; } } return false; } catch (CGalaxyException ex) { Log.Error($"查找相机失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); return false; } } /// <summary> /// 获取设备类型描述(文档3.3节,GX_DEVICE_CLASS_LIST) /// </summary> private string GetDeviceClassDesc(GX_DEVICE_CLASS_LIST deviceClass) { string device = ""; switch (deviceClass) { case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_UNKNOWN: device = "Unknown"; break; case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_USB2: device = "GX_DEVICE_CLASS_USB2"; break; case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_GEV: device = "GEV"; break; case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_U3V: device = "U3V"; break; case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_SMART: device = "MART"; break; case GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_CXP: device = "CXP"; break; default: device = "Unknown"; break; } return device; } /// <summary> /// 千兆网相机设置最优包长(文档2.11.1节) /// </summary> private void SetGigEOptimalPacketSize() { try { if (_curDeviceInfo.GetDeviceClass() != GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_GEV) { return; } // 检查是否支持GevSCPSPacketSize功能 if (_remoteFeatureCtrl.IsImplemented("GevSCPSPacketSize") && _remoteFeatureCtrl.IsWritable("GevSCPSPacketSize")) { // 获取最优包长 uint optimalPacketSize = _gxStream.GetOptimalPacketSize(); // 设置包长 var packetSizeFeature = _remoteFeatureCtrl.GetIntFeature("GevSCPSPacketSize"); packetSizeFeature?.SetValue(optimalPacketSize); Log.Addlog(2, $"千兆网相机最优包长设置为:{optimalPacketSize}"); } } catch (CGalaxyException ex) { Log.Error($"设置最优包长失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 获取图像宽高和有效载荷(文档6.14.14节) /// </summary> private void GetImageSizeAndPayload() { try { // 获取有效载荷大小 _payloadSize = (uint)_gxStream.GetPayloadSize(); // 获取图像宽高 var widthFeature = _remoteFeatureCtrl.GetIntFeature("Width"); var heightFeature = _remoteFeatureCtrl.GetIntFeature("Height"); if (widthFeature != null && heightFeature != null) { mWidth = (int)widthFeature.GetValue(); mHeight = (int)heightFeature.GetValue(); mWidthMax = (int)widthFeature.GetMax(); mHeightMax = (int)heightFeature.GetMax(); } // 计算行步长(文档2.11.5节,根据像素格式) var pixelFormatFeature = _remoteFeatureCtrl.GetEnumFeature("PixelFormat"); if (pixelFormatFeature != null) { GX_PIXEL_FORMAT_ENTRY pixelFormat = (GX_PIXEL_FORMAT_ENTRY)pixelFormatFeature.GetEnumValue().nCurValue; _rowStep = (uint)mWidth * GetPixelByteCount(pixelFormat); } } catch (CGalaxyException ex) { Log.Error($"获取图像参数失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 获取像素格式对应的字节数(文档3.6节,GX_PIXEL_FORMAT_ENTRY) /// </summary> private uint GetPixelByteCount(GX_PIXEL_FORMAT_ENTRY pixelFormat) { uint format = 0; switch (pixelFormat) { case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_UNDEFINED: format = 0; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8: format = 1; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8_SIGNED: format = 1; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10: format = 2; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10_P: format = 2; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12: format = 3; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12_P: format = 3; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO14: format = 4; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO14_P: format = 4; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO16: format = 5; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG8: format = 6; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10: format = 7; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10_P: format = 7; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12: format = 8; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12_P: format = 8; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG14: format = 9; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG14_P: format = 9; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG16: format = 10; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB8: format = 11; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10: format = 12; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10_P: format = 12; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12: format = 13; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12_P: format = 13; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB14: format = 14; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB14_P: format = 14; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB16: format = 15; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR8: format = 16; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10: format = 17; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10_P: format = 17; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12: format = 18; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12_P: format = 18; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR14: format = 19; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR14_P: format = 19; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR16: format = 20; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGBA8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB10_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB12_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB16_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGRA8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_R8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_G8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_B8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_ABC32F: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_ABC32F_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV422_8_UYVY: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV420_8_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_ARGB8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_ABGR8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_I16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_S16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_I16_S16: break; default: break; } return format; } /// <summary> /// 设置增益(文档2.10.6.3节) /// </summary> private void SetGain(float gain) { try { var gainFeature = _remoteFeatureCtrl.GetFloatFeature("Gain"); if (gainFeature != null && _remoteFeatureCtrl.IsWritable("Gain")) { gainFeature.SetValue(gain); mGain = gain; } } catch (CGalaxyException ex) { Log.Error($"设置增益失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 设置曝光时间(文档2.10.6.3节) /// </summary> private void SetExposureTime(float exposeTime) { try { var exposeFeature = _remoteFeatureCtrl.GetFloatFeature("ExposureTime"); if (exposeFeature != null && _remoteFeatureCtrl.IsWritable("ExposureTime")) { exposeFeature.SetValue(exposeTime); mExposeTime = exposeTime; } } catch (CGalaxyException ex) { Log.Error($"设置曝光时间失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 设置图像宽度(文档2.10.6.2节) /// </summary> private void SetImageWidth(int width) { try { if (width <= 0 || width > mWidthMax) { Log.Error($"宽度值{width}超出范围(0-{mWidthMax})"); return; } // 正确:通过IGXFeatureControl调用IsWritable()判断属性是否可写 IGXFeatureControl featureCtrl = _gxDevice.GetRemoteFeatureControl(); // 获取属性控制器 var widthFeature = _remoteFeatureCtrl.GetIntFeature("Width"); if (widthFeature != null && featureCtrl.IsWritable("Width")) { widthFeature.SetValue(width); mWidth = width; // 重新计算行步长 GetImageSizeAndPayload(); } } catch (CGalaxyException ex) { Log.Error($"设置宽度失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 设置图像高度(文档2.10.6.2节) /// </summary> private void SetImageHeight(int height) { try { if (height <= 0 || height > mHeightMax) { Log.Error($"高度值{height}超出范围(0-{mHeightMax})"); return; } // 正确:通过IGXFeatureControl调用IsWritable()判断属性是否可写 IGXFeatureControl featureCtrl = _gxDevice.GetRemoteFeatureControl(); // 获取属性控制器 var heightFeature = _remoteFeatureCtrl.GetIntFeature("Height"); if (heightFeature != null && featureCtrl.IsWritable("Height")) { heightFeature.SetValue(height); mHeight = height; // 重新计算行步长 GetImageSizeAndPayload(); } } catch (CGalaxyException ex) { Log.Error($"设置高度失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); } } /// <summary> /// 帧回调函数(文档2.11.3节,使用IframeData接口) /// </summary> private void frameCallbackFunc(object objUserParam, IframeData frameData) { IntPtr pConvertedBuffer = IntPtr.Zero; try { // 检查帧状态(文档6.15.1节) if (frameData.GetStatus() != GX_frame_STATUS_LIST.GX_frame_STATUS_SUCCESS) { Log.Error($"接收无效帧,状态码:{frameData.GetStatus()}"); return; } // 获取帧信息(文档6.15节) uint width = (uint)frameData.GetWidth(); uint height = (uint)frameData.GetHeight(); GX_PIXEL_FORMAT_ENTRY pixelFormat = frameData.GetPixelFormat(); // 图像格式转换(文档6.18节) pConvertedBuffer = ConvertImageFormat(frameData, pixelFormat, width, height); if (pConvertedBuffer == IntPtr.Zero) { Log.Error("图像格式转换失败"); return; } // 转换为VisionPro图像 Image = ConvertToCogImage(pConvertedBuffer, width, height, pixelFormat); if (Image != null) { ImageGrab?.Invoke(Image); } EventWait.Set(); } catch (CGalaxyException ex) { Log.Error($"帧回调失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); EventWait.Set(); } catch (Exception ex) { Log.Error($"帧回调异常:{ex.Message}"); EventWait.Set(); } finally { // 释放转换缓冲区 if (pConvertedBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(pConvertedBuffer); pConvertedBuffer = IntPtr.Zero; } } } /// <summary> /// 图像格式转换(文档6.18节,IGXImageFormatConvert接口) /// </summary> private IntPtr ConvertImageFormat(IframeData frameData, GX_PIXEL_FORMAT_ENTRY srcFormat, uint width, uint height) { try { // 创建格式转换对象(文档6.1.13节) IGXImageFormatConvert convertor = IGXFactory.GetInstance().CreateImageFormatConvert(); if (convertor == null) { return IntPtr.Zero; } // 设置目标格式(优先转为Mono8或RGB8) GX_PIXEL_FORMAT_ENTRY dstFormat = IsMonoPixelFormat(srcFormat) ? GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8 : GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8; convertor.SetDstFormat(dstFormat); convertor.SetInterpolationType(GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR); // 设置有效位(文档6.18.12节) if (!IsMono8OrRgb8(srcFormat)) { convertor.SetValidBits(GetDefaultValidBits(srcFormat)); } // 计算目标缓冲区大小(文档6.18.8节) ulong bufferSize = convertor.GetBufferSizeForConversion(width, height, srcFormat); IntPtr dstBuffer = Marshal.AllocHGlobal((int)bufferSize); if (dstBuffer == IntPtr.Zero) { return IntPtr.Zero; } // 执行转换(文档6.18.11节) convertor.Convert(frameData.GetBuffer(), width, height, srcFormat, dstBuffer, bufferSize, false); return dstBuffer; } catch (CGalaxyException ex) { Log.Error($"格式转换失败:错误码{ex.GetErrorCode()},描述:{ex.Message}"); return IntPtr.Zero; } } /// <summary> /// 判断是否为Mono像素格式(文档3.6节) /// </summary> private bool IsMonoPixelFormat(GX_PIXEL_FORMAT_ENTRY pixelFormat) { return pixelFormat.ToString().StartsWith("GX_PIXEL_FORMAT_MONO"); } /// <summary> /// 判断是否为Mono8或RGB8格式 /// </summary> private bool IsMono8OrRgb8(GX_PIXEL_FORMAT_ENTRY pixelFormat) { return pixelFormat == GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8 || pixelFormat == GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8; } /// <summary> /// 获取默认有效位(文档3.10节,GX_VALID_BIT_LIST) /// </summary> private GX_VALID_BIT_LIST GetDefaultValidBits(GX_PIXEL_FORMAT_ENTRY pixelFormat) { GX_VALID_BIT_LIST gX_VALID_BIT_LIST = new GX_VALID_BIT_LIST(); switch (pixelFormat) { case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_UNDEFINED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8_SIGNED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10: gX_VALID_BIT_LIST = GX_VALID_BIT_LIST.GX_BIT_2_9; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12: gX_VALID_BIT_LIST = GX_VALID_BIT_LIST.GX_BIT_4_11; break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG14_P: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGBA8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB8_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB10_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB12_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_RGB16_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGRA8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR10: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR12: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR14: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BGR16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_R8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_G8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_B8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_ABC32F: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_ABC32F_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR601_422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YCBCR709_422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV444_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV411_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV422_8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV422_8_UYVY: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12_PACKED: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_YUV420_8_PLANAR: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_ARGB8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_ABGR8: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_I16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_S16: break; case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_COORD3D_C16_I16_S16: break; default: break; } return gX_VALID_BIT_LIST; } /// <summary> /// 转换为VisionPro ICogImage(文档2.11.5节) /// </summary> private ICogImage ConvertToCogImage(IntPtr pBuffer, uint width, uint height, GX_PIXEL_FORMAT_ENTRY pixelFormat) { try { if (IsMonoPixelFormat(pixelFormat) || pixelFormat == GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8) { // Mono8图像 CogImage8Root greyRoot = new CogImage8Root(); greyRoot.Initialize((int)width, (int)height, pBuffer, (int)width, null); CogImage8Grey greyImage = new CogImage8Grey(); greyImage.SetRoot(greyRoot); return greyImage.ScaleImage((int)width, (int)height); } else { // RGB8图像 CogImage8Root rRoot = new CogImage8Root(); CogImage8Root gRoot = new CogImage8Root(); CogImage8Root bRoot = new CogImage8Root(); // 分离RGB通道 byte[] rgbData = new byte[width * height * 3]; Marshal.Copy(pBuffer, rgbData, 0, rgbData.Length); byte[] rData = new byte[width * height]; byte[] gData = new byte[width * height]; byte[] bData = new byte[width * height]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int index = (int)(i * width * 3 + j * 3); rData[i * width + j] = rgbData[index]; gData[i * width + j] = rgbData[index + 1]; bData[i * width + j] = rgbData[index + 2]; } } // 初始化RGB通道 rRoot.Initialize((int)width, (int)height, Marshal.UnsafeAddrOfPinnedArrayElement(rData, 0), (int)width, null); gRoot.Initialize((int)width, (int)height, Marshal.UnsafeAddrOfPinnedArrayElement(gData, 0), (int)width, null); bRoot.Initialize((int)width, (int)height, Marshal.UnsafeAddrOfPinnedArrayElement(bData, 0), (int)width, null); CogImage24PlanarColor colorImage = new CogImage24PlanarColor(); colorImage.SetRoots(rRoot, gRoot, bRoot); return colorImage.ScaleImage((int)width, (int)height); } } catch (Exception ex) { Log.Error($"转换VisionPro图像失败:{ex.Message}"); return null; } } #endregion #region IDisposable实现(文档2.3节,SDK反初始化) public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) { return; } // 释放托管资源 if (disposing) { EventWait?.Close(); EventWait = null; } // 释放非托管资源 DisConnectDev(); // 反初始化SDK(文档2.3节) IGXFactory.GetInstance().Uninit(); _disposed = true; } #endregion }}二、相机工厂生成相机
using Common.Camera;namespace Common.Factorys{ public static class CameraFactory { public static Camerasbase CreateCamera(CameraType type) { Camerasbase camerasbase = null; switch (type) { case CameraType.Basler: camerasbase = new CamerasHikvision(); break; case CameraType.Hikvision: camerasbase = new CamerasHikvision(); break; case CameraType.Dahua: camerasbase = new CamerasHikvision(); break; case CameraType.Daheng: camerasbase = new CamerasHikvision(); break; default: camerasbase = new CamerasHikvision(); break; } return camerasbase; } } public enum CameraType { Basler, Hikvision, Dahua, Daheng }}三、程序调用
四、界面展示
代码说明
架构一致性:完全沿用代码架构(抽象基类Camerasbase、相机信息类CamerasInfo、触发模式枚举等),降低迁移成本。
核心功能完整:实现了大恒相机的搜索、连接、断开、抓图、参数设置 / 获取、配置加载 / 保存等全部核心功能。
资源优化:
实现IDisposable接口,确保托管 / 非托管资源(相机设备、流对象、事件句柄)安全释放。
使用try/finally确保非托管内存(Marshal.AllocHGlobal)不泄漏。
自动释放帧缓存(m_gxStream.ReleaseBuffer),避免 SDK 资源占用。
逻辑严谨:
替换魔法值为常量,提高代码可读性。
增加空值判断和参数校验,避免空引用异常。
正确计算行步长(宽度 × 像素字节数),解决图像畸变问题。
兼容性强:支持大恒千兆网(GigE)和USB3.0相机,兼容 VisionPro 图像格式转换。
错误处理:统一使用Log类输出日志支持非 UI 场景运行。
