| | |
| | | using MVSDK_Net; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Diagnostics; |
| | | using System.Drawing; |
| | | using System.Drawing.Imaging; |
| | | using System.Linq; |
| | |
| | | private Thread _grabThread; // 图像采集线程 |
| | | private bool _isGrabbing; // 采集状态标志 |
| | | private bool _threadRunning; // 线程运行标志 |
| | | private bool _handleCreated = false; // 句柄是否已创建 |
| | | private Thread _callbackThread; // 回调处理线程 |
| | | private List<IMVDefine.IMV_Frame> _frameList; // 图像缓存列表 |
| | | private readonly object _frameLock = new object(); // 帧缓存锁 |
| | |
| | | else |
| | | { |
| | | // 记录日志或抛出异常 |
| | | throw new Exception($"枚举设备失败,错误码:{result}"); |
| | | //throw new Exception($"枚举设备失败,错误码:{result}"); |
| | | Debug.WriteLine("枚举设备失败!"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | |
| | | { |
| | | try |
| | | { |
| | | // 如果相机已打开,先关闭 |
| | | if (_camera != null && _camera.IMV_IsOpen()) |
| | | // 确保彻底关闭和清理 |
| | | if (_camera != null) |
| | | { |
| | | CloseDevice(); |
| | | if (_camera.IMV_IsOpen()) |
| | | { |
| | | _camera.IMV_Close(); |
| | | } |
| | | |
| | | if (_handleCreated) |
| | | { |
| | | _camera.IMV_DestroyHandle(); |
| | | _handleCreated = false; |
| | | } |
| | | } |
| | | |
| | | // 枚举设备并匹配SN |
| | |
| | | { |
| | | throw new Exception($"创建设备句柄失败,错误码:{result}"); |
| | | } |
| | | _handleCreated = true; |
| | | |
| | | // 打开设备 |
| | | result = _camera.IMV_Open(); |
| | |
| | | } |
| | | |
| | | // 设置设备属性 |
| | | SN = SN; |
| | | this.SN = SN; |
| | | isGrabbing = false; |
| | | |
| | | // 设置缓存个数为8 |
| | |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"关闭相机失败,错误码:{result}"); |
| | | } |
| | | } |
| | | |
| | | // 销毁句柄 |
| | | if (_handleCreated) |
| | | { |
| | | _camera.IMV_DestroyHandle(); |
| | | _handleCreated = false; |
| | | } |
| | | |
| | | // 释放资源 |
| | |
| | | throw new Exception("相机未打开"); |
| | | } |
| | | |
| | | string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw"; |
| | | |
| | | // 验证增益范围 |
| | | double minGain = 0, maxGain = 0; |
| | | int result = _camera.IMV_GetDoubleFeatureMin("GainRaw", ref minGain); |
| | | int result = _camera.IMV_GetDoubleFeatureMin(gainFeature, ref minGain); |
| | | if (result != IMVDefine.IMV_OK) |
| | | { |
| | | throw new Exception($"获取增益最小值失败,错误码:{result}"); |
| | | } |
| | | |
| | | result = _camera.IMV_GetDoubleFeatureMax("GainRaw", ref maxGain); |
| | | result = _camera.IMV_GetDoubleFeatureMax(gainFeature, ref maxGain); |
| | | if (result != IMVDefine.IMV_OK) |
| | | { |
| | | throw new Exception($"获取增益最大值失败,错误码:{result}"); |
| | | } |
| | | |
| | | if (gain < minGain || gain > maxGain) |
| | | { |
| | | throw new Exception($"增益值超出范围,有效范围:{minGain} - {maxGain}"); |
| | | } |
| | | if (gain < minGain) gain = minGain; |
| | | if (gain > maxGain) gain = maxGain; |
| | | |
| | | // 设置增益 |
| | | result = _camera.IMV_SetDoubleFeatureValue("GainRaw", gain); |
| | | result = _camera.IMV_SetDoubleFeatureValue(gainFeature, gain); |
| | | if (result != IMVDefine.IMV_OK) |
| | | { |
| | | throw new Exception($"设置增益失败,错误码:{result}"); |
| | |
| | | throw new Exception("相机未打开"); |
| | | } |
| | | |
| | | int result = _camera.IMV_GetDoubleFeatureValue("GainRaw", ref gain); |
| | | string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw"; |
| | | int result = _camera.IMV_GetDoubleFeatureValue(gainFeature, ref gain); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | catch (Exception ex) |
| | |
| | | } |
| | | } |
| | | |
| | | // 其他参数设置方法类似,这里省略部分实现... |
| | | |
| | | /// <summary> |
| | | /// 设置触发极性 |
| | | /// </summary> |
| | | /// <param name="polarity">触发极性</param> |
| | | /// <returns>是否成功</returns> |
| | | public override bool SetTriggerPolarity(TriggerPolarity polarity) |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | string activation = (polarity == TriggerPolarity.RisingEdge || polarity == TriggerPolarity.HighLevel) |
| | | ? "RisingEdge" : "FallingEdge"; |
| | | |
| | | int result = _camera.IMV_SetEnumFeatureSymbol("TriggerActivation", activation); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"设置触发极性失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取触发极性 |
| | | /// </summary> |
| | | /// <param name="polarity">触发极性</param> |
| | | /// <returns>是否成功</returns> |
| | | public override bool GetTriggerPolarity(out TriggerPolarity polarity) |
| | | { |
| | | polarity = TriggerPolarity.RisingEdge; |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | IMVDefine.IMV_String activation = new IMVDefine.IMV_String(); |
| | | int result = _camera.IMV_GetEnumFeatureSymbol("TriggerActivation", ref activation); |
| | | if (result == IMVDefine.IMV_OK) |
| | | { |
| | | polarity = activation.str == "RisingEdge" ? TriggerPolarity.RisingEdge : TriggerPolarity.FallingEdge; |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"获取触发极性失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置触发滤波时间 (us) |
| | | /// </summary> |
| | | public override bool SetTriggerFliter(double flitertime) |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | // 华睿相机通常使用 LineDebouncerTime 控制滤波 |
| | | if (_camera.IMV_FeatureIsValid("LineDebouncerTime")) |
| | | { |
| | | int result = _camera.IMV_SetDoubleFeatureValue("LineDebouncerTime", flitertime); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"设置触发滤波失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取触发滤波时间 (us) |
| | | /// </summary> |
| | | public override bool GetTriggerFliter(out double flitertime) |
| | | { |
| | | flitertime = 0; |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | if (_camera.IMV_FeatureIsValid("LineDebouncerTime")) |
| | | { |
| | | int result = _camera.IMV_GetDoubleFeatureValue("LineDebouncerTime", ref flitertime); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"获取触发滤波失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置触发延时 (us) |
| | | /// </summary> |
| | | public override bool SetTriggerDelay(double delay) |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | if (_camera.IMV_FeatureIsValid("TriggerDelay")) |
| | | { |
| | | int result = _camera.IMV_SetDoubleFeatureValue("TriggerDelay", delay); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"设置触发延时失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取触发延时 (us) |
| | | /// </summary> |
| | | public override bool GetTriggerDelay(out double delay) |
| | | { |
| | | delay = 0; |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | if (_camera.IMV_FeatureIsValid("TriggerDelay")) |
| | | { |
| | | int result = _camera.IMV_GetDoubleFeatureValue("TriggerDelay", ref delay); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"获取触发延时失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置信号线模式 |
| | | /// </summary> |
| | | public override bool SetLineMode(IOLines line, LineMode mode) |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | // 选择线路 |
| | | int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); |
| | | if (result != IMVDefine.IMV_OK) return false; |
| | | |
| | | // 设置模式 |
| | | string lineMode = mode == LineMode.Input ? "Input" : "Output"; |
| | | result = _camera.IMV_SetEnumFeatureSymbol("LineMode", lineMode); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"设置信号线模式失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置信号线电平状态 |
| | | /// </summary> |
| | | public override bool SetLineStatus(IOLines line, LineStatus linestatus) |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | // 仅对输出线路有效 |
| | | int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); |
| | | if (result != IMVDefine.IMV_OK) return false; |
| | | |
| | | bool status = linestatus == LineStatus.Hight; |
| | | result = _camera.IMV_SetBoolFeatureValue("UserOutputValue", status); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"设置信号线状态失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取信号线电平状态 |
| | | /// </summary> |
| | | public override bool GetLineStatus(IOLines line, out LineStatus lineStatus) |
| | | { |
| | | lineStatus = LineStatus.Low; |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); |
| | | if (result != IMVDefine.IMV_OK) return false; |
| | | |
| | | bool status = false; |
| | | result = _camera.IMV_GetBoolFeatureValue("LineStatus", ref status); |
| | | if (result == IMVDefine.IMV_OK) |
| | | { |
| | | lineStatus = status ? LineStatus.Hight : LineStatus.Low; |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"获取信号线状态失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 自动白平衡 |
| | | /// </summary> |
| | | public override bool AutoBalanceWhite() |
| | | { |
| | | try |
| | | { |
| | | if (_camera == null || !_camera.IMV_IsOpen()) return false; |
| | | |
| | | if (_camera.IMV_FeatureIsValid("BalanceWhiteAuto")) |
| | | { |
| | | int result = _camera.IMV_SetEnumFeatureSymbol("BalanceWhiteAuto", "Once"); |
| | | return result == IMVDefine.IMV_OK; |
| | | } |
| | | return false; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"自动白平衡失败:{ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 辅助方法 |
| | | |
| | | #region 采集和转换辅助方法 |
| | | |
| | | /// <summary> |
| | | /// 图像采集线程处理函数 |
| | | /// </summary> |
| | | private void GrabThreadProc() |
| | | { |
| | | IMVDefine.IMV_Frame frame = new IMVDefine.IMV_Frame(); |
| | | |
| | | |
| | | while (_threadRunning) |
| | | { |
| | | try |
| | | { |
| | | // 获取图像帧 |
| | | int result = _camera.IMV_GetFrame(ref frame, 100); |
| | | |
| | | |
| | | if (result == IMVDefine.IMV_OK) |
| | | { |
| | | // 处理图像帧 |
| | | ProcessFrame(frame); |
| | | } |
| | | else if (result != 0x80000001) // 超时错误代码(常见值) |
| | | else if (result != 0x80000001) // 超时错误代码 |
| | | { |
| | | // 非超时错误 |
| | | System.Diagnostics.Debug.WriteLine($"获取图像帧失败,错误码:{result}"); |
| | |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"采集线程异常:{ex.Message}"); |
| | | } |
| | | |
| | | |
| | | Thread.Sleep(1); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 处理图像帧 |
| | | /// </summary> |
| | |
| | | { |
| | | // 将图像数据转换为Bitmap |
| | | Bitmap bitmap = ConvertFrameToBitmap(frame); |
| | | |
| | | |
| | | if (bitmap != null) |
| | | { |
| | | // 触发图像采集事件 |
| | | CameraEventArgs args = new CameraEventArgs(SN, bitmap); |
| | | ImageGrabbed?.Invoke(this, args); |
| | | |
| | | |
| | | // 更新回调图像 |
| | | CallBackImg = bitmap; |
| | | } |
| | | |
| | | |
| | | // 释放帧 |
| | | _camera.IMV_ReleaseFrame(ref frame); |
| | | var tempFrame = frame; |
| | | _camera.IMV_ReleaseFrame(ref tempFrame); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | System.Diagnostics.Debug.WriteLine($"处理图像帧失败:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 将图像帧转换为Bitmap |
| | | /// </summary> |
| | |
| | | try |
| | | { |
| | | Bitmap bitmap = null; |
| | | |
| | | |
| | | switch (frame.frameInfo.pixelFormat) |
| | | { |
| | | case IMVDefine.IMV_EPixelType.gvspPixelMono8: |
| | |
| | | bitmap = ConvertToBGR8(frame); |
| | | break; |
| | | } |
| | | |
| | | |
| | | return bitmap; |
| | | } |
| | | catch (Exception ex) |
| | |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 创建Mono8格式Bitmap |
| | | /// </summary> |
| | | private Bitmap CreateMono8Bitmap(IMVDefine.IMV_Frame frame) |
| | | { |
| | | Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format8bppIndexed); |
| | | |
| | | |
| | | // 设置灰度调色板 |
| | | ColorPalette palette = bitmap.Palette; |
| | | for (int i = 0; i < 256; i++) |
| | |
| | | palette.Entries[i] = Color.FromArgb(i, i, i); |
| | | } |
| | | bitmap.Palette = palette; |
| | | |
| | | |
| | | // 复制图像数据 |
| | | BitmapData bmpData = bitmap.LockBits( |
| | | new Rectangle(0, 0, bitmap.Width, bitmap.Height), |
| | | ImageLockMode.WriteOnly, |
| | | bitmap.PixelFormat); |
| | | |
| | | |
| | | // 使用CopyMemory API进行内存复制 |
| | | CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height))); |
| | | |
| | | |
| | | bitmap.UnlockBits(bmpData); |
| | | return bitmap; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 创建BGR8格式Bitmap |
| | | /// </summary> |
| | | private Bitmap CreateBgr8Bitmap(IMVDefine.IMV_Frame frame) |
| | | { |
| | | Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format24bppRgb); |
| | | |
| | | |
| | | BitmapData bmpData = bitmap.LockBits( |
| | | new Rectangle(0, 0, bitmap.Width, bitmap.Height), |
| | | ImageLockMode.WriteOnly, |
| | | bitmap.PixelFormat); |
| | | |
| | | |
| | | // 使用CopyMemory API进行内存复制 |
| | | CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height))); |
| | | |
| | | |
| | | bitmap.UnlockBits(bmpData); |
| | | return bitmap; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 转换为BGR8格式 |
| | | /// </summary> |
| | |
| | | eDstPixelFormat = IMVDefine.IMV_EPixelType.gvspPixelBGR8, |
| | | nDstBufSize = frame.frameInfo.width * frame.frameInfo.height * 3 |
| | | }; |
| | | |
| | | |
| | | IntPtr dstBuffer = Marshal.AllocHGlobal((int)convertParam.nDstBufSize); |
| | | convertParam.pDstBuf = dstBuffer; |
| | | |
| | | |
| | | try |
| | | { |
| | | int result = _camera.IMV_PixelConvert(ref convertParam); |
| | |
| | | Marshal.FreeHGlobal(dstBuffer); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 从缓冲区创建BGR8 Bitmap |
| | | /// </summary> |
| | | private Bitmap CreateBgr8BitmapFromBuffer(IntPtr buffer, uint width, uint height) |
| | | { |
| | | Bitmap bitmap = new Bitmap((int)width, (int)height, PixelFormat.Format24bppRgb); |
| | | |
| | | |
| | | BitmapData bmpData = bitmap.LockBits( |
| | | new Rectangle(0, 0, bitmap.Width, bitmap.Height), |
| | | ImageLockMode.WriteOnly, |
| | | bitmap.PixelFormat); |
| | | |
| | | |
| | | // 使用CopyMemory API进行安全的内存复制 |
| | | CopyMemory(bmpData.Scan0, buffer, (uint)(width * height * 3)); |
| | | |
| | | |
| | | bitmap.UnlockBits(bmpData); |
| | | return bitmap; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 将触发源枚举转换为字符串 |
| | | /// </summary> |
| | |
| | | _ => "Software" |
| | | }; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 将字符串转换为触发源枚举 |
| | | /// </summary> |
| | |
| | | _ => TriggerSource.Software |
| | | }; |
| | | } |
| | | |
| | | |
| | | #endregion |
| | | |
| | | #region 未实现的抽象方法(简化版实现) |
| | | |
| | | public override bool SetTriggerPolarity(TriggerPolarity polarity) |
| | | { |
| | | // 华睿相机可能不支持该功能,返回成功 |
| | | return true; |
| | | } |
| | | |
| | | public override bool GetTriggerPolarity(out TriggerPolarity polarity) |
| | | { |
| | | polarity = TriggerPolarity.RisingEdge; |
| | | return true; |
| | | } |
| | | |
| | | public override bool SetTriggerFliter(double flitertime) |
| | | { |
| | | // 暂不支持 |
| | | return true; |
| | | } |
| | | |
| | | public override bool GetTriggerFliter(out double flitertime) |
| | | { |
| | | flitertime = 0; |
| | | return true; |
| | | } |
| | | |
| | | public override bool SetTriggerDelay(double delay) |
| | | { |
| | | // 暂不支持 |
| | | return true; |
| | | } |
| | | |
| | | public override bool GetTriggerDelay(out double delay) |
| | | { |
| | | delay = 0; |
| | | return true; |
| | | } |
| | | |
| | | public override bool SetLineMode(IOLines line, LineMode mode) |
| | | { |
| | | // 暂不支持 |
| | | return true; |
| | | } |
| | | |
| | | public override bool SetLineStatus(IOLines line, LineStatus linestatus) |
| | | { |
| | | // 暂不支持 |
| | | return true; |
| | | } |
| | | |
| | | public override bool GetLineStatus(IOLines line, out LineStatus lineStatus) |
| | | { |
| | | lineStatus = LineStatus.Low; |
| | | return true; |
| | | } |
| | | |
| | | public override bool AutoBalanceWhite() |
| | | { |
| | | // 暂不支持自动白平衡 |
| | | return true; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region IDisposable实现 |
| | | |