using HalconDotNet;
using LB_SmartVisionCommon;
using LB_VisionProcesses.Cameras.LBCameras;
using MVSDK_Net;
using Sunny.UI;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LB_VisionProcesses.Cameras.HRCameras
{
///
/// 华睿2D工业相机实现类
/// 基于MVSDK_Net SDK封装
///
public class HRCamera : BaseCamera
{
#region 私有字段
private MyCamera _camera; // 华睿相机设备对象
private Thread _grabThread; // 图像采集线程
private bool _isGrabbing; // 采集状态标志
private bool _threadRunning; // 线程运行标志
private bool _handleCreated = false; // 句柄是否已创建
private Thread _callbackThread; // 回调处理线程
private List _frameList; // 图像缓存列表
private readonly object _frameLock = new object(); // 帧缓存锁
// 新增:CollectedImages操作锁,保证线程安全
private readonly object _collectedImagesLock = new object();
private IMVDefine.IMV_EPixelType type = IMVDefine.IMV_EPixelType.gvspPixelMono8;
private IMVDefine.IMV_PixelConvertParam stPixelConvertParam = new IMVDefine.IMV_PixelConvertParam();
HObject Hobj = new HObject();
IntPtr pTemp = IntPtr.Zero;
IntPtr pConvertDstBuffer = IntPtr.Zero;
int nConvertBufSize = 0;
private IMVDefine.IMV_FrameCallBack frameCallBack;
// CopyMemory API声明
[System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
#endregion
#region 构造函数和析构函数
///
/// 构造函数
///
public HRCamera()
{
_camera = new MyCamera();
_frameList = new List();
Brand = CameraBrand.HRCamera;
_isGrabbing = false;
_threadRunning = false;
}
///
/// 析构函数
///
~HRCamera()
{
Dispose(false);
}
#endregion
#region 设备管理操作
///
/// 获取相机SN枚举列表
///
/// 相机SN列表
public override List GetListEnum()
{
List cameraList = new List();
try
{
// 枚举所有设备
IMVDefine.IMV_DeviceList deviceList = new IMVDefine.IMV_DeviceList();
IMVDefine.IMV_EInterfaceType interfaceType = IMVDefine.IMV_EInterfaceType.interfaceTypeAll;
int result = MyCamera.IMV_EnumDevices(ref deviceList, (uint)interfaceType);
if (result == 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));
string cameraInfo = $"{deviceInfo.cameraName}[{deviceInfo.serialNumber}]";
if (!cameraInfo.Contains("L"))
{
cameraList.Add(cameraInfo);
}
}
}
else
{
// 记录日志或抛出异常
//throw new Exception($"枚举设备失败,错误码:{result}");
AsyncLogHelper.Error("枚举设备失败!");
System.Diagnostics.Debug.WriteLine("枚举设备失败!");
}
}
catch (Exception ex)
{
// 记录错误日志
System.Diagnostics.Debug.WriteLine($"获取相机列表失败:{ex.Message}");
AsyncLogHelper.Error($"获取相机列表失败:{ex.Message}");
throw;
}
return cameraList;
}
///
/// 初始化相机设备
///
/// 相机序列号
/// 窗口句柄(可选)
/// 初始化是否成功
public override bool InitDevice(string SN, object Handle = null)
{
try
{
// 确保彻底关闭和清理
if (_camera != null)
{
if (_camera.IMV_IsOpen())
{
_camera.IMV_Close();
}
if (_handleCreated)
{
_camera.IMV_DestroyHandle();
_handleCreated = false;
}
}
// 枚举设备并匹配SN
List cameraList = GetListEnum();
int cameraIndex = -1;
for (int i = 0; i < cameraList.Count; i++)
{
if (cameraList[i].Contains(SN))
{
cameraIndex = i;
break;
}
}
if (cameraIndex == -1)
{
AsyncLogHelper.Error($"未找到序列号为 {SN} 的相机");
throw new Exception($"未找到序列号为 {SN} 的相机");
}
// 创建设备句柄
int result = _camera.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByIndex, cameraIndex);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error($"创建设备句柄失败,错误码:{result}");
throw new Exception($"创建设备句柄失败,错误码:{result}");
}
_handleCreated = true;
// 打开设备
result = _camera.IMV_Open();
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error($"打开相机失败,错误码:{result}");
throw new Exception($"打开相机失败,错误码:{result}");
}
// 设置设备属性
this.SN = SN;
isGrabbing = false;
// 注册数据帧回调函数
// Register data frame callback function
frameCallBack = onGetFrame;
int res = _camera.IMV_AttachGrabbing(frameCallBack, IntPtr.Zero);
if (res != IMVDefine.IMV_OK)
{
System.Diagnostics.Debug.WriteLine("Attach grabbing failed! ErrorCode:[{0}]", res);
AsyncLogHelper.Error("Attach grabbing failed! ErrorCode:[{0}]" + res);
}
// 设置缓存个数为12
_camera.IMV_SetBufferCount(12);
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"初始化相机失败:{ex.Message}");
AsyncLogHelper.Error($"初始化相机失败:{ex.Message}");
return false;
}
}
// 数据帧回调函数
// Data frame callback function
private void onGetFrame(ref IMVDefine.IMV_Frame frame, IntPtr pUser)
{
if (frame.frameHandle == IntPtr.Zero)
{
AsyncLogHelper.Info(SN + "frame is NULL");
return;
}
try
{
// 1. 将帧转换为Bitmap
Bitmap bitmap = ConvertFrameToBitmap(frame);
// 释放原始帧数据(SDK层面释放)
_camera.IMV_ReleaseFrame(ref frame);
// 2. 空值校验:转换失败则直接返回
if (bitmap == null)
{
AsyncLogHelper.Warn(SN + "帧转换为Bitmap失败,跳过处理");
return;
}
// 3. 获取/创建线程安全队列
var queue = CollectedImages.GetOrAdd(SN, new ConcurrentQueue());
AsyncLogHelper.Info(SN + $"图像已加入缓存,当前缓存数量:{CollectedImages[SN].Count}");
// 4. 队列限流,防止内存溢出
if (queue.Count >= MAX_QUEUE_CAPACITY)
{
if (queue.TryDequeue(out Bitmap old))
{
old.Dispose(); // 丢弃最旧帧,释放内存
AsyncLogHelper.Warn($"HRCamera[{SN}]: 队列已满,自动丢弃最旧帧");
}
}
// 5. 入队
queue.Enqueue(bitmap);
AsyncLogHelper.Info($"LBCamera[{SN}]: 图像入队,当前队列:{queue.Count}");
// 6. 启动队列(单例,避免多线程重复)
StartConsumeQueue();
//Task.Factory.StartNew(() =>
//{
// CallBackImg = (Bitmap)bitmap.Clone();
// if (CallBackImg == null)
// {
// return;
// }
// if (GetTriggerMode(out TriggerMode mode, out TriggerSource source))
// {
// if (mode == TriggerMode.On && source != TriggerSource.Software)
// TriggerRunMessageReceived?.Invoke(SN, source.ToString()); // 触发运行事件
// }
// bitmap.Dispose();
//});
}
catch { }
AsyncLogHelper.Info(SN + "Get frame blockId = {0}" + frame.frameInfo.blockId);
}
///
/// 启动队列(保证单线程)
///
private void StartConsumeQueue()
{
// 使用轻量级判断,避免重复启动消费任务
if (CollectedImages.TryGetValue(SN, out var queue) && !queue.IsEmpty)
{
Task.Factory.StartNew(ProcessImageQueue, TaskCreationOptions.LongRunning);
}
}
///
/// 处理CollectedImages中的缓存图像
/// 核心逻辑:遍历取第一个图像 -> 赋值给CallBackImg -> 触发事件 -> 释放并移除
///
private void ProcessImageQueue()
{
try
{
if (!CollectedImages.TryGetValue(SN, out var queue) || queue.IsEmpty)
return;
// 短锁:仅出队,不阻塞生产
while (queue.TryDequeue(out Bitmap bitmap))
{
using (bitmap) // 自动释放:using 是最安全的方式
{
try
{
// 关键:事件传递克隆对象,绝对安全,不传递原资源
using (Bitmap clone = (Bitmap)bitmap.Clone())
{
// 触发图像事件
ImageGrabbed?.Invoke(this, new LBCameraEventArgs(SN, clone, true));
CallBackImg = (Bitmap)clone.Clone();
}
// 触发模式判断
if (GetTriggerMode(out TriggerMode mode, out TriggerSource source))
{
if (mode == TriggerMode.On && source != TriggerSource.Software)
{
TriggerRunMessageReceived?.Invoke(SN, source.ToString());
AsyncLogHelper.Info($"LBCamera[{SN}]: 硬触发事件 - {source}");
}
}
}
catch (Exception ex)
{
AsyncLogHelper.Error($"LBCamera[{SN}]: 处理单帧图像异常 - {ex.Message}", ex);
continue; // 单帧异常,继续处理下一帧
}
}
}
}
catch (Exception ex)
{
AsyncLogHelper.Error($"LBCamera[{SN}]: 消费队列异常 - {ex.Message}", ex);
}
}
///
/// 图像是否为Mono格式
///
///
///
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 override bool CloseDevice()
{
try
{
if (_camera == null)
return true;
// 停止采集
if (_camera.IMV_IsGrabbing())
{
StopGrabbing();
}
// 关闭设备
if (_camera.IMV_IsOpen())
{
int result = _camera.IMV_Close();
if (result != IMVDefine.IMV_OK)
{
System.Diagnostics.Debug.WriteLine($"关闭相机失败,错误码:{result}");
AsyncLogHelper.Info(SN + $"关闭相机失败,错误码:{result}");
}
}
// 销毁句柄
if (_handleCreated)
{
_camera.IMV_DestroyHandle();
_handleCreated = false;
}
// 释放资源
_isGrabbing = false;
isGrabbing = false;
// 清理帧缓存
lock (_frameLock)
{
foreach (var frame in _frameList)
{
var tempFrame = frame; // 创建临时变量
_camera.IMV_ReleaseFrame(ref tempFrame);
}
_frameList.Clear();
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"关闭相机失败:{ex.Message}");
AsyncLogHelper.Info(SN + $"关闭相机失败:{ex.Message}");
return false;
}
}
#endregion
#region 图像采集操作
///
/// 开始图像采集
///
/// 采集启动是否成功
public override bool StartGrabbing()
{
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
// 停止现有采集
if (_isGrabbing)
{
StopGrabbing();
}
// 启动采集
int result = _camera.IMV_StartGrabbing();
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"启动采集失败,错误码:{result}");
throw new Exception(SN + $"启动采集失败,错误码:{result}");
}
_isGrabbing = true;
isGrabbing = true;
// 启动采集线程
_threadRunning = true;
_grabThread = new Thread(GrabThreadProc);
_grabThread.IsBackground = true;
_grabThread.Start();
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"启动采集失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"启动采集失败:{ex.Message}");
_isGrabbing = false;
isGrabbing = false;
return false;
}
}
///
/// 停止图像采集
///
/// 停止是否成功
public override bool StopGrabbing()
{
try
{
if (_camera == null || !_isGrabbing)
return true;
// 停止线程
_threadRunning = false;
if (_grabThread != null && _grabThread.IsAlive)
{
_grabThread.Join(1000);
_grabThread = null;
}
// 停止采集
int result = _camera.IMV_StopGrabbing();
if (result != IMVDefine.IMV_OK)
{
System.Diagnostics.Debug.WriteLine(SN + $"停止采集失败,错误码:{result}");
AsyncLogHelper.Info(SN + $"停止采集失败,错误码:{result}");
}
_isGrabbing = false;
isGrabbing = false;
// 清理缓存
lock (_frameLock)
{
foreach (var frame in _frameList)
{
var tempFrame = frame; // 创建临时变量
_camera.IMV_ReleaseFrame(ref tempFrame);
}
_frameList.Clear();
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"停止采集失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"停止采集失败:{ex.Message}");
return false;
}
}
///
/// 执行软触发
///
/// 触发是否成功
public override bool SoftTrigger()
{
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
int result = _camera.IMV_ExecuteCommandFeature("TriggerSoftware");
return result == IMVDefine.IMV_OK;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"软触发失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"软触发失败:{ex.Message}");
return false;
}
}
#endregion
#region 参数设置和获取
///
/// 设置触发模式
///
/// 触发模式
/// 触发源
/// 设置是否成功
public override bool SetTriggerMode(TriggerMode mode, TriggerSource source = TriggerSource.Line0)
{
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
// 设置触发模式
string triggerMode = mode == TriggerMode.On ? "On" : "Off";
int result = _camera.IMV_SetEnumFeatureSymbol("TriggerMode", triggerMode);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"设置触发模式失败,错误码:{result}");
throw new Exception(SN + $"设置触发模式失败,错误码:{result}");
}
// 设置触发源
if (mode == TriggerMode.On)
{
string triggerSource = GetTriggerSourceString(source);
result = _camera.IMV_SetEnumFeatureSymbol("TriggerSource", triggerSource);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"设置触发源失败,错误码:{result}");
throw new Exception(SN + $"设置触发源失败,错误码:{result}");
}
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"设置触发模式失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置触发模式失败:{ex.Message}");
return false;
}
}
///
/// 获取触发模式
///
/// 触发模式
/// 触发源
/// 获取是否成功
public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source)
{
mode = TriggerMode.Off;
source = TriggerSource.Software;
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
// 获取触发模式
IMVDefine.IMV_String triggerMode = new IMVDefine.IMV_String();
int result = _camera.IMV_GetEnumFeatureSymbol("TriggerMode", ref triggerMode);
if (result == IMVDefine.IMV_OK)
{
mode = triggerMode.str == "On" ? TriggerMode.On : TriggerMode.Off;
}
// 获取触发源
IMVDefine.IMV_String triggerSource = new IMVDefine.IMV_String();
result = _camera.IMV_GetEnumFeatureSymbol("TriggerSource", ref triggerSource);
if (result == IMVDefine.IMV_OK)
{
source = GetTriggerSourceFromString(triggerSource.str);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"获取触发模式失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取触发模式失败:{ex.Message}");
return false;
}
}
///
/// 设置曝光时间
///
/// 曝光时间(微秒)
/// 设置是否成功
public override bool SetExpouseTime(double value)
{
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
// 验证曝光时间范围
double minExp = 0, maxExp = 0;
int result = _camera.IMV_GetDoubleFeatureMin("ExposureTime", ref minExp);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"获取曝光时间最小值失败,错误码:{result}");
throw new Exception(SN + $"获取曝光时间最小值失败,错误码:{result}");
}
result = _camera.IMV_GetDoubleFeatureMax("ExposureTime", ref maxExp);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"获取曝光时间最大值失败,错误码:{result}");
throw new Exception(SN + $"获取曝光时间最大值失败,错误码:{result}");
}
if (value < minExp || value > maxExp)
{
AsyncLogHelper.Error(SN + $"曝光时间超出范围,有效范围:{minExp} - {maxExp}");
throw new Exception(SN + $"曝光时间超出范围,有效范围:{minExp} - {maxExp}");
}
// 设置曝光时间
result = _camera.IMV_SetDoubleFeatureValue("ExposureTime", value);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"设置曝光时间失败,错误码:{result}");
throw new Exception(SN + $"设置曝光时间失败,错误码:{result}");
}
return true;
}
catch (Exception ex)
{
AsyncLogHelper.Error(SN + $"设置曝光时间失败:{ex.Message}");
System.Diagnostics.Debug.WriteLine(SN + $"设置曝光时间失败:{ex.Message}");
return false;
}
}
///
/// 获取曝光时间
///
/// 曝光时间
/// 获取是否成功
public override bool GetExpouseTime(out double value)
{
value = 0;
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
int result = _camera.IMV_GetDoubleFeatureValue("ExposureTime", ref value);
return result == IMVDefine.IMV_OK;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"获取曝光时间失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取曝光时间失败:{ex.Message}");
return false;
}
}
///
/// 设置增益
///
/// 增益值
/// 设置是否成功
public override bool SetGain(double gain)
{
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw";
// 验证增益范围
double minGain = 0, maxGain = 0;
int result = _camera.IMV_GetDoubleFeatureMin(gainFeature, ref minGain);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"获取增益最小值失败,错误码:{result}");
throw new Exception(SN + $"获取增益最小值失败,错误码:{result}");
}
result = _camera.IMV_GetDoubleFeatureMax(gainFeature, ref maxGain);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"获取增益最大值失败,错误码:{result}");
throw new Exception(SN + $"获取增益最大值失败,错误码:{result}");
}
if (gain < minGain) gain = minGain;
if (gain > maxGain) gain = maxGain;
// 设置增益
result = _camera.IMV_SetDoubleFeatureValue(gainFeature, gain);
if (result != IMVDefine.IMV_OK)
{
AsyncLogHelper.Error(SN + $"设置增益失败,错误码:{result}");
throw new Exception(SN + $"设置增益失败,错误码:{result}");
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"设置增益失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置增益失败:{ex.Message}");
return false;
}
}
///
/// 获取增益值
///
/// 增益值
/// 获取是否成功
public override bool GetGain(out double gain)
{
gain = 0;
try
{
if (_camera == null || !_camera.IMV_IsOpen())
{
AsyncLogHelper.Error(SN + "相机未打开");
throw new Exception(SN + "相机未打开");
}
string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw";
int result = _camera.IMV_GetDoubleFeatureValue(gainFeature, ref gain);
return result == IMVDefine.IMV_OK;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"获取增益失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取增益失败:{ex.Message}");
return false;
}
}
///
/// 设置触发极性
///
/// 触发极性
/// 是否成功
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(SN + $"设置触发极性失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置触发极性失败:{ex.Message}");
return false;
}
}
///
/// 获取触发极性
///
/// 触发极性
/// 是否成功
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(SN + $"获取触发极性失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取触发极性失败:{ex.Message}");
return false;
}
}
///
/// 设置触发滤波时间 (us)
///
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(SN + $"设置触发滤波失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置触发滤波失败:{ex.Message}");
return false;
}
}
///
/// 获取触发滤波时间 (us)
///
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(SN + $"获取触发滤波失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取触发滤波失败:{ex.Message}");
return false;
}
}
///
/// 设置触发延时 (us)
///
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(SN + $"设置触发延时失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置触发延时失败:{ex.Message}");
return false;
}
}
///
/// 获取触发延时 (us)
///
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(SN + $"获取触发延时失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取触发延时失败:{ex.Message}");
return false;
}
}
///
/// 设置信号线模式
///
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(SN + $"设置信号线模式失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置信号线模式失败:{ex.Message}");
return false;
}
}
///
/// 设置信号线电平状态
///
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(SN + $"设置信号线状态失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"设置信号线状态失败:{ex.Message}");
return false;
}
}
///
/// 获取信号线电平状态
///
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(SN + $"获取信号线状态失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"获取信号线状态失败:{ex.Message}");
return false;
}
}
///
/// 自动白平衡
///
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(SN + $"自动白平衡失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"自动白平衡失败:{ex.Message}");
return false;
}
}
#endregion
#region 采集和转换辅助方法
///
/// 图像采集线程处理函数
///
private void GrabThreadProc()
{
while (_threadRunning)
{
IMVDefine.IMV_Frame frame = new IMVDefine.IMV_Frame();
try
{
// 获取图像帧
int result = _camera.IMV_GetFrame(ref frame, 1000);
if (result == IMVDefine.IMV_OK)
{
// 处理图像帧
ProcessFrame(frame);
}
else
{
// 即使获取失败,也尝试释放帧,防止SDK内部缓存泄露
// 注意:frame是每次新建的,如果GetFrame没填充,这里释放应该是安全的(视SDK实现而定)
var tempFrame = frame;
_camera.IMV_ReleaseFrame(ref tempFrame);
if ((uint)result != 0x80000001 && result != -119 && result != -102) // 超时错误代码
{
// 非超时错误
System.Diagnostics.Debug.WriteLine(SN + $"获取图像帧失败,错误码:{result}");
AsyncLogHelper.Error(SN + $"获取图像帧失败,错误码:{result}");
Thread.Sleep(10); // 出错时稍作等待
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"采集线程异常:{ex.Message}");
AsyncLogHelper.Error(SN + $"采集线程异常:{ex.Message}");
Thread.Sleep(10);
}
Thread.Sleep(1);
}
}
///
/// 处理图像帧
///
/// 图像帧
private void ProcessFrame(IMVDefine.IMV_Frame frame)
{
try
{
// 将图像数据转换为Bitmap
Bitmap bitmap = ConvertFrameToBitmap(frame);
if (bitmap != null)
{
// 触发图像采集事件
CameraEventArgs args = new CameraEventArgs(SN, bitmap);
ImageGrabbed?.Invoke(this, args);
// 更新回调图像
CallBackImg = bitmap;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"处理图像帧失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"处理图像帧失败:{ex.Message}");
}
finally
{
// 确保无论如何都释放帧,防止缓存占满
var tempFrame = frame;
_camera.IMV_ReleaseFrame(ref tempFrame);
}
}
///
/// 将图像帧转换为Bitmap
///
/// 图像帧
/// Bitmap对象
private Bitmap ConvertFrameToBitmap(IMVDefine.IMV_Frame frame)
{
try
{
Bitmap bitmap = null;
switch (frame.frameInfo.pixelFormat)
{
case IMVDefine.IMV_EPixelType.gvspPixelMono8:
bitmap = CreateMono8Bitmap(frame);
break;
case IMVDefine.IMV_EPixelType.gvspPixelBGR8:
bitmap = CreateBgr8Bitmap(frame);
break;
default:
// 其他格式需要转换
bitmap = ConvertToBGR8(frame);
break;
}
return bitmap;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(SN + $"图像格式转换失败:{ex.Message}");
AsyncLogHelper.Error(SN + $"图像格式转换失败:{ex.Message}");
return null;
}
}
///
/// 创建Mono8格式Bitmap
///
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;
}
///
/// 创建BGR8格式Bitmap
///
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;
}
///
/// 转换为BGR8格式
///
private Bitmap ConvertToBGR8(IMVDefine.IMV_Frame frame)
{
IMVDefine.IMV_PixelConvertParam convertParam = new IMVDefine.IMV_PixelConvertParam
{
nWidth = frame.frameInfo.width,
nHeight = frame.frameInfo.height,
ePixelFormat = frame.frameInfo.pixelFormat,
pSrcData = frame.pData,
nSrcDataLen = frame.frameInfo.size,
nPaddingX = frame.frameInfo.paddingX,
nPaddingY = frame.frameInfo.paddingY,
eBayerDemosaic = IMVDefine.IMV_EBayerDemosaic.demosaicBilinear,
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);
if (result == IMVDefine.IMV_OK)
{
return CreateBgr8BitmapFromBuffer(dstBuffer, frame.frameInfo.width, frame.frameInfo.height);
}
return null;
}
finally
{
Marshal.FreeHGlobal(dstBuffer);
}
}
///
/// 从缓冲区创建BGR8 Bitmap
///
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;
}
///
/// 将触发源枚举转换为字符串
///
private string GetTriggerSourceString(TriggerSource source)
{
return source switch
{
TriggerSource.Software => "Software",
TriggerSource.Line0 => "Line0",
TriggerSource.Line1 => "Line1",
TriggerSource.Line2 => "Line2",
TriggerSource.Line3 => "Line3",
TriggerSource.Line4 => "Line4",
TriggerSource.Line5 => "Line5",
_ => "Software"
};
}
///
/// 将字符串转换为触发源枚举
///
private TriggerSource GetTriggerSourceFromString(string source)
{
return source switch
{
"Software" => TriggerSource.Software,
"Line0" => TriggerSource.Line0,
"Line1" => TriggerSource.Line1,
"Line2" => TriggerSource.Line2,
"Line3" => TriggerSource.Line3,
"Line4" => TriggerSource.Line4,
"Line5" => TriggerSource.Line5,
_ => TriggerSource.Software
};
}
#endregion
#region IDisposable实现
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 释放托管资源
}
// 释放非托管资源
CloseDevice();
_disposed = true;
}
}
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
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 GetImage(out Bitmap bitmap, int outtime = 3000)
{
bitmap = null;
try
{
// 设置超时时间
DateTime lastTime = DateTime.Now.AddMilliseconds(outtime);
// 判断是否超时
while (lastTime > DateTime.Now)// 设置超时时间为 3 秒
{
if (CallBackImg != null)
{
lock (CallBackImg)
{
// 保存旧 Bitmap 并释放
bitmap = CallBackImg; // 创建副本
}
//// 释放旧资源
//CallBackImg.Dispose();
//CallBackImg = null;
return true;
}
}
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 StartContinuousGrab()
{
return true;
}
#endregion
}
}