// CameraGroup.cs 相机组线程管理类,控制相机采集和识别的线程运行,开放有算法的接口 using MvCameraControl; using MvvmScaffoldFrame48.DLL.CameraTools; 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; //相机 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; } //图像队列 public ConcurrentQueue ImageQueue { get; set; } //信号量 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.FrameGrabbedEvent += CameraCaptureLoop; //Camera.StartReceiveFuntion(); Camera.StartReceiveFuntionSetFrameGrabedEvent(); // 重置取消令牌 CancellationTokenSource = new CancellationTokenSource(); var token = CancellationTokenSource.Token; // 启动相机采集线程 //CameraTask = Task.Factory.StartNew(() => CameraCaptureLoop(token), // token, TaskCreationOptions.LongRunning, TaskScheduler.Default); // 启动图像处理线程 ProcessingTask = Task.Factory.StartNew(() => ImageProcessingbatchLoop(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) { 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 - 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 - 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 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}"); } } #endregion public DateTime FromUnixTimestamp(long timestamp, bool isMilliseconds = false) { try { // 如果未指定单位,尝试自动检测 if (!isMilliseconds) { // 如果时间戳看起来像毫秒级(13位数字) if (timestamp.ToString().Length > 10) { isMilliseconds = true; } } if (isMilliseconds) { // 验证毫秒级时间戳范围 const long minMilliTimestamp = -62135596800000; // 0001-01-01 00:00:00 UTC const long maxMilliTimestamp = 253402300799999; // 9999-12-31 23:59:59 UTC if (timestamp < minMilliTimestamp || timestamp > maxMilliTimestamp) { throw new ArgumentOutOfRangeException(nameof(timestamp), "毫秒级时间戳超出有效范围"); } DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp); return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local); } else { // 验证秒级时间戳范围 const long minTimestamp = -62135596800; const long maxTimestamp = 253402300799; if (timestamp < minTimestamp || timestamp > maxTimestamp) { throw new ArgumentOutOfRangeException(nameof(timestamp), "秒级时间戳超出有效范围"); } DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local); } } catch (ArgumentOutOfRangeException) { throw; } catch (Exception ex) { throw new ArgumentException($"无法转换时间戳 {timestamp}: {ex.Message}", ex); } } } }