C#海康工业相机对接及问题汇总
项目背景:
7台相机通过多网口链接到同一台电脑,帧率 50+/s ,每炉次采集大概持续30s,炉次间隔2min左右,要求不能丢帧且保存为BMP格式,不能卡顿闪退,程序正常7x24小时运行。
注意:首先得保证所有相机链接成功且使用MVS稳定运行不丢帧
电脑配置:

碰到的问题以及解决方式:
1、丢帧(通过MV_CC_SetImageNodeNum_NET增加节点、增加硬盘读写速率等)
2、内存飙升(无法使用Marshal.Copy内存拷贝,因为数据量大根本顶不住、只能依靠硬盘高速写入从而更快的释放指针,如果数据都存内存肯定顶不住,所以只能存文件)
3、成像异常(时间错乱)
4、内存稳定增长不下去(我这里是WPF程序,前端显示的图片比较大,每次打开没有回收,导致使用内存一直增长,每次打开新图的时候先将他释放再给新数据)
详细源码
采集
using DevComponents.DotNetBar; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Dnn; using Newtonsoft.Json; using StackExchange.Redis; using System; using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.IO; using System.IO.Packaging; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Media3D; using System.Xml.Linq; using WaveformStressPatterninSpection.Models; using WaveformStressPatterninSpection.Models.Enums; using WaveformStressPatterninSpection.Server.Helper; using WaveformStressPatterninSpection.Server.HKCamera; using WaveformStressPatterninSpection.Server.IServer; using WaveformStressPatterninSpection.Server.Server; using static WaveformStressPatterninSpection.Server.HKCamera.MyCamera; namespace WaveformStressPatterninSpection.Server.Base { public class HK_CameraHelper { private static readonly Lazy<HK_CameraHelper> _instance = new Lazy<HK_CameraHelper>(() => new HK_CameraHelper()); // 提供全局访问点 public static HK_CameraHelper Instance => _instance.Value; // 通过属性注入依赖(公共可写属性) public ICancheService _cancheService { get; set; } MyCamera.cbOutputExdelegate cbImage; int m_nDevNum; public MyCamera[] m_pMyCamera; MyCamera.MV_CC_DEVICE_INFO[] m_pDeviceInfo; MyCamera.MV_CC_DEVICE_INFO_LIST m_pDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST(); public bool m_bGrabbing = false; public MyCamera.MV_FRAME_OUT_INFO_EX[] m_stFrameInfo; private Object[] m_BufForSaveImageLock; int[] m_nFrames; public UInt32[] m_nSaveImageBufSize; public IntPtr[] m_pSaveImageBuf; public string savePathByDB = Path.Combine(Environment.CurrentDirectory, "productPic"); public bool[] runStatus; public string[] snNo; public WriteLogFileHelper _writeLogFile = new WriteLogFileHelper(); public readonly SimpleObjectPool<SaveImageDto> _objectPool = new SimpleObjectPool<SaveImageDto>(20000); public ConcurrentDictionary<int, Channel<SaveImageDto>> _perCameraChannels = new(); private HK_CameraHelper() { cbImage = new cbOutputExdelegate(ImageCallBack); _cancheService = ContainerLocator.Container.Resolve<ICancheService>(); } [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); public string DeviceListAcq() { System.GC.Collect(); int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_pDeviceList); m_nDevNum = CommonHelper.UsingCamera.Length; m_pMyCamera = new MyCamera[m_nDevNum]; m_pDeviceInfo = new MyCamera.MV_CC_DEVICE_INFO[m_nDevNum]; m_BufForSaveImageLock = new Object[m_nDevNum]; m_nSaveImageBufSize = new UInt32[m_nDevNum]; m_pSaveImageBuf = new IntPtr[m_nDevNum]; m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX[m_nDevNum]; m_nFrames = new int[m_nDevNum]; runStatus = new bool[m_nDevNum]; snNo = new string[m_nDevNum]; if (m_nDevNum > 0) { for (int i = 0; i < m_nDevNum; ++i) { m_BufForSaveImageLock[i] = new Object(); m_nFrames[i] = 0; m_nSaveImageBufSize[i] = 0; m_pSaveImageBuf[i] = IntPtr.Zero; m_stFrameInfo[i] = new MyCamera.MV_FRAME_OUT_INFO_EX(); runStatus[i] = true; snNo[i] = ""; } return bnOpen_Click(); } else { return "未发现相机,请检查相机是否正确连接或被其他设备占用"; } } public string bnOpen_Click() { string resMes = ""; List<CameraInfo> cbDeviceList = new List<CameraInfo>(); List<CameraInfo> detectedCameras = new List<CameraInfo>(); for (int j = 0; j < (int)m_pDeviceList.nDeviceNum; j++) { //ch:获取选择的设备信息 | en:Get Selected Device Information MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[j], typeof(MyCamera.MV_CC_DEVICE_INFO))!; string cameraSn = ""; string chManufacturerName = ""; if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { MyCamera.MV_GIGE_DEVICE_INFO_EX gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO_EX)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO_EX)); cameraSn = gigeInfo.chSerialNumber; chManufacturerName = gigeInfo.chManufacturerName; } else if (device.nTLayerType == MyCamera.MV_USB_DEVICE) { MyCamera.MV_USB3_DEVICE_INFO_EX usbInfo = (MyCamera.MV_USB3_DEVICE_INFO_EX)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO_EX)); cameraSn = usbInfo.chSerialNumber; chManufacturerName = usbInfo.chManufacturerName; } detectedCameras.Add(new CameraInfo { snNo = cameraSn, device = device, chManufacturerName = chManufacturerName }); } for (int i = 0; i < CommonHelper.UsingCamera.Length; i++) { //ch:打开设备 | en:Open Device CameraInfo info = new CameraInfo() { CanmeraId = i }; var camera = detectedCameras.FirstOrDefault(x => x.snNo == CommonHelper.UsingCamera[i]); if (camera == null) continue; var device = camera.device; if (null == m_pMyCamera[i]) { m_pMyCamera[i] = new MyCamera(); if (null == m_pMyCamera[i]) { resMes = "设备初始化失败"; continue; } } int nRet = m_pMyCamera[i].MV_CC_CreateDevice_NET(ref device); if (MyCamera.MV_OK != nRet) { resMes = "创建失败"; continue; } nRet = m_pMyCamera[i].MV_CC_OpenDevice_NET(); if (MyCamera.MV_OK != nRet) { resMes = "打开失败"; continue; } else { m_pDeviceInfo[i] = camera.device; // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) if (camera.device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { int nPacketSize = m_pMyCamera[i].MV_CC_GetOptimalPacketSize_NET(); if (nPacketSize > 0) { nRet = m_pMyCamera[i].MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", nPacketSize); if (nRet != MyCamera.MV_OK) { resMes = "最佳包大小设置失败"; continue; } } else { } } nRet = m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF); uint nImageNodeNum = 100; //这个尽量设置小一点,太大会导致内存回收不及时,多次残余将会导致内存逐渐溢出 nRet = m_pMyCamera[i].MV_CC_SetImageNodeNum_NET(nImageNodeNum); if (MV_OK != nRet) { resMes = "节点缓存设置失败"; continue; } nRet = m_pMyCamera[i].MV_CC_RegisterImageCallBackEx_NET(cbImage, (IntPtr)i); if (MyCamera.MV_OK != nRet) { resMes = "回调方式设置失败"; continue; } } //加载用户集1,某些情况下海康相机自身Bug导致丢帧,手动加载一下用户集就好了 nRet = m_pMyCamera[i].MV_CC_SetEnumValue_NET("UserSetSelector", 1); if (MV_OK != nRet) { resMes = "用户集1加载失败"; continue; } nRet = m_pMyCamera[i].MV_CC_SetCommandValue_NET("UserSetLoad"); if (MV_OK != nRet) { resMes = "用户集1加载失败"; continue; } MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE(); MVCC_FLOATVALUE mVCC_FLOATVALUE = new MVCC_FLOATVALUE(); m_pMyCamera[i].MV_CC_GetIntValue_NET("Width", ref stParam); info.Width = stParam.nCurValue + ""; m_pMyCamera[i].MV_CC_GetIntValue_NET("Height", ref stParam); info.Height = stParam.nCurValue + ""; m_pMyCamera[i].MV_CC_GetIntValue_NET("OffsetX", ref stParam); info.OffsetX = stParam.nCurValue + ""; m_pMyCamera[i].MV_CC_GetIntValue_NET("OffsetY", ref stParam); info.OffsetY = stParam.nCurValue + ""; m_pMyCamera[i].MV_CC_GetFloatValue_NET("Gain", ref mVCC_FLOATVALUE); info.Gain = mVCC_FLOATVALUE.fCurValue + ""; m_pMyCamera[i].MV_CC_GetFloatValue_NET("ExposureTime", ref mVCC_FLOATVALUE); info.ExposureTime = mVCC_FLOATVALUE.fCurValue + ""; snNo[i] = camera.snNo; info.CanmeraId = i; info.snNo = camera.snNo; info.chManufacturerName = camera.chManufacturerName; info.CanmeraName = "相机" + (1 + i).ToString(); info.isOpenCamera = false; info.isStartCatchPic = true; info.device = device; info.device_Hik = m_pMyCamera[i]; info.isOpenCamera = true; info.m_BufForSaveImageLock = new object(); info.m_nFrames = new int(); info.m_nSaveImageBufSize = 0; info.m_pSaveImageBuf = IntPtr.Zero; info.m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX(); info.isWave = i > 1; info.isSoft = false; cbDeviceList.Add(info); } if (!string.IsNullOrEmpty(resMes)) return resMes; if (cbDeviceList == null || cbDeviceList.Count < 1) return "未发现相机,请检查相机是否正确连接或被其他设备占用"; _cancheService.UpdateCameraSettingByCache(cbDeviceList); //更新缓存中的相机信息 m_bGrabbing = true; LineTrigger(); for (int i = 0; i < m_pMyCamera.Length; i++) { int cameraIndex = i; var camera = m_pMyCamera[i]; var nRet = camera.MV_CC_StartGrabbing_NET(); if (nRet != MyCamera.MV_OK) { return "相机打开失败"; } CommonHelper.CreateDirectoryIfNotExists(Path.Combine(Environment.CurrentDirectory, "TempPic")); CommonHelper.CreateDirectoryIfNotExists(Path.Combine(Environment.CurrentDirectory, "BackTempPic")); //执行失败后的备份文件 } //todo 完善实际数据数据库然后再放出来 //var cameraListByDb = _cancheService.GetCameraList(); //if (cameraListByDb != null && cameraListByDb.Count > 0) //{ // foreach (var cameraInfo in cameraListByDb) // { // SettingParameter(cameraInfo); //参数下发 // } //} return ""; } /// <summary> /// 工业相机回调方法 /// </summary> /// <param name="pData"></param> /// <param name="pFrameInfo"></param> /// <param name="pUser"></param> public void ImageCallBack(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser) { if (pData == IntPtr.Zero || pFrameInfo.nFrameLen <= 0) { _writeLogFile.WriteLog("Error", $"无效帧:Camera={pUser.ToInt64()}, Length={pFrameInfo.nFrameLen}"); return; } int cameraIndex = (int)pUser; if (snNo == null || snNo.Any(c => string.IsNullOrEmpty(c))) return; //var dto = _objectPool.Take(); try { //还在获取炉次中,不要创建图片 if (string.IsNullOrEmpty(CommonHelper._curTemperingInfo.TemperNo)) return; long frameId = Interlocked.Increment(ref CommonHelper.CamearaIncrementNum[cameraIndex]); Debug.WriteLine("开始创建图片"); string filePath = Path.Combine(Environment.CurrentDirectory, "TempPic", CommonHelper._curTemperingInfo.TemperNo, $"{snNo[cameraIndex]}", $"{frameId:D6}.bmp"); using var mat = new Mat(pFrameInfo.nHeight, pFrameInfo.nWidth, DepthType.Cv8U, 1, pData, pFrameInfo.nWidth); CvInvoke.Imwrite(filePath, mat); //dto.pData = pData; //dto.SaveName = $"{frameId:D6}.bmp"; //// 设置 DTO 属性 //dto.nIndex = cameraIndex; //dto.snNo = snNo[cameraIndex]; //dto.pFrameInfo = pFrameInfo; //var channel = _perCameraChannels.GetOrAdd(cameraIndex, _ => // Channel.CreateBounded<SaveImageDto>(new BoundedChannelOptions(500) // { // SingleWriter = true, // 保证单生产者顺序性 // FullMode = BoundedChannelFullMode.DropOldest // 智能背压 // })); //if (!channel.Writer.TryWrite(dto)) //{ // try { dto.Dispose(); } finally { _objectPool.Return(dto); } //} } catch (Exception ex) { _writeLogFile.WriteLog("Error", $"相机 {cameraIndex} 处理失败:{ex.Message}"); //if (dto != null) //{ // try // { // dto.Dispose(); // } // finally // { // _objectPool.Return(dto); // 即使 Dispose 抛出异常,仍需归还池 // } //} } } /// <summary> /// 关闭摄像头 /// </summary> /// <returns></returns> public string bnClose_Click() { var resMsg = ""; for (int i = 0; i < m_nDevNum; ++i) { try { int nRet; if (m_pMyCamera[i] != null) { nRet = m_pMyCamera[i].MV_CC_CloseDevice_NET(); if (MyCamera.MV_OK != nRet) { resMsg = "相机关闭失败"; continue; } nRet = m_pMyCamera[i].MV_CC_DestroyDevice_NET(); if (MyCamera.MV_OK != nRet) { resMsg = "相机关闭失败"; continue; } if (m_pSaveImageBuf[i] != IntPtr.Zero) { Marshal.FreeHGlobal(m_pSaveImageBuf[i]); m_pSaveImageBuf[i] = IntPtr.Zero; } } } catch (Exception ex) { continue; } } // ch:取流标志位清零 | en:Zero setting grabbing flag bit m_bGrabbing = false; return resMsg; } /// <summary> /// 硬触发 /// </summary> /// <returns></returns> public void LineTrigger() { for (int i = 0; i < m_nDevNum; ++i) { lock (m_BufForSaveImageLock[i]) { var nRet = m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON); nRet = m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0); } } } /// <summary> /// 软触发 /// </summary> /// <param name="device_Hik"></param> /// <returns></returns> public string SoftTrigger(MyCamera device_Hik) { int nRet; int i = Array.IndexOf(m_pMyCamera, device_Hik); if (i == -1) return ""; m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON); m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE); nRet = m_pMyCamera[i].MV_CC_SetCommandValue_NET("TriggerSoftware"); if (MyCamera.MV_OK != nRet) { return ""; } MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX(); MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT(); nRet = m_pMyCamera[i].MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000); pFrameInfo = stFrameInfo.stFrameInfo; var pData = stFrameInfo.pBufAddr; if (nRet != MyCamera.MV_OK) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{snNo[i]}_软触发抓图失败,硬触发进行中!"), StartTime = DateTime.Now }); return ""; } var picUrl = StartCatchPic(i, m_pMyCamera[i], pFrameInfo, pData); // 必须释放缓冲区! m_pMyCamera[i].MV_CC_FreeImageBuffer_NET(ref stFrameInfo); //保存 m_pMyCamera[i].MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0); return picUrl; } /// <summary> /// 软触发抓图 /// </summary> public string StartCatchPic(int nIndex, MyCamera m_MyCameraInfo, MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pData = default) { if (m_MyCameraInfo == null) return ""; int nRet = 0; if (m_pSaveImageBuf[nIndex] == IntPtr.Zero || pFrameInfo.nFrameLen > m_nSaveImageBufSize[nIndex]) { if (m_pSaveImageBuf[nIndex] != IntPtr.Zero) { Marshal.Release(m_pSaveImageBuf[nIndex]); m_pSaveImageBuf[nIndex] = IntPtr.Zero; } m_pSaveImageBuf[nIndex] = Marshal.AllocHGlobal((Int32)pFrameInfo.nFrameLen); if (m_pSaveImageBuf[nIndex] == IntPtr.Zero) { return ""; } m_nSaveImageBufSize[nIndex] = pFrameInfo.nFrameLen; } m_stFrameInfo[nIndex] = pFrameInfo; CopyMemory(m_pSaveImageBuf[nIndex], pData, pFrameInfo.nFrameLen); if (m_stFrameInfo[nIndex].nFrameLen != 0) { var curTime = DateTime.Now; var fileName = string.Format($"{snNo[nIndex]}_Dev_ti{m_stFrameInfo[nIndex].nHostTimeStamp}.jpeg"); if (!Directory.Exists(savePathByDB)) { Directory.CreateDirectory(savePathByDB); } MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg; stSaveFileParam.enPixelType = m_stFrameInfo[nIndex].enPixelType; stSaveFileParam.pData = m_pSaveImageBuf[nIndex]; stSaveFileParam.nDataLen = m_stFrameInfo[nIndex].nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo[nIndex].nHeight; stSaveFileParam.nWidth = m_stFrameInfo[nIndex].nWidth; stSaveFileParam.nQuality = 80; stSaveFileParam.iMethodValue = 2; stSaveFileParam.pImagePath = Path.Combine(savePathByDB, fileName); nRet = m_MyCameraInfo.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); if (MyCamera.MV_OK != nRet) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{snNo[nIndex]}_软触发失败"), StartTime = DateTime.Now }); return ""; } else { //先清除字典中的无效数据 //_cancheService.ClearInVainData(); //插入玻璃信息 var fileNameByDb = string.Format($"pack://siteoforigin:,,,/productPic/{fileName}"); //var temperName = _cancheService.GetCurMaxTemperNo(); //var temperNo = _cancheService.GetCurMaxTemperNo(); //var insertRes = _cancheService.UpdateTemperingHistory(new TemperingHistory() //{ // TemperName = temperName, // TemperNo = temperNo, // ImageSource = fileNameByDb, // StartTime = curTime, // Num = 1, // Status = true, // GlassSize = "", //}); ////插入日志 //if (insertRes) //{ //_cancheService.UpdateLogging(new Logging() { Content = string.Format($"{temperName}成像完成"), StartTime = curTime }); //} return fileNameByDb; } } return ""; } /// <summary> /// 更新摄像头状态 /// </summary> public void UpdateCameraStatu() { for (int i = 0; i < m_nDevNum; i++) { try { runStatus[i] = m_pMyCamera[i]?.MV_CC_IsDeviceConnected_NET()??false; } catch { runStatus[i] = false; } } } /// <summary> /// 参数下发 /// </summary> public string SettingParameter(CameraSetting cameraByDb) { var cameraInfoByCache = _cancheService.GetCameraListByCache().FirstOrDefault(x => x.snNo == cameraByDb.SnNo); if (cameraInfoByCache == null) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{cameraByDb.SnNo} 参数下发失败"), StartTime = DateTime.Now }); return "参数下发失败"; } int nRet; nRet = cameraInfoByCache.device_Hik.MV_CC_StopGrabbing_NET(); if (nRet != MyCamera.MV_OK) { return "参数下发失败"; } nRet = cameraInfoByCache.device_Hik.MV_CC_SetFloatValue_NET("ExposureTime", float.Parse(cameraByDb.ExposureTime)); if (nRet != MyCamera.MV_OK) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{cameraByDb.SnNo} 曝光时间参数下发失败"), StartTime = DateTime.Now }); return "曝光时间参数下发失败"; } nRet = cameraInfoByCache.device_Hik.MV_CC_SetFloatValue_NET("Gain", float.Parse(cameraByDb.Gain.ToString())); if (nRet != MyCamera.MV_OK) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{cameraByDb.SnNo} 增益参数下发失败"), StartTime = DateTime.Now }); return "增益参数下发失败"; } nRet = cameraInfoByCache.device_Hik.MV_CC_SetWidth_NET(uint.Parse(cameraByDb.Width.ToString())); nRet = cameraInfoByCache.device_Hik.MV_CC_SetHeight_NET(uint.Parse(cameraByDb.Height.ToString())); nRet = cameraInfoByCache.device_Hik.MV_CC_SetIntValue_NET("OffsetX", uint.Parse(cameraByDb.OffsetX)); nRet = cameraInfoByCache.device_Hik.MV_CC_SetIntValue_NET("OffsetY", uint.Parse(cameraByDb.OffsetY.ToString())); if (0 != nRet) { _cancheService.UpdateLogging(new Logging() { Content = string.Format($"{cameraByDb.SnNo} ROI参数下发失败"), StartTime = DateTime.Now }); return "ROI参数下发失败"; } nRet = cameraInfoByCache.device_Hik.MV_CC_StartGrabbing_NET(); if (nRet != MyCamera.MV_OK) { return "程序启动失败,请关闭程序重新启动"; } return ""; } } }
消费保存(1、直接存图 2、通过异步队列边存边消费)
private ConcurrentDictionary<int, Channel<SaveImageDto>> _perCameraChannels = new();
在构造函数中初始化
foreach (var cameraId in Enumerable.Range(0, CommonHelper.UsingCamera.Length))
{
Task.Run(() => ConsumeCamera(cameraId));
}
private async Task ConsumeCamera(int cameraId)
{
var channel = _perCameraChannels.GetOrAdd(cameraId,
_ => Channel.CreateBounded<SaveImageDto>(new BoundedChannelOptions(500)
{
SingleReader = true,
FullMode = BoundedChannelFullMode.DropOldest
}));
await foreach (var item in channel.Reader.ReadAllAsync())
{
try
{
await SaveImageToFileAsync(item);
}
finally
{
item?.Dispose();
_objectPool.Return(item);
}
}
}
/// <summary>
/// 保存图片到指定文件
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task SaveImageToFileAsync(SaveImageDto item)
{
Debug.WriteLine("开始创建图片");
string filePath = Path.Combine(Environment.CurrentDirectory, "TempPic", CommonHelper._curTemperingInfo.TemperNo, $"{item.snNo}", item.SaveName);
try
{
using var mat = new Mat(item.pFrameInfo.nHeight, item.pFrameInfo.nWidth, DepthType.Cv8U, 1, item.pData, item.pFrameInfo.nWidth);
CvInvoke.Imwrite(filePath, mat);
}
catch (IOException ex) when (ex.Message.Contains("已存在"))
{
await writeLogFileHelper.WriteLogAsync("Error.log", $"文件已存在:{filePath}");
}
catch (Exception ex)
{
await writeLogFileHelper.WriteLogAsync("Error.log", $"保存图像失败:{filePath},错误:{ex.Message}");
}
}
以上包含软触发和硬触发两种模式,通过TriggerMode方法触发
错误代码对照:
https://blog.csdn.net/qq_38960753/article/details/127374053
https://blog.csdn.net/qq_43445867/article/details/126283026
常见问题:
1.无法加载 dll“mvcameracontrol.dll”: 找不到指定的模块;缺少运行时,官网上有一个runtime需要安装【官网地址:https://www.hikrobotics.com/cn/machinevision/service/download/?module=0】

2.dll引用,分为两种方式,一种是引用dll,另一种是直接引用resource下的源文件,如果有框架版本的问题,建议直接使用第二种方式,直接拷贝源码文件【而且还能看到对应方法的注释】
3.mvs工具可以添加虚拟相机,方便代码调试,而且安装目录下面有对应的开发demo

浙公网安备 33010602011771号