// CameraGroup.cs 相机组线程管理类,控制相机采集和识别的线程运行,开放有算法的接口 using MvCameraControl; using MvvmScaffoldFrame48.DLL.CameraTools; using MvvmScaffoldFrame48.DLL.LogTools; using MvvmScaffoldFrame48.DLL.SystemTools; using MvvmScaffoldFrame48.Model.ResultModel; using MvvmScaffoldFrame48.Model.StorageModel.SystemConfig; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace MvvmScaffoldFrame48.DLL.ThreadManager { /// /// 相机组配置和状态管理类 /// 管理单个相机的采集和处理线程及相关资源 /// public class CameraGroup { #region 变量与实例 //相机线程的休眠时间,默认为0,即不休眠。在采集节拍没有那么高时,增加此值降低性能消耗 private int CameraSleepTime = 0; //帧编号记录 private long lastframeNum = -1; //相机 public HikCamera Camera { get; set; } //相机ID public int CameraId { get; set; } //是否运行中 public bool IsRunning { get; set; } //相机处理线程 public Task CameraTask { get; set; } //图像处理线程 public Task ProcessingTask { get; set; } //图像队列-ConcurrentQueue public ConcurrentQueue ImageQueue { get; set; } //图像队列-自定义RingBuffer public LockFreeRingBuffer ImageRingBuffer = new LockFreeRingBuffer(300); //信号量 public SemaphoreSlim QueueSemaphore { get; set; } //取消令牌 public CancellationTokenSource CancellationTokenSource { get; set; } // 图像处理算法(接口方式) public IImageProcessingAlgorithmHikVision ImageProcessor { get; set; } // 结果发送事件,当图像处理完成时触发 public event EventHandler ProcessResultAvailable; // 参数配置 public CameraProcessConfigModel Configuration { get; set; } #endregion #region 公共方法 /// /// 启动相机组(包括采集和处理线程) /// public void Start() { if (Camera == null) { Console.WriteLine($"相机 {CameraId} 未初始化"); return; } if (Camera.Device == null) { Console.WriteLine($"相机 {CameraId} 未初始化"); return; } if (Camera.Device.IsConnected == false) { Console.WriteLine($"相机 {CameraId} 未打开"); return; } if (IsRunning) return; // 根据配置加载算法 LoadAlgorithmFromConfiguration(); //Camera.StartReceiveFuntion(); Camera.FrameGrabbedEvent += CameraCaptureLoop2; Camera.StartReceiveFuntionSetFrameGrabedEvent(); // 重置取消令牌 CancellationTokenSource = new CancellationTokenSource(); var token = CancellationTokenSource.Token; // 启动相机采集线程 //CameraTask = Task.Factory.StartNew(() => CameraCaptureLoop(token), // token, TaskCreationOptions.LongRunning, TaskScheduler.Default); // 启动图像处理线程 ProcessingTask = Task.Factory.StartNew(() => ImageProcessingbatchLoop2(token), token, TaskCreationOptions.LongRunning, TaskScheduler.Default); IsRunning = true; Console.WriteLine($"相机组 {CameraId} 已启动"); } /// /// 停止相机组 /// public async void Stop() { if (!IsRunning) return; // 发送取消信号 CancellationTokenSource.Cancel(); // 等待线程完成 try { if (CameraTask != null) await CameraTask; if (ProcessingTask != null) await ProcessingTask; } catch (OperationCanceledException) { // 正常取消,忽略异常 } IsRunning = false; Camera.StopReceiveFuntionSetFrameGrabedEvent(); Console.WriteLine($"相机组 {CameraId} 已停止"); } /// /// 更新算法参数 /// /// 新参数 public void UpdateAlgorithmParameters(object parameters) { if (ImageProcessor != null && parameters != null) { string parametersjson = ImageProcessor.GetSaveJson(); ImageProcessor.Configure(parametersjson); // 更新配置 if (Configuration != null) { Configuration.AlgorithmParameters = parametersjson; } Console.WriteLine($"相机 {CameraId} 算法参数已更新"); } } #endregion #region 私有方法 /// /// 设置图像处理算法 /// /// 算法名称 private void SetProcessingAlgorithm(string algorithmName, string parameters = null) { if (string.IsNullOrEmpty(algorithmName)) { ImageProcessor = null; return; } try { // 根据算法名称创建实例(这里可以使用简单的工厂方法或反射) ImageProcessor = ImageProcessingAlgorithmHikVisionFactory.CreateAlgorithm(algorithmName); // 配置参数 if (parameters != null && ImageProcessor != null) { ImageProcessor.Configure(parameters); } Console.WriteLine($"相机 {CameraId} 成功设置算法: {algorithmName}"); } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 设置算法 {algorithmName} 失败: {ex.Message}"); } } /// /// 根据配置动态加载算法 /// private void LoadAlgorithmFromConfiguration() { if (Configuration != null && !string.IsNullOrEmpty(Configuration.ProcessingAlgorithmName)) { SetProcessingAlgorithm(Configuration.ProcessingAlgorithmName, Configuration.AlgorithmParameters); } else { // 使用默认算法(无参数) SetProcessingAlgorithm("Default"); } } /// /// 加载相机配置 /// private void LoadCameraConfiguration() { if(Configuration != null&& !string.IsNullOrEmpty(Configuration.CameraParameters)) { } } /// /// 发送处理结果到通信线程的方法 /// 多个线程可以共用此方法 /// /// 处理结果数据 private void SendProcessResult(object resultData) { var eventArgs = new CameraProcessEventArgsResultModel { CameraId = this.CameraId, ResultData = resultData, Timestamp = DateTime.Now }; // 触发事件,通知订阅者有新的处理结果 ProcessResultAvailable?.Invoke(this, eventArgs); } /// /// 图像处理实现 /// private void ProcessImage(IImage imageData, int cameraId) { object resultData = null; try { // 优先使用接口方式的算法 if (ImageProcessor != null && imageData!=null) { resultData = ImageProcessor.ProcessImage(imageData, cameraId); } // 如果都没有设置,则使用默认处理 else { Console.WriteLine($"相机 {CameraId} 未加载算法"); // 默认处理逻辑 } if(resultData!=null) { // 发送处理结果 SendProcessResult(resultData); } // 输出处理结果 } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 图像处理错误: {ex.Message}"); // 发送错误结果 } } #endregion #region 线程主体方法 /// /// 相机采集循环 /// private async void CameraCaptureLoop(CancellationToken token) { //int frameCount = 0; try { while (!token.IsCancellationRequested) { // 模拟相机图像采集(无休眠,持续采集) if (Camera.GetOnceImage(out IFrameOut imageData)) { // 将图像放入队列 ImageQueue.Enqueue(imageData.Image.Clone() as IImage); double QuTuYanshi = (DateTime.Now - TimeStampTools.FromUnixTimestamp((long)imageData.HostTimeStamp)).TotalMilliseconds; //Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}"); if(QuTuYanshi > 12) { Console.WriteLine($"识别落后时间{QuTuYanshi}"); } QueueSemaphore.Release(); // 通知处理线程有新数据 imageData.Dispose(); } // 这里不添加休眠,保持最大帧率采集 await Task.Delay(CameraSleepTime); } } catch (OperationCanceledException) { // 线程被取消,正常退出 } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 采集异常: {ex.Message}"); } } /// /// 相机回调取图 /// 与循环取图二选一 /// /// /// private void CameraCaptureLoop(object sender, FrameGrabbedEventArgs e) { try { // 将图像放入队列 ImageQueue.Enqueue(e.FrameOut.Image.Clone() as IImage); double QuTuYanshi = (DateTime.Now - TimeStampTools.FromUnixTimestamp((long)e.FrameOut.HostTimeStamp)).TotalMilliseconds; //Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}"); if (QuTuYanshi > 12) { Console.WriteLine($"识别落后时间{QuTuYanshi}"); } QueueSemaphore.Release(); // 通知处理线程有新数据 e.FrameOut.Dispose(); } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 采集异常: {ex.Message}"); } } /// /// 相机回调取图 /// 更换了存储图像的队列 /// /// /// private void CameraCaptureLoop2(object sender, FrameGrabbedEventArgs e) { try { // 将图像放入队列 ImageRingBuffer.TryEnqueue(e.FrameOut.Image.Clone() as IImage); double QuTuYanshi = (DateTime.Now - TimeStampTools.FromUnixTimestamp((long)e.FrameOut.HostTimeStamp)).TotalMilliseconds; //Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}"); if (QuTuYanshi > 12) { Console.WriteLine($"识别落后时间{QuTuYanshi}"); } if (lastframeNum == -1) { lastframeNum = e.FrameOut.FrameNum; } else if (lastframeNum == e.FrameOut.FrameNum - 1) { lastframeNum = e.FrameOut.FrameNum; } else { //丢帧记录 TxtLog.log(string.Format("lost frame: Width[{0}] , Height[{1}] , FrameNum[{2}] ,Frevous[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum, lastframeNum), 6); Console.WriteLine("lost frame: Width[{0}] , Height[{1}] , FrameNum[{2}] ,Frevous[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum, lastframeNum); lastframeNum = e.FrameOut.FrameNum; } QueueSemaphore.Release(); // 通知处理线程有新数据 e.FrameOut.Dispose(); } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 采集异常: {ex.Message}"); } } /// /// 图像处理循环 /// 适用取图频率不高的处理,或者处理速度远快于取图速度的方法 /// private async void ImageProcessingLoop(CancellationToken token) { try { while (!token.IsCancellationRequested) { // 等待图像数据 await QueueSemaphore.WaitAsync(token); // 从队列获取图像 if (ImageQueue.TryDequeue(out IImage imageData)) { // 执行图像处理逻辑 ProcessImage(imageData, CameraId); imageData.Dispose(); // 可以根据队列长度决定是否短暂休眠以避免过度占用CPU if (ImageQueue.Count == 0) { await Task.Delay(1, token); // 短暂让出CPU } } } } catch (OperationCanceledException) { // 线程被取消,正常退出 } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 处理异常: {ex.Message}"); } } /// /// 图像处理循环-引入批处理 /// 处理速度与取图速度的差不多的时候 /// /// private async void ImageProcessingbatchLoop(CancellationToken token) { const int batchSize = 5; // 每批处理的最大图像数量 var batch = new List(batchSize); // 存储当前批次的图像 try { while (!token.IsCancellationRequested) { // 等待至少一张图像数据 await QueueSemaphore.WaitAsync(token); // 收集一批图像(严格按队列顺序) while (batch.Count < batchSize && ImageQueue.TryDequeue(out IImage imageData)) { batch.Add(imageData); if (ImageQueue.Count > 0) { QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程 } } // 顺序处理当前批次的所有图像 foreach (var image in batch) { ProcessImage(image, CameraId); // 严格按顺序执行处理逻辑 image.Dispose(); // 处理完成后立即释放资源 } // 清空当前批次,准备下一轮处理 batch.Clear(); // 如果队列为空,短暂休眠以避免过度占用CPU if (ImageQueue.Count == 0) { await Task.Delay(1, token); // 固定短时间休眠 } } } catch (OperationCanceledException) { // 线程被取消,正常退出 } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 处理异常: {ex.Message}"); } } private async void ImageProcessingbatchLoop2(CancellationToken token) { const int batchSize = 5; // 每批处理的最大图像数量 var batch = new List(batchSize); // 存储当前批次的图像 try { while (!token.IsCancellationRequested) { // 等待至少一张图像数据 await QueueSemaphore.WaitAsync(token); // 收集一批图像(严格按队列顺序) while (batch.Count < batchSize && ImageRingBuffer.TryDequeue(out IImage imageData)) { if (imageData == null) continue; batch.Add(imageData); if (ImageRingBuffer.Count > 0) { QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程 } } // 顺序处理当前批次的所有图像 foreach (var image in batch) { ProcessImage(image, CameraId); // 严格按顺序执行处理逻辑 image.Dispose(); // 处理完成后立即释放资源 } // 清空当前批次,准备下一轮处理 batch.Clear(); // 如果队列为空,短暂休眠以避免过度占用CPU if (ImageRingBuffer.Count == 0) { await Task.Delay(1, token); // 固定短时间休眠 } } } catch (OperationCanceledException) { // 线程被取消,正常退出 } catch (Exception ex) { Console.WriteLine($"相机 {CameraId} 处理异常: {ex.Message}"); } } #endregion } }